From b195450b86a365ecae2d1771cfa63d2471ce9543 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Wed, 19 Aug 2026 16:16:02 -0700 Subject: [PATCH] Adding Reusable CMake build workflow This workflow absorbs the boilerplate in installing and setting CMake builds across macOS, Linux, and Windows. The workflow defaults to using CMake 4.0.3, and has the appropriate hashes for each architecture and platform. If you override the version, you will also need to provide the appropriate hashes for the platforms. Each platform can be optionally enabled or disabled with the `enable_*_checks`. By default, only Linux is tested, but macOS and Windows can both be enabled. Controlling each build is done with three commands for each platform: 1. `*_pre_build_command` 2. `*_configure_command` 3. `*_build_command` The pre-build command runs first, allowing you to install dependencies like Ninja. The `configure` command is used to run CMake to configure the build. By default, this runs the platform-equivalent of `$CMAKE -G Ninja -B build -S .`. The build-command is for running the build. By default, it runs the platform-equivalent of `$CMAKE --build build` Each of these commands has a `CMAKE` and `CTEST` environment variable defined, specifying the path to the installed CMake and CTest binaries. Linux and Windows both run x86_64 and Arm64, while macOS only builds Arm64. --- .github/workflows/cmake_build.yml | 237 +++++++++++++++++++++++++++++ .github/workflows/pull_request.yml | 10 ++ README.md | 50 ++++++ tests/CMakeProject/CMakeLists.txt | 3 + tests/CMakeProject/main.c | 13 ++ 5 files changed, 313 insertions(+) create mode 100644 .github/workflows/cmake_build.yml create mode 100644 tests/CMakeProject/CMakeLists.txt create mode 100644 tests/CMakeProject/main.c diff --git a/.github/workflows/cmake_build.yml b/.github/workflows/cmake_build.yml new file mode 100644 index 00000000..c9c95a61 --- /dev/null +++ b/.github/workflows/cmake_build.yml @@ -0,0 +1,237 @@ +name: CMake Build + +permissions: + contents: read + +on: + workflow_call: + inputs: + cmake_version: + type: string + description: "CMake version to install and build with" + default: "4.0.3" + linux_x86_64_hash: + type: string + description: "SHA-256 of cmake--linux-x86_64.tar.gz" + default: "585ae9e013107bc8e7c7c9ce872cbdcbdff569e675b07ef57aacfb88c886faac" + linux_aarch64_hash: + type: string + description: "SHA-256 of cmake--linux-aarch64.tar.gz" + default: "391da1544ef50ac31300841caaf11db4de3976cdc4468643272e44b3f4644713" + macos_hash: + type: string + description: "SHA-256 of cmake--macos-universal.tar.gz" + default: "4e85de4daf1c3e82d7dc6b8ba5683972944b466343aeb9c327a742437bb3ce9a" + windows_x86_64_hash: + type: string + description: "SHA-256 of cmake--windows-x86_64.zip" + default: "b59a31dfbfa376a4aaea9ff560ff2b29f78ee5f9fb15447fc71ae7bf9fea9379" + windows_arm64_hash: + type: string + description: "SHA-256 of cmake--windows-arm64.zip" + default: "86ccd6485bbd4bb41a1a858db397be5bca5e0de96858bf8dbba7a64407bd6c00" + enable_linux_checks: + type: boolean + description: "Boolean to enable the Linux build. Defaults to true" + default: true + enable_macos_checks: + type: boolean + description: "Boolean to enable the macOS build (requires self-hosted runners). Defaults to false" + default: false + enable_windows_checks: + type: boolean + description: "Boolean to enable the Windows build. Defaults to true" + default: true + linux_container_image: + type: string + description: "Container image used for the Linux build" + default: "swift:6.2-jammy" + linux_prerequisites_command: + type: string + description: "Linux command to install prerequisites the install-cmake action needs (it shells out to curl)" + default: "apt-get update && apt-get install -y curl" + linux_pre_build_command: + type: string + description: "Linux command to execute before configuring the CMake project" + default: "" + linux_configure_command: + type: string + description: "Linux command to configure the CMake project" + default: "$CMAKE -G Ninja -B build -S ." + linux_build_command: + type: string + description: "Linux command to build the CMake project" + default: "$CMAKE --build build" + macos_pre_build_command: + type: string + description: "macOS command to execute before configuring the CMake project" + default: "" + macos_configure_command: + type: string + description: "macOS command to configure the CMake project" + default: "$CMAKE -G Ninja -B build -S ." + macos_build_command: + type: string + description: "macOS command to build the CMake project" + default: "$CMAKE --build build" + windows_pre_build_command: + type: string + description: | + Windows PowerShell command to execute before configuring the CMake project. + The Invoke-Program helper is available to propagate non-zero exit codes. + default: "" + windows_configure_command: + type: string + description: | + Windows PowerShell command to configure the CMake project. + The Invoke-Program helper is available to propagate non-zero exit codes. + default: "Invoke-Program cmake -G Ninja -B build -S ." + windows_build_command: + type: string + description: | + Windows PowerShell command to build the CMake project. + The Invoke-Program helper is available to propagate non-zero exit codes. + default: "Invoke-Program cmake --build build" + workflow_dispatch: + +jobs: + linux-build: + name: "Linux (${{ matrix.arch }})" + if: ${{ inputs.enable_linux_checks }} + strategy: + fail-fast: false + matrix: + include: + - arch: X64 + runner: ubuntu-24.04 + hash: ${{ inputs.linux_x86_64_hash }} + - arch: ARM64 + runner: ubuntu-24.04-arm + hash: ${{ inputs.linux_aarch64_hash }} + runs-on: ${{ matrix.runner }} + container: + image: ${{ inputs.linux_container_image }} + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Install prerequisites + shell: bash + env: + PREREQUISITES_COMMAND: ${{ inputs.linux_prerequisites_command }} + run: | + set -euo pipefail + eval "${PREREQUISITES_COMMAND}" + - name: Install CMake + id: install + uses: ./.github/actions/install-cmake + with: + cmake-version: ${{ inputs.cmake_version }} + hash: ${{ matrix.hash }} + - name: Pre-build + if: ${{ inputs.linux_pre_build_command != '' }} + shell: bash + env: + CMAKE: ${{ steps.install.outputs.cmake }} + CTEST: ${{ steps.install.outputs.ctest }} + # zizmor: ignore[template-injection] + run: ${{ inputs.linux_pre_build_command }} + - name: Configure + shell: bash + env: + CMAKE: ${{ steps.install.outputs.cmake }} + CTEST: ${{ steps.install.outputs.ctest }} + # zizmor: ignore[template-injection] + run: ${{ inputs.linux_configure_command }} + - name: Build + shell: bash + env: + CMAKE: ${{ steps.install.outputs.cmake }} + CTEST: ${{ steps.install.outputs.ctest }} + # zizmor: ignore[template-injection] + run: ${{ inputs.linux_build_command }} + + macos-build: + name: "macOS (ARM64)" + if: ${{ inputs.enable_macos_checks }} + runs-on: [self-hosted, macos, tahoe, ARM64] + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Install CMake + id: install + uses: ./.github/actions/install-cmake + with: + cmake-version: ${{ inputs.cmake_version }} + hash: ${{ inputs.macos_hash }} + - name: Pre-build + if: ${{ inputs.macos_pre_build_command != '' }} + shell: bash + env: + CMAKE: ${{ steps.install.outputs.cmake }} + CTEST: ${{ steps.install.outputs.ctest }} + # zizmor: ignore[template-injection] + run: ${{ inputs.macos_pre_build_command }} + - name: Configure + shell: bash + env: + CMAKE: ${{ steps.install.outputs.cmake }} + CTEST: ${{ steps.install.outputs.ctest }} + # zizmor: ignore[template-injection] + run: ${{ inputs.macos_configure_command }} + - name: Build + shell: bash + env: + CMAKE: ${{ steps.install.outputs.cmake }} + CTEST: ${{ steps.install.outputs.ctest }} + # zizmor: ignore[template-injection] + run: ${{ inputs.macos_build_command }} + + windows-build: + name: "Windows (${{ matrix.runner }})" + if: ${{ inputs.enable_windows_checks }} + strategy: + fail-fast: false + matrix: + include: + - runner: windows-2022 + hash: ${{ inputs.windows_x86_64_hash }} + - runner: windows-11-arm + hash: ${{ inputs.windows_arm64_hash }} + runs-on: ${{ matrix.runner }} + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Install CMake + id: install + uses: ./.github/actions/install-cmake + with: + cmake-version: ${{ inputs.cmake_version }} + hash: ${{ matrix.hash }} + - name: Pre-build + if: ${{ inputs.windows_pre_build_command != '' }} + env: + CMAKE: ${{ steps.install.outputs.cmake }} + CTEST: ${{ steps.install.outputs.ctest }} + # zizmor: ignore[template-injection] + run: | + $ErrorActionPreference = "Stop" + function Invoke-Program($Executable) { & $Executable @args; if ($LastExitCode -ne 0) { exit $LastExitCode } } + ${{ inputs.windows_pre_build_command }} + - name: Configure + env: + CMAKE: ${{ steps.install.outputs.cmake }} + CTEST: ${{ steps.install.outputs.ctest }} + # zizmor: ignore[template-injection] + run: | + $ErrorActionPreference = "Stop" + function Invoke-Program($Executable) { & $Executable @args; if ($LastExitCode -ne 0) { exit $LastExitCode } } + ${{ inputs.windows_configure_command }} + - name: Build + env: + CMAKE: ${{ steps.install.outputs.cmake }} + CTEST: ${{ steps.install.outputs.ctest }} + # zizmor: ignore[template-injection] + run: | + $ErrorActionPreference = "Stop" + function Invoke-Program($Executable) { & $Executable @args; if ($LastExitCode -ne 0) { exit $LastExitCode } } + ${{ inputs.windows_build_command }} diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index bcd264ae..48582c9d 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -114,6 +114,16 @@ jobs: name: Install CMake uses: ./.github/workflows/install_cmake_test.yml + cmake_build: + name: CMake Build + uses: ./.github/workflows/cmake_build.yml + with: + # Linux uses the default Ninja generator; install Ninja first. + linux_pre_build_command: "apt-get install -y ninja-build" + linux_configure_command: "$CMAKE -G Ninja -B build -S tests/CMakeProject" + # Windows uses CMake's default generator (MSVC), so Ninja is not required. + windows_configure_command: "Invoke-Program cmake -B build -S tests/CMakeProject" + soundness-docs: name: "Soundness - Docs (override_target_name: ${{ matrix.override_target_name }} ; post command: ${{ matrix.post_command }})" strategy: diff --git a/README.md b/README.md index de2f40de..a756df0f 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,56 @@ Linked PR: swiftlang/swift-syntax#2859 Enabling cross-PR testing will add about 10s to PR testing time. +### CMake Build + +The `cmake_build` workflow installs a pinned CMake (using the `install-cmake` +action) and runs your configure and build commands across Linux, macOS, and +Windows. It removes the boilerplate of installing CMake and wiring up +`$CMAKE`-based build steps in each consumer. + +By default it installs CMake 4.0.3, configures with +`$CMAKE -G Ninja -B build -S .`, and builds with `$CMAKE --build build`. +Linux builds run by default. macOS and Windows builds can be enabled with +`enable_macos_checks` and `enable_windows_checks` respectively. A minimal +example: + +```yaml +name: Pull request + +on: + pull_request: + types: [opened, reopened, synchronize] + +jobs: + cmake_build: + name: CMake Build + uses: swiftlang/github-workflows/.github/workflows/cmake_build.yml@ +``` + +Override the per-platform configure and build commands to point at your +project. `$CMAKE` and `$CTEST` are exposed as environment variables, and the +installed CMake is also on `PATH`: + +```yaml +with: + linux_configure_command: "$CMAKE -G Ninja -B build -S Sources -DCMAKE_BUILD_TYPE=Release" + linux_build_command: "$CMAKE --build build" +``` + +The default configure command uses the Ninja generator, but the workflow does +not install Ninja. Ensure Ninja is available in your environment, install it +via a `*_pre_build_command`, or override the configure command to use a +different generator: + +```yaml +with: + linux_pre_build_command: "apt-get install -y ninja-build" +``` + +To pin a different CMake version, override `cmake_version` together with the +matching per-platform SHA-256 hashes (`linux_x86_64_hash`, `linux_aarch64_hash`, +`macos_hash`, `windows_x86_64_hash`, `windows_arm64_hash`). + ### Evolution Proposal Validation The proposal validation workflow validates added and changed proposals in a pull request to check for formatting and content errors that will cause metadata extraction to fail or be incomplete. diff --git a/tests/CMakeProject/CMakeLists.txt b/tests/CMakeProject/CMakeLists.txt new file mode 100644 index 00000000..42f9ddaf --- /dev/null +++ b/tests/CMakeProject/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.0) +project(Sample C) +add_executable(sample main.c) diff --git a/tests/CMakeProject/main.c b/tests/CMakeProject/main.c new file mode 100644 index 00000000..8a57f3e9 --- /dev/null +++ b/tests/CMakeProject/main.c @@ -0,0 +1,13 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +int main(void) { return 0; }