build-module.ps1
param([Switch]$isolated,[Switch]$test) pushd $PSScriptRoot $ErrorActionPreference = "Stop" if( $PSVersionTable.PSVersion.Major -lt 6 ) { popd write-error "This script requires Core PowerShell (don't worry: generated cmdlets can work in Core PowerShell or Windows Powershell)" } if( -not $isolated ) { # this ensures that we can run the script repeatedly without worrying about locked files/folders write-host -fore green "Spawning in isolated process." $pwsh = [System.Diagnostics.Process]::GetCurrentProcess().Path & $pwsh -command $MyInvocation.MyCommand.Path -isolated if( $lastExitCode -ne 0) { popd return; } if($test) { $mpath = $(( dir ./*.psd1)[0].fullname) $mname = $(( dir ./*.psd1)[0].basename) & $pwsh -noexit -command "function prompt { `$ESC = [char]27 ; Write-host -nonewline -foregroundcolor green ('PS ' + `$(get-location) ) ; Write-Host (' ['+ `$ESC +'[02;46m testing $mname '+ `$ESC +'[0m] >') -nonewline -foregroundcolor white ; write-host -fore white -nonewline '' ; return ' ' } ; ipmo '$mpath' " } else { write-host -fore cyan "To test this module in a new powershell process, run `n" write-host -fore white " & '$([System.Diagnostics.Process]::GetCurrentProcess().Path)' -noexit -command ipmo '$( (dir ./*.psd1)[0].fullname )' " write-host -fore cyan "`nor use -test with this script`n" } popd return } write-host -fore green "Cleaning folders..." @('./exported','./obj', './bin') |% { $shh = remove-item -recurse -ea 0 $_ } if( test-path ./bin ) { popd write-error "Unable to clean binary folder. (a process may have an open handle.)" } write-host -fore green "Compiling private module code" $shh = dotnet publish --configuration Release --output bin if( $lastExitCode -ne 0 ) { # if it fails, let's do it again so the output comes out nicely. dotnet publish --configuration Release --output bin popd write-error "Compilation failed" } @('./bin/Debug','./bin/Release') |% { $shh = remove-item -recurse -ea 0 $_ } $dll = (dir bin\*.private.dll)[0] if( -not (test-path $dll) ) { popd write-error "Unable to find output assembly." } $commands = get-command -module (ipmo $dll -passthru) write-host -fore gray "Private Module loaded ($dll)." # merge scripts into one file $modulename = (dir *.psd1)[0].Name -replace ".psd1","" $scriptmodule = $dll -replace ".private.",".scripts." -replace ".dll",".psm1" $scriptfile = ""; dir -recurse private\*.ps1 |% { $scriptfile = $scriptfile +"`n`n# Included from: $(resolve-path -relative $_)`n" + (get-content -raw $_) } ; Set-Content $scriptmodule -Value $scriptfile if( $scriptfile -ne '' ) { $commands = $commands + (get-command -module (ipmo $scriptmodule -passthru)) write-host -fore gray "Private Scripts Module loaded ($scriptmodule)." } # No commands? if( $commands.length -eq 0 ) { popd write-error "Unable get commands from private module." } $outputs = @{} write-host -fore green "Processing cmdlet variants" $commands |% { $metadata = New-Object System.Management.Automation.CommandMetaData($_) if( $metadata.Name.IndexOf("_") -gt -1 ) { $targetCmdlet = $metadata.Name.split("_")[0]; $variant = $metadata.Name.split("_")[1]; } else { $targetCmdlet = $metadata.Name $variant = "default" } if( -not ($outputs.ContainsKey($targetCmdlet))) { $newCmdlet = @{ cmdlet = New-Object System.Management.Automation.CommandMetaData($metadata) name = $targetCmdlet variants = @{} } # create the new target cmdlet $newCmdlet.cmdlet.Parameters.Clear(); $outputs[$targetCmdlet] = $newCmdlet; } $cmdlet = $outputs[$targetCmdlet] # add the variant $gb = [System.Management.Automation.ProxyCommand]::GetBegin( $metadata ) $ct = $metadata.Parameters.Keys.Count $cmdlet.variants.add( $variant, @{ method = $gb; pcount = $ct; name = $variant} ) # copy parameters across $metadata.Parameters.Keys |% { $name = $_; $p = $metadata.Parameters[$name] if( -not ($cmdlet.cmdlet.parameters.ContainsKey($name) ) ) { # add the parameter to the target $newParam = New-Object System.Management.Automation.ParameterMetadata($p); $newParam.ParameterSets.Clear() $cmdlet.cmdlet.Parameters.add($name, $newParam) } $param = $cmdlet.cmdlet.Parameters[$name]; if( $p.ParameterSets["__AllParameterSets"] ) { $param.ParameterSets.Add( $variant, $p.ParameterSets["__AllParameterSets"]); } } } $shh = mkdir "./exported" write-host -fore green "Generating unified cmdlet proxies" # Now, loop thru and spit out the proxies $outputs.Keys |% { $cmdletname= $_ $each = $outputs[$cmdletname] $cmd = [System.Management.Automation.ProxyCommand]::create($each.cmdlet) if( $each.variants.Count -eq 1 ) { if($cmd -match "GetCommand[^\\]*$" ) { $cmd = $cmd -replace ".InvokeCommand.GetCommand\('",".InvokeCommand.GetCommand('$modulename.scripts\" } $text = $cmd } else { $b = [System.Management.Automation.ProxyCommand]::GetBegin($each.cmdlet) $newBegin = "`n" $newBegin = $newBegin + ' switch ($PsCmdlet.ParameterSetName) { '; $newBegin = $newBegin + "`n" $pc = 100 $each.variants.Keys |% { $name = $_; if ( $each.variants[$name].pcount -lt $pc ) { $pc = $each.variants[$name].pcount $defaultImpl = $each.variants[$name].method $defaultName = $name } $variant = $each.variants[$name].method; if($variant -match "GetCommand[^\\]*$" ) { $variant = $variant -replace ".InvokeCommand.GetCommand\('",".InvokeCommand.GetCommand('$modulename.scripts\" } $t = "`n '$_' {`n" $t = $t + $variant $t = $t + "`n}`n" $newBegin = $newBegin + $t; } # add a default case (choose the first?) $t = "`n default {`n" $t = $t + $defaultImpl $t = $t + "`n}`n" $newBegin = $newBegin + $t; $newBegin = $newBegin + "`n}`n"; $text = $cmd.replace( $b, $newBegin ) $text = $text.replace( "[CmdletBinding()]", "[CmdletBinding(DefaultParameterSetName='$defaultName')]") } $text = "function ${cmdletname} {`n$text`n}`n" $filename = $cmdletname -replace ".*[\\|/]","" -replace '\.ps1$','' set-content "exported/${filename}.ps1" -value $text } popd write-host -fore green "Done." write-host -fore green "-------------------------------" |