load("@crates//:data.bzl", "DEP_DATA") load("@crates//:defs.bzl", "all_crate_deps") load("@rules_platform//platform_data:defs.bzl", "platform_data") load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro", "rust_test") load("@rules_rust//cargo/private:cargo_build_script_wrapper.bzl", "cargo_build_script") PLATFORMS = [ "linux_arm64_musl", "linux_amd64_musl", "macos_amd64", "macos_arm64", "windows_amd64", "windows_arm64", ] def multiplatform_binaries(name, platforms = PLATFORMS): for platform in platforms: platform_data( name = name + "_" + platform, platform = "@llvm//platforms:" + platform, target = name, tags = ["manual"], ) native.filegroup( name = "release_binaries", srcs = [name + "_" + platform for platform in platforms], tags = ["manual"], ) def codex_rust_crate( name, crate_name, crate_features = [], crate_srcs = None, crate_edition = None, proc_macro = False, build_script_enabled = True, build_script_data = [], compile_data = [], lib_data_extra = [], rustc_flags_extra = [], rustc_env = {}, deps_extra = [], integration_compile_data_extra = [], test_data_extra = [], test_tags = [], extra_binaries = []): """Defines a Rust crate with library, binaries, and tests wired for Bazel + Cargo parity. The macro mirrors Cargo conventions: it builds a library when `src/` exists, wires build scripts, exports `CARGO_BIN_EXE_*` for integration tests, and creates unit + integration test targets. Dependency buckets map to the Cargo.lock resolution in `@crates`. Args: name: Bazel target name for the library, should be the directory name. Example: `app-server`. crate_name: Cargo crate name from Cargo.toml Example: `codex_app_server`. crate_features: Cargo features to enable for this crate. Crates are only compiled in a single configuration across the workspace, i.e. with all features in this list enabled. So use sparingly, and prefer to refactor optional functionality to a separate crate. crate_srcs: Optional explicit srcs; defaults to `src/**/*.rs`. crate_edition: Rust edition override, if not default. You probably don't want this, it's only here for a single caller. proc_macro: Whether this crate builds a proc-macro library. build_script_data: Data files exposed to the build script at runtime. compile_data: Non-Rust compile-time data for the library target. lib_data_extra: Extra runtime data for the library target. rustc_env: Extra rustc_env entries to merge with defaults. deps_extra: Extra normal deps beyond @crates resolution. Typically only needed when features add additional deps. integration_compile_data_extra: Extra compile_data for integration tests. test_data_extra: Extra runtime data for tests. test_tags: Tags applied to unit + integration test targets. Typically used to disable the sandbox, but see https://bazel.build/reference/be/common-definitions#common.tags extra_binaries: Additional binary labels to surface as test data and `CARGO_BIN_EXE_*` environment variables. These are only needed for binaries from a different crate. """ test_env = { "INSTA_WORKSPACE_ROOT": ".", "INSTA_SNAPSHOT_PATH": "src", } rustc_env = { "BAZEL_PACKAGE": native.package_name(), } | rustc_env binaries = DEP_DATA.get(native.package_name())["binaries"] lib_srcs = crate_srcs or native.glob(["src/**/*.rs"], exclude = binaries.values(), allow_empty = True) maybe_deps = [] if build_script_enabled and native.glob(["build.rs"], allow_empty = True): cargo_build_script( name = name + "-build-script", srcs = ["build.rs"], deps = all_crate_deps(build = True), data = build_script_data, # Some build script deps sniff version-related env vars... version = "0.0.0", ) maybe_deps += [name + "-build-script"] if lib_srcs: lib_rule = rust_proc_macro if proc_macro else rust_library lib_rule( name = name, crate_name = crate_name, crate_features = crate_features, deps = all_crate_deps() + maybe_deps + deps_extra, compile_data = compile_data, data = lib_data_extra, srcs = lib_srcs, edition = crate_edition, rustc_flags = rustc_flags_extra, rustc_env = rustc_env, visibility = ["//visibility:public"], ) rust_test( name = name + "-unit-tests", crate = name, env = test_env, deps = all_crate_deps(normal = True, normal_dev = True) + maybe_deps + deps_extra, rustc_flags = rustc_flags_extra, rustc_env = rustc_env, data = test_data_extra, tags = test_tags, ) maybe_deps += [name] sanitized_binaries = [] cargo_env = {} for binary, main in binaries.items(): #binary = binary.replace("-", "_") sanitized_binaries.append(binary) cargo_env["CARGO_BIN_EXE_" + binary] = "$(rlocationpath :%s)" % binary rust_binary( name = binary, crate_name = binary.replace("-", "_"), crate_root = main, deps = all_crate_deps() + maybe_deps + deps_extra, edition = crate_edition, rustc_flags = rustc_flags_extra, srcs = native.glob(["src/**/*.rs"]), visibility = ["//visibility:public"], ) for binary_label in extra_binaries: sanitized_binaries.append(binary_label) binary = Label(binary_label).name cargo_env["CARGO_BIN_EXE_" + binary] = "$(rlocationpath %s)" % binary_label for test in native.glob(["tests/*.rs"], allow_empty = True): test_file_stem = test.removeprefix("tests/").removesuffix(".rs") test_crate_name = test_file_stem.replace("-", "_") test_name = name + "-" + test_file_stem.replace("/", "-") if not test_name.endswith("-test"): test_name += "-test" rust_test( name = test_name, crate_name = test_crate_name, crate_root = test, srcs = [test], data = native.glob(["tests/**"], allow_empty = True) + sanitized_binaries + test_data_extra, compile_data = native.glob(["tests/**"], allow_empty = True) + integration_compile_data_extra, deps = all_crate_deps(normal = True, normal_dev = True) + maybe_deps + deps_extra, # Keep `file!()` paths Cargo-like (`core/tests/...`) instead of # Bazel workspace-prefixed (`codex-rs/core/tests/...`) for snapshot parity. rustc_flags = rustc_flags_extra + ["--remap-path-prefix=codex-rs="], rustc_env = rustc_env, # Important: do not merge `test_env` here. Its unit-test-only # `INSTA_WORKSPACE_ROOT="."` can point integration tests at the # runfiles cwd and cause false `.snap.new` churn on Linux. env = cargo_env, tags = test_tags, )