From c6ad8f532f59c1aad61ccf042b39f8c7be0a0f9c Mon Sep 17 00:00:00 2001 From: Dennis Ameling Date: Thu, 27 Feb 2025 12:05:09 +0100 Subject: [PATCH 1/2] Add support for Windows x64 and arm64 builds Previously, GCM was only built for x86 on Windows. This commit adds support for building for and on Windows x64 and arm64. It builds the host architecture by default and supports explicitly specifying the target architecture. Signed-off-by: Dennis Ameling --- .azure-pipelines/release.yml | 32 +++++++- .github/workflows/continuous-integration.yml | 28 +++++-- .../Git-Credential-Manager.csproj | 5 +- .../Installer.Windows.csproj | 25 ++++-- src/windows/Installer.Windows/Setup.iss | 15 +++- src/windows/Installer.Windows/layout.ps1 | 79 +++++++++++++++---- 6 files changed, 148 insertions(+), 36 deletions(-) diff --git a/.azure-pipelines/release.yml b/.azure-pipelines/release.yml index a95e96451..3e99b1454 100644 --- a/.azure-pipelines/release.yml +++ b/.azure-pipelines/release.yml @@ -31,12 +31,24 @@ parameters: - name: windows_matrix type: object default: - - id: windows_x64 + - id: windows_x86 jobName: 'Windows (x86)' runtime: win-x86 pool: GitClientPME-1ESHostedPool-intel-pc image: win-x86_64-ado1es os: windows + - id: windows_x64 + jobName: 'Windows (x64)' + runtime: win-x64 + pool: GitClientPME-1ESHostedPool-intel-pc + image: win-x86_64-ado1es + os: windows + - id: windows_arm64 + jobName: 'Windows (ARM64)' + runtime: win-arm64 + pool: GitClientPME-1ESHostedPool-intel-pc + image: win-x86_64-ado1es + os: windows - name: macos_matrix type: object @@ -136,14 +148,15 @@ extends: arguments: | -Configuration Release ` -Output $(Build.ArtifactStagingDirectory)\payload ` - -SymbolOutput $(Build.ArtifactStagingDirectory)\symbols_raw + -SymbolOutput $(Build.ArtifactStagingDirectory)\symbols_raw ` + -RuntimeIdentifier ${{ dim.runtime }} - task: ArchiveFiles@2 displayName: 'Archive symbols' inputs: rootFolderOrFile: '$(Build.ArtifactStagingDirectory)\symbols_raw' includeRootFolder: false archiveType: zip - archiveFile: '$(Build.ArtifactStagingDirectory)\symbols\gcm-win-x86-$(version)-symbols.zip' + archiveFile: '$(Build.ArtifactStagingDirectory)\symbols\gcm-${{ dim.runtime }}-$(version)-symbols.zip' - task: EsrpCodeSigning@5 condition: and(succeeded(), eq('${{ parameters.esrp }}', true)) displayName: 'Sign payload' @@ -195,6 +208,7 @@ extends: -p:NoLayout=true ` -p:PayloadPath="$(Build.ArtifactStagingDirectory)\payload" ` -p:OutputPath="$(Build.ArtifactStagingDirectory)\installers" + -p:RuntimeIdentifier="${{ dim.runtime }}" - task: EsrpCodeSigning@5 condition: and(succeeded(), eq('${{ parameters.esrp }}', true)) displayName: 'Sign installers' @@ -239,7 +253,7 @@ extends: rootFolderOrFile: '$(Build.ArtifactStagingDirectory)\payload' includeRootFolder: false archiveType: zip - archiveFile: '$(Build.ArtifactStagingDirectory)\installers\gcm-win-x86-$(version).zip' + archiveFile: '$(Build.ArtifactStagingDirectory)\installers\gcm-${{ dim.runtime }}-$(version).zip' - task: PowerShell@2 displayName: 'Collect artifacts for publishing' inputs: @@ -774,6 +788,12 @@ extends: - input: pipelineArtifact artifactName: 'win-x86' targetPath: $(Pipeline.Workspace)/assets/win-x86 + - input: pipelineArtifact + artifactName: 'win-x64' + targetPath: $(Pipeline.Workspace)/assets/win-x64 + - input: pipelineArtifact + artifactName: 'win-arm64' + targetPath: $(Pipeline.Workspace)/assets/win-arm64 - input: pipelineArtifact artifactName: 'osx-x64' targetPath: $(Pipeline.Workspace)/assets/osx-x64 @@ -805,6 +825,10 @@ extends: assets: | $(Pipeline.Workspace)/assets/win-x86/*.exe $(Pipeline.Workspace)/assets/win-x86/*.zip + $(Pipeline.Workspace)/assets/win-x64/*.exe + $(Pipeline.Workspace)/assets/win-x64/*.zip + $(Pipeline.Workspace)/assets/win-arm64/*.exe + $(Pipeline.Workspace)/assets/win-arm64/*.zip $(Pipeline.Workspace)/assets/osx-x64/*.pkg $(Pipeline.Workspace)/assets/osx-x64/*.tar.gz $(Pipeline.Workspace)/assets/osx-arm64/*.pkg diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 24617f881..7bb45f26a 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -13,7 +13,16 @@ jobs: # ================================ windows: name: Windows - runs-on: windows-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - runtime: win-x86 + os: windows-latest + - runtime: win-x64 + os: windows-latest + - runtime: win-arm64 + os: windows-11-arm steps: - uses: actions/checkout@v6 @@ -27,24 +36,29 @@ jobs: run: dotnet restore - name: Build - run: dotnet build --configuration WindowsRelease + run: | + dotnet build src/windows/Installer.Windows/Installer.Windows.csproj ` + --configuration=Release ` + --runtime=${{ matrix.runtime }} - name: Test run: | - dotnet test --verbosity normal --configuration=WindowsRelease + dotnet test --verbosity normal ` + --configuration=WindowsRelease ` + --runtime=${{ matrix.runtime }} - name: Prepare artifacts shell: bash run: | mkdir -p artifacts/bin - mv out/windows/Installer.Windows/bin/Release/net472/win-x86 artifacts/bin/ - cp out/windows/Installer.Windows/bin/Release/net472/win-x86.sym/* artifacts/bin/win-x86/ - mv out/windows/Installer.Windows/bin/Release/net472/gcm*.exe artifacts/ + mv out/windows/Installer.Windows/bin/Release/net472/${{ matrix.runtime }}/gcm*.exe artifacts/ + mv out/windows/Installer.Windows/bin/Release/net472/${{ matrix.runtime }} artifacts/bin/ + cp out/windows/Installer.Windows/bin/Release/net472/${{ matrix.runtime }}.sym/* artifacts/bin/${{ matrix.runtime }}/ - name: Upload artifacts uses: actions/upload-artifact@v6 with: - name: win-x86 + name: ${{ matrix.runtime }} path: | artifacts diff --git a/src/shared/Git-Credential-Manager/Git-Credential-Manager.csproj b/src/shared/Git-Credential-Manager/Git-Credential-Manager.csproj index 456adf547..8c469897e 100644 --- a/src/shared/Git-Credential-Manager/Git-Credential-Manager.csproj +++ b/src/shared/Git-Credential-Manager/Git-Credential-Manager.csproj @@ -1,11 +1,10 @@ - + Exe net8.0 net472;net8.0 - win-x86;osx-x64;linux-x64;osx-arm64;linux-arm64;linux-arm - x86 + win-x86;win-x64;win-arm64;osx-x64;linux-x64;osx-arm64;linux-arm64;linux-arm git-credential-manager GitCredentialManager $(RepoAssetsPath)gcmicon.ico diff --git a/src/windows/Installer.Windows/Installer.Windows.csproj b/src/windows/Installer.Windows/Installer.Windows.csproj index bbd49a291..eae3631f0 100644 --- a/src/windows/Installer.Windows/Installer.Windows.csproj +++ b/src/windows/Installer.Windows/Installer.Windows.csproj @@ -1,12 +1,19 @@ - + + + + win-x64 + win-x86 + win-arm64 + + net472 false false - $(PlatformOutPath)Installer.Windows\bin\$(Configuration)\net472\win-x86 + $(PlatformOutPath)Installer.Windows\bin\$(Configuration)\net472\$(RuntimeIdentifier) 6.3.1 @@ -27,12 +34,20 @@ - "$(NuGetPackageRoot)Tools.InnoSetup\$(InnoSetupVersion)\tools\ISCC.exe" /DPayloadDir="$(PayloadPath)" /DInstallTarget=system "$(RepoSrcPath)\windows\Installer.Windows\Setup.iss" /O"$(OutputPath)" - "$(NuGetPackageRoot)Tools.InnoSetup\$(InnoSetupVersion)\tools\ISCC.exe" /DPayloadDir="$(PayloadPath)" /DInstallTarget=user "$(RepoSrcPath)\windows\Installer.Windows\Setup.iss" /O"$(OutputPath)" + "$(NuGetPackageRoot)Tools.InnoSetup\$(InnoSetupVersion)\tools\ISCC.exe" /DPayloadDir="$(PayloadPath)" /DInstallTarget=system /DGcmRuntimeIdentifier="$(RuntimeIdentifier)" "$(RepoSrcPath)\windows\Installer.Windows\Setup.iss" /O"$(OutputPath)" + "$(NuGetPackageRoot)Tools.InnoSetup\$(InnoSetupVersion)\tools\ISCC.exe" /DPayloadDir="$(PayloadPath)" /DInstallTarget=user /DGcmRuntimeIdentifier="$(RuntimeIdentifier)" "$(RepoSrcPath)\windows\Installer.Windows\Setup.iss" /O"$(OutputPath)" - + + + + + + diff --git a/src/windows/Installer.Windows/Setup.iss b/src/windows/Installer.Windows/Setup.iss index f03d16c9b..c15efe6d8 100644 --- a/src/windows/Installer.Windows/Setup.iss +++ b/src/windows/Installer.Windows/Setup.iss @@ -15,6 +15,10 @@ #error Installer target property 'InstallTarget' must be specifed #endif +#ifndef GcmRuntimeIdentifier + #error GCM Runtime Identifier 'GcmRuntimeIdentifier' must be specifed (e.g. win-x64) +#endif + #if InstallTarget == "user" #define GcmAppId "{{aa76d31d-432c-42ee-844c-bc0bc801cef3}}" #define GcmLongName "Git Credential Manager (User)" @@ -40,7 +44,6 @@ #define GcmRepoRoot "..\..\.." #define GcmAssets GcmRepoRoot + "\assets" #define GcmExe "git-credential-manager.exe" -#define GcmArch "x86" #ifnexist PayloadDir + "\" + GcmExe #error Payload files are missing @@ -67,9 +70,17 @@ AppUpdatesURL={#GcmUrl} AppContact={#GcmUrl} AppCopyright={#GcmCopyright} AppReadmeFile={#GcmReadme} +; Windows ARM64 supports installing and running x64 binaries, but not vice versa. +#if GcmRuntimeIdentifier=="win-x64" +ArchitecturesAllowed=x64compatible +ArchitecturesInstallIn64BitMode=x64compatible +#elif GcmRuntimeIdentifier=="win-arm64" +ArchitecturesAllowed=arm64 +ArchitecturesInstallIn64BitMode=arm64 +#endif VersionInfoVersion={#GcmVersion} LicenseFile={#GcmRepoRoot}\LICENSE -OutputBaseFilename={#GcmSetupExe}-win-{#GcmArch}-{#GcmVersionSimple} +OutputBaseFilename={#GcmSetupExe}-{#GcmRuntimeIdentifier}-{#GcmVersionSimple} DefaultDirName={autopf}\{#GcmShortName} Compression=lzma2 SolidCompression=yes diff --git a/src/windows/Installer.Windows/layout.ps1 b/src/windows/Installer.Windows/layout.ps1 index 818ee01c6..3fc43ab36 100644 --- a/src/windows/Installer.Windows/layout.ps1 +++ b/src/windows/Installer.Windows/layout.ps1 @@ -1,8 +1,29 @@ # Inputs -param ([Parameter(Mandatory)] $Configuration, [Parameter(Mandatory)] $Output, $SymbolOutput) +param ([Parameter(Mandatory)] $Configuration, [Parameter(Mandatory)] $Output, $RuntimeIdentifier, $SymbolOutput) Write-Output "Output: $Output" +# Determine a runtime if one was not provided +if (-not $RuntimeIdentifier) { + $arch = $env:PROCESSOR_ARCHITECTURE + switch ($arch) { + "AMD64" { $RuntimeIdentifier = "win-x64" } + "x86" { $RuntimeIdentifier = "win-x86" } + "ARM64" { $RuntimeIdentifier = "win-arm64" } + default { + Write-Host "Unknown architecture: $arch" + exit 1 + } + } +} + +Write-Output "Building for runtime '$RuntimeIdentifier'" + +if ($RuntimeIdentifier -ne 'win-x86' -and $RuntimeIdentifier -ne 'win-x64' -and $RuntimeIdentifier -ne 'win-arm64') { + Write-Host "Unsupported RuntimeIdentifier: $RuntimeIdentifier" + exit 1 +} + # Directories $THISDIR = $PSScriptRoot $ROOT = (Get-Item $THISDIR).Parent.Parent.Parent.FullName @@ -41,27 +62,55 @@ Write-Output "Publishing core application..." dotnet publish "$GCM_SRC" ` --framework net472 ` --configuration "$Configuration" ` - --runtime win-x86 ` + --runtime $RuntimeIdentifier ` --output "$PAYLOAD" # Delete libraries that are not needed for Windows but find their way # into the publish output. Remove-Item -Path "$PAYLOAD/*.dylib" -Force -ErrorAction Ignore -# Delete extraneous files that get included for other architectures -# We only care about x86 as the core GCM executable is only targeting x86 -Remove-Item -Path "$PAYLOAD/arm/" -Recurse -Force -ErrorAction Ignore -Remove-Item -Path "$PAYLOAD/arm64/" -Recurse -Force -ErrorAction Ignore -Remove-Item -Path "$PAYLOAD/x64/" -Recurse -Force -ErrorAction Ignore +# Delete extraneous files that get included for other runtimes Remove-Item -Path "$PAYLOAD/musl-x64/" -Recurse -Force -ErrorAction Ignore -Remove-Item -Path "$PAYLOAD/runtimes/win-arm64/" -Recurse -Force -ErrorAction Ignore -Remove-Item -Path "$PAYLOAD/runtimes/win-x64/" -Recurse -Force -ErrorAction Ignore - -# The Avalonia and MSAL binaries in these directories are already included in -# the $PAYLOAD directory directly, so we can delete these extra copies. -Remove-Item -Path "$PAYLOAD/x86/libSkiaSharp.dll" -Recurse -Force -ErrorAction Ignore -Remove-Item -Path "$PAYLOAD/x86/libHarfBuzzSharp.dll" -Recurse -Force -ErrorAction Ignore -Remove-Item -Path "$PAYLOAD/runtimes/win-x86/native/msalruntime_x86.dll" -Recurse -Force -ErrorAction Ignore + +switch ($RuntimeIdentifier) { + "win-x86" { + Remove-Item -Path "$PAYLOAD/arm/" -Recurse -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/arm64/" -Recurse -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/x64/" -Recurse -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/runtimes/win-arm64/" -Recurse -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/runtimes/win-x64/" -Recurse -Force -ErrorAction Ignore + # The Avalonia and MSAL binaries are already included in the $PAYLOAD directory directly + Remove-Item -Path "$PAYLOAD/x86/libSkiaSharp.dll" -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/x86/libHarfBuzzSharp.dll" -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/runtimes/win-x86/native/msalruntime_x86.dll" -Force -ErrorAction Ignore + } + "win-x64" { + Remove-Item -Path "$PAYLOAD/arm/" -Recurse -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/arm64/" -Recurse -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/x86/" -Recurse -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/runtimes/win-arm64/" -Recurse -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/runtimes/win-x86/" -Recurse -Force -ErrorAction Ignore + # The Avalonia and MSAL binaries are already included in the $PAYLOAD directory directly + Remove-Item -Path "$PAYLOAD/x64/libSkiaSharp.dll" -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/x64/libHarfBuzzSharp.dll" -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/x64/libSkiaSharp.so" -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/x64/libHarfBuzzSharp.so" -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/runtimes/win-x64/native/msalruntime.dll" -Force -ErrorAction Ignore + } + "win-arm64" { + Remove-Item -Path "$PAYLOAD/arm/" -Recurse -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/x86/" -Recurse -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/x64/" -Recurse -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/runtimes/win-x86/" -Recurse -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/runtimes/win-x64/" -Recurse -Force -ErrorAction Ignore + # The Avalonia and MSAL binaries are already included in the $PAYLOAD directory directly + Remove-Item -Path "$PAYLOAD/arm64/libSkiaSharp.dll" -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/arm64/libHarfBuzzSharp.dll" -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/arm64/libSkiaSharp.so" -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/arm64/libHarfBuzzSharp.so" -Force -ErrorAction Ignore + Remove-Item -Path "$PAYLOAD/runtimes/win-arm64/native/msalruntime_arm64.dll" -Force -ErrorAction Ignore + } +} # Delete localized resource assemblies - we don't localize the core GCM assembly anyway Get-ChildItem "$PAYLOAD" -Recurse -Include "*.resources.dll" | Remove-Item -Force -ErrorAction Ignore From 5c321e36fd96deff794c6971696ab48adc59cf15 Mon Sep 17 00:00:00 2001 From: Dennis Ameling Date: Tue, 13 Jan 2026 22:06:04 +0100 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Matthew John Cheetham --- .azure-pipelines/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.azure-pipelines/release.yml b/.azure-pipelines/release.yml index 3e99b1454..aaf5e2d43 100644 --- a/.azure-pipelines/release.yml +++ b/.azure-pipelines/release.yml @@ -46,8 +46,8 @@ parameters: - id: windows_arm64 jobName: 'Windows (ARM64)' runtime: win-arm64 - pool: GitClientPME-1ESHostedPool-intel-pc - image: win-x86_64-ado1es + pool: GitClientPME-1ESHostedPool-arm64-pc + image: win-arm64-ado1es os: windows - name: macos_matrix @@ -207,7 +207,7 @@ extends: --no-dependencies ` -p:NoLayout=true ` -p:PayloadPath="$(Build.ArtifactStagingDirectory)\payload" ` - -p:OutputPath="$(Build.ArtifactStagingDirectory)\installers" + -p:OutputPath="$(Build.ArtifactStagingDirectory)\installers" ` -p:RuntimeIdentifier="${{ dim.runtime }}" - task: EsrpCodeSigning@5 condition: and(succeeded(), eq('${{ parameters.esrp }}', true))