Skip to content

Phase 6: consumer Bazel rules + hermetic in-build generation - #14

Merged
aaylward merged 6 commits into
mainfrom
claude/smithy-cpp-generator-plan-fpeqzt
Jul 7, 2026
Merged

aaylward merged 6 commits into
mainfrom
claude/smithy-cpp-generator-plan-fpeqzt

Conversation

@aaylward

@aaylward aaylward commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Phase 6: first-class Bazel consumption (BCR publishing deferred)

Consumers now write smithy_cpp_client_library / smithy_cpp_server_library / smithy_cpp_types_library in their own Bazel 8/9 module and get ordinary cc_library targets — generation runs inside the build graph as a hermetic Java action (correct caching, remote-execution compatible), never as a "run this script first" step. No Gradle anywhere in the consumer path.

How it works

  • //codegen:generator: the generator compiled from the same sources the Gradle build uses, via rules_java + rules_jvm_external with a pinned Maven lockfile (maven_install.json). Gradle remains the dev workflow (unit tests, fixture regeneration); this target exists for consumers and doubles as the CLI (bazel run //codegen:generator -- --model … --mode both --out …).
  • New generator knobs make per-target output sets predictable: --mode types|client|server|both gates client/server emission, --emit-build-file false suppresses the generated BUILD.bazel, and --model is now repeatable for multi-file models.
  • bazel/defs.bzl: the rules declare exact outputs from namespace + mode, split headers/sources via output groups, and wire include paths so dependents just #include "<ns>/client.h". The service's protocol comes from the model itself.

Tests, both flavors (as requested)

  • Rule unit tests (//bazel/tests, rules_testing analysis tests): pin the generated file set per mode, the SmithyCppGenerate action's arguments (--mode, --emit-build-file false, --service), and the cc_library include wiring. The rule instances they inspect also compile as an in-tree build test.
  • Out-of-tree integration (examples/bazel-consumer): a standalone module (local_path_override) with its own model, client + server libraries, and a loopback+socket integration test covering round-trips, typed modeled errors, and framework-side constraint validation. A new CI job builds it standalone — the quick-start acceptance test — and .bazelignore keeps it out of this workspace's target graph.

Also in this PR

  • googletest → 1.17.0.bcr.2 and the cc-rules autoload flag dropped from both .bazelrcs (per review feedback).
  • rules_jvm_external pinned at 6.7: 7.0 exists but isn't on mirror.bazel.build, which the dev sandbox needs; bumping later is a one-line change + lockfile repin.
  • docs/quickstart.md: empty directory → generated client integration-testing a generated server; every step is the consumer example, so CI keeps the tutorial honest.
  • PLAN updates: BCR/Maven publishing explicitly deferred until production validation (consumers use git_override/local_path_override meanwhile); @requestCompression (gzip) slotted into Phase 7; JSON-RPC 2.0 recorded as a candidate future protocol on the existing ProtocolGenerator seam.

Deferred from Phase 6 scope (beyond BCR): the docs site and a smithy-cpp init template command — the consumer example serves as the copyable template for now.

Validation

  • bazel test //...: 41/41 green (gcc and clang --config=asan --config=ubsan), plus the standalone consumer module
  • gradle build green; goldens regenerated with zero drift
  • buildifier / clang-format clean

🤖 Generated with Claude Code

https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf


Generated by Claude Code

claude added 6 commits July 7, 2026 06:47
Consumers now write smithy_cpp_client_library / smithy_cpp_server_library /
smithy_cpp_types_library (bazel/defs.bzl) in their own Bazel 8/9 module and
get ordinary cc_library targets - generation runs inside the build graph
as a hermetic Java action, never as a run-this-script-first step:

- //codegen:generator compiles the generator from the same sources the
  Gradle build uses (rules_java + rules_jvm_external, Maven lockfile
  pinned in-repo); Gradle stays the dev workflow.
- New runner/settings knobs make output sets predictable per target:
  --mode types|client|server|both gates client/server emission and
  --emit-build-file false suppresses the generated BUILD.bazel; --model
  is now repeatable for multi-file models.
- The rules declare exact outputs from the namespace + mode, split
  hdrs/srcs via output groups, and wire include paths so dependents just
  #include "<ns>/client.h".

Testing, both flavors:
- Rule unit tests: rules_testing analysis tests pin the generated file
  sets per mode, the SmithyCppGenerate action's arguments, and the
  cc_library include wiring; the rule instances they inspect also
  compile as an in-tree build test (//bazel/tests).
- Out-of-tree integration: examples/bazel-consumer is a standalone
  module (local_path_override) with its own model, client + server
  libraries, and a loopback+socket integration test - the quick-start
  acceptance test, built standalone by a new CI job and .bazelignore'd
  from this workspace's target graph.

Also: googletest bumped to 1.17.0.bcr.2 and the cc-rules autoload flag
dropped everywhere; docs/quickstart.md walks the consumer path start to
finish; PLAN records the deferrals and follow-ups (BCR/Maven publishing
deferred until production validation, @requestCompression gzip slotted
into Phase 7, JSON-RPC 2.0 recorded as a candidate future protocol on
the existing ProtocolGenerator seam).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
Two one-line root causes from the Phase 6 CI run:

- The root .gitignore's 'bazel-*' pattern (meant for the convenience
  symlinks) also matched examples/bazel-consumer, so the whole consumer
  module was silently left out of the commit and its CI job found no
  directory. Both .gitignore patterns are now anchored ('/bazel-*').
- Dropping the cc-rules autoload flag exposed that nlohmann_json 3.12.0's
  BCR BUILD file doesn't load() its cc rules (masked locally by a sandbox
  override); 3.12.0.bcr.1 adds the rules_cc dependency. The boost and
  googletest modules already load correctly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
The consumer module's .bazelrc deliberately contains only what a real
consumer needs; the ci config lives in the main repo's .bazelrc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
boost.container (transitive via boost.beast 1.87) calls native.cc_test
without a load(), which fails now that the cc-rules autoload flag is
gone. The 1.90.0.bcr.1 module family's overlays load from @rules_cc
properly (verified against the registry), so bump boost.asio and
boost.beast; MVS lifts the transitive boost graph with them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
The 1.90 boost family fixes the load() statements but boost.asio 1.90
adds a boringssl dependency whose toolchain graph (rules_perl,
pybind11_bazel) is heavy and unavailable in restricted environments.
An audit of all 58 modules in the boost 1.87 dependency graph found
exactly one file calling removed native cc rules —
boost.container's gen_test_targets.bzl — so a one-file
single_version_override patch adds the missing load() and keeps the
dependency graph unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
@aaylward
aaylward merged commit 49ca649 into main Jul 7, 2026
8 checks passed
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.

2 participants