From 8c218554f5167eec47d106fdfb3af180aaf75ba6 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 5 May 2026 13:47:21 -0700 Subject: [PATCH 1/7] Fix BCR presubmit: declare apple_support and pin host deployment target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BCR presubmit (PR #8711) failed in two distinct ways that didn't surface in our GitHub Actions CI: 1. `Build SafeDI targets` failed analysis with `Invalid target triple: local, this likely means you're using the wrong CC toolchain, make sure you include apple_support in your project`. rules_swift 3.x on Bazel 9 needs `apple_support` declared at the *root* MODULE.bazel so the proper Apple-aware CC toolchain is registered. Without it, Bazel falls back to auto-config that returns the literal string `"local"` for `cc_toolchain.target_gnu_system_name`. Our example's MODULE.bazel had `apple_support`; SafeDI's didn't. The auto-config path happened to produce a parseable triple on `macos-26`, so the issue was invisible on our runner but reliably broken on BCR's. 2. `Build downstream example` failed at runtime with `dyld: Library not loaded ... built for macOS 26.0 which is newer than running OS`. SafeDITool got compiled fresh under exec config with Xcode 26's SDK, pinning macOS 26 as the deployment target. BCR's runner has Xcode 26 installed but runs an older OS. Our `macos-26` runner actually runs macOS 26, so SDK and runner aligned and the issue couldn't surface. swift-syntax solves the same problem with `--host_macos_minimum_os` in its `.bazelrc`. Fixes: - Add `apple_support` `bazel_dep` to root MODULE.bazel. - Set `--host_macos_minimum_os=14.0` in both root and example `.bazelrc` so tools (exec config) get a backwards-compatible deployment target. The example's `.bazelrc` matters because it becomes the root when the BCR `bcr_test_module` task runs and downstream consumers don't inherit our root `.bazelrc`. - Mirror BCR's `verify_safedi_build` target list explicitly in ci.yml (was `//Sources/...`) and add a sync pointer at the top of the `bazel` job so we keep the two configs aligned. These fixes need to ship in 2.0.0-rc-3 — the BCR PR is built from the published 2.0.0-rc-2 tarball and can't pick them up retroactively. Co-Authored-By: Claude Opus 4.7 (1M context) --- .bazelrc | 6 +++++ .github/workflows/ci.yml | 23 +++++++++++++++---- Examples/ExampleBazelIntegration/.bazelrc | 6 +++++ .../ExampleBazelIntegration/MODULE.bazel.lock | 2 +- MODULE.bazel | 1 + MODULE.bazel.lock | 2 +- 6 files changed, 33 insertions(+), 7 deletions(-) diff --git a/.bazelrc b/.bazelrc index c57089e7..7534af36 100644 --- a/.bazelrc +++ b/.bazelrc @@ -5,3 +5,9 @@ common --enable_bzlmod # Fail loudly on errors — makes CI logs readable. common --verbose_failures + +# Pin host (exec-config) deployment target so tools like SafeDITool stay +# runnable on macOS hosts older than the SDK used to build them. Without +# this, a build on a machine with Xcode 26 produces a SafeDITool binary +# that dyld refuses to load on a runner with an older macOS. +common --host_macos_minimum_os=14.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de11f5bf..f338ec08 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -254,8 +254,16 @@ jobs: bazel: name: Bazel Build on macOS - # 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 + # This job mirrors BCR's presubmit so issues that block our BCR + # publish (https://registry.bazel.build/modules/safedi) surface + # here first. Keep it in sync with `.bcr/presubmit.yml`: + # - Bazel version: matches `.bazelversion` (consumed by bazelisk) + # and BCR's `bazel: ["9.x"]` matrix entry. + # - Xcode 26.0 — matches BCR's `macos_arm64` runner platform + # defined at https://github.com/bazelbuild/continuous-integration/blob/master/buildkite/bazelci.py + # - `build_targets` here mirror the two BCR tasks + # (`verify_safedi_build` and `bcr_test_module.build_example`). + # If you change BCR's matrix or task list, update this job too. runs-on: macos-26 permissions: contents: read @@ -275,11 +283,16 @@ 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 + 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..af9a51a9 100644 --- a/Examples/ExampleBazelIntegration/.bazelrc +++ b/Examples/ExampleBazelIntegration/.bazelrc @@ -1 +1,7 @@ common --enable_bzlmod + +# Pin host (exec-config) deployment target 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, so an unpinned default makes dyld refuse the binary. +common --host_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", From c1c4927ce678398dc48bc3b80ccae0ae2087a463 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 5 May 2026 14:09:30 -0700 Subject: [PATCH 2/7] ci: keep bazel build command on a single line for retry action The retry composite action splices `${{ inputs.command }}` directly into a bash `for` loop and appends ` && break` on the same source line. A multi-line YAML command (via `|`) leaves `&& break` orphaned, producing a syntax error at line 9. Inline the target list so the command stays on a single line. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/ci.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f338ec08..8a0808d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -286,12 +286,11 @@ jobs: - name: Build SafeDI targets (mirrors BCR `verify_safedi_build`) uses: ./.github/actions/retry with: - command: | - bazelisk build \ - //Sources/SafeDI:SafeDI \ - //Sources/SafeDICore:SafeDICore \ - //Sources/SafeDIMacros:SafeDIMacros \ - //Sources/SafeDITool:SafeDITool + # 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: From a26d7b0eb529d9efd0c605533822b474c351632f Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 5 May 2026 15:19:39 -0700 Subject: [PATCH 3/7] ci(bazel): run on macos-15 to mirror BCR's SDK-newer-than-OS shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BCR's `macos_arm64` runner has Xcode 26 installed but its OS is older than macOS 26, which is exactly why a SafeDITool binary built with macOS 26 deployment target fails dyld at execution time on BCR. Running our bazel job on `macos-26` aligned SDK and runner OS, so a deployment-target regression couldn't fail here. Switch to `macos-15` (Sequoia) and keep `xcode-select` pointed at Xcode 26.0 — the runner image ships it at the same path as before (/Applications/Xcode_26.0.app, a symlink to 26.0.1). This reproduces BCR's SDK-vs-runner gap locally, so a future regression on `--host_macos_minimum_os` (or any other host-deployment-target lever) fails our CI before it fails BCR. Also flag the `9.x` vs `.bazelversion` drift risk in the comment, so a future maintainer knows to bump `.bazelversion` when BCR's `9.x` matrix selector starts resolving to something newer than ours. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/ci.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a0808d6..e031a6a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -258,13 +258,22 @@ jobs: # publish (https://registry.bazel.build/modules/safedi) surface # here first. Keep it in sync with `.bcr/presubmit.yml`: # - Bazel version: matches `.bazelversion` (consumed by bazelisk) - # and BCR's `bazel: ["9.x"]` matrix entry. + # and BCR's `bazel: ["9.x"]` matrix entry. NOTE: `9.x` resolves + # to whatever 9.x point release is current; we pin a specific + # version. If BCR ships a newer 9.x before we bump, drift is + # possible — bump `.bazelversion` when you notice the gap. # - Xcode 26.0 — matches BCR's `macos_arm64` runner platform # defined at https://github.com/bazelbuild/continuous-integration/blob/master/buildkite/bazelci.py + # - Runner OS: `macos-15` (Sequoia), NOT `macos-26`. BCR's + # `macos_arm64` runs an OS older than the Xcode 26 SDK it has + # installed, so a binary built for macOS 26 deployment target + # fails dyld at execution time. Running on `macos-26` masks + # that failure (SDK == runner OS); `macos-15` reproduces the + # SDK-vs-runner gap so we catch it locally. # - `build_targets` here mirror the two BCR tasks # (`verify_safedi_build` and `bcr_test_module.build_example`). # If you change BCR's matrix or task list, update this job too. - runs-on: macos-26 + runs-on: macos-15 permissions: contents: read steps: From 081bde20e2dbfa146491c68ca05ccf4849f5c4fa Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 5 May 2026 15:43:54 -0700 Subject: [PATCH 4/7] ci(bazel): revert to macos-26, document BCR as canonical mac check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switching to macos-15 to reproduce BCR's SDK-vs-runner-OS gap was reinventing a wheel we can't actually finish: BazelCI's BuildKite infrastructure (the right way to mirror BCR exactly) is closed to non-`bazelbuild` projects, and any GH Actions runner we pick will still differ from BCR's image in ways we can't fully control — CC toolchain auto-detection, exact Bazel point release, etc. Treat BCR's presubmit as the canonical macOS-on-older-OS check instead. This job runs on macos-26 as a general bazel sanity gate; target list stays aligned with `.bcr/presubmit.yml` so divergence is visible in review. The two real fixes — `apple_support` and `--host_macos_minimum_os=14.0` — still ship and address the underlying BCR failures. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/ci.yml | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e031a6a6..62d8621d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -254,26 +254,23 @@ jobs: bazel: name: Bazel Build on macOS - # This job mirrors BCR's presubmit so issues that block our BCR - # publish (https://registry.bazel.build/modules/safedi) surface - # here first. Keep it in sync with `.bcr/presubmit.yml`: - # - Bazel version: matches `.bazelversion` (consumed by bazelisk) - # and BCR's `bazel: ["9.x"]` matrix entry. NOTE: `9.x` resolves - # to whatever 9.x point release is current; we pin a specific - # version. If BCR ships a newer 9.x before we bump, drift is - # possible — bump `.bazelversion` when you notice the gap. - # - Xcode 26.0 — matches BCR's `macos_arm64` runner platform - # defined at https://github.com/bazelbuild/continuous-integration/blob/master/buildkite/bazelci.py - # - Runner OS: `macos-15` (Sequoia), NOT `macos-26`. BCR's - # `macos_arm64` runs an OS older than the Xcode 26 SDK it has - # installed, so a binary built for macOS 26 deployment target - # fails dyld at execution time. Running on `macos-26` masks - # that failure (SDK == runner OS); `macos-15` reproduces the - # SDK-vs-runner gap so we catch it locally. - # - `build_targets` here mirror the two BCR tasks - # (`verify_safedi_build` and `bcr_test_module.build_example`). - # If you change BCR's matrix or task list, update this job too. - runs-on: macos-15 + # 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 permissions: contents: read steps: From 11e11a154524641f416e4ee200c6aa0cf7f5dd00 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 5 May 2026 15:51:29 -0700 Subject: [PATCH 5/7] Align BCR config with swift-syntax: build_flags + macos_minimum_os MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit swift-syntax (modules/swift-syntax/603.0.1/presubmit.yml in BCR) is the closest prior art for our setup — same dep shape (swift-syntax + rules_swift on Bazel 9.x with Xcode 26 SDK on an older runner OS). Their BCR `presubmit.yml` carries three flags we were missing: --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 Stops Bazel from auto-detecting the host CC toolchain. This is a more direct fix for Failure 1 than relying on `apple_support` to register the right toolchain — without auto-detection, `xcode_swift_toolchain` never falls through to the broken `target_gnu_system_name == "local"` path. Belt-and-suspenders with the apple_support bazel_dep we already have. --host_macos_minimum_os=14.0 The dyld fix for Failure 2 (matches what we already had in .bazelrc). --macos_minimum_os=14.0 Same idea for target-config builds. Matches swift-syntax. Carrying the flags in `.bcr/presubmit.yml`'s `build_flags` field is the cleaner channel because BCR's test runner passes them directly, no `.bazelrc` propagation guesswork — sidesteps "whose .bazelrc is root?" entirely. Also mirrored both flags in our and the example's `.bazelrc` for local dev consistency. Verified on a local build: `otool -l SafeDITool` reports `minos 14.0, sdk 26.4` — the flag actually takes effect in the binary's LC_BUILD_VERSION, so SafeDITool is loadable on macOS 14+ regardless of which SDK built it. Co-Authored-By: Claude Opus 4.7 (1M context) --- .bazelrc | 11 ++++++--- .bcr/presubmit.yml | 30 +++++++++++++++++++++++ Examples/ExampleBazelIntegration/.bazelrc | 11 ++++++--- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/.bazelrc b/.bazelrc index 7534af36..37c96cd5 100644 --- a/.bazelrc +++ b/.bazelrc @@ -6,8 +6,11 @@ common --enable_bzlmod # Fail loudly on errors — makes CI logs readable. common --verbose_failures -# Pin host (exec-config) deployment target so tools like SafeDITool stay -# runnable on macOS hosts older than the SDK used to build them. Without -# this, a build on a machine with Xcode 26 produces a SafeDITool binary -# that dyld refuses to load on a runner with an older macOS. +# 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=14.0 +common --macos_minimum_os=14.0 diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml index 52b9a654..4e77c36b 100644 --- a/.bcr/presubmit.yml +++ b/.bcr/presubmit.yml @@ -1,3 +1,25 @@ +# BCR presubmit configuration. Mirrors the pattern used by +# swift-syntax (modules/swift-syntax/603.0.1/presubmit.yml in the +# bazel-central-registry repo) — same dep shape (swift-syntax + +# rules_swift on Bazel 9.x with Xcode 26 SDK on an older runner OS), +# same set of `build_flags` needed. +# +# The `build_flags` block is the substantive part: +# - --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. (Belt-and-suspenders with the +# `apple_support` bazel_dep we register in MODULE.bazel.) +# - --host_macos_minimum_os=14.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. +# - --macos_minimum_os=14.0 +# Same idea for target-config builds. Matches swift-syntax's +# configuration for consistency. matrix: platform: ["macos_arm64"] bazel: ["9.x"] @@ -7,6 +29,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=14.0" + - "--macos_minimum_os=14.0" build_targets: - "@safedi//Sources/SafeDI:SafeDI" - "@safedi//Sources/SafeDICore:SafeDICore" @@ -23,6 +49,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=14.0" + - "--macos_minimum_os=14.0" build_targets: - "//Subproject:Subproject" - "//ExampleBazelIntegration:ExampleBazelIntegration" diff --git a/Examples/ExampleBazelIntegration/.bazelrc b/Examples/ExampleBazelIntegration/.bazelrc index af9a51a9..d60f11f8 100644 --- a/Examples/ExampleBazelIntegration/.bazelrc +++ b/Examples/ExampleBazelIntegration/.bazelrc @@ -1,7 +1,10 @@ common --enable_bzlmod -# Pin host (exec-config) deployment target 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, so an unpinned default makes dyld refuse the binary. +# Pin both host (exec-config) and target deployment targets 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, so an unpinned +# default makes dyld refuse the binary. Pattern matches +# swift-syntax's .bazelrc. common --host_macos_minimum_os=14.0 +common --macos_minimum_os=14.0 From ca3d6a60cea3f827e9264c1f123c21245089936b Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 5 May 2026 16:09:27 -0700 Subject: [PATCH 6/7] Lower deployment target to 12.0, matching swift-index-store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Surveyed five BCR-published Swift modules for prior art on the exact deployment-target value: swift-syntax 603.0.1: --macos_minimum_os=13.0 swift-index-store 1.9.2: --macos_minimum_os=12.0 swift-filename-matcher: not pinned asc_swift 1.6.0: not pinned rules_xcodeproj 4.0.1: not pinned The previous 14.0 was a guess. Lowering to 12.0 (swift-index-store's value, since it's also a Swift binary tool) is strictly more permissive — anything that runs on 12.0 runs on 14.0+ — and matches the lowest empirically-validated BCR value in the survey. Eliminates the "what if BCR's runner OS is older than 14?" risk. Verified: `otool -l SafeDITool` after rebuild reports `minos 12.0`. Co-Authored-By: Claude Opus 4.7 (1M context) --- .bazelrc | 4 ++-- .bcr/presubmit.yml | 12 ++++++------ Examples/ExampleBazelIntegration/.bazelrc | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.bazelrc b/.bazelrc index 37c96cd5..15134861 100644 --- a/.bazelrc +++ b/.bazelrc @@ -12,5 +12,5 @@ common --verbose_failures # 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=14.0 -common --macos_minimum_os=14.0 +common --host_macos_minimum_os=12.0 +common --macos_minimum_os=12.0 diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml index 4e77c36b..ae90ef9e 100644 --- a/.bcr/presubmit.yml +++ b/.bcr/presubmit.yml @@ -11,13 +11,13 @@ # `cc_toolchain.target_gnu_system_name` returning "local" on # BCR's runner and analysis fails. (Belt-and-suspenders with the # `apple_support` bazel_dep we register in MODULE.bazel.) -# - --host_macos_minimum_os=14.0 +# - --host_macos_minimum_os=12.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. -# - --macos_minimum_os=14.0 +# - --macos_minimum_os=12.0 # Same idea for target-config builds. Matches swift-syntax's # configuration for consistency. matrix: @@ -31,8 +31,8 @@ tasks: bazel: ${{ bazel }} build_flags: - "--repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1" - - "--host_macos_minimum_os=14.0" - - "--macos_minimum_os=14.0" + - "--host_macos_minimum_os=12.0" + - "--macos_minimum_os=12.0" build_targets: - "@safedi//Sources/SafeDI:SafeDI" - "@safedi//Sources/SafeDICore:SafeDICore" @@ -51,8 +51,8 @@ bcr_test_module: bazel: ${{ bazel }} build_flags: - "--repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1" - - "--host_macos_minimum_os=14.0" - - "--macos_minimum_os=14.0" + - "--host_macos_minimum_os=12.0" + - "--macos_minimum_os=12.0" build_targets: - "//Subproject:Subproject" - "//ExampleBazelIntegration:ExampleBazelIntegration" diff --git a/Examples/ExampleBazelIntegration/.bazelrc b/Examples/ExampleBazelIntegration/.bazelrc index d60f11f8..6da99f9d 100644 --- a/Examples/ExampleBazelIntegration/.bazelrc +++ b/Examples/ExampleBazelIntegration/.bazelrc @@ -6,5 +6,5 @@ common --enable_bzlmod # with Xcode 26 but executes on an older macOS, so an unpinned # default makes dyld refuse the binary. Pattern matches # swift-syntax's .bazelrc. -common --host_macos_minimum_os=14.0 -common --macos_minimum_os=14.0 +common --host_macos_minimum_os=12.0 +common --macos_minimum_os=12.0 From 8f16e1dd74b7096e44d38909ffa1ef36ec11aa74 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 5 May 2026 16:19:38 -0700 Subject: [PATCH 7/7] Match deployment targets to actual platform support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously pinned 12.0 (then 14.0) everywhere — both higher than SafeDI's declared platform support (.macOS(.v11) in Package.swift). The library would have shipped tagged macOS 12+ via the Bazel path, inconsistent with what we publish for SPM consumers. Split host vs. target deployment targets per real requirements: Host (--host_macos_minimum_os=11.0): SafeDITool only needs to *run* on the build machine, not exercise newer APIs. Lowest reasonable value; ensures the binary loads on whatever older macOS BCR's runner happens to be on. Matches Package.swift. Target (--macos_minimum_os): * SafeDI library: 11.0 — matches Package.swift, what we claim to support. * ExampleBazelIntegration: 14.0 — driven by the example app's own source. LoggedInView's onChange(of:initial:_:) is macOS 14+, NameEntryView's TextField init is macOS 12+. The example app's deployment requirements are independent of SafeDI's. Verified `otool -l SafeDITool` reports `minos 11.0` after rebuild. Both root SafeDI build and example build pass locally. Co-Authored-By: Claude Opus 4.7 (1M context) --- .bazelrc | 4 +-- .bcr/presubmit.yml | 43 ++++++++++++++--------- Examples/ExampleBazelIntegration/.bazelrc | 18 +++++----- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/.bazelrc b/.bazelrc index 15134861..54abdbd8 100644 --- a/.bazelrc +++ b/.bazelrc @@ -12,5 +12,5 @@ common --verbose_failures # 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=12.0 -common --macos_minimum_os=12.0 +common --host_macos_minimum_os=11.0 +common --macos_minimum_os=11.0 diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml index ae90ef9e..9b47993d 100644 --- a/.bcr/presubmit.yml +++ b/.bcr/presubmit.yml @@ -1,25 +1,34 @@ -# BCR presubmit configuration. Mirrors the pattern used by -# swift-syntax (modules/swift-syntax/603.0.1/presubmit.yml in the -# bazel-central-registry repo) — same dep shape (swift-syntax + -# rules_swift on Bazel 9.x with Xcode 26 SDK on an older runner OS), -# same set of `build_flags` needed. +# 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). # -# The `build_flags` block is the substantive part: +# `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. (Belt-and-suspenders with the -# `apple_support` bazel_dep we register in MODULE.bazel.) -# - --host_macos_minimum_os=12.0 +# 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. -# - --macos_minimum_os=12.0 -# Same idea for target-config builds. Matches swift-syntax's -# configuration for consistency. +# 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"] @@ -31,8 +40,8 @@ tasks: bazel: ${{ bazel }} build_flags: - "--repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1" - - "--host_macos_minimum_os=12.0" - - "--macos_minimum_os=12.0" + - "--host_macos_minimum_os=11.0" + - "--macos_minimum_os=11.0" build_targets: - "@safedi//Sources/SafeDI:SafeDI" - "@safedi//Sources/SafeDICore:SafeDICore" @@ -51,8 +60,8 @@ bcr_test_module: bazel: ${{ bazel }} build_flags: - "--repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1" - - "--host_macos_minimum_os=12.0" - - "--macos_minimum_os=12.0" + - "--host_macos_minimum_os=11.0" + - "--macos_minimum_os=14.0" build_targets: - "//Subproject:Subproject" - "//ExampleBazelIntegration:ExampleBazelIntegration" diff --git a/Examples/ExampleBazelIntegration/.bazelrc b/Examples/ExampleBazelIntegration/.bazelrc index 6da99f9d..5e1e01fa 100644 --- a/Examples/ExampleBazelIntegration/.bazelrc +++ b/Examples/ExampleBazelIntegration/.bazelrc @@ -1,10 +1,12 @@ common --enable_bzlmod -# Pin both host (exec-config) and target deployment targets 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, so an unpinned -# default makes dyld refuse the binary. Pattern matches -# swift-syntax's .bazelrc. -common --host_macos_minimum_os=12.0 -common --macos_minimum_os=12.0 +# 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