src/CoreBundledCompiler.ps1

# PowerShell SDK 打包后端(Build.Core.Backend='Bundled')。
# 与 Shared 后端「从目标机 $PSHOME 解析 SMA」不同,这里把 PowerShell 以 NuGet 包 Microsoft.PowerShell.SDK
# 直接编进产物,因此目标机无需安装 pwsh,且可以 SelfContained / Trimmed / ReadyToRun / InvariantGlobalization / AOT。
# 产物显著更大(几十 MB 起步)。常量脚本不依赖 PowerShell,仍用 constexpr.cs 且不引用 SDK,只是套用发布属性。
#
# 由 CoreCompiler.ps1 点源调用(那里已备好 $dotnet 与工程辅助),调用方提供:$tfm / $rid / $assemblyName /
# $isConst / $programFrame / $outputFile / $TempDir / $resourceElements / $versionElements / $iconElement /
# $defineConstants / $debugType / $prepareDebug / $noConsole / $conHost / $coreTargetFramework /
# $powerShellVersion / $selfContained / $trimmed / $trimMode / $readyToRun / $invariantGlobalization /
# $aot / $singleFile。

# PowerShell 版本 → 最低 .NET TFM(只按 major.minor 匹配)。
$psDotnetMap = @{
    '7.2' = 'net6.0'
    '7.3' = 'net7.0'
    '7.4' = 'net8.0'
    '7.5' = 'net9.0'
    '7.6' = 'net10.0'
    '7.7' = 'net11.0'
}

$psSdkVersion = if ($powerShellVersion) { $powerShellVersion } else { $PSVersionTable.PSVersion.ToString() }
$psVersionObject = $null
try { $psVersionObject = [version]$psSdkVersion } catch {}
if (-not $psVersionObject) {
    Write-I18n Error InvalidCorePowerShellVersion $psSdkVersion -Category InvalidArgument
    throw 'ps12exe:core-bundled-bad-psver'
}
$psKey = "$($psVersionObject.Major).$($psVersionObject.Minor)"
$mappedTfm = $psDotnetMap[$psKey]
if (-not $mappedTfm) {
    $runtimeVersion = [System.Environment]::Version
    $mappedTfm = "net$($runtimeVersion.Major).$($runtimeVersion.Minor)"
    Write-I18n Warning CoreVersionNoMapping $psSdkVersion $mappedTfm
}
$tfmBase = if ($coreTargetFramework) { $coreTargetFramework } else { $mappedTfm }
$tfmBaseMajor = if ($tfmBase -match '^net(\d+)\.') { [int]$Matches[1] } else { $null }
$mappedMajor = if ($mappedTfm -match '^net(\d+)\.') { [int]$Matches[1] } else { $null }
if ($tfmBaseMajor -and $mappedMajor -and $tfmBaseMajor -lt $mappedMajor) {
    Write-I18n Error CoreTargetFrameworkTooLow $tfmBase $mappedTfm -Category InvalidArgument
    throw 'ps12exe:core-bundled-tfm-too-low'
}
$tfm = $tfmBase

# GUI 框架检测:console 应用默认不引用 WindowsDesktop 框架,脚本用到 WinForms/WPF 时按需打开(PS2EXE.Core #6),
# 并据此让 TFM 带 -windows。常量脚本只用预定义类型,不可能用到 GUI。
$guiUsage = if ($isConst) { @{ WinForms = $false; Wpf = $false } } else { Get-GuiFrameworkUsage $Content }
$useWinForms = $guiUsage.WinForms -and $rid -like 'win-*'
$useWpf = $guiUsage.Wpf -and $rid -like 'win-*'
if (($noConsole -or $useWinForms -or $useWpf) -and $tfm -notmatch '-windows$') { $tfm += '-windows' }

$bundleOutputType = if ($noConsole -or ($conHost -and -not $isConst)) { 'WinExe' } else { 'Exe' }
$bundleWinForms = ''
if ($noConsole -or $useWinForms) { $bundleWinForms += '<UseWindowsForms>true</UseWindowsForms>' }
if ($useWpf) { $bundleWinForms += '<UseWPF>true</UseWPF>' }
# 非常量帧用 CoreHost 定义选择 Environment.ProcessPath(单文件发布下 Assembly.Location 为空)。
$bundleConstants = if ($isConst) { $defineConstants } else { (($coreConstants + 'CoreHost') | Sort-Object -Unique) -join ';' }

$selfContainedStr = $selfContained.ToString().ToLowerInvariant()
$singleFileStr = $singleFile.ToString().ToLowerInvariant()
$trimmedStr = $trimmed.ToString().ToLowerInvariant()
$r2rStr = $readyToRun.ToString().ToLowerInvariant()
$invariantStr = $invariantGlobalization.ToString().ToLowerInvariant()
$aotStr = $aot.ToString().ToLowerInvariant()

$bundleTrimItems = ''
if ($trimmed) {
    # TrimMode 只在启用裁剪时才写入;AOT 会隐式裁剪。
    $trimModeElement = "<TrimMode>$trimMode</TrimMode>"
    $bundleTrimItems = @"
        <TrimmerRootAssembly Include="System.Management.Automation" />
        <TrimmerRootAssembly Include="Microsoft.PowerShell.Commands.Diagnostics" />
        <TrimmerRootAssembly Include="Microsoft.PowerShell.Commands.Management" />
        <TrimmerRootAssembly Include="Microsoft.PowerShell.Commands.Utility" />
        <TrimmerRootAssembly Include="Microsoft.PowerShell.ConsoleHost" />
        <TrimmerRootAssembly Include="Microsoft.PowerShell.Security" />
        <TrimmerRootAssembly Include="Microsoft.WSMan.Management" />
"@

}
else { $trimModeElement = '' }
$bundleAotItems = ''
if ($aot -and $tfmBase -match '^net(\d+)\.' -and [int]$Matches[1] -ge 8) {
    $bundleAotItems = ' <PackageReference Include="System.Formats.Nrbf" Version="10.*" />'
}
$bundleSdkReference = if ($isConst) { '' } else { " <PackageReference Include=`"Microsoft.PowerShell.SDK`" Version=`"$psSdkVersion`" />" }
$bundleScriptItem = if ($isConst) { '' } else { ' <EmbeddedResource Include="main.ps1" LogicalName="main.ps1" />' }

# ---------- 工程缓存 ----------
$smaVersion = if ($isConst) { 'const' } else { $psSdkVersion }
$bundleInvariant = @(
    "tfm=$tfm", "tfmBase=$tfmBase", "rid=$rid", "outputType=$bundleOutputType", "debugType=$debugType"
    "assemblyName=$assemblyName", "winForms=$bundleWinForms", "icon=$iconElement"
    "resources=$resourceElements", "version=$versionElements"
    "define=$bundleConstants", "isConst=$isConst", "sdk=$smaVersion"
    "selfContained=$selfContainedStr", "singleFile=$singleFileStr", "trimmed=$trimmedStr"
    "trimMode=$trimMode", "r2r=$r2rStr", "invariant=$invariantStr", "aot=$aotStr"
)
$bundleBuildKey = Get-CoreBuildKey $bundleInvariant

$bundleProject = Enter-CoreProject -CacheTag 'corebundle' -BuildKey $bundleBuildKey -TempDir $TempDir
$projectDir = $bundleProject.ProjectDir

$env:DOTNET_CLI_TELEMETRY_OPTOUT = '1'
$env:DOTNET_NOLOGO = '1'

$publishDir = Join-Path $projectDir 'publish'

try {
    [System.IO.File]::WriteAllText((Join-Path $projectDir 'frame.cs'), $programFrame, [System.Text.UTF8Encoding]::new($false))
    if (-not $isConst) {
        Copy-Item -LiteralPath (Join-Path $TempDir 'main.ps1') -Destination (Join-Path $projectDir 'main.ps1') -Force
    }

    # TrimMode 与裁剪根项一起写入(裁剪器根项必须保留,故并入同一个条件块)。
    $bundleCsproj = @"
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>$bundleOutputType</OutputType>
        <TargetFramework>$tfm</TargetFramework>
        <RuntimeIdentifier>$rid</RuntimeIdentifier>
        <SelfContained>$selfContainedStr</SelfContained>
        <PublishSingleFile>$singleFileStr</PublishSingleFile>
        <IncludeNativeLibrariesForSelfExtract>$singleFileStr</IncludeNativeLibrariesForSelfExtract>
        <IncludeAllContentForSelfExtract>$singleFileStr</IncludeAllContentForSelfExtract>
        <PublishTrimmed>$trimmedStr</PublishTrimmed>
        $trimModeElement
        <PublishReadyToRun>$r2rStr</PublishReadyToRun>
        <InvariantGlobalization>$invariantStr</InvariantGlobalization>
        <PublishAot>$aotStr</PublishAot>
        <NoWarn>`$(NoWarn);CA1416;IL3000;IL2026;IL3050;CS8073</NoWarn>
        <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
        <AssemblyName>$assemblyName</AssemblyName>
        <Nullable>disable</Nullable>
        <ImplicitUsings>disable</ImplicitUsings>
        <DebugType>$debugType</DebugType>
        <GenerateDocumentationFile>false</GenerateDocumentationFile>
        <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
        <EnableWindowsTargeting>true</EnableWindowsTargeting>
        <DefineConstants>$([System.Security.SecurityElement]::Escape($bundleConstants))</DefineConstants>
        $bundleWinForms
        $iconElement
        $resourceElements
        $versionElements
    </PropertyGroup>
    <ItemGroup>
$bundleSdkReference
$bundleAotItems
$bundleTrimItems
    </ItemGroup>
    <ItemGroup>
        <Compile Include="frame.cs" />
$bundleScriptItem
    </ItemGroup>
</Project>
"@

    [System.IO.File]::WriteAllText((Join-Path $projectDir 'bundle.csproj'), $bundleCsproj, [System.Text.UTF8Encoding]::new($false))

    Write-I18n Host CoreCompilePublishing
    Write-Debug "Core bundled compiler: dotnet publish (tfm=$tfm, rid=$rid, sdk=$psSdkVersion, selfContained=$selfContainedStr, aot=$aotStr)"
    Remove-Item -LiteralPath $publishDir -Recurse -Force -ErrorAction Ignore
    $null = Invoke-CoreDotnet -DotnetArgs @('publish', (Join-Path $projectDir 'bundle.csproj'), '-c', 'Release', '-o', $publishDir) -AssetsPath (Join-Path $projectDir 'obj/project.assets.json') -DotnetPath $dotnet.Source

    Copy-CorePublishOutput -PublishDir $publishDir -AssemblyName $assemblyName -OutputFile $outputFile -SingleFile $singleFile -PrepareDebug $prepareDebug
}
finally {
    Exit-CoreProject $bundleProject
}