psake / PowerShellBuild

Common build tasks for psake and Invoke-Build that build and test PowerShell modules
MIT License
133 stars 24 forks source link

$PSBPreference.Build.CompileModule = $true seems problematic #43

Closed neopsyon closed 4 years ago

neopsyon commented 4 years ago

Expected Behavior

I am using the original module structure built by Stucco(Plaster template). Next to that, I am utilizing PowershellBuild. Once I set the variable $PSBPreference.Build.CompileModule = $true I expect my module files(functions) to be combined in the monolith structure.

Current Behavior

At the moment Powershell is throwing the exception, It seems like that .psm1 file has been created with its original - non-monolith structure, and it is trying to import functions from the public and private folder.

Steps to Reproduce (for bugs)

C:\Users\njovic\Desktop> .\build.ps1 -Task build Task: INIT

Build System Details: Build Module: PowerShellBuild:0.4.0 PowerShell Version: 5.1.18362.752

Environment variables: BHProjectName mymodule BHModulePath C:\Users\njovic\Desktop\mymodule BHPSModulePath C:\Users\njovic\Desktop\mymodule BHBuildOutput C:\Users\njovic\Desktop\Output\mymodule\0.1.0 BHPSModuleManifest C:\Users\njovic\Desktop\mymodule\mymodule.psd1 BHBuildSystem Unknown BHBuildNumber 0 BHProjectPath C:\Users\njovic\Desktop Task: CLEAN

Task: STAGEFILES

Task: GENERATEMARKDOWN

Error: 4/17/2020 9:13:11 AM: At C:\Users\njovic\Desktop\Output\mymodule\0.1.0\mymodule.psm1:2 char:14 + ... public = @(Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -Child ... + ~~~~~~~~~~~~~ [<<==>>] Exception: Cannot find path 'C:\Users\njovic\Desktop\Output\mymodule\0.1.0\Public\' because it does not exist.

How to solve?

In the function Build-PSBuildModule there is a conditional - if ($Compile.IsPresent) , it should contain Clear-Content $rootModule -force , which will wipe the file content - stripping down the classical import process of the module and then append the content of all functions combined.

Your Environment

Stucco version: 0.2.0 PowershellBuild: 0.4.0

JustinGrote commented 4 years ago

In my PowerCD module I approached this a bit different.

If you specify a #region SourceInit block in your module, that block will be excluded from compilation, so that's where I put the code to import my module in normal source mode with subfolders and whatnot. When PowerCD "compiles" the module, it removes anything in the sourceinit block, thus avoiding this problem. If the setting is to not do the compile/consolidation, then it leaves it alone.

The reason for this is sometimes you have things you want to preserve items in your psm1 that is specific to your module, like a special way of loading an assembly, or some special variable initialization. I suppose it could go into yet another separate file and loaded in during compilation but some things like classes have problems with that and I wanted to keep things intuitive.

I'd suggest something similar for PowerShellBuild

pauby commented 4 years ago

@nemanja-jovic If you are overwriting your PSM1 file by 'compiling' all the code into it, then the file should either be empty or not exist. To have code in there doesn't make sense (to me anyway). But belt and braces maybe the code should see if it exists and remove it.

There are four variables that can control the content of your PSM1 file:

$PSBPreference.Build.CompileHeader - String that appears at the top of your compiled PSM1 file
$PSBPreference.Build.CompileFooter - String that appears at the bottom of your compiled PSM1 file
$PSBPreference.Build.CompileScriptHeader - String that appears in your compiled PSM1 file before each added script
$PSBPreference.Build.CompileScriptFooter - String that appears in your compiled PSM1 file after each added script

They allow you to add code at the top and bottom of your PSM1 file and before and after each function.

devblackops commented 4 years ago

What @pauby said. If "compiling" the module, it is assumed that the PSM1 will be created from scratch. You could use the $PSBPreference.Build.CompileHeader or $PSBPreference.Build.CompileFooter properties to have the build process insert extra code into the PSM1 in addition to the contents of the Public/Private directories.

I do like the idea of adding some validation logic to create a warning or error if $PSBPreference.Build.CompileModule is $true AND there is an existing PSM1 file in the source directory with contents in it.

neopsyon commented 4 years ago

@pauby - I absolutely agree, but in this case on the develop branch we have PSM1 file which is sourcing the files from public/private, once we compile it 'making ready for the production', we want to have only our functions code inside, without the sourcing part.

JustinGrote commented 4 years ago

In my build process I put the code for source loading in a #region SourceInit block, that is removed on compilation. That way you can have both!

pauby commented 4 years ago

@nemanja-jovic In your production build script just remove your PSM1 file manually. Or use the solution posted by @JustinGrote ?

I think it might be a good idea to throw the build if the PSM1 file exists though as you said @devblackops ?

neopsyon commented 4 years ago

region Sourceinit block is the acceptable solution.