CodeGenerator/Add-UIModule.ps1
function Add-UIModule { #.Synopsis # Generate a Module with commands for creating UI Elements #.Description # Generate a PowerShell Module from one or more assemblies (or types) [CmdletBinding()] param( # The Path to an assembly to generate a UIModule for [Parameter(ParameterSetName='Path', Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [Alias('PSPath')] [string[]] $Path, # The name of a GAC assembly to generate a UI module for [Parameter(ParameterSetName='Assembly', Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [Alias('AN')] [string[]] $AssemblyName, # The full name(s) of one or more types to generate into a UI module [Parameter(ParameterSetName='Type', Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [Type[]] $Type, # A whitelist for types that you want to generate cmdlets for *in addition* to types that pass Select-UIType [Parameter()] [String[]] $TypeNameWhiteList, # A blacklist for types that you do not want to generate cmdlets for even if they pass Select-UIType [Parameter()] [String[]] $TypeNameBlackList, # The name of the module to create (either a simple name, or a full path to the psd1) [string] $Name, # Additional assemblies (assembly names, or full paths) that are required as references for the module [string[]] $RequiredAssemblies, # Generate CSharp Cmdlets instead of script functions [switch] $AsCmdlet, # If set, don't generate the psd1 metadata file [switch] $AssemblyOnly, # Override the default placement of the source code output [string] $SourceCodePath, # Import the module after generating it [switch] $Import, # Output the module info after generating it [switch] $Passthru, # A scriptblock to run whenever the module is imported [ScriptBlock] $On_ImportModule, # A scriptblock to run whenever the module is removed [ScriptBlock] $On_RemoveModule, # The Write-Progress id for nesting with other calls to Write-Progress [Int] $ProgressId = $(Get-Random), # The Write-Progress parent id for nesting with other calls to Write-Progress [Int] $ProgressParentId = -1 ) begin { $typeCounter = 0 $ConstructorCmdletNames = New-Object Collections.Generic.List[String] $resultList = New-Object Collections.Generic.List[String] } process { if ($psCmdlet.ParameterSetName -eq 'Type') { $filteredTypes = $type } else { for($p=0;$p -lt $Path.Count;$p++){ $Path[$p] = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath(($Path[$p])) } $RequiredAssemblies += @($Path) + @($AssemblyName) Write-Progress "Filtering Types" " " -ParentId $ProgressParentId -Id $ProgressId $filteredTypes = Select-UIType -Path @($Path) -AssemblyName @($AssemblyName) -TypeNameWhiteList @($TypeNameWhiteList) -TypeNameBlackList @($TypeNameBlackList) } $ofs = [Environment]::NewLine $count = @($filteredTypes).Count foreach ($type in $filteredTypes) { if (-not $type) { continue } if (-not $type.Fullname -and $type[0].fullName) { $t = $type[0] } else { $t = $type } $typeCounter++ if($count -gt 1) { $perc = $typeCounter * 100/ $count Write-Progress "Generating Code" $t.Fullname -PercentComplete $perc -Id $ProgressId -ParentId $ProgressParentId } else { Write-Progress "Generating Code" $t.Fullname -Id $ProgressId -ParentId $ProgressParentId } $typeCode = ConvertFrom-TypeToScriptCmdlet -Type $t -AsScript:(!$AsCmdlet) ` -ConstructorCmdletNames ([ref]$ConstructorCmdletNames) -ErrorAction SilentlyContinue $null = $resultList.Add( "$typeCode" ) } } end { Write-Progress "Code Generation Complete" " " -PercentComplete 100 -Id $ProgressId -ParentId $ProgressParentId $resultList = $resultList | Where-Object { $_ } $ConstructorCmdletNames = $ConstructorCmdletNames | Where-Object { $_ } $code = "$resultList" if ($name.Contains("\")) { # It's definitely a path $semiResolved = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Name) if ($semiResolved -like "*.psd1") { $moduleMetadataPath = $semiResolved } elseif ($semiResolved -like "*.psm1") { $moduleMetadataPath = $semiResolved.Replace(".psm1", ".psd1") } elseif ($semiResolved -like "*.dll") { $AssemblyPath = $SemiResolved $moduleMetadataPath = $semiResolved.replace(".dll",".psd1") } else { $leaf = Split-Path -Path $semiResolved -Leaf $moduleMetadataPath = Join-Path $semiResolved "${leaf}.psd1" } } elseif ($name -like "*.dll") { $moduleMetadataPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($name.replace(".dll",".psd1")) } elseif ($name -like "*.psd1") { $moduleMetadataPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($name) } elseif ($name -like ".psm1" ) { $moduleMetadataPath = $moduleMetadataPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($name.Replace(".psm1",".psd1")) } else { # It's just a name, figure out what the real manifest path will be $moduleMetadataPath = "$env:UserProfile\Documents\WindowsPowerShell\Modules\$Name\$Name.psd1" } $moduleroot = Split-Path $moduleMetadataPath if (-not (Test-Path $moduleroot)) { New-Item -ItemType Directory -Path $moduleRoot | Out-Null } $psm1Path = $moduleMetadataPath.Replace(".psd1", ".psm1") if ($AsCmdlet) { if(!$SourceCodePath) { $SourceCodePath = $moduleMetadataPath.Replace(".psd1","Commands.cs") } Set-Content -LiteralPath $SourceCodePath -Value $Code if(!$AssemblyPath) { $AssemblyPath = $moduleMetadataPath.Replace(".psd1","Commands.dll") } } else { $modulePath = $moduleMetadataPath.Replace(".psd1", ".psm1") } if(!$AssemblyOnly) { # Ok, build the module scaffolding @" @{ ModuleVersion = '1.0' RequiredModules = 'ShowUI' RequiredAssemblies = '$($RequiredAssemblies -Join "','")' ModuleToProcess = '$psm1Path' GUID = '$([GUID]::NewGuid())' $( if($AsCmdlet) { "NestedModules = '$AssemblyPath' CmdletsToExport = " } else { "FunctionsToExport = " } )@('New-$($ConstructorCmdletNames -join ''',''New-' ) ' ) AliasesToExport = @( '$($ConstructorCmdletNames -join ''',''')' ) } "@ | Set-Content -Path $moduleMetadataPath -Encoding Unicode } if(!$AssemblyOnly -or !$AsCmdlet) { " $On_ImportModule $( if(!$AsCmdlet) { $code } if($On_RemoveModule) {" `$myInvocation.MyCommand.ScriptBlock.Module.OnRemove = { $On_RemoveModule } " } foreach($n in $ConstructorCmdletNames) {" Set-Alias -Name $n -Value New-$n " } ) Export-ModuleMember -Cmdlet * -Function * -Alias * " | Set-Content -Path $psm1Path -Encoding Unicode } if ($AsCmdlet) { # if(!$RequiredAssemblies) { # $RequiredAssemblies = # [Reflection.Assembly]::Load("WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"), # [Reflection.Assembly]::Load("PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"), # [Reflection.Assembly]::Load("PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35") # } if($PSVersionTable.CLRVersion -ge "4.0") { $RequiredAssemblies += [Reflection.Assembly]::Load("System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"), [Reflection.Assembly]::Load("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") } $AddTypeParameters = @{ TypeDefinition = $code IgnoreWarnings = $true Language = 'CSharpVersion3' ReferencedAssemblies = Get-AssemblyName -RequiredAssemblies $RequiredAssemblies -ExcludedAssemblies "MSCorLib","System","System.Core" } # If we're running in .Net 4, we shouldn't specify the Language, because it'll use CSharp4 if ($PSVersionTable.ClrVersion.Major -ge 4) { $AddTypeParameters.Remove("Language") } # Check to see if the outputpath can be written to: we don't *have* to save it as a dll $TestPath = "$(Split-Path $AssemblyPath)\test.write" if (Set-Content $TestPath -Value "1" -ErrorAction SilentlyContinue -PassThru) { Remove-Item $TestPath -ErrorAction SilentlyContinue $AddTypeParameters.OutputAssembly = $AssemblyPath } Write-Debug "Type Parameters:`n$($addTypeParameters | Out-String)" Add-Type @addTypeParameters } if($Import) { Import-Module $moduleMetadataPath -Passthru:$Passthru } elseif($Passthru) { Get-Module $Name -ListAvailable } } } |