Can the Powershell "using" statement with dynamic module paths? - powershell

I have some powershell modules which contain classes I need to instantiate from various powershell scripts. To access the classes from the powershell scripts I am have statements like:
using module "..\..\Library\Mymodule.psm1"
But I dont know ahead of time where in the folder hierarchy my library will be relative to the script.
We dont want to use the standard powershell modules folders because these classes are under source control and deploying to user's folders would be nightmarish.
This is an extremely hard topic to google because, well "using" is used everywhere!
Tried provide multiple possible locations for the same file but of course we get an error for the alternate locations that do not exist:
The specified module 'C:...Mymodule.psm1' was not loaded because no valid module file was found in any module directory.
At line:0 char:0
using module "..\..\Library\Mymodule.psm1"
using module "..\Library\Mymodule.psm1"
using module ".\Library\Mymodule.psm1"
But I'd rather run a function to first determine the correct module path, and then use something like this
using module "$foundModulePath"
Is there any way to dynamically set the path of a module and then "using" it?

I would version my modules in a separate repository and write a function the clones that repository inside the workspace at runtime.
Using tags in that repo - would allow me to control the version of the modules I am loading for each script.
This will make the development of the modules possible, even when a script still has to use the old versions and I don't want to "invest" time in re-writing it , just because I've changed a dependency.
idea no 2.
Gradle is very good in handling messy dependencies lists in big projects.
You can build a gradle script that prepares the workspace and then, from inside of it, execute your powershell script.
See Link here

Related

Call a chocolatey package in powershell based on its package name

I recently set up a new machine and installed/enabled chocolatey. As far as I can remember I was able to call a package via powershell based on the package name. For instance, if I wanted to install mongodb, I used to type choco install mongodb - and was able to call the mongo client by simply typing mongo in the powershell console. Is there a way to see if something is bound to a specific shim ? or is there an option to enable it?
I don't think there is a way to match packages with shims, but you can check the executable a shim points to, along with general information about it and what would happen if you run the shim:
shimname.exe --shimgen-noop
I tried crafting a command to check all the shims in the $env:ChocolateyInstall\bin directory, but there's no guarantee that executables there are going to be a shim. I tried filtering out the known Chocolatey executables as well, but some packages (like putty) drop their real executables right in the bin folder, and won't respond to the shim parameters like you'd expect.
Looking at the Install-BinFile cmdlet, it doesn't look like Chocolatey provides a way to track shims at all as it doesn't even do this itself. I think it uses the same logic to track automatically generated shims at package uninstall time, but any shims explicitly created with Install-BinFile also need to have Uninstall-BinFile called in the associated chocolateyUninstall.ps1 script or the shim won't be removed at package uninstall time.
Short of crawling the $env:ChocolateyInstall\lib\packageName directory for potential automatic shim names, or the chocolateyInstall.ps1/chocolateyUninstall.ps1 scripts for explicit shims, you're not going to be able to match a shim to a package.

PowerShell Module Deployment Duplication

I am using Azure DevOps to deploy PowerShell modules to a server. This release task deploys the modules to the directory C:\Windows\System32\WindowsPowerShell\v1.0\Modules\. I am able to use the modules once they are deployed to this folder successfully.
If I modify one of the modules and re-release it the file in C:\Windows\System32\WindowsPowerShell\v1.0\Modules\ gets updated, however the old version of the module is still used when running from a batch file using pwsh.
I discovered that the module file also exists in the following paths:
C:\Program Files\PowerShell\Modules\
C:\Program Files\PowerShell\6\Modules\
When deploying the new version using Azure DevOps the old version in the above two directories are not updated. Manually updating the module in those locations fixes the problem.
Why is the module file being copied into those two additional paths?
Should those copies be overwritten when a new version of the module is deployed?
What is the correct way of deploying a module in this scenario?
Powershell uses different paths to load modules. Use $env:PSModulePath -split ";" to know which are the paths being used.
The difference between each path is user scope and usage scope (e.g. made for custom modules or windows official modules).
Now, by default, PS looks for the latest version of each module across all the paths. So maybe the old version is being run because at the time you re-deploy. You are not updating the module version in the Module Manifest, so if PS see they are the "same" version it gets the last one loaded on the PSModulePath.
Take a look at this awesome post for more details: Everything you wanted to know about PowerShell's Module Path
Now to your questions.
Why is the module file being copied into those two additional paths?
This could be a server configuration or the script that you are using to deploy.
Should those copies be overwritten when a new version of the module is deployed?
Not necessarily, if the versions are maintained correctly. On the post shared says how to check the versions of each module.

