gerbil-bazel provides reusable Bazel APIs for discovering a native Gerbil
installation and building Gerbil projects on Darwin and Linux. It is independent
of any application framework: the consumer's build.ss remains the canonical
description of module topology and project semantics.
Use one platform-adaptive declaration when Darwin should resolve the native Homebrew installation and Linux should import an immutable prebuilt capability:
gerbil = use_extension("@gerbil_bazel//gerbil:extensions.bzl", "gerbil")
gerbil.auto(
name = "local_gerbil",
expected_version_prefixes = ["Gerbil 07c8481", "Gerbil v0.18.2"],
linux_prebuilt_arch = "x86_64",
linux_prebuilt_install_digest = "ee5a236818e1b8fd9cc6e886b510795495e95402e790892961e6a1059a5c3208",
linux_prebuilt_sha256 = "741d67f1d1a37e057181bfe8b3348ce2a9cfce06ac3b9c4e914400978f9d5fe7",
linux_prebuilt_urls = [
"https://github.com/tao3k/gerbil-bazel/releases/download/gerbil-v0.18.2-07c84815-linux-x86_64-ee5a236818e1b8fd9cc6e886b510795495e95402e790892961e6a1059a5c3208/gerbil-v0.18.2-07c84815-linux-x86_64-ee5a236818e1b8fd9cc6e886b510795495e95402e790892961e6a1059a5c3208.tar.gz",
],
project_root_marker = "//:gerbil.pkg",
)
use_repo(gerbil, "local_gerbil")
register_toolchains("@local_gerbil//:registered_toolchain")The extension resolves auto before repository creation and instantiates
exactly one provider. Darwin uses native host discovery; Linux validates that
the declared archive architecture matches the runner and instantiates the
prebuilt provider. Unsupported systems and architecture mismatches fail closed.
The module extension is declared os_dependent and arch_dependent, so Bazel
records separate operating-system and architecture evaluations instead of
reusing a Darwin-generated provider graph on Linux or a Linux prebuilt graph
across incompatible architectures through MODULE.bazel.lock.
Use explicit native host discovery when the consumer does not need automatic cross-platform selection:
gerbil = use_extension("@gerbil_bazel//gerbil:extensions.bzl", "gerbil")
gerbil.host(name = "local_gerbil")
use_repo(gerbil, "local_gerbil")
register_toolchains("@local_gerbil//:registered_toolchain")Use the explicit immutable provider for a Linux-only consumer:
gerbil = use_extension("@gerbil_bazel//gerbil:extensions.bzl", "gerbil")
gerbil.prebuilt(
install_digest = "ee5a236818e1b8fd9cc6e886b510795495e95402e790892961e6a1059a5c3208",
name = "gerbil_linux_x86_64",
expected_version_prefixes = ["Gerbil v0.18.2", "Gerbil 07c8481"],
sha256 = "741d67f1d1a37e057181bfe8b3348ce2a9cfce06ac3b9c4e914400978f9d5fe7",
urls = [
"https://github.com/tao3k/gerbil-bazel/releases/download/gerbil-v0.18.2-07c84815-linux-x86_64-ee5a236818e1b8fd9cc6e886b510795495e95402e790892961e6a1059a5c3208/gerbil-v0.18.2-07c84815-linux-x86_64-ee5a236818e1b8fd9cc6e886b510795495e95402e790892961e6a1059a5c3208.tar.gz",
],
)
use_repo(gerbil, "gerbil_linux_x86_64")
register_toolchains("@gerbil_linux_x86_64//:registered_toolchain")The prebuilt provider validates the archive digest, embedded manifest, execution platform, runtime version, and native ABI shape. It fails closed and never falls back to a source build. See RFC 0002 for the archive, release, receipt, and performance contracts.
Source production is a separate, explicit operation. The Source Producer v1 runbook defines default-branch registration, runner selection, cold-build receipt acceptance, and the independent promotion boundary.
Both auto providers and the explicit prebuilt provider support the same
project-package manifest view as host. Declare project_root_marker pointing
at the consumer gerbil.pkg; the repository reads its depend: declarations,
strips package revisions, projects ready packages into lib/<package>, and
records every package as ready or missing in toolchain.receipt.json.
This keeps gerbil.pkg as the single package lock and keeps the downstream
BUILD graph identical on Darwin and Linux while leaving dependency
installation under the separate install_dependencies capability. The
project_dependency_packages attribute remains as an explicit compatibility
override for non-standard manifests; published consumers should prefer the
manifest-driven path. Repositories using that override record
dependencyPolicy: "project-dependency-override" instead of the manifest
policy, so receipts do not confuse the compatibility path with gerbil.pkg
ownership.
Every host and prebuilt repository publishes //:install_dependencies. The
launcher enters the consumer workspace, uses its workspace-local .gerbil
root, and runs the standard gxpkg deps --install command through the selected
provider environment. Gerbil-Bazel does not wrap this command in another
gxpkg env lifecycle and does not duplicate package-manager verification with
an unconditional gxpkg list. A dependency installation can make
previously missing project packages ready; the next Bazel command then
re-evaluates the watched project-library view.
bazel_dep(name = "gerbil_bazel", version = "0.1.0")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "rules_shell", version = "0.8.0")
gerbil = use_extension("@gerbil_bazel//gerbil:extensions.bzl", "gerbil")
gerbil.host(
name = "local_gerbil",
expected_version_prefixes = ["0.18.2"],
)
use_repo(gerbil, "local_gerbil")
register_toolchains("@local_gerbil//:registered_toolchain")During local development, add a local_path_override for gerbil_bazel in the
consumer module. Published consumers should use a registry release instead.
Each discovered host repository publishes a workspace-aware dependency target:
bazel run @local_gerbil//:install_dependenciesThe target enters BUILD_WORKSPACE_DIRECTORY, defaults GERBIL_PATH to
$BUILD_WORKSPACE_DIRECTORY/.gerbil, runs the standard
gxpkg deps --install workflow through the normalized native tool
environment. It defaults GERBIL_BUILD_CORES=1 for the package-manager
install lifecycle to avoid provider/runtime hangs during dependency builds;
set GERBIL_BAZEL_INSTALL_BUILD_CORES to opt into a different install-time
parallelism. This is an explicit developer-side effect target, never build
truth inside a hermetic compile action.
load(
"@gerbil_bazel//gerbil:defs.bzl",
"gerbil_project_compile",
"gerbil_project_dev",
"gerbil_project_test",
)
gerbil_project_compile(
name = "scheme",
build_script = "build.ss",
args = ["compile", "--tests"],
srcs = glob(["src/**/*.ss"]),
)
gerbil_project_dev(
name = "scheme_dev",
build_script = "build.ss",
args = ["compile", "--tests"],
)
gerbil_project_test(
name = "scheme_test",
project = ":scheme",
srcs = glob(["test/**/*.ss"]),
tests = ["test/project-test.ss"],
)The compile action stages every declared source in a writable tree before it
executes build.ss. Tests always consume an explicit GerbilProjectInfo, so a
matrix reuses one compile action. Set receipt_line_prefix when the build
script emits canonical JSON. Every action receipt keeps the stable outer
gerbil-bazel.project-receipt.v1 schema and its original six required fields.
The validated producer value is nested under the optional buildReceipt
field. Guarded project actions also expose their Scheme resource-guard evidence
under the optional resourceGuard field. The normative contract is
schemas/gerbil-bazel.project-receipt.v1.schema.json; execution capability
changes do not create a new project receipt version.
The default execution policy is machine-adaptive: max_rss_bytes = 0 asks the
Scheme guard to retain host headroom from live available memory, and it never
means unlimited memory. Linux admission also caps host observations by cgroup
v2 memory.max and memory.current, so containers and remote executors retain
their own headroom. Linux enforcement sums process-tree PSS so shared
compiler/runtime pages are not charged once per worker; other platforms use
process-tree RSS. A positive max_rss_bytes remains an optional upper bound and
is always capped by the adaptive executor budget.
Host-specific paths, available logical CPUs, physical memory, compiler,
assembler, linker, SDK, optional Homebrew native libraries, and Gerbil
executables are discovered dynamically. Override only explicit capabilities
such as GERBIL_CC, GERBIL_GXI, or GERBIL_NATIVE_ABI. For fully declared
installations, gerbil.host(tool_paths = {...}) takes precedence over
environment overrides and PATH discovery.
Gerbil-Bazel leaves the installed gambuild-C immutable. Repository discovery
reads its literal C_COMPILER declaration and queries FLAGS_DYN and
FLAGS_OBJ without writing the installation. A generated executable-path
GERBIL_GSC launcher uses Gambit's public -cc, -cc-options, and
-ld-options interfaces. Because a -cc override clears Gambit's configured
mode flags, implicit or explicit dynamic compilation restores the complete
FLAGS_DYN value before adding any platform option, while -obj restores the
complete FLAGS_OBJ value. -c, -link, and -exe pass through without a
wrapper-added compiler override.
On Darwin, dynamic compilation appends -Wl,-undefined,dynamic_lookup after
the installed producer flags, and Gerbil's public GERBIL_GCC boundary selects
the Xcode Clang driver for the final executable link. Linux restores the same
installed producer mode flags without the Darwin option or a separate final
link driver. gambitProducerOptions, gambitDynamicLinkOptions, and
gerbilExecutableLinker are recorded in toolchain.receipt.json and
participate in Bazel action identity.
Package semantics remain upstream-owned. gerbil.pkg owns package and
dependency metadata, gxpkg owns package lifecycle, and build.ss plus
std/make own compile membership, ordering, and incrementality. Bazel declares
the source closure and dependency tree artifacts needed by the sandbox; it does
not expose a second package identity/revision graph.