diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ebe939d863..a473e7d39d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,6 +16,7 @@ stages: - build - test - test-ext + - package - upload variables: @@ -40,6 +41,7 @@ variables: # Job prefixes: # - `b:` build +# - `k:` package # - `l:` lint # - `p:` prep # - `t:` test @@ -1436,11 +1438,23 @@ b:windows-x86_64-package: extends: - .windows_x86_64_package - .cmake_build_windows - - .cmake_release_artifacts + - .cmake_build_package_artifacts - .windows_x86_64_tags_nonconcurrent_vs2022 - .run_only_for_package needs: - p:doc-package + variables: + CMAKE_CI_ARTIFACTS_NAME: "artifacts-windows-x86_64-build" + +k:windows-x86_64-package: + extends: + - .windows_x86_64_package + - .cmake_package_windows + - .cmake_release_artifacts + - .windows_x86_64_tags_nonconcurrent_sign + - .run_only_for_package + needs: + - b:windows-x86_64-package variables: CMAKE_CI_ARTIFACTS_NAME: "artifacts-windows-x86_64" @@ -1449,17 +1463,29 @@ u:windows-x86_64-package: - .rsync_upload_package - .run_only_for_package needs: - - b:windows-x86_64-package + - k:windows-x86_64-package b:windows-i386-package: extends: - .windows_i386_package - .cmake_build_windows - - .cmake_release_artifacts + - .cmake_build_package_artifacts - .windows_x86_64_tags_nonconcurrent_vs2022 - .run_only_for_package needs: - p:doc-package + variables: + CMAKE_CI_ARTIFACTS_NAME: "artifacts-windows-i386-build" + +k:windows-i386-package: + extends: + - .windows_i386_package + - .cmake_package_windows + - .cmake_release_artifacts + - .windows_x86_64_tags_nonconcurrent_sign + - .run_only_for_package + needs: + - b:windows-i386-package variables: CMAKE_CI_ARTIFACTS_NAME: "artifacts-windows-i386" @@ -1468,17 +1494,29 @@ u:windows-i386-package: - .rsync_upload_package - .run_only_for_package needs: - - b:windows-i386-package + - k:windows-i386-package b:windows-arm64-package: extends: - .windows_arm64_package - .cmake_build_windows - - .cmake_release_artifacts + - .cmake_build_package_artifacts - .windows_x86_64_tags_nonconcurrent_vs2022_arm64 - .run_only_for_package needs: - p:doc-package + variables: + CMAKE_CI_ARTIFACTS_NAME: "artifacts-windows-arm64-build" + +k:windows-arm64-package: + extends: + - .windows_arm64_package + - .cmake_package_windows + - .cmake_release_artifacts + - .windows_x86_64_tags_nonconcurrent_sign + - .run_only_for_package + needs: + - b:windows-arm64-package variables: CMAKE_CI_ARTIFACTS_NAME: "artifacts-windows-arm64" @@ -1487,4 +1525,4 @@ u:windows-arm64-package: - .rsync_upload_package - .run_only_for_package needs: - - b:windows-arm64-package + - k:windows-arm64-package diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml index 97b8f38537..007252519c 100644 --- a/.gitlab/artifacts.yml +++ b/.gitlab/artifacts.yml @@ -67,6 +67,36 @@ annotations: - ${CMAKE_CI_BUILD_DIR}/annotations.json +.cmake_build_package_artifacts: + artifacts: + expire_in: 1d + name: "$CMAKE_CI_ARTIFACTS_NAME" + paths: + # Allow CPack to find CMAKE_ROOT and license text. + - build/CMakeFiles/CMakeSourceDir.txt + - build/CMakeFiles/LICENSE.txt + + # Install rules. + - build/**/cmake_install.cmake + + # We need the main binaries. + - build/bin/ + + # Pass through the documentation. + - build/install-doc/ + + # CPack configuration. + - build/CPackConfig.cmake + - build/CMakeCPackOptions.cmake + - build/Source/QtDialog/QtDialogCPack.cmake + + # CPack/IFW packaging files. + - build/CMake*.qs + + # CPack/WIX packaging files. + - build/Utilities/Release/WiX/custom_action_dll*.wxs + - build/Utilities/Release/WiX/CustomAction/CMakeWiXCustomActions.* + .cmake_release_artifacts: artifacts: expire_in: 5d diff --git a/.gitlab/ci/CMakeCPack.cmake b/.gitlab/ci/CMakeCPack.cmake deleted file mode 100644 index 971fe54dc4..0000000000 --- a/.gitlab/ci/CMakeCPack.cmake +++ /dev/null @@ -1,3 +0,0 @@ -if(NOT "$ENV{CMAKE_CI_PACKAGE}" MATCHES "^(dev)?$") - configure_file(${CMAKE_CURRENT_LIST_DIR}/package_info.cmake.in ${CMake_BINARY_DIR}/ci_package_info.cmake @ONLY) -endif() diff --git a/.gitlab/ci/configure_windows_package_common.cmake b/.gitlab/ci/configure_windows_package_common.cmake index 11bfad5b6f..70fca72ee6 100644 --- a/.gitlab/ci/configure_windows_package_common.cmake +++ b/.gitlab/ci/configure_windows_package_common.cmake @@ -21,6 +21,4 @@ set(Python_FIND_REGISTRY NEVER CACHE STRING "") set(CMake_BUILD_WIX_CUSTOM_ACTION ON CACHE BOOL "") -set(CMake_CPACK_CUSTOM_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/CMakeCPack.cmake" CACHE FILEPATH "") - include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake") diff --git a/.gitlab/ci/env_windows_arm64_package.ps1 b/.gitlab/ci/env_windows_arm64_package.ps1 deleted file mode 100644 index 74c9c2badf..0000000000 --- a/.gitlab/ci/env_windows_arm64_package.ps1 +++ /dev/null @@ -1 +0,0 @@ -. .gitlab/ci/wix4-env.ps1 diff --git a/.gitlab/ci/env_windows_i386_package.ps1 b/.gitlab/ci/env_windows_i386_package.ps1 deleted file mode 100644 index 74c9c2badf..0000000000 --- a/.gitlab/ci/env_windows_i386_package.ps1 +++ /dev/null @@ -1 +0,0 @@ -. .gitlab/ci/wix4-env.ps1 diff --git a/.gitlab/ci/env_windows_x86_64_package.ps1 b/.gitlab/ci/env_windows_x86_64_package.ps1 deleted file mode 100644 index 74c9c2badf..0000000000 --- a/.gitlab/ci/env_windows_x86_64_package.ps1 +++ /dev/null @@ -1 +0,0 @@ -. .gitlab/ci/wix4-env.ps1 diff --git a/.gitlab/ci/package_info.cmake.in b/.gitlab/ci/package_info.cmake.in deleted file mode 100644 index f9a5bb778b..0000000000 --- a/.gitlab/ci/package_info.cmake.in +++ /dev/null @@ -1 +0,0 @@ -set(CPACK_PACKAGE_FILE_NAME "@CPACK_PACKAGE_FILE_NAME@") diff --git a/.gitlab/ci/package_windows.ps1 b/.gitlab/ci/package_windows.ps1 index 9ec2942eaf..5b787f404b 100644 --- a/.gitlab/ci/package_windows.ps1 +++ b/.gitlab/ci/package_windows.ps1 @@ -1,7 +1,3 @@ -if (Test-Path -Path "build/ci_package_info.cmake" -PathType Leaf) { - cmake -P .gitlab/ci/package_windows_build.cmake -} else { - cd build - cpack -G ZIP - cpack -G WIX -} +cd build +. ../Utilities/Release/win/sign-package.ps1 -cpack cpack +if (-not $?) { Exit $LastExitCode } diff --git a/.gitlab/ci/package_windows_build.cmake b/.gitlab/ci/package_windows_build.cmake deleted file mode 100644 index a2b7ed44f5..0000000000 --- a/.gitlab/ci/package_windows_build.cmake +++ /dev/null @@ -1,42 +0,0 @@ -cmake_minimum_required(VERSION 3.29) -include(build/ci_package_info.cmake) - -set(build "${CMAKE_CURRENT_BINARY_DIR}/build") - -file(GLOB paths RELATIVE "${CMAKE_CURRENT_BINARY_DIR}" - # Allow CPack to find CMAKE_ROOT and license text. - "${build}/CMakeFiles/CMakeSourceDir.txt" - "${build}/CMakeFiles/LICENSE.txt" - - # We need the main binaries. - "${build}/bin" - - # Pass through the documentation. - "${build}/install-doc" - - # CPack configuration. - "${build}/CPackConfig.cmake" - "${build}/CMakeCPackOptions.cmake" - "${build}/Source/QtDialog/QtDialogCPack.cmake" - - # CPack/IFW packaging files. - "${build}/CMake*.qs" - - # CPack/WIX packaging files. - "${build}/Utilities/Release/WiX/custom_action_dll*.wxs" - "${build}/Utilities/Release/WiX/CustomAction/CMakeWiXCustomActions.*" - ) - -file(GLOB_RECURSE paths_recurse RELATIVE "${CMAKE_CURRENT_BINARY_DIR}" - # Install rules. - "${build}/cmake_install.cmake" - "${build}/*/cmake_install.cmake" - ) - -# Create a "package" containing the build-tree files needed to build a package. -file(MAKE_DIRECTORY build/unsigned) -file(ARCHIVE_CREATE - OUTPUT build/unsigned/${CPACK_PACKAGE_FILE_NAME}.build.zip - PATHS ${paths} ${paths_recurse} - FORMAT zip - ) diff --git a/.gitlab/ci/post_build_windows_arm64_package.ps1 b/.gitlab/ci/post_build_windows_arm64_package.ps1 deleted file mode 100644 index f98d99597d..0000000000 --- a/.gitlab/ci/post_build_windows_arm64_package.ps1 +++ /dev/null @@ -1 +0,0 @@ -. .gitlab/ci/package_windows.ps1 diff --git a/.gitlab/ci/post_build_windows_i386_package.ps1 b/.gitlab/ci/post_build_windows_i386_package.ps1 deleted file mode 100644 index f98d99597d..0000000000 --- a/.gitlab/ci/post_build_windows_i386_package.ps1 +++ /dev/null @@ -1 +0,0 @@ -. .gitlab/ci/package_windows.ps1 diff --git a/.gitlab/ci/post_build_windows_x86_64_package.ps1 b/.gitlab/ci/post_build_windows_x86_64_package.ps1 deleted file mode 100644 index f98d99597d..0000000000 --- a/.gitlab/ci/post_build_windows_x86_64_package.ps1 +++ /dev/null @@ -1 +0,0 @@ -. .gitlab/ci/package_windows.ps1 diff --git a/.gitlab/ci/signtool-env.ps1 b/.gitlab/ci/signtool-env.ps1 new file mode 100644 index 0000000000..5467b98c5d --- /dev/null +++ b/.gitlab/ci/signtool-env.ps1 @@ -0,0 +1,22 @@ +if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") { + $arch = "x64" +} elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") { + $arch = "arm64" +} else { + throw ('unknown PROCESSOR_ARCHITECTURE: ' + "$env:PROCESSOR_ARCHITECTURE") +} + +$regKey = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Microsoft SDKs\Windows\v10.0' +$signtoolPath = $null +if ($sdkDir = Get-ItemPropertyValue -Path $regKey -Name "InstallationFolder") { + if ($sdkBin = Get-ChildItem -Path "$sdkDir/bin" -Recurse -Name "$arch" | + Where-Object { Test-Path -Path "$sdkDir/bin/$_/signtool.exe" -PathType Leaf } | + Select-Object -Last 1) { + $signtoolPath = "$sdkDir/bin/$sdkBin" + } +} +if ($signtoolPath) { + Set-Item -Force -Path "env:PATH" -Value "$env:PATH;$signtoolPath" +} else { + throw ('No signtool.exe found in Windows SDK') +} diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml index d024f41025..2881edcc87 100644 --- a/.gitlab/os-windows.yml +++ b/.gitlab/os-windows.yml @@ -310,6 +310,14 @@ ## Tags +.windows_x86_64_tags_nonconcurrent_sign: + tags: + - cmake # Since this is a bare runner, pin to a project. + - windows-x86_64 + - shell + - sign-windows-v1 + - nonconcurrent + .windows_x86_64_tags_nonconcurrent_vs2022: tags: - cmake # Since this is a bare runner, pin to a project. @@ -413,6 +421,18 @@ interruptible: true +.cmake_package_windows: + stage: package + environment: + name: sign-windows + script: + - . .gitlab/ci/env.ps1 + - . .gitlab/ci/signtool-env.ps1 + - . .gitlab/ci/cmake-env.ps1 + - . .gitlab/ci/wix4-env.ps1 + - . .gitlab/ci/package_windows.ps1 + interruptible: true + .cmake_test_windows: stage: test diff --git a/.gitlab/rules.yml b/.gitlab/rules.yml index c7102f95c7..efb7290f76 100644 --- a/.gitlab/rules.yml +++ b/.gitlab/rules.yml @@ -70,7 +70,9 @@ .run_only_for_package: rules: - - if: '$CMAKE_CI_PACKAGE == "dev"' + - if: '$CMAKE_CI_PACKAGE == "dev" && $CI_JOB_STAGE != "upload"' + when: on_success + - if: '$CMAKE_CI_PACKAGE == "dev" && $CI_JOB_STAGE == "upload"' variables: RSYNC_DESTINATION: "kitware@cmake.org:dev/" when: on_success diff --git a/CMakeCPack.cmake b/CMakeCPack.cmake index afece50406..3ac6030318 100644 --- a/CMakeCPack.cmake +++ b/CMakeCPack.cmake @@ -259,9 +259,5 @@ set(CPACK_SOURCE_IGNORE_FILES "~$" ) -if(CMake_CPACK_CUSTOM_SCRIPT) - include(${CMake_CPACK_CUSTOM_SCRIPT}) -endif() - # include CPack model once all variables are set include(CPack) diff --git a/Utilities/Release/win/sign-package.ps1 b/Utilities/Release/win/sign-package.ps1 index 7465c213ea..2927108c2b 100644 --- a/Utilities/Release/win/sign-package.ps1 +++ b/Utilities/Release/win/sign-package.ps1 @@ -6,24 +6,89 @@ param ( [string]$signtool = 'signtool', [string]$cpack = 'bin\cpack', - [switch]$trace + [string]$pass = $null ) -if ($trace -eq $true) { - Set-PSDebug -Trace 1 -} - $ErrorActionPreference = 'Stop' +# Cleanup temporary file(s) on exit. +$null = Register-EngineEvent PowerShell.Exiting -Action { + if ($certFile) { + Remove-Item $certFile -Force + } +} + +# If the passphrase was not provided on the command-line, +# check for a GitLab CI variable in the environment. +if (-not $pass) { + $pass = $env:SIGNTOOL_PASS + + # If the environment variable looks like a GitLab CI file-type variable, + # replace it with the content of the file. + if ($pass -and + $pass.EndsWith("SIGNTOOL_PASS") -and + (Test-Path -Path "$pass" -IsValid) -and + (Test-Path -Path "$pass" -PathType Leaf)) { + $pass = Get-Content -Path "$pass" + } +} + +# Collect signtool arguments to specify a certificate. +$cert = @() + +# Select a signing certificate to pass to signtool. +if ($certX509 = Get-ChildItem -Recurse -Path "Cert:" -CodeSigningCert | + Where-Object { $_.PublicKey.Oid.FriendlyName -eq "RSA" } | + Select-Object -First 1) { + # Identify the private key provider name and container name. + if ($certRSA = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($certX509)) { + # $certRSA -is [System.Security.Cryptography.RSACng] + # Cryptography Next Generation (CNG) implementation + $csp = $certRSA.Key.Provider + $kc = $certRSA.Key.KeyName + } elseif ($certRSA = $certX509.PrivateKey) { + # $certRSA -is [System.Security.Cryptography.RSACryptoServiceProvider] + $csp = $certRSA.CspKeyContainerInfo.ProviderName + $kc = $certRSA.CspKeyContainerInfo.KeyContainerName + } + + # Pass the selected certificate to signtool. + $certFile = New-TemporaryFile + $certBase64 = [System.Convert]::ToBase64String($certX509.RawData, [System.Base64FormattingOptions]::InsertLineBreaks) + $certPEM = "-----BEGIN CERTIFICATE-----", $certBase64, "-----END CERTIFICATE-----" -join "`n" + $certPEM | Out-File -FilePath "$certFile" -Encoding Ascii + $cert += "-f","$certFile" + + # Tell signtool how to find the certificate's private key. + if ($csp) { + $cert += "-csp","$csp" + } + if ($kc) { + if ($pass) { + # The provider offers a syntax to encode the token passphrase in the key container name. + # https://web.archive.org/web/20250315200813/https://stackoverflow.com/questions/17927895/automate-extended-validation-ev-code-signing-with-safenet-etoken + $cert += "-kc","[{{$pass}}]=$kc" + $pass = $null + } else { + $cert += "-kc","$kc" + } + } +} else { + $cert += @("-a") +} + # Sign binaries with SHA-1 for Windows 7 and below. -& $signtool sign -v -a -t http://timestamp.digicert.com -fd sha1 bin\*.exe +& $signtool sign -v $cert -t http://timestamp.digicert.com -fd sha1 bin\*.exe +if (-not $?) { Exit $LastExitCode } # Sign binaries with SHA-256 for Windows 8 and above. -& $signtool sign -v -a -tr http://timestamp.digicert.com -fd sha256 -td sha256 -as bin\*.exe +& $signtool sign -v $cert -tr http://timestamp.digicert.com -fd sha256 -td sha256 -as bin\*.exe +if (-not $?) { Exit $LastExitCode } # Create packages. -& $cpack -G ZIP -& $cpack -G WIX +& $cpack -G "ZIP;WIX" +if (-not $?) { Exit $LastExitCode } # Sign installer with SHA-256. -& $signtool sign -v -a -tr http://timestamp.digicert.com -fd sha256 -td sha256 -d "CMake Windows Installer" cmake-*-win*.msi +& $signtool sign -v $cert -tr http://timestamp.digicert.com -fd sha256 -td sha256 -d "CMake Windows Installer" cmake-*-win*.msi +if (-not $?) { Exit $LastExitCode }