Proper installation of powershell modules for all users

I've looked at this document, and it lists $Env:ProgramFiles\WindowsPowerShell\Modules (%ProgramFiles%\WindowsPowerShell\Modules) as a good place for installing modules for all users. About naming, it says:
Use the Correct Module Directory Name
A "well-formed" module is a module that is stored in a directory that
has the same name as the base name of at least one file in the module
directory. If a module is not well-formed, Windows PowerShell does not
recognize it as a module.
The "base name" of a file is the name without the file name extension.
In a well-formed module, the name of the directory that contains the
module files must match the base name of at least one file in the
module.
For example, in the sample Fabrikam module, the directory that
contains the module files is named "Fabrikam" and at least one file
has the "Fabrikam" base name. In this case, both Fabrikam.psd1 and
Fabrikam.dll have the "Fabrikam" base name.
C:\Program Files Fabrikam Technologies
Fabrikam Manager
Modules
Fabrikam
Fabrikam.psd1 (module manifest)
Fabrikam.dll (module assembly)
However, I'm still unclear; suppose I have a module composed ABC.psd1 and ABC.psm1. Am I understanding correctly that it should be installed in $Env:ProgramFiles\WindowsPowerShell\Modules\ABC, with the .psd1 and .psm1 directly under that directory?
Looking at some of the microsoft modules, I see, for example:
C:\Program Files
WindowsPowerShell
Modules
AzureRM
5.7.0
AzureRM[.psd1,psm1]
So at least based on the referenced document, it breaks the rule of "well-formed module" since the statement does not seem to allow for that 5.7.0 directory, or does not talk about a recursive search. Is that a hierarchy specifically intended for versioning, is it of the developer's choice? Even for versioning, it doesn't match the recommendations in the above document, for "Using Multiple Versions of a Module" which suggest it is added to the directory name, not as a subdirectory.
The gist of my question is this:
Is there a better explanation on how powershell searches for modules in this directory, with respect to nesting?
Suppose as my product grows, I have a second module DEF.psd1. Should I group under a company or product folder, and will all versions of powershell be happy with this (that is, without me altering the PSModulePath).
e.g.:
C:\Program Files
WindowsPowerShell
Modules
My Company
ABC
ABC[.psd1,psm1]
DEF
DEF[.psd1,psm1]
I am looking to support Powershell 3.0 and later. Also, I am noticing that the powershell ISE module browser uses different rules for searching modules, than powershell itself. For example, using my proposed hierarchy with a company name works fine in powershell, ISE, and user scripts, but breaks the ISE module browser which gives a "cannot find module" error when clicking on "Show details".
Looks like you pretty much have this answered. Not sure why/how the AzureRM module has a versioning folder...but the module was created by Microsoft so maybe they know a loop hole.
In my experience and in the documentation I've read, the proper file structure is what you have in your example, except WITHOUT the 'My Company' folder. How you named the folders and the .psm1/psd1 files (with the same base name) is correct. In fact, for a minimal script based module, all you need is the .psm1 file and it will work (load whatever functions are in the file), although generating a module file with New-ModuleManifest doesn't hurt. As mentioned in the comments, versioning can then be tracked and recognized by PowerShell in the .psd1 file.
Now, I've never put a module in the ProgramFiles folder for all users but I imagine it uses the same rules as the user based folders (they are just paths in an enviroment variable that powershell looks at).
I've also used the link below as a method or template for building PowerShell modules that are easy to maintain:
http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/
You can ignore the parts you aren't interested in or not using (folders for tests or views or using a Git repo)...the base structure works as well as the technique for loading individual functions with the .psm1 file.

