diff --git a/.bazelrc b/.bazelrc index c57089e7..54abdbd8 100644 --- a/.bazelrc +++ b/.bazelrc @@ -5,3 +5,12 @@ common --enable_bzlmod # Fail loudly on errors — makes CI logs readable. common --verbose_failures + +# Pin both host (exec-config) and target deployment targets so tools +# like SafeDITool stay runnable on macOS hosts older than the SDK +# used to build them. Without --host_macos_minimum_os, a build on a +# machine with Xcode 26 produces a SafeDITool binary that dyld +# refuses to load on a runner with an older macOS. Pattern matches +# swift-syntax's .bazelrc. +common --host_macos_minimum_os=11.0 +common --macos_minimum_os=11.0 diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml index 52b9a654..9b47993d 100644 --- a/.bcr/presubmit.yml +++ b/.bcr/presubmit.yml @@ -1,3 +1,34 @@ +# BCR presubmit configuration. Pattern adapted from swift-syntax +# (modules/swift-syntax/603.0.1/presubmit.yml) and swift-index-store +# (modules/swift-index-store/1.9.2/presubmit.yml) — same dep shape +# (swift-syntax + rules_swift on Bazel 9.x with Xcode 26 SDK on an +# older runner OS). +# +# `build_flags` per task: +# - --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 +# Stops Bazel from auto-detecting the host CC toolchain; without +# this, rules_swift's xcode_swift_toolchain falls through to +# `cc_toolchain.target_gnu_system_name` returning "local" on +# BCR's runner and analysis fails. Unanimous across the BCR +# Swift modules surveyed (swift-syntax, swift-index-store, +# swift-filename-matcher, asc_swift). Belt-and-suspenders with +# the `apple_support` bazel_dep we register in MODULE.bazel. +# - --host_macos_minimum_os=11.0 +# Pins the deployment target for tools built under exec config +# (e.g. SafeDITool itself), so a binary built with the macOS 26 +# SDK still runs on BCR's older runner OS. Without this, dyld +# refuses to load SafeDITool when the example task tries to +# execute it during code generation. 11.0 matches SafeDI's +# Package.swift declared platform — lowest reasonable value, +# and SafeDITool only needs to run, not exercise newer APIs. +# - --macos_minimum_os= +# Target-config deployment target. Differs per task: +# * verify_safedi_build: 11.0 — matches Package.swift, what +# SafeDI as a library claims to support. +# * bcr_test_module.build_example: 14.0 — the example app +# uses macOS 14+ SwiftUI APIs (LoggedInView's onChange, +# NameEntryView's TextField init), so it can't compile +# lower. This is the example app's requirement, not SafeDI's. matrix: platform: ["macos_arm64"] bazel: ["9.x"] @@ -7,6 +38,10 @@ tasks: name: "Build SafeDI targets (${{ platform }}, Bazel ${{ bazel }})" platform: ${{ platform }} bazel: ${{ bazel }} + build_flags: + - "--repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1" + - "--host_macos_minimum_os=11.0" + - "--macos_minimum_os=11.0" build_targets: - "@safedi//Sources/SafeDI:SafeDI" - "@safedi//Sources/SafeDICore:SafeDICore" @@ -23,6 +58,10 @@ bcr_test_module: name: "Build downstream example (${{ platform }}, Bazel ${{ bazel }})" platform: ${{ platform }} bazel: ${{ bazel }} + build_flags: + - "--repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1" + - "--host_macos_minimum_os=11.0" + - "--macos_minimum_os=14.0" build_targets: - "//Subproject:Subproject" - "//ExampleBazelIntegration:ExampleBazelIntegration" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de11f5bf..62d8621d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -254,6 +254,20 @@ jobs: bazel: name: Bazel Build on macOS + # General bazel sanity check on every PR. We can't perfectly + # mirror BCR's presubmit (https://registry.bazel.build/modules/safedi): + # BazelCI's BuildKite infrastructure is closed to non-`bazelbuild` + # projects (https://github.com/bazelbuild/continuous-integration/blob/main/.github/ISSUE_TEMPLATE/adding-your-project-to-bazel-ci.md), + # and any GH Actions runner we pick differs from BCR's image in + # ways we can't fully control (CC toolchain auto-detection, + # SDK-vs-runner-OS gap, exact Bazel point release). So treat BCR + # as the canonical macOS-on-older-OS check; this job catches + # general regressions before we publish. + # + # Keep `build_targets` aligned with `.bcr/presubmit.yml` so a + # divergence is visible in review. If you change BCR's task list, + # update this job too. + # # Pinned to Xcode 26.0 — matches BCR's `macos_arm64` runner # platform defined at https://github.com/bazelbuild/continuous-integration/blob/master/buildkite/bazelci.py runs-on: macos-26 @@ -275,11 +289,15 @@ jobs: # # Each command runs in a subshell so a `cd` for the example # build doesn't leak its cwd change into subsequent retries. - - name: Build Sources + - name: Build SafeDI targets (mirrors BCR `verify_safedi_build`) uses: ./.github/actions/retry with: - command: bazelisk build //Sources/... - - name: Build Example + # Kept on one line — the retry composite action splices + # `${{ inputs.command }}` directly into a bash `for` loop + # and tacks ` && break` onto the same source line, so a + # multi-line YAML command leaves `&& break` orphaned. + command: bazelisk build //Sources/SafeDI:SafeDI //Sources/SafeDICore:SafeDICore //Sources/SafeDIMacros:SafeDIMacros //Sources/SafeDITool:SafeDITool + - name: Build downstream example (mirrors BCR `bcr_test_module.build_example`) uses: ./.github/actions/retry with: command: (cd Examples/ExampleBazelIntegration && bazelisk build //Subproject:Subproject //ExampleBazelIntegration:ExampleBazelIntegration) diff --git a/Examples/ExampleBazelIntegration/.bazelrc b/Examples/ExampleBazelIntegration/.bazelrc index 3ce91d27..5e1e01fa 100644 --- a/Examples/ExampleBazelIntegration/.bazelrc +++ b/Examples/ExampleBazelIntegration/.bazelrc @@ -1 +1,12 @@ common --enable_bzlmod + +# Pin host (exec-config) deployment target low (11.0, matching +# SafeDI's Package.swift) so SafeDITool — built fresh under this +# module's exec config — stays runnable on hosts older than the +# SDK. The BCR macOS runner builds with Xcode 26 but executes on +# an older macOS; an unpinned default makes dyld refuse the binary. +common --host_macos_minimum_os=11.0 +# Target deployment target (14.0) is driven by the example app's +# own source: LoggedInView and NameEntryView use macOS 14+ APIs. +# This is the example app's requirement, not SafeDI's. +common --macos_minimum_os=14.0 diff --git a/Examples/ExampleBazelIntegration/MODULE.bazel.lock b/Examples/ExampleBazelIntegration/MODULE.bazel.lock index b51baa21..425cd094 100644 --- a/Examples/ExampleBazelIntegration/MODULE.bazel.lock +++ b/Examples/ExampleBazelIntegration/MODULE.bazel.lock @@ -567,7 +567,7 @@ "@@rules_swift_package_manager+//:extensions.bzl%swift_deps": { "general": { "bzlTransitiveDigest": "QPzonOjYtXIWyUjGi44AUsRd7aYzf/8r9Sn0ghJjwRk=", - "usagesDigest": "pQmj+Q+/edkevpSUCxREEcjQ29wuwHQKQwYUL/4vx/8=", + "usagesDigest": "HHSBXogfC27ANWGaOy8xxo1Wthz5SbilpIUNFnD00Dw=", "recordedInputs": [ "REPO_MAPPING:rules_swift_package_manager+,bazel_skylib bazel_skylib+", "REPO_MAPPING:rules_swift_package_manager+,bazel_tools bazel_tools", diff --git a/MODULE.bazel b/MODULE.bazel index 3fa3c723..9058e1be 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -15,6 +15,7 @@ module( bazel_dep(name = "rules_swift", version = "3.6.0", repo_name = "build_bazel_rules_swift") bazel_dep(name = "rules_swift_package_manager", version = "1.15.0") +bazel_dep(name = "apple_support", version = "1.24.2", repo_name = "build_bazel_apple_support") # Translate SPM dependencies into Bazel targets. The resulting # @swiftpkg_* repos are referenced from per-module BUILD.bazel files. diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 454f6e46..c8385b85 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -530,7 +530,7 @@ "@@rules_swift_package_manager+//:extensions.bzl%swift_deps": { "general": { "bzlTransitiveDigest": "QPzonOjYtXIWyUjGi44AUsRd7aYzf/8r9Sn0ghJjwRk=", - "usagesDigest": "Inz16quqqq9sQ7gZ28Q7sfWU1B/K9/hDAtYlxQOcJnA=", + "usagesDigest": "Cqv8otPjpwbQp42uEGj5z8Zkn1QTMFEkit5F8KDG2Ok=", "recordedInputs": [ "REPO_MAPPING:rules_swift_package_manager+,bazel_skylib bazel_skylib+", "REPO_MAPPING:rules_swift_package_manager+,bazel_tools bazel_tools",