src/CodeDomCompiler.ps1
|
$type = ('System.Collections.Generic.Dictionary`2') -as "Type" $type = $type.MakeGenericType(@([String], [String]) ) $o = [Activator]::CreateInstance($type) if ($isPwsh20Sma) { $o.Add("CompilerVersion", "v3.5") } else { $o.Add("CompilerVersion", "v4.0") } $cop = (New-Object Microsoft.CSharp.CSharpCodeProvider($o)) [string[]]$BaseCompilerOptions = @($CompilerOptions) $manifestParam = if (($AstAnalyzeResult.IsConst -or $virtualize) -and -not $requireAdmin) { "/nowin32manifest" } elseif ($requireAdmin -or $DPIAware -or $supportOS -or $longPaths) { "`"/win32manifest:$($outputFile+".win32manifest")`"" @" <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> $(if ($DPIAware -or $longPaths) {@" <application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> $(if ($DPIAware) {@" <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> "@})$(if ($longPaths) {@" <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware> "@}) </windowsSettings> </application> "@})$(if ($requireAdmin) {@" <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/> </requestedPrivileges> </security> </trustInfo> "@})$(if ($supportOS) {@" <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> </application> </compatibility> "@}) </assembly> "@ | Set-Content ($outputFile + ".win32manifest") -Encoding UTF8 } [string[]]$CompilerOptions = $BaseCompilerOptions if ($virtualize) { Write-I18n Host ForceX86byVirtualization $architecture = "x86" } $CompilerOptions += "/platform:$architecture" $CompilerOptions += "/target:$( if ($noConsole){'winexe'}else{'exe'})" $CompilerOptions += $manifestParam $configFileForEXE3 = @" <?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> $(if ($winFormsDPIAware) {'<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7"/>'} else {'<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>'}) </startup> $(if ($longPaths) {@' <runtime> <AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false;Switch.System.IO.BlockLongPaths=false"/> </runtime> '@})$( if ($winFormsDPIAware) {@' <System.Windows.Forms.ApplicationConfigurationSection> <add key="DpiAwareness" value="PerMonitorV2"/> </System.Windows.Forms.ApplicationConfigurationSection> '@}) </configuration> "@ if ($iconFile) { $CompilerOptions += "`"/win32icon:$iconFile`"" } $CompilerOptions += "/define:$($Constants -join ';')" function New-PS12ExeCompilerParameters([string]$outFile, [string[]]$opts, [bool]$debug) { $p = New-Object System.CodeDom.Compiler.CompilerParameters($referenceAssembies, $outFile) $p.GenerateInMemory = $FALSE $p.GenerateExecutable = $TRUE $p.IncludeDebugInformation = $debug $p.CompilerOptions = ($opts -ne '') -join ' ' $p.TempFiles = New-Object System.CodeDom.Compiler.TempFileCollection($TempDir) if ($debug) { $p.TempFiles.KeepFiles = $TRUE } Write-Debug "Using Compiler Options: $($p.CompilerOptions)" return $p } # 默认路径:先编出普通托管程序集作为负载,gzip 后塞进一个极小的 launcher 里。 # launcher 启动时在内存中解压并用 Assembly.Load 载入负载,因此负载不会落到磁盘。 # 仅当无法打包时才退化为普通编译(-prepareDebug 需要负载源码/PDB、DllExportList、真实 PS2 SMA)。 # 常量脚本的 constexpr.cs 入口是无参 Main(),与 pack launcher 的 Main(string[]) 调用约定不符,故不走 pack。 $packEnabled = ( -not $prepareDebug -and -not $isPwsh20Sma -and -not $DllExportList -and -not $AstAnalyzeResult.IsConst -and $TempDir ) if ($packEnabled) { $payloadPath = Join-Path $TempDir 'PS12ExePayload.exe' $payloadOptions = @($BaseCompilerOptions) + @( "/platform:$architecture", "/target:exe", "/nowin32manifest", "/define:$($Constants -join ';')" ) $pcp = New-PS12ExeCompilerParameters $payloadPath $payloadOptions $FALSE if (!$AstAnalyzeResult.IsConst) { [VOID]$pcp.EmbeddedResources.Add("$TempDir\main.ps1") } $pcr = $cop.CompileAssemblyFromSource($pcp, $programFrame) if ($pcr.Errors.Count -gt 0) { throw $pcr.Errors -join "`n" } # csc 默认会塞进 manifest/版本信息资源;负载用不到这些,先剥掉再压缩省一点。 $exeSinker = Join-Path $PSScriptRoot 'ExeSinker.ps1' if (Test-Path $exeSinker) { & $exeSinker $payloadPath -removeResources } $gzPath = Join-Path $TempDir 'main' [byte[]]$payloadBytes = [System.IO.File]::ReadAllBytes($payloadPath) $fileStream = [System.IO.File]::Create($gzPath) try { $gzip = New-Object System.IO.Compression.GZipStream($fileStream, [System.IO.Compression.CompressionMode]::Compress) try { $gzip.Write($payloadBytes, 0, $payloadBytes.Length) } finally { $gzip.Dispose() } } finally { $fileStream.Dispose() } # 和 default.cs 一样:编译进 ps12exe.exe 时内嵌 pack.cs,脚本模式从磁盘读取。 #_if PSEXE #_include_as_value launcherSource "$PSScriptRoot/programFrames/pack.cs" #_else [string]$launcherSource = Get-Content $PSScriptRoot/programFrames/pack.cs -Raw -Encoding UTF8 #_endif # 资源参数走 #if + $placeholder 替换,pack.cs 自身保持纯 C#。 $launcherSource = $launcherSource.Replace("`$TargetFramework", $TargetFramework) $resourceParamKeys | ForEach-Object { $launcherSource = $launcherSource.Replace("`$$_", $resourceParams[$_]) } [string[]]$LauncherCompilerOptions = $CompilerOptions if (-not $manifestParam) { # 没有自定义清单需求时,launcher 也不需要默认清单。 $LauncherCompilerOptions += "/nowin32manifest" } $lcp = New-PS12ExeCompilerParameters $outputFile $LauncherCompilerOptions $FALSE [VOID]$lcp.EmbeddedResources.Add($gzPath) $cr = $cop.CompileAssemblyFromSource($lcp, $launcherSource) if ($cr.Errors.Count -gt 0) { throw $cr.Errors -join "`n" } } else { $cp = New-PS12ExeCompilerParameters $outputFile $CompilerOptions $prepareDebug if (!$AstAnalyzeResult.IsConst) { [VOID]$cp.EmbeddedResources.Add("$TempDir\main.ps1") } $cr = $cop.CompileAssemblyFromSource($cp, $programFrame) if ($cr.Errors.Count -gt 0) { throw $cr.Errors -join "`n" } } if ( #_if PSEXE #_!! $AstAnalyzeResult.IsConst -or #_endif $requireAdmin -or $DPIAware -or $supportOS -or $longPaths) { if (Test-Path $($outputFile + ".win32manifest")) { Remove-Item $($outputFile + ".win32manifest") -Verbose:$FALSE } } |