cmake which package name to pass to find_package

I am trying to link against the libconfig++ library using cmake. I installed the library
using apt-get so I am assuming it will have a .cmake file so I can use find_package. Problem is I don't know what package name to use. I tried libconfig, config, config++ as the package name to no avail.
As a general question, how does one find out which package is associated with a library.
I know that find_package looks into CMAKE_MODULE_PATH to see if there is a .cmake script. How to I find out what is the value of CMAKE_MODULE_PATH on my system. It's not an environment variable. I am running ubuntu 12.04.
Any help is appreciated.
To use find_package you need to have corresponding Find or Config cmake file. But library may not to provide it, seems with your library is such a case. You can use find_library for finding libraries and find_path to find include directories. With these commands you can even write FindXXX.cmake yourself.
CMAKE_MODULE_PATH is not an environment variable, it is CMake's one. This variable is intended for you to set, if you have additional directories with modules, by default it's empty. This is used in the "Module" mode. In this mode CMake searches FindXXX.cmake in the CMAKE_MODULE_PATH (your modules) or in modules shipped with CMake and if it's found, it then used to find library and it's headers.
If that module wasn't found, it then switches into "Config" mode. On Unix it searches for ConfigXXX.cmake in the following directories:
<prefix>/(lib/<arch>|lib|share)/cmake/<name>*/
<prefix>/(lib/<arch>|lib|share)/<name>*/
<prefix>/(lib/<arch>|lib|share)/<name>*/(cmake|CMake)/
This files is shipped with the library, so there is no need to find anything, they contain all information, where library and includes located, etc.
About naming scheme, there is no standard one. You can look at Standard CMake modules. Modules found in internet for your library named FindLibConfig.cmake
For your case, library ships without corresponding cmake file, so you should write it your self (or find already written) and add directory with that file to CMAKE_MODULE_PATH.
I suggest you to read how find_package command works and how to write FindXXX.cmake files.

How can I force Perl to run the module from the test directory only?

I have perl module file and test script pl which I use to run some function, and after browsing through the use lib and this previous question here...
I still don't know how to do this.
Basically I have a production directory which contains the module file, and I have a test directory file which contains the same module and the test script file. each time I try to run the script file, it will automatically calls the module in the directory. by printing out the #INC, it seems that the production directory is hosted in there. thus I try to remove it by using
no lib qw(prod_dir);
and while printing out the #INC shows that the directory is no longer there, somehow the script is still calling that other module...
I know this probably sounds really dumb, but hope someone can help me see the light here :)
thanks.
After you have required or used the module, check %INC to see where it came from.
For example:
use Data::Dumper;
print $INC{'Data/Dumper.pm'}."\n";
Note that "::" becomes "/" and you append ".pm". That might give you a clue.
Remember that the current directory (".") is usually an entry in #INC. But the first step is finding out what directory the module was loaded from.
Another thing to remember is that the directories in #INC are searched in order. use lib prepends to that list (making it the first-searched directory), so you may just need to add the appropriate directory.
Can you say more about what you are trying to do and how you are trying to do it? Is this stuff in a standard Perl distribution structure? If you aren't using the standard distribution structure, can you show us a directory listing so we know where things are? Can you also include the code you use to try to load the module? Just update your original question when you pull together the details.
Typically, I run tests through the build runner, which automatically sets up the right #INC.
If I want to run one test in my distribution, I use the blib module to find the build library which has the development versions of my modules:
% perl -Mblib t/test.t
Some people do the same thing with prove.
If you aren't using the basic distribution set-up, consider using it. The tools and best techniques rely on it.
If you just have your module and test file in the same directory, have you tried adding the current directory to #INC with PERL5LIB?