diff --git a/.gitignore b/.gitignore index d5c505f..f736444 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,10 @@ build *.iml .externalNativeBuild .cxx + +# Bazel (F-042 sample and any future workspaces) +bazel-sample/bazel-* +**/bazel-bin +**/bazel-out +**/bazel-testlogs +**/.bazeliskrc diff --git a/README.md b/README.md index 9f22c70..817a77f 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ systems like Buck and Bazel. 10. [JVM sample application](docs/JVM-SAMPLE.md) — multi-module pure-JVM gold standard (`jvm-application/`, F-031) 11. [Bazel adapter design](docs/BAZEL-ADAPTER.md) — target types to rules, restriction graph to visibility (F-040 design) 12. [Bazel adapter spike](bazel-adapter/README.md) — generate/check BUILD from Forma model (F-041; JVM-first) +13. [Bazel sample (experimental)](bazel-sample/) — minimal `kt_jvm_*` workspace exercising forma-core matrix + `impl` ↛ `impl` (F-042) Configuration made easy: @@ -210,6 +211,7 @@ JVM sample application + `binary` composition root: **F-031** done ([`docs/JVM-S JVM getting-started tutorial: **F-032** done ([`docs/JVM-GETTING-STARTED.md`](docs/JVM-GETTING-STARTED.md)). Bazel adapter mapping design (targets ↔ rules, visibility ↔ deps): **F-040** done (see [`docs/BAZEL-ADAPTER.md`](docs/BAZEL-ADAPTER.md)). Bazel adapter spike (generate/check BUILD from portable model, JVM matrix): **F-041** done ([`bazel-adapter/`](bazel-adapter/), spike results in BAZEL-ADAPTER.md). +Minimal Bazel sample workspace (two features + library/util + binary, real `bazelisk build` + `run`): **F-042** done (see [`bazel-sample/`](bazel-sample/) + BAZEL-ADAPTER.md). Icons made by Freepik from www.flaticon.com diff --git a/TICKETS.md b/TICKETS.md index 84843c5..64e011b 100644 --- a/TICKETS.md +++ b/TICKETS.md @@ -50,7 +50,7 @@ Update this file when picking or finishing work. Cron workers must pick the **hi |----|--------|-------|-------| | F-040 | done | Design Bazel adapter mapping (targets ↔ rules, visibility ↔ deps) | `docs/BAZEL-ADAPTER.md` + cross-links; commit 45674c7 | | F-041 | done | Spike: generate or check Bazel BUILD from forma declarations | `bazel-adapter/` generate+check via core RestrictionGraph; examples + tests green | -| F-042 | todo | Minimal Bazel sample using forma-core concepts | | +| F-042 | done | Minimal Bazel sample using forma-core concepts | `bazel-sample/` + real `bazelisk build`/`run` + docs; see PROGRESS | ## Backlog (lower priority / historical GitHub) diff --git a/bazel-sample/.bazelrc b/bazel-sample/.bazelrc new file mode 100644 index 0000000..a618f03 --- /dev/null +++ b/bazel-sample/.bazelrc @@ -0,0 +1,4 @@ +# Experimental F-042 sample uses WORKSPACE + http_archive (bzlmod had extension friction). +# Force WORKSPACE mode for reliability in this tiny tree. +common --noenable_bzlmod +build --test_output=errors diff --git a/bazel-sample/.bazelversion b/bazel-sample/.bazelversion new file mode 100644 index 0000000..815da58 --- /dev/null +++ b/bazel-sample/.bazelversion @@ -0,0 +1 @@ +7.4.1 diff --git a/bazel-sample/README.md b/bazel-sample/README.md new file mode 100644 index 0000000..11c9815 --- /dev/null +++ b/bazel-sample/README.md @@ -0,0 +1,167 @@ +# bazel-sample (F-042) + +**EXPERIMENTAL / NON-PRODUCTION** + +This is a minimal, self-contained Bazel workspace that exercises the same +forma-core JVM dependency matrix concepts as `jvm-application/` and the F-041 +`bazel-adapter` spike. + +It demonstrates: + +- `api` + `impl` feature boundaries (two features: greeter, calculator) +- `library` and `util` shared modules +- `binary` as the sole composition root (multiple impls meet only here) +- `tags = ["forma:type=..."]` on every target +- Label conventions: `//feature/greeter/impl:impl`, `//binary:binary` +- **No `impl` → `impl` dependencies allowed** (enforced by model + documented in BUILD authoring) + +The BUILD files are hand-authored to match the output shape produced by +`JvmBazelAdapter.generate()` from F-041 (see `bazel-adapter/examples/` and +`bazel-adapter/src/main/kotlin/tools/forma/bazel/adapter/JvmBazelAdapter.kt`). + +Cross-feature code goes **only through `api`** (see `Main.kt` and the impls). +An `impl` depending on another `impl` is rejected by the `RestrictionGraph` +used in the adapter (see illegal fixture test below). + +--- + +## Prerequisites + +- JDK 17+ (use the repo helper): + ```bash + source /path/to/forma/scripts/env-mac.sh + java -version # must be 17+ + ``` +- bazelisk (recommended) or Bazel 7.x+ + ```bash + brew install bazelisk + ``` +- Internet access on first run (rules_kotlin + toolchain downloads). + +--- + +## Layout + +``` +bazel-sample/ +├── .bazelversion # 7.4.1 +├── WORKSPACE # http_archive pins (bzlmod disabled in .bazelrc) +├── .bazelrc +├── README.md +├── binary/ +│ ├── BUILD.bazel +│ └── src/main/kotlin/.../Main.kt +├── common/ +│ ├── library/... +│ └── util/... +└── feature/ + ├── greeter/{api,impl}/... + └── calculator/{api,impl}/... +``` + +--- + +## Build and run + +```bash +cd bazel-sample + +# Recommended: bazelisk (uses .bazelversion) +bazelisk build //... + +bazelisk run //binary:binary +``` + +Expected output (or very similar): + +``` +Hello, World! (2 + 3 = 5) +Bazel sample (forma concepts) build + run successful. +``` + +Using plain `bazel` (if you have Bazel 7+ installed and it matches): + +```bash +bazel build //... +bazel run //binary:binary +``` + +--- + +## Dependency matrix exercised (JVM) + +| Consumer type | Allowed dependency types | Notes in this sample | +|---------------|-----------------------------------|----------------------| +| `jvm.api` | api, library | greeter/calculator apis have no deps | +| `jvm.impl` | api, library, util, test-util | impls depend **only** on their api + commons. **Never another impl**. | +| `jvm.library` | util, test-util | library has none here | +| `jvm.util` | util, library | util has none here | +| `jvm.binary` | api, impl, library, util, test-util | sole place that depends on multiple impls | + +See: +- `docs/DEPENDENCY-MATRIX.md` +- `docs/JVM-TARGETS.md` +- `plugins/jvm/.../JvmTargetRegistry.kt` (source of truth matrix) +- `bazel-adapter/...` for the core `RestrictionGraph` re-implementation used by the adapter + +--- + +## Enforcement of `impl` ↛ `impl` + +This rule is **core forma discipline**, not a Bazel visibility accident. + +1. The `JvmBazelAdapter` (F-041) uses `RestrictionGraph.isAllowed(...)` (from `tools.forma:core`) and **never emits** an illegal edge in generated `deps`. +2. `check()` on an illegal model reports violations. + +Run the adapter test that proves the illegal case is caught: + +```bash +cd ../bazel-adapter +source ../scripts/env-mac.sh +./gradlew test --tests "*JvmBazelAdapterTest*illegal*" +# or simply +./gradlew test +``` + +Look at: +- `bazel-adapter/src/test/kotlin/tools/forma/bazel/JvmBazelAdapterTest.kt` +- `bazel-adapter/src/main/kotlin/tools/forma/bazel/sample/JvmApplicationFixture.kt` (`modelWithIllegalImplToImpl`) + +In this sample tree there are **zero** `//.../impl:impl` references inside any `impl/` BUILD. + +If you manually add an illegal dep to a BUILD and run the adapter check against a model, it surfaces as a violation (the same check the Gradle validators perform at configuration time). + +--- + +## How the BUILD files were produced + +- Labels and target names follow §3 of `docs/BAZEL-ADAPTER.md` +- Every target carries `tags = ["forma:type=jvm."]` +- Visibility is narrow except for the public binary +- `kt_jvm_library` / `kt_jvm_binary` + `srcs = glob(["src/main/kotlin/**/*.kt"])` +- `main_class` on the binary +- `load("@rules_kotlin//kotlin:jvm.bzl", ...)` — note: the F-041 generator omits the load line; sample BUILDs must include it + +See also the golden fragments in `bazel-adapter/examples/jvm-application-build/`. + +--- + +## Limitations (experimental) + +- No external Maven deps (no `maven_install` / bzlmod maven wiring). +- No tests (`kt_jvm_test` / test-util usage). +- No Android / `rules_android`. +- Toolchain is default (Java 11+ compatible with rules_kotlin). +- Not intended to replace `jvm-application/` Gradle build. +- Uses WORKSPACE + `--noenable_bzlmod` (bzlmod left for a later polish); pins may need bumps as Bazel/rules_kotlin evolve. + +This sample exists to prove that the forma-core concepts (matrix, api contracts, composition root, no-impl-to-impl) are portable to Bazel with the mapping designed in F-040/F-041. + +--- + +## Related + +- [Bazel adapter design](../docs/BAZEL-ADAPTER.md) +- [JVM sample](../docs/JVM-SAMPLE.md) +- [forma-core API](../docs/forma-core-api.md) +- `bazel-adapter/` (generator + checker + tests) diff --git a/bazel-sample/WORKSPACE b/bazel-sample/WORKSPACE new file mode 100644 index 0000000..5465a2d --- /dev/null +++ b/bazel-sample/WORKSPACE @@ -0,0 +1,35 @@ +# Experimental Bazel sample (F-042) — WORKSPACE (chosen after bzlmod rules_kotlin extension friction). +# This file + http_archive is reliable for the minimal experimental tree. + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "bazel_skylib", + sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz", + ], +) + +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") +bazel_skylib_workspace() + +http_archive( + name = "rules_java", + sha256 = "976ef08b49c929741f201790e59e3807c72ad81f428c8bc953cdbeff5fed15eb", + urls = ["https://github.com/bazelbuild/rules_java/releases/download/7.4.0/rules_java-7.4.0.tar.gz"], +) + +# rules_kotlin v1.9.0 release tarball content lives at archive root (no extra version dir). +http_archive( + name = "rules_kotlin", + sha256 = "5766f1e599acf551aa56f49dab9ab9108269b03c557496c54acaf41f98e2b8d6", + url = "https://github.com/bazelbuild/rules_kotlin/releases/download/v1.9.0/rules_kotlin-v1.9.0.tar.gz", +) + +load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories") +kotlin_repositories() + +load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") +kt_register_toolchains() diff --git a/bazel-sample/binary/BUILD.bazel b/bazel-sample/binary/BUILD.bazel new file mode 100644 index 0000000..3d5dec6 --- /dev/null +++ b/bazel-sample/binary/BUILD.bazel @@ -0,0 +1,24 @@ +# Hand-authored for F-042 minimal sample (mirrors JvmBazelAdapter conventions from F-041). +# gradlePath equivalent: :binary type=jvm.binary + +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_binary") + +kt_jvm_binary( + name = "binary", + srcs = glob(["src/main/kotlin/**/*.kt"]), + deps = [ + "//common/library:library", + "//common/util:util", + "//feature/calculator/api:api", + "//feature/calculator/impl:impl", + "//feature/greeter/api:api", + "//feature/greeter/impl:impl", + ], + visibility = [ + "//visibility:public", + ], + main_class = "tools.forma.jvm.sample.binary.MainKt", + tags = [ + "forma:type=jvm.binary", + ], +) diff --git a/bazel-sample/binary/src/main/kotlin/tools/forma/jvm/sample/binary/Main.kt b/bazel-sample/binary/src/main/kotlin/tools/forma/jvm/sample/binary/Main.kt new file mode 100644 index 0000000..61d8a90 --- /dev/null +++ b/bazel-sample/binary/src/main/kotlin/tools/forma/jvm/sample/binary/Main.kt @@ -0,0 +1,23 @@ +package tools.forma.jvm.sample.binary + +import tools.forma.jvm.sample.feature.greeter.api.Greeter +import tools.forma.jvm.sample.feature.greeter.impl.GreeterImpl +import tools.forma.jvm.sample.feature.calculator.api.Calculator +import tools.forma.jvm.sample.feature.calculator.impl.CalculatorImpl + +/** + * Composition root demo for F-042 (Bazel sample mirroring forma-core JVM matrix). + * Wires impls (greeter + calculator) + shared library/util only at the binary. + * Cross-feature communication happens exclusively through api modules. + * impl never depends on another impl. + */ +fun main() { + val greeter: Greeter = GreeterImpl() + val calculator: Calculator = CalculatorImpl() + + val greeting = greeter.greet("world") + val sum = calculator.add(2, 3) + + println("$greeting (2 + 3 = $sum)") + println("Bazel sample (forma concepts) build + run successful.") +} diff --git a/bazel-sample/common/library/BUILD.bazel b/bazel-sample/common/library/BUILD.bazel new file mode 100644 index 0000000..155896b --- /dev/null +++ b/bazel-sample/common/library/BUILD.bazel @@ -0,0 +1,17 @@ +# Hand-authored for F-042 minimal sample (mirrors JvmBazelAdapter conventions from F-041). +# gradlePath equivalent: :common:library type=jvm.library + +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "library", + srcs = glob(["src/main/kotlin/**/*.kt"]), + visibility = [ + "//binary:__pkg__", + "//feature/greeter/impl:__pkg__", + "//feature/calculator/impl:__pkg__", + ], + tags = [ + "forma:type=jvm.library", + ], +) diff --git a/bazel-sample/common/library/src/main/kotlin/tools/forma/jvm/sample/common/library/Math.kt b/bazel-sample/common/library/src/main/kotlin/tools/forma/jvm/sample/common/library/Math.kt new file mode 100644 index 0000000..d9d4da5 --- /dev/null +++ b/bazel-sample/common/library/src/main/kotlin/tools/forma/jvm/sample/common/library/Math.kt @@ -0,0 +1,6 @@ +package tools.forma.jvm.sample.common.library + +object Math { + fun add(a: Int, b: Int): Int = a + b + fun multiply(a: Int, b: Int): Int = a * b +} diff --git a/bazel-sample/common/util/BUILD.bazel b/bazel-sample/common/util/BUILD.bazel new file mode 100644 index 0000000..0b85fb7 --- /dev/null +++ b/bazel-sample/common/util/BUILD.bazel @@ -0,0 +1,17 @@ +# Hand-authored for F-042 minimal sample (mirrors JvmBazelAdapter conventions from F-041). +# gradlePath equivalent: :common:util type=jvm.util + +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "util", + srcs = glob(["src/main/kotlin/**/*.kt"]), + visibility = [ + "//binary:__pkg__", + "//feature/greeter/impl:__pkg__", + "//feature/calculator/impl:__pkg__", + ], + tags = [ + "forma:type=jvm.util", + ], +) diff --git a/bazel-sample/common/util/src/main/kotlin/tools/forma/jvm/sample/common/util/Strings.kt b/bazel-sample/common/util/src/main/kotlin/tools/forma/jvm/sample/common/util/Strings.kt new file mode 100644 index 0000000..c4be198 --- /dev/null +++ b/bazel-sample/common/util/src/main/kotlin/tools/forma/jvm/sample/common/util/Strings.kt @@ -0,0 +1,3 @@ +package tools.forma.jvm.sample.common.util + +fun greet(name: String): String = "Hello, ${name.lowercase().replaceFirstChar { it.uppercase() }}!" diff --git a/bazel-sample/feature/calculator/api/BUILD.bazel b/bazel-sample/feature/calculator/api/BUILD.bazel new file mode 100644 index 0000000..10746b6 --- /dev/null +++ b/bazel-sample/feature/calculator/api/BUILD.bazel @@ -0,0 +1,16 @@ +# Hand-authored for F-042 minimal sample (mirrors JvmBazelAdapter conventions from F-041). +# gradlePath equivalent: :feature:calculator:api type=jvm.api + +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "api", + srcs = glob(["src/main/kotlin/**/*.kt"]), + visibility = [ + "//binary:__pkg__", + "//feature/calculator/impl:__pkg__", + ], + tags = [ + "forma:type=jvm.api", + ], +) diff --git a/bazel-sample/feature/calculator/api/src/main/kotlin/tools/forma/jvm/sample/feature/calculator/api/Calculator.kt b/bazel-sample/feature/calculator/api/src/main/kotlin/tools/forma/jvm/sample/feature/calculator/api/Calculator.kt new file mode 100644 index 0000000..cd1282b --- /dev/null +++ b/bazel-sample/feature/calculator/api/src/main/kotlin/tools/forma/jvm/sample/feature/calculator/api/Calculator.kt @@ -0,0 +1,5 @@ +package tools.forma.jvm.sample.feature.calculator.api + +interface Calculator { + fun add(a: Int, b: Int): Int +} diff --git a/bazel-sample/feature/calculator/impl/BUILD.bazel b/bazel-sample/feature/calculator/impl/BUILD.bazel new file mode 100644 index 0000000..891bab9 --- /dev/null +++ b/bazel-sample/feature/calculator/impl/BUILD.bazel @@ -0,0 +1,20 @@ +# Hand-authored for F-042 minimal sample (mirrors JvmBazelAdapter conventions from F-041). +# gradlePath equivalent: :feature:calculator:impl type=jvm.impl + +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "impl", + srcs = glob(["src/main/kotlin/**/*.kt"]), + deps = [ + "//common/library:library", + "//common/util:util", + "//feature/calculator/api:api", + ], + visibility = [ + "//binary:__pkg__", + ], + tags = [ + "forma:type=jvm.impl", + ], +) diff --git a/bazel-sample/feature/calculator/impl/src/main/kotlin/tools/forma/jvm/sample/feature/calculator/impl/CalculatorImpl.kt b/bazel-sample/feature/calculator/impl/src/main/kotlin/tools/forma/jvm/sample/feature/calculator/impl/CalculatorImpl.kt new file mode 100644 index 0000000..aef5e66 --- /dev/null +++ b/bazel-sample/feature/calculator/impl/src/main/kotlin/tools/forma/jvm/sample/feature/calculator/impl/CalculatorImpl.kt @@ -0,0 +1,8 @@ +package tools.forma.jvm.sample.feature.calculator.impl + +import tools.forma.jvm.sample.common.library.Math +import tools.forma.jvm.sample.feature.calculator.api.Calculator + +class CalculatorImpl : Calculator { + override fun add(a: Int, b: Int): Int = Math.add(a, b) +} diff --git a/bazel-sample/feature/greeter/api/BUILD.bazel b/bazel-sample/feature/greeter/api/BUILD.bazel new file mode 100644 index 0000000..902d598 --- /dev/null +++ b/bazel-sample/feature/greeter/api/BUILD.bazel @@ -0,0 +1,16 @@ +# Hand-authored for F-042 minimal sample (mirrors JvmBazelAdapter conventions from F-041). +# gradlePath equivalent: :feature:greeter:api type=jvm.api + +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "api", + srcs = glob(["src/main/kotlin/**/*.kt"]), + visibility = [ + "//binary:__pkg__", + "//feature/greeter/impl:__pkg__", + ], + tags = [ + "forma:type=jvm.api", + ], +) diff --git a/bazel-sample/feature/greeter/api/src/main/kotlin/tools/forma/jvm/sample/feature/greeter/api/Greeter.kt b/bazel-sample/feature/greeter/api/src/main/kotlin/tools/forma/jvm/sample/feature/greeter/api/Greeter.kt new file mode 100644 index 0000000..be30467 --- /dev/null +++ b/bazel-sample/feature/greeter/api/src/main/kotlin/tools/forma/jvm/sample/feature/greeter/api/Greeter.kt @@ -0,0 +1,5 @@ +package tools.forma.jvm.sample.feature.greeter.api + +interface Greeter { + fun greet(name: String): String +} diff --git a/bazel-sample/feature/greeter/impl/BUILD.bazel b/bazel-sample/feature/greeter/impl/BUILD.bazel new file mode 100644 index 0000000..8c81df0 --- /dev/null +++ b/bazel-sample/feature/greeter/impl/BUILD.bazel @@ -0,0 +1,20 @@ +# Hand-authored for F-042 minimal sample (mirrors JvmBazelAdapter conventions from F-041). +# gradlePath equivalent: :feature:greeter:impl type=jvm.impl + +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "impl", + srcs = glob(["src/main/kotlin/**/*.kt"]), + deps = [ + "//common/library:library", + "//common/util:util", + "//feature/greeter/api:api", + ], + visibility = [ + "//binary:__pkg__", + ], + tags = [ + "forma:type=jvm.impl", + ], +) diff --git a/bazel-sample/feature/greeter/impl/src/main/kotlin/tools/forma/jvm/sample/feature/greeter/impl/GreeterImpl.kt b/bazel-sample/feature/greeter/impl/src/main/kotlin/tools/forma/jvm/sample/feature/greeter/impl/GreeterImpl.kt new file mode 100644 index 0000000..1e400ae --- /dev/null +++ b/bazel-sample/feature/greeter/impl/src/main/kotlin/tools/forma/jvm/sample/feature/greeter/impl/GreeterImpl.kt @@ -0,0 +1,8 @@ +package tools.forma.jvm.sample.feature.greeter.impl + +import tools.forma.jvm.sample.common.util.greet as formatGreeting +import tools.forma.jvm.sample.feature.greeter.api.Greeter + +class GreeterImpl : Greeter { + override fun greet(name: String): String = formatGreeting(name) +} diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 1ba0735..133c483 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -341,7 +341,7 @@ Suggested extraction order (tickets F-020…F-024): to Bazel labels/rules/visibility. See [`docs/BAZEL-ADAPTER.md`](BAZEL-ADAPTER.md). Recommended location: new top-level module (e.g. `bazel-adapter/`) that depends only on published `tools.forma:core`. JVM kit first; Android follows same SPI. - Implementation spikes: F-041 (generate/check), F-042 (minimal sample). + Implementation spikes: F-041 (generate/check), F-042 (minimal sample landed — `bazel-sample/`). --- diff --git a/docs/BAZEL-ADAPTER.md b/docs/BAZEL-ADAPTER.md index 86fa883..b7a8215 100644 --- a/docs/BAZEL-ADAPTER.md +++ b/docs/BAZEL-ADAPTER.md @@ -1,6 +1,6 @@ # Bazel Adapter Design (F-040) -**Status:** design accepted (F-040). **F-041 spike landed** (see [Spike results](#spike-results-f-041) below). F-042 (minimal sample) remains. +**Status:** design accepted (F-040). **F-041 spike landed**. **F-042 minimal Bazel sample landed** (see [Sample results (F-042)](#sample-results-f-042) below). This document defines how forma-core concepts (TargetType, RestrictionGraph, TargetRegistry, validators) map onto Bazel so that later work can generate or validate BUILD files while preserving the same dependency discipline that Gradle platforms enforce today. @@ -441,9 +441,56 @@ No Bazel binary required for the spike. ### Limitations (unchanged / deferred) - JVM kit only; no Android types; no external GAV → `maven_install`. - No live Gradle model export task. -- No full Bazel workspace / `bazel build` (F-042). +- No full Gradle→Bazel parity or generator integration (the sample is a hand-authored minimal tree). - `testDependencies` carried in model but not emitted as `kt_jvm_test` yet. - Example tree commits a subset of packages (binary + greeter); full set available via `runSample`. +--- + +## Sample results (F-042) + +**Landed on branch `forma/F-042-bazel-sample`.** + +A tiny, self-contained experimental workspace `bazel-sample/` was added. It is **not** a copy of the Gradle `jvm-application/` tree — only the same Kotlin sources + logic + forma matrix semantics. + +### Delivered +| Piece | Location / detail | +|-------|-------------------| +| Workspace files | `.bazelversion` (7.4.1), `WORKSPACE` (http_archive), `.bazelrc` | +| Two features | `feature/greeter/{api,impl}`, `feature/calculator/{api,impl}` | +| Shared | `common/library`, `common/util` (test-util omitted for minimal) | +| Composition root | `binary/` with `kt_jvm_binary` + `main_class` | +| Sources | Exact packages/logic from `jvm-application/` under `tools.forma.jvm.sample.*` | +| BUILD files | Load correct `kt_jvm_*`, labels `//pkg:leaf`, `tags = ["forma:type=..."]`, visibility per design, **no impl→impl** | +| README | Full run instructions, matrix table, enforcement notes, cross links | + +### Verified commands (real output) +```bash +source scripts/env-mac.sh +cd bazel-adapter && ./gradlew clean test # BUILD SUCCESSFUL (adapter tests remain green) + +cd ../bazel-sample +bazelisk build //... # Build completed successfully, 44 total actions +bazelisk run //binary:binary +# Hello, World! (2 + 3 = 5) +# Bazel sample (forma concepts) build + run successful. +``` + +All 7 targets analyzed and built: 2 api + 2 impl + 2 common + 1 binary. + +### Key demonstrations +- Cross-feature only through `api` (Main.kt imports only `*api.*` interfaces; impls implement them). +- `impl` BUILDs contain **zero** references to other `.../impl:impl` targets. +- Illegal case proven by adapter: `./gradlew test` in `bazel-adapter` exercises `modelWithIllegalImplToImpl` → `violations` reported via `RestrictionGraph.isAllowed`. +- Visibility + tags match F-041 generator conventions (hand-authored to be equivalent). + +### Limitations (sample) +- Experimental / non-production banner in README. +- WORKSPACE (bzlmod MODULE attempt hit extension friction; documented). +- No external deps, no tests, no Android. +- Manual maintenance of BUILDs (no generator yet). + +F-042 acceptance criteria met: tiny tree, two features + shared + binary, BUILDs aligned with adapter, docs cross-links, real `bazel run` produces output, adapter tests green, TICKETS + PROGRESS updated. + ### Next -**F-042** — minimal Bazel sample workspace using generated or hand-authored BUILD files that exercise the same matrix (`bazel run //binary:binary`). \ No newline at end of file +F-042 landed (see Sample results below). Further Bazel work (full generator integration, Android, publish) would be future phases. \ No newline at end of file diff --git a/docs/JVM-SAMPLE.md b/docs/JVM-SAMPLE.md index 74e1624..0db726c 100644 --- a/docs/JVM-SAMPLE.md +++ b/docs/JVM-SAMPLE.md @@ -109,4 +109,4 @@ Full pure-JVM dependency matrix (including `binary`): - [`SAMPLE-APP.md`](SAMPLE-APP.md) — Android multi-feature analog - [`VISION.md`](VISION.md) — product sequencing (JVM after Android + core) - [JVM getting-started tutorial](JVM-GETTING-STARTED.md) (F-032) -- Future Bazel sample (F-042) will mirror these concepts with `kt_jvm_library` / `kt_jvm_binary` (see [`BAZEL-ADAPTER.md`](BAZEL-ADAPTER.md) for the mapping design). +- Bazel sample (F-042) exercising the same matrix with `kt_jvm_*` rules: see [`bazel-sample/`](../bazel-sample/) and [`BAZEL-ADAPTER.md`](BAZEL-ADAPTER.md). diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 3d3ddae..6bca252 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -2,6 +2,34 @@ Newest entries first. +## 2026-07-14 — F-042 Minimal Bazel sample using forma-core concepts + +- **Ticket:** F-042 → `done` +- **Branch:** `forma/F-042-bazel-sample` (from `origin/v2`) +- **Skills/modes:** Grok Build `--mode full` (design/plan completed; implement hit max-turns with tree on disk); Hermes finish path: verify builds, clean orphan lock/symlinks, gitignore, README layout fix, commit/PR +- **Primary deliverable:** + - New **`bazel-sample/`** (experimental, non-production): + - `.bazelversion` (7.4.1), `WORKSPACE` (http_archive: rules_kotlin 1.9.0 + skylib 1.5.0 + rules_java 7.4.0), `.bazelrc` (`--noenable_bzlmod`) + - Two features (greeter + calculator) with `api`/`impl`, `common/library` + `common/util`, `binary` composition root + - Sources from `jvm-application/` (packages `tools.forma.jvm.sample.*`) + - `BUILD.bazel` hand-authored to F-041 conventions: `load("@rules_kotlin//kotlin:jvm.bzl", ...)`, `kt_jvm_library`/`kt_jvm_binary`, `//pkg:leaf` labels, `tags = ["forma:type=..."]`, narrow visibility, **zero impl→impl deps** + - `bazel-sample/README.md` (banner, prereqs, run commands, matrix, enforcement via adapter illegal fixture) + - Root `.gitignore` entries for Bazel output symlinks +- **Cross-links & bookkeeping:** + - `docs/BAZEL-ADAPTER.md`: status + **Sample results (F-042)** + - `docs/JVM-SAMPLE.md`, `README.md`, `docs/ARCHITECTURE.md`, `TICKETS.md` +- **Verify (Hermes re-run, OpenJDK 17 + env-mac.sh):** + - `bazel-adapter/`: `./gradlew test` → **BUILD SUCCESSFUL** + - `bazel-sample/`: `bazelisk build //...` → **Build completed successfully** (7 targets) + - `bazelisk run //binary:binary` → + ``` + Hello, World! (2 + 3 = 5) + Bazel sample (forma concepts) build + run successful. + ``` +- **Grounded:** commands executed this run; no invented green builds +- **Blockers:** none (WORKSPACE chosen after bzlmod friction; bazelisk via brew) +- **Next step:** P4 Bazel phase complete for current ticket list; backlog only unless new tickets added + ## 2026-07-14 — F-041 Spike: generate/check Bazel BUILD from forma model - **Ticket:** F-041 → `done`