Skip to content

Bazel: native build of SafeDI + safedi_compile rule + restored example#282

Draft
dfed wants to merge 7 commits intomainfrom
claude/bazel-example-project-d6f783
Draft

Bazel: native build of SafeDI + safedi_compile rule + restored example#282
dfed wants to merge 7 commits intomainfrom
claude/bazel-example-project-d6f783

Conversation

@dfed
Copy link
Copy Markdown
Owner

@dfed dfed commented Apr 22, 2026

Summary

Adds a complete Bazel-native story for SafeDI in one PR: (1) Bazel builds of the four source modules, (2) a user-facing safedi_compile rule that mirrors the Tuist plugin's helper, (3) a restored consumer example exercising the full pipeline.

Scope is consumer-build-only. Test targets and BCR publishing are deferred to follow-up PRs.

What's in it

1. Bzlmod scaffolding + native build (e873029)

  • MODULE.bazelrules_swift 3.6.0 + rules_swift_package_manager 1.15.0; translates swift-syntax / swift-argument-parser from Package.swift into Bazel targets.
  • Per-module BUILD.bazel:
    • //Sources/SafeDICore:SafeDICoreswift_library.
    • //Sources/SafeDIMacros:SafeDIMacrosswift_compiler_plugin. SPM's Sources/SafeDIMacros/SafeDICore symlink isn't followed by Bazel's glob, so a SafeDICoreSources filegroup hands the files over explicitly.
    • //Sources/SafeDI:SafeDIswift_library with SafeDIMacros wired as compiler plugin.
    • //Sources/SafeDITool:SafeDIToolswift_binary.
  • .bazelversion, .bazelrc, .bazelignore, MODULE.bazel.lock, root BUILD.bazel.
  • New bazel CI job on macos-26.

2. safedi_compile rule (5136cd9, latest)

bazel/safedi.bzl ships a single rule that runs SafeDITool generate --combined-output --module-info-output once per module and emits both artifacts:

load("@safedi//bazel:safedi.bzl", "safedi_compile")

safedi_compile(
    name = "Foo_safedi",
    srcs = glob([...]),
    dependents = ["//OtherModule:OtherModule_safedi"],  # optional
)
  • <rule>.swift (combined dependency tree + mocks + mock configuration) → exposed via DefaultInfo, so swift_library.srcs = glob([...]) + [":<rule>"] picks it up exactly like Tuist's SafeDI.generatedSource.
  • <rule>.safedi (module-info artifact) → carried via the new SafeDIInfo provider; cross-module consumers list the producer label in their own safedi_compile.dependents.

The rule mirrors the Tuist plugin helper that landed in #281 (SafeDI.preCompileScript(module:dependents:)). Previously this PR shipped two rules (safedi_module_info + safedi_generate); the collapse halves SafeDITool invocations for producer-with-mocks targets and gives a tighter API parallel with Tuist.

3. Restored Examples/ExampleBazelIntegration/

