From c2675558a028ff576bad7a7c2d97adbc948a2f77 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Fri, 9 Oct 2026 15:40:25 +0800 Subject: [PATCH] build: retarget to .NET 10 and publish GUI/CLI as Native AOT Align the toolset with the organization's other .NET tools (MetahookInstaller): - Pin the .NET 10 SDK via global.json; migrate the solution to .slnx. - Retarget all projects to net10.0 / net10.0-windows10.0.17763.0. - Enable Native AOT (PublishAot + full trimming) for the GUI and CLI, folding the AOT settings into the csproj so every publish gets them. - Upgrade Avalonia 11.3.8 -> 12.1.3 and ReactiveUI.Avalonia 11.3.0 -> 12.1.6 (add System.Reactive 7.0.0, which is no longer transitive). - Adapt to Avalonia 12: TextBox.Watermark -> PlaceholderText, and drop x:CompileBindings="False" on the map ContextMenu so its bindings compile (reflection bindings break under AOT). - Replace the two anonymous-type JsonSerializer calls with source-generated request types (LlmRequestJsonContext); the anonymous-type overloads are not AOT-compatible. JSON output is unchanged. - Drop the redundant System.Text.Encoding.CodePages reference (in-box on .NET 10) and the unused Avalonia.Diagnostics package (removed in Avalonia 12). - Update the build workflow and README for .NET 10 / Native AOT. Co-Authored-By: Claude Code --- .github/actions/build-windows-x64/action.yml | 49 +++++++++-- .gitignore | 2 + BSPLocalizationTools.sln | 84 ------------------- BSPLocalizationTools.slnx | 15 ++++ README.md | 25 ++++++ global.json | 6 ++ .../BSPLocalizationTools.csproj | 22 +++-- src/BSPLocalizationTools/Program.cs | 2 +- .../Views/MainWindow.axaml | 16 ++-- .../BSPLocalizationToolsCLI.csproj | 4 +- .../BSPLocalizationToolsCore.csproj | 3 +- .../LlmRequestJson.cs | 52 ++++++++++++ .../OpenAICompatibleLLMClient.cs | 60 ++++++------- .../TranslationPromptBuilder.cs | 23 ++--- .../BSPLocalizationTools.Tests.csproj | 2 +- 15 files changed, 210 insertions(+), 155 deletions(-) delete mode 100644 BSPLocalizationTools.sln create mode 100644 BSPLocalizationTools.slnx create mode 100644 global.json create mode 100644 src/BSPLocalizationToolsCore/LlmRequestJson.cs diff --git a/.github/actions/build-windows-x64/action.yml b/.github/actions/build-windows-x64/action.yml index d7e8599..e4035a2 100644 --- a/.github/actions/build-windows-x64/action.yml +++ b/.github/actions/build-windows-x64/action.yml @@ -12,23 +12,58 @@ runs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: '8.0.x' + dotnet-version: '10.0.x' - name: Restore shell: cmd - run: dotnet restore BSPLocalizationTools.sln + run: dotnet restore BSPLocalizationTools.slnx - name: Test shell: cmd - run: dotnet test BSPLocalizationTools.sln -c Release --no-restore + run: dotnet test BSPLocalizationTools.slnx -c Release --no-restore - - name: Publish GUI (single file) + - name: Publish GUI (Native AOT) shell: cmd - run: dotnet publish src\BSPLocalizationTools\BSPLocalizationTools.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -o BSPLocalizationTools-Output\GUI + run: dotnet publish src\BSPLocalizationTools\BSPLocalizationTools.csproj -c Release -r win-x64 -o BSPLocalizationTools-Output\GUI - - name: Publish CLI (single file) + - name: Publish CLI (Native AOT) shell: cmd - run: dotnet publish src\BSPLocalizationToolsCLI\BSPLocalizationToolsCLI.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -o BSPLocalizationTools-Output\CLI + run: dotnet publish src\BSPLocalizationToolsCLI\BSPLocalizationToolsCLI.csproj -c Release -r win-x64 -o BSPLocalizationTools-Output\CLI + + - name: Verify publish output + shell: pwsh + run: | + # Native AOT discards PDBs; the release payload never ships them. + Get-ChildItem -LiteralPath 'BSPLocalizationTools-Output' -Filter *.pdb -Recurse | Remove-Item -Force + + $cliDirectory = 'BSPLocalizationTools-Output\CLI' + $cliFiles = @(Get-ChildItem -LiteralPath $cliDirectory -Recurse -File) + if ($cliFiles.Count -ne 1 -or $cliFiles[0].Name -ne 'BSPLocalizationToolsCLI.exe') { + throw 'CLI publish must contain exactly one self-contained executable.' + } + + # Native AOT cannot fold Avalonia's native backends into the executable, and + # satellite resource assemblies are still emitted for localized strings. The + # set is exact: anything else means an unintended payload crept in. + $exeName = 'BSPLocalizationTools.exe' + $nativeDlls = @('av_libglesv2.dll', 'libSkiaSharp.dll', 'libHarfBuzzSharp.dll') + $guiDirectory = 'BSPLocalizationTools-Output\GUI' + $guiFiles = @(Get-ChildItem -LiteralPath $guiDirectory -Recurse -File) + $unexpected = @($guiFiles | Where-Object { + $_.Name -ne $exeName -and $nativeDlls -notcontains $_.Name -and + $_.Name -ne 'BSPLocalizationTools.resources.dll' + }) + if ($unexpected.Count -gt 0) { + throw "GUI publish contains unexpected files: $($unexpected.Name -join ', ')" + } + if (!(Test-Path -LiteralPath (Join-Path $guiDirectory $exeName) -PathType Leaf)) { + throw "GUI publish is missing its executable: $exeName" + } + foreach ($nativeDll in $nativeDlls) { + if (!(Test-Path -LiteralPath (Join-Path $guiDirectory $nativeDll) -PathType Leaf)) { + throw "GUI publish is missing its native backend library: $nativeDll" + } + } - name: Copy README to output shell: cmd diff --git a/.gitignore b/.gitignore index b5c4649..31a4bf4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ .vs/ .env **/.env +/BSPLocalizationTools-Output/ +/build/ # Generated at build time by Microsoft.CodeAnalysis.ResxSourceGenerator **/Resources.Designer.cs diff --git a/BSPLocalizationTools.sln b/BSPLocalizationTools.sln deleted file mode 100644 index 82751cc..0000000 --- a/BSPLocalizationTools.sln +++ /dev/null @@ -1,84 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BSPLocalizationTools", "src\BSPLocalizationTools\BSPLocalizationTools.csproj", "{E465BB12-D220-4171-BEA2-73E63C0A00E1}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BSPLocalizationTools.Tests", "tests\BSPLocalizationTools.Tests\BSPLocalizationTools.Tests.csproj", "{FE47E594-ABEB-49BE-87F4-080B6359C840}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BSPLocalizationToolsCore", "src\BSPLocalizationToolsCore\BSPLocalizationToolsCore.csproj", "{3B0AE2B3-8A62-4548-84B9-518E624D8688}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BSPLocalizationToolsCLI", "src\BSPLocalizationToolsCLI\BSPLocalizationToolsCLI.csproj", "{30F77649-3BAC-4265-81A7-499A334434F2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E465BB12-D220-4171-BEA2-73E63C0A00E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E465BB12-D220-4171-BEA2-73E63C0A00E1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E465BB12-D220-4171-BEA2-73E63C0A00E1}.Debug|x64.ActiveCfg = Debug|Any CPU - {E465BB12-D220-4171-BEA2-73E63C0A00E1}.Debug|x64.Build.0 = Debug|Any CPU - {E465BB12-D220-4171-BEA2-73E63C0A00E1}.Debug|x86.ActiveCfg = Debug|Any CPU - {E465BB12-D220-4171-BEA2-73E63C0A00E1}.Debug|x86.Build.0 = Debug|Any CPU - {E465BB12-D220-4171-BEA2-73E63C0A00E1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E465BB12-D220-4171-BEA2-73E63C0A00E1}.Release|Any CPU.Build.0 = Release|Any CPU - {E465BB12-D220-4171-BEA2-73E63C0A00E1}.Release|x64.ActiveCfg = Release|Any CPU - {E465BB12-D220-4171-BEA2-73E63C0A00E1}.Release|x64.Build.0 = Release|Any CPU - {E465BB12-D220-4171-BEA2-73E63C0A00E1}.Release|x86.ActiveCfg = Release|Any CPU - {E465BB12-D220-4171-BEA2-73E63C0A00E1}.Release|x86.Build.0 = Release|Any CPU - {FE47E594-ABEB-49BE-87F4-080B6359C840}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FE47E594-ABEB-49BE-87F4-080B6359C840}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE47E594-ABEB-49BE-87F4-080B6359C840}.Debug|x64.ActiveCfg = Debug|Any CPU - {FE47E594-ABEB-49BE-87F4-080B6359C840}.Debug|x64.Build.0 = Debug|Any CPU - {FE47E594-ABEB-49BE-87F4-080B6359C840}.Debug|x86.ActiveCfg = Debug|Any CPU - {FE47E594-ABEB-49BE-87F4-080B6359C840}.Debug|x86.Build.0 = Debug|Any CPU - {FE47E594-ABEB-49BE-87F4-080B6359C840}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FE47E594-ABEB-49BE-87F4-080B6359C840}.Release|Any CPU.Build.0 = Release|Any CPU - {FE47E594-ABEB-49BE-87F4-080B6359C840}.Release|x64.ActiveCfg = Release|Any CPU - {FE47E594-ABEB-49BE-87F4-080B6359C840}.Release|x64.Build.0 = Release|Any CPU - {FE47E594-ABEB-49BE-87F4-080B6359C840}.Release|x86.ActiveCfg = Release|Any CPU - {FE47E594-ABEB-49BE-87F4-080B6359C840}.Release|x86.Build.0 = Release|Any CPU - {3B0AE2B3-8A62-4548-84B9-518E624D8688}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3B0AE2B3-8A62-4548-84B9-518E624D8688}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3B0AE2B3-8A62-4548-84B9-518E624D8688}.Debug|x64.ActiveCfg = Debug|Any CPU - {3B0AE2B3-8A62-4548-84B9-518E624D8688}.Debug|x64.Build.0 = Debug|Any CPU - {3B0AE2B3-8A62-4548-84B9-518E624D8688}.Debug|x86.ActiveCfg = Debug|Any CPU - {3B0AE2B3-8A62-4548-84B9-518E624D8688}.Debug|x86.Build.0 = Debug|Any CPU - {3B0AE2B3-8A62-4548-84B9-518E624D8688}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3B0AE2B3-8A62-4548-84B9-518E624D8688}.Release|Any CPU.Build.0 = Release|Any CPU - {3B0AE2B3-8A62-4548-84B9-518E624D8688}.Release|x64.ActiveCfg = Release|Any CPU - {3B0AE2B3-8A62-4548-84B9-518E624D8688}.Release|x64.Build.0 = Release|Any CPU - {3B0AE2B3-8A62-4548-84B9-518E624D8688}.Release|x86.ActiveCfg = Release|Any CPU - {3B0AE2B3-8A62-4548-84B9-518E624D8688}.Release|x86.Build.0 = Release|Any CPU - {30F77649-3BAC-4265-81A7-499A334434F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {30F77649-3BAC-4265-81A7-499A334434F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {30F77649-3BAC-4265-81A7-499A334434F2}.Debug|x64.ActiveCfg = Debug|Any CPU - {30F77649-3BAC-4265-81A7-499A334434F2}.Debug|x64.Build.0 = Debug|Any CPU - {30F77649-3BAC-4265-81A7-499A334434F2}.Debug|x86.ActiveCfg = Debug|Any CPU - {30F77649-3BAC-4265-81A7-499A334434F2}.Debug|x86.Build.0 = Debug|Any CPU - {30F77649-3BAC-4265-81A7-499A334434F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {30F77649-3BAC-4265-81A7-499A334434F2}.Release|Any CPU.Build.0 = Release|Any CPU - {30F77649-3BAC-4265-81A7-499A334434F2}.Release|x64.ActiveCfg = Release|Any CPU - {30F77649-3BAC-4265-81A7-499A334434F2}.Release|x64.Build.0 = Release|Any CPU - {30F77649-3BAC-4265-81A7-499A334434F2}.Release|x86.ActiveCfg = Release|Any CPU - {30F77649-3BAC-4265-81A7-499A334434F2}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {E465BB12-D220-4171-BEA2-73E63C0A00E1} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {FE47E594-ABEB-49BE-87F4-080B6359C840} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {3B0AE2B3-8A62-4548-84B9-518E624D8688} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {30F77649-3BAC-4265-81A7-499A334434F2} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - EndGlobalSection -EndGlobal diff --git a/BSPLocalizationTools.slnx b/BSPLocalizationTools.slnx new file mode 100644 index 0000000..c8bc86e --- /dev/null +++ b/BSPLocalizationTools.slnx @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index d1cf1ca..2a8473c 100644 --- a/README.md +++ b/README.md @@ -63,3 +63,28 @@ The **Settings** tab loads and saves LLM and prompt path settings to `.env`. ## Encodings CSV output is always written as UTF-8 with BOM. + +## Build + +Use Windows x64 with the **.NET 10 SDK**. Publishing the GUI and CLI as Native +AOT additionally requires the Visual Studio 2022 **Desktop development with C++** +workload (MSVC linker) and a Windows SDK. + +From the repository root: + +```powershell +dotnet restore BSPLocalizationTools.slnx +dotnet test BSPLocalizationTools.slnx -c Release +dotnet run --project src\BSPLocalizationTools # Avalonia GUI +dotnet run --project src\BSPLocalizationToolsCLI -- "-bsp=path\to\map.bsp" # CLI +``` + +Both executables are published as self-contained Native AOT programs, so the +target machine does not need the .NET runtime. Native AOT cannot fold Avalonia's +native backends into the executable, so the GUI ships as the executable plus its +native libraries: + +```powershell +dotnet publish src\BSPLocalizationTools\BSPLocalizationTools.csproj -c Release -r win-x64 -o out\GUI +dotnet publish src\BSPLocalizationToolsCLI\BSPLocalizationToolsCLI.csproj -c Release -r win-x64 -o out\CLI +``` diff --git a/global.json b/global.json new file mode 100644 index 0000000..512142d --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "10.0.100", + "rollForward": "latestFeature" + } +} diff --git a/src/BSPLocalizationTools/BSPLocalizationTools.csproj b/src/BSPLocalizationTools/BSPLocalizationTools.csproj index 228887d..d3a9dcb 100644 --- a/src/BSPLocalizationTools/BSPLocalizationTools.csproj +++ b/src/BSPLocalizationTools/BSPLocalizationTools.csproj @@ -1,7 +1,7 @@ WinExe - net8.0-windows10.0.17763.0 + net10.0-windows10.0.17763.0 BSPLocalizationTools.GUI enable enable @@ -10,15 +10,23 @@ true 7.0 x64 + true + true + + full + true + false + true - - - - - - + + + + + + + diff --git a/src/BSPLocalizationTools/Program.cs b/src/BSPLocalizationTools/Program.cs index cb96c2a..6267dec 100644 --- a/src/BSPLocalizationTools/Program.cs +++ b/src/BSPLocalizationTools/Program.cs @@ -17,6 +17,6 @@ public static AppBuilder BuildAvaloniaApp() .UsePlatformDetect() .WithInterFont() .LogToTrace() - .UseReactiveUI(); + .UseReactiveUI(_ => { }); } } diff --git a/src/BSPLocalizationTools/Views/MainWindow.axaml b/src/BSPLocalizationTools/Views/MainWindow.axaml index b51c5f8..0ce081b 100644 --- a/src/BSPLocalizationTools/Views/MainWindow.axaml +++ b/src/BSPLocalizationTools/Views/MainWindow.axaml @@ -15,7 +15,7 @@ - +