Bazel port of ExampleTuistIntegration's multi-target structure — same Swift sources (types, mocks, previews), different build system:

  • Two swift_library targets (//Subproject:Subproject + //ExampleBazelIntegration:ExampleBazelIntegration).
  • One safedi_compile per module with cross-module .safedi handoff via dependents.
  • MODULE.bazel uses bazel_dep(name = "safedi") + local_path_override(path = "../.."). The override applies only when this module is root (our CI, local dev); BCR presubmit (PR BCR: automate publishing SafeDI to the Bazel Central Registry #287) consuming the published source archive ignores it.
  • SafeDITool built from source (no prebuilt-fetcher).
  • CI bazel job extended to also build the example.

Not in it (deferred)

Test plan

  • bazelisk build //Sources/... from repo root — green.
  • cd Examples/ExampleBazelIntegration && bazelisk build //Subproject:Subproject //ExampleBazelIntegration:ExampleBazelIntegration — green; one SafeDITool invocation per module emitting both .safedi and .swift.
  • swift build --traits sourceBuild — green (SPM path unaffected).
  • ./CLI/lint.sh — clean.
  • bazel CI job passes on macos-26 (both stages).

🤖 Generated with Claude Code

@dfed dfed force-pushed the claude/bazel-example-project-d6f783 branch from f32076c to 1b399b0 Compare April 22, 2026 15:45
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (c2f83b4) to head (5136cd9).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##              main      #282   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           41        41           
  Lines         7043      7043           
=========================================
  Hits          7043      7043           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dfed dfed force-pushed the claude/bazel-example-project-d6f783 branch from eb3f8ed to 996cba5 Compare April 22, 2026 16:17
@dfed
Copy link
Copy Markdown
Owner Author

dfed commented Apr 22, 2026

@codex please review this PR — it's a Bazel port of #281 demonstrating the same .safedi cross-module handoff. Two notable design choices: (1) safedi_generate concatenates SafeDITool's per-root / per-mock output files into a single <rule_name>.swift so the rule's output is statically known at Bazel's analysis time without any hand-maintained filename list, and (2) scan + generate run back-to-back in one ctx.actions.run_shell because SafeDITool's scan-produced manifest encodes sandbox-absolute paths that don't survive across separate Bazel actions. Build passes locally (bazelisk clean --expunge && bazelisk build //..., ~3min first build); new spm-bazel-integration CI job is green.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

dfed added a commit that referenced this pull request Apr 23, 2026
## Summary

Adds an opt-in `--combined-output <path>` flag on the `generate`
subcommand that concatenates every Swift file the tool would otherwise
emit — each root's dependency tree, each mock, the shared
mock-configuration extensions — into a single file at the given path.

## Motivation

Build systems whose code-generation rules require every output to be
declared statically at analysis time (Bazel, Buck2) currently have two
bad choices with SafeDI:

1. Hand-maintain a list of per-root / per-mock filenames in the BUILD /
`*.bzl` file, edited every time a new `@Instantiable(isRoot: true)` or
`generateMock: true` type is added.
2. Do the concatenation themselves in a `ctx.actions.run_shell(...)` —
brings in shell escaping, manifest-path handling, sandbox-absolute
paths, and throwaway declared outputs.

`--combined-output` makes single-output rule shapes trivial: one action,
one declared output, `ctx.actions.run` instead of `run_shell`.

## What's here

- `@Option` on `Generate` with a clear help string.
- Accumulates each entry's body in discovery order (roots first, then
mocks, then shared mock configuration) and writes the file header
exactly once at the top of the combined output. Empty bodies are skipped
so the combined file doesn't contain duplicate headers with nothing
underneath.
- Composes with existing flags:
- **`--swift-manifest` / `--output-directory`** — manifest still drives
discovery; `--combined-output` only replaces placement.
- **`--module-info-output`** — a module can emit both a combined
`.swift` and a `.safedi` artifact.
- **Neither of the above set** — tool synthesizes a temp scratch
directory so the existing inline-scan path fires.
- Parse errors: `#error(...)` stub routes to `--combined-output` in
combined mode rather than each manifest entry's path.
- Two tests (root + mock happy path; empty-module header-only edge case)
in `Tests/SafeDIToolTests/SafeDIToolCombinedOutputTests.swift`. The test
helper gains a `buildCombinedOutput` parameter that downstream tests can
opt into.
- `Documentation/Manual.md` gains a subsection on the flag.

## Follow-ups (not in this PR)

- #282 will rebase onto main once this lands; its `safedi_generate`
Bazel rule becomes `ctx.actions.run` with `--combined-output
<declared>`, no concatenation, no `run_shell`.
- TODO: BCR submission + release automation for SafeDI.

## Test plan

- [x] `xcrun swift test --traits sourceBuild` — all 910 existing tests
still pass; 2 new tests pass.
- [x] `swift run swiftformat --lint Sources/ Tests/` clean.
- [ ] CI green on macOS + Linux.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dfed dfed force-pushed the claude/bazel-example-project-d6f783 branch from 996cba5 to 81702b9 Compare April 23, 2026 18:32
@dfed dfed changed the title Examples: add Bazel integration (ARM mac) Bazel: add native build of SafeDI Apr 23, 2026
@dfed dfed changed the base branch from claude/tuist-example-project-VXdUq to main April 23, 2026 18:33
@dfed
Copy link
Copy Markdown
Owner Author

dfed commented Apr 23, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@dfed dfed changed the title Bazel: add native build of SafeDI Bazel: native build of SafeDI + Starlark rules + restored example Apr 23, 2026
@dfed
Copy link
Copy Markdown
Owner Author

dfed commented Apr 23, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f56f3df2da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let package = Package(
name: "ExampleBazelIntegration",
dependencies: [
.package(url: "https://github.com/dfed/SafeDI.git", from: "2.0.0-beta-5"),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use local SafeDI source for in-repo Bazel example builds

The example is pinned to the released GitHub package (from: "2.0.0-beta-5"), so the CI Build Example step in .github/workflows/ci.yml validates an external release instead of the code in this repo. That means Bazel integration regressions introduced in unreleased local changes (including changes to bazel/safedi.bzl or SafeDITool) can slip through while this job still passes, defeating the purpose of running the example build in this repository.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — the commit that intended to delete Package.swift missed the stage (the file was AD in git status; git commit only committed the A, leaving the deletion unstaged). Fixed in d1bd9b5Package.swift / Package.resolved are gone, and the example now consumes SafeDI via bazel_dep + local_path_override pointing at the workspace root. Our CI's Build Example step now validates local SafeDI changes as intended.

@dfed
Copy link
Copy Markdown
Owner Author

dfed commented Apr 23, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9418578d6f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread bazel/safedi.bzl
Comment on lines +88 to +90
ctx.actions.run(
executable = ctx.executable._safeditool,
inputs = ctx.files.srcs + [input_csv] + extra_inputs,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Declare non-src inputs before running SafeDITool

safedi_generate only declares ctx.files.srcs (and optional module-info files) as action inputs, but SafeDITool can also parse files discovered via #SafeDIConfiguration(additionalDirectoriesToInclude:). In Bazel sandbox/remote execution, those discovered files are undeclared and therefore unavailable, so generation can fail or silently omit types for modules that rely on additional directories; the rule should add a scan/input-discovery phase (or explicit extra input attrs) before invoking generate.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 2842816 via documentation rather than rule-level support. Bazel's hermetic model requires every input declared up front, and #SafeDIConfiguration(additionalDirectoriesToInclude:) is fundamentally a runtime-directory-walk feature — rewiring it to play with Bazel's sandbox would either require users to hand over the same files twice (once via srcs, once via a new rule attr) or smuggle non-declared files past the sandbox, which would break remote execution. I documented the incompatibility prominently at the top of bazel/safedi.bzl, pointing users at srcs as the single hand-off for "here's every source SafeDITool should see". If this turns into a real pain point once users hit it, we can add a data-style attr in a follow-up — but for v1 the doc + Bazel's own loud errors (missing-include-directory surfaces as a SafeDITool failure, not silent divergence) should be enough.

@@ -0,0 +1,25 @@
load("@rules_swift//swift:swift.bzl", "swift_library")
load("//bazel:safedi.bzl", "safedi_generate")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use exported @Safedi rules in Bazel example

The example target loads a local copy of safedi.bzl instead of @safedi//bazel:safedi.bzl, so the CI “Build Example” job is not validating the newly exported consumer rules introduced in this commit. This allows regressions in the public SafeDI Bazel integration to slip through while the example still passes, because it exercises duplicated example-local Starlark instead of the module consumers are expected to load.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same commit-staging miss as the Package.swift thread. Fixed in d1bd9b5 — both Examples/ExampleBazelIntegration/ExampleBazelIntegration/BUILD.bazel and Subproject/BUILD.bazel now load("@safedi//bazel:safedi.bzl", ...) and depend on @safedi//Sources/SafeDI (not the old @swiftpkg_safedi). The example now exercises the exported consumer rules.

dfed added a commit that referenced this pull request Apr 23, 2026
Addresses codex P1 feedback on PR #282. `safedi_generate` declares
only `srcs` (and optional `module_infos`) as action inputs. If a
`#SafeDIConfiguration` in those sources uses
`additionalDirectoriesToInclude`, SafeDITool would try to walk those
paths at runtime — but Bazel's hermetic sandbox only exposes declared
inputs, so either SafeDITool can't find the directory (loud error)
or finds a subset of what SPM/Xcode would see (silent divergence).

Rather than build Starlark heuristics to detect the flag in Swift
sources, or silently smuggle extra files past Bazel's dependency
model, document the incompatibility at the top of `safedi.bzl` and
point users at `srcs` as the single hand-off for "here's every
source SafeDITool should see." Matches Bazel's hermetic idiom and
keeps the rule surface small.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dfed
Copy link
Copy Markdown
Owner Author

dfed commented Apr 23, 2026

@codex review — d1bd9b5 lands the staged-but-not-committed edits the first two P2s flagged; 2842816 documents the additionalDirectoriesToInclude incompatibility for the P1.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@dfed
Copy link
Copy Markdown
Owner Author

dfed commented May 2, 2026

@codex review

dfed and others added 6 commits May 1, 2026 22:19
…SafeDITool)

Provides Bazel-native build targets for SafeDI's four source modules
as an alternative to SPM. Downstream Bazel users can now depend on
SafeDI via `@safedi//Sources/SafeDI:SafeDI` without routing through
rules_swift_package_manager to consume SPM.

## What's in it

- **Root bzlmod scaffolding** — `MODULE.bazel`, `MODULE.bazel.lock`,
  `BUILD.bazel`, `.bazelversion` (9.1.0), `.bazelrc`, `.bazelignore`.
- **Per-module `BUILD.bazel`** for the four source targets:
  - `//Sources/SafeDICore:SafeDICore` — swift_library, depends on
    SwiftSyntax/SwiftDiagnostics/SwiftSyntaxBuilder.
  - `//Sources/SafeDIMacros:SafeDIMacros` — swift_compiler_plugin.
    Inlines SafeDICore's sources into the same module via a
    `SafeDICoreSources` filegroup (SPM does this via the
    `Sources/SafeDIMacros/SafeDICore` symlink; Bazel's glob doesn't
    follow symlinked directories, so we hand the files over
    explicitly).
  - `//Sources/SafeDI:SafeDI` — swift_library, wires SafeDIMacros as
    its compiler plugin.
  - `//Sources/SafeDITool:SafeDITool` — swift_binary, depends on
    SafeDICore + ArgumentParser + SwiftParser.
- **External deps via rules_swift_package_manager** — reads
  `Package.swift` / `Package.resolved` at bzlmod resolution and
  generates `@swiftpkg_swift_syntax` / `@swiftpkg_swift_argument_parser`
  targets. Keeps SPM and Bazel in sync from a single manifest.
- **CI job** — new `bazel` job in `.github/workflows/ci.yml` runs
  `bazelisk build //Sources/...` on `macos-26`.

## Not in it (deferred)

- Test targets (SafeDITests, SafeDICoreTests, SafeDIMacrosTests,
  SafeDIToolTests) — build-only first pass; tests can be added later
  once the production build is proven.
- Publishing to the Bazel Central Registry — separate issue.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ships the Starlark surface that downstream Bazel consumers need to
actually use SafeDI (not just link against it).

## New

- `bazel/safedi.bzl` — two rules:
  - `safedi_module_info(srcs)` — runs `SafeDITool generate
    --module-info-output` and publishes the `.safedi` artifact for
    cross-module consumers.
  - `safedi_generate(srcs, module_infos)` — runs `SafeDITool generate
    --combined-output` and emits a single declared `<rule_name>.swift`.
    The `--combined-output` mode (landed in #283) is what makes
    Bazel-native codegen clean: one statically-known output
    regardless of how many `@Instantiable(isRoot:)` /
    `generateMock: true` types the module declares.
  Both default `_safeditool` to `Label("//Sources/SafeDITool:SafeDITool")`,
  so consumers get the Bazel-built tool for free — no prebuilt bundle
  fetching.
- `bazel/BUILD.bazel` — exports `safedi.bzl` for `load("@safedi//bazel:safedi.bzl", ...)`.

## Restored example

Brings `Examples/ExampleBazelIntegration/` back, adapted for the new
world:

- **Dep resolution via `bazel_dep` + `local_path_override`.** Dropped
  `rules_swift_package_manager` + a bespoke `Package.swift`. The
  example's `MODULE.bazel` declares `bazel_dep(name = "safedi", ...)`
  and `local_path_override`s to `../..`. `local_path_override` only
  applies when this module is root (our CI, local dev) — when BCR
  eventually runs presubmit against a published source archive, it
  ignores the override and resolves `safedi` from the registry. Same
  file works in both contexts.
- **SafeDITool from source.** Dropped `bazel/safeditool_extension.bzl`
  and its prebuilt artifact-bundle fetch. `safedi_generate` /
  `safedi_module_info` reach `@safedi//Sources/SafeDITool:SafeDITool`,
  which Bazel builds once and caches.
- **Load site updated** — `@safedi//bazel:safedi.bzl` instead of a
  local `bazel/safedi.bzl` copy.

README reflects the new build model.

## CI

Extended the `bazel` job in `.github/workflows/ci.yml` to also build
the example (`cd Examples/ExampleBazelIntegration && bazelisk build
//Subproject:Subproject //ExampleBazelIntegration:ExampleBazelIntegration`)
so rule regressions get caught on every PR.

## Verified locally

- `bazelisk build //Sources/...` (outer workspace) — green.
- `cd Examples/ExampleBazelIntegration && bazelisk build //Subproject:Subproject //ExampleBazelIntegration:ExampleBazelIntegration` — green, emits one 190-line `safedi_generated.swift` containing the root + mocks + mock configuration.
- `swift build --traits sourceBuild` — green (SPM path unaffected).
- `./CLI/lint.sh` — clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The link-check job failed because `../ExampleTuistIntegration` only
exists on the still-open PR #281 branch, not on main. Reference the
Tuist example by name without the hyperlink — avoids cross-PR
coupling without losing the intent.
Earlier commits `f56f3df` / `9418578` restored the example from the
pre-reset commit via `git checkout 996cba5 -- Examples/...`, which
staged the restore as a fresh addition. Subsequent Write/Edit calls
modified the working tree but those edits weren't re-staged before
committing — the committed state was the *restored* pre-repurpose
content, not the adapted content. Codex correctly flagged this.

This commit lands the actually-intended state:

- `Package.swift` / `Package.resolved` — deleted. We now consume
  SafeDI via `bazel_dep`, not via `rules_swift_package_manager`.
- `bazel/{BUILD.bazel,safedi.bzl,safeditool_extension.bzl}` — deleted.
  Rules live at `@safedi//bazel:safedi.bzl`; the prebuilt-fetcher
  extension is obsolete now that SafeDITool builds from source.
- `MODULE.bazel` — switched to `bazel_dep(name = "safedi", ...)` +
  `local_path_override(path = "../..")`. Same file works in our CI
  and in downstream BCR presubmit.
- `BUILD.bazel` — cleared root-file exports (Package.swift/Resolved
  are gone).
- `ExampleBazelIntegration/BUILD.bazel` + `Subproject/BUILD.bazel` —
  load rules from `@safedi//bazel:safedi.bzl` (not a local copy) and
  depend on `@safedi//Sources/SafeDI` (not `@swiftpkg_safedi//:SafeDI`).
- `.gitignore` — stopped ignoring `MODULE.bazel.lock`; the lock file
  is now committed for reproducibility (matches the root workspace).

Addresses the "Use exported @Safedi rules in Bazel example" and
"Use local SafeDI source for in-repo Bazel example builds" codex
comments — both were pointing at stale-in-repo state that the
earlier commits had intended but failed to actually land.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses codex P1 feedback on PR #282. `safedi_generate` declares
only `srcs` (and optional `module_infos`) as action inputs. If a
`#SafeDIConfiguration` in those sources uses
`additionalDirectoriesToInclude`, SafeDITool would try to walk those
paths at runtime — but Bazel's hermetic sandbox only exposes declared
inputs, so either SafeDITool can't find the directory (loud error)
or finds a subset of what SPM/Xcode would see (silent divergence).

Rather than build Starlark heuristics to detect the flag in Swift
sources, or silently smuggle extra files past Bazel's dependency
model, document the incompatibility at the top of `safedi.bzl` and
point users at `srcs` as the single hand-off for "here's every
source SafeDITool should see." Matches Bazel's hermetic idiom and
keeps the rule surface small.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The earlier copy ("would regress the 'no hardcoded filenames'
property we cared about for Tuist") singled out Tuist as the source
of that concern, but every build system worth using cares about not
making authors hand-maintain output lists. Recast as a generic
build-author-burden argument.
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…safedi_compile

Mirrors the shape of the Tuist plugin helper that landed in #281
(`SafeDI.preCompileScript(module:dependents:)`), which writes both
the `.safedi` module-info artifact and the combined `.swift` source
in a single SafeDITool invocation. The previous Bazel split forced
producer-with-mocks targets to invoke SafeDITool twice on the same
sources for no gain.

## Rule shape

```starlark
load("@safedi//bazel:safedi.bzl", "safedi_compile")

safedi_compile(
    name = "Foo_safedi",
    srcs = glob([...]),
    dependents = ["//OtherModule:OtherModule_safedi"],  # optional
)
```

Outputs two artifacts:

- `<rule>.swift` — combined dependency tree + mocks + mock
  configuration. Exposed via `DefaultInfo`, so a downstream
  `swift_library.srcs = glob([...]) + [":<rule>"]` picks it up
  exactly the way Tuist's `SafeDI.generatedSource` adds the
  generated source to a target.
- `<rule>.safedi` — module-info artifact. Carried via the new
  `SafeDIInfo` provider; cross-module consumers list the producer
  rule label in their own `safedi_compile.dependents`.

## Wins

- Halves SafeDITool invocations for any producer-with-mocks target.
- Tighter API parallel with Tuist (one call site per module).
- The `providers = [SafeDIInfo]` constraint on `dependents` blocks
  passing arbitrary labels — type-safer than the old
  `allow_files = [".safedi"]` constraint.

## Updates

- `bazel/safedi.bzl` — single `safedi_compile` rule + `SafeDIInfo`
  provider. File-level docstring rewritten to call out the Tuist
  helper parallel.
- `bazel/BUILD.bazel` — comment refers to `safedi_compile`.
- `Examples/ExampleBazelIntegration/Subproject/BUILD.bazel` — uses
  `safedi_compile`; the Subproject's own `swift_library` now
  includes its `:Subproject_safedi` output (matters once mocks land
  for Subproject, harmless before).
- `Examples/ExampleBazelIntegration/ExampleBazelIntegration/BUILD.bazel`
  — uses `safedi_compile` with `dependents` instead of
  `module_infos`.
- `Examples/ExampleBazelIntegration/README.md` — comparison table
  + prose updated to reference `safedi_compile`.

## Verified

- `bazelisk build //Sources/...` from repo root — green.
- `cd Examples/ExampleBazelIntegration && bazelisk build //...` —
  green.
- `swift build --traits sourceBuild` — green.
- `./CLI/lint.sh` — clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dfed dfed force-pushed the claude/bazel-example-project-d6f783 branch from 7547902 to 5136cd9 Compare May 2, 2026 05:23
@dfed dfed changed the title Bazel: native build of SafeDI + Starlark rules + restored example Bazel: native build of SafeDI + safedi_compile rule + restored example May 2, 2026
@dfed
Copy link
Copy Markdown
Owner Author

dfed commented May 2, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant