This guide explains how to set up an out-of-tree project that builds against a fork of Pigweed rather than upstream, using the Earl Grey integration in this repo as the reference pattern.
# Fork https://pigweed.googlesource.com/pigweed/pigweed to your GitHub/GitLab # Example: https://github.com/<your-org>/pigweed
In the root MODULE.bazel, change the git_override for Pigweed:
# ── BEFORE (upstream) ── git_override( module_name = "pigweed", commit = "f52059663fee1e2ab2f2ab752301b159da3ca806", remote = "https://pigweed.googlesource.com/pigweed/pigweed", ) # ── AFTER (your fork) ── git_override( module_name = "pigweed", commit = "<your_fork_commit_sha>", remote = "https://github.com/<your-org>/pigweed", )
If you want to track a branch instead of a pinned commit, you can use local_path_override during development (faster iteration, no re-fetch):
# For local development against a co-located Pigweed checkout: local_path_override( module_name = "pigweed", path = "../pigweed", # Relative path to your local Pigweed clone )
Warning:
local_path_overrideis not reproducible across machines. Always switch back togit_overridewith a pinned commit for CI/production.
module( name = "my_pigweed_project", version = "0.0.1", ) # ── Core dependencies ── bazel_dep(name = "bazel_skylib", version = "1.8.2") bazel_dep(name = "pigweed") bazel_dep(name = "platforms", version = "1.0.0") bazel_dep(name = "rules_rust", version = "0.66.0") bazel_dep(name = "rules_platform", version = "0.1.0") bazel_dep(name = "rules_python", version = "1.6.3") # ── Point to your fork ── git_override( module_name = "pigweed", commit = "<your_fork_commit_sha>", remote = "https://github.com/<your-org>/pigweed", ) # ── Register crate generator (if using ureg for peripheral registers) ── bazel_dep(name = "ureg") git_override( module_name = "ureg", commit = "412ca40146d5d2012417e493b4a01096b04edf4b", remote = "https://github.com/chipsalliance/caliptra-ureg", ) # ── Rust toolchain (managed by Pigweed) ── pw_rust = use_extension("@pigweed//pw_toolchain/rust:extensions.bzl", "pw_rust") pw_rust.toolchain(cipd_tag = "<rust_toolchain_cipd_tag>") use_repo(pw_rust, "pw_rust_toolchains") # ── C/C++ and Rust toolchain registration ── register_toolchains( # Host "@pigweed//pw_toolchain/host_clang:host_cc_toolchain_linux", "@pigweed//pw_toolchain/host_clang:host_cc_toolchain_macos", # Target (pick one or more): "@pigweed//pw_toolchain/riscv_clang:riscv_clang_cc_toolchain_rv32imc", # "@pigweed//pw_toolchain/arm_clang:arm_clang_cc_toolchain_cortex_m4", "@pw_rust_toolchains//:all", ) # ── Rust crate dependencies ── crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate") crate.from_cargo( name = "rust_crates", cargo_lockfile = "//third_party/crates_io:Cargo.lock", manifests = ["//third_party/crates_io:Cargo.toml"], supported_platform_triples = [ "aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "aarch64-apple-darwin", "x86_64-apple-darwin", # Your SoC target triple: "riscv32imc-unknown-none-elf", # "thumbv7em-none-eabihf", ], ) use_repo(crate, "rust_crates") # Override Pigweed's internal crates with our unified crate universe pw_rust_crates_ext = use_extension( "@pigweed//pw_build:pw_rust_crates_extension.bzl", "pw_rust_crates_extension", ) override_repo(pw_rust_crates_ext, rust_crates = "rust_crates") # ── (Optional) SoC-specific devbundle for simulator/runner tools ── # bazel_dep(name = "my_soc_devbundle") # archive_override( # module_name = "my_soc_devbundle", # integrity = "sha256-...", # url = "https://...", # )
my-project/
├── MODULE.bazel # See above
├── BUILD.bazel # Root BUILD (can be minimal)
├── third_party/
│ └── crates_io/
│ ├── BUILD.bazel # Empty (just package())
│ ├── Cargo.toml # Rust deps for Bazel crate universe
│ └── Cargo.lock # Generated: cargo generate-lockfile
└── target/
└── <soc>/ # Your SoC integration
├── BUILD.bazel # platform(), entry, console, config libs
├── defs.bzl # TARGET_COMPATIBLE_WITH
├── config.rs # KernelConfig implementation
├── entry.rs # Boot entry point
├── <mpu>.rs # Memory protection setup
├── console.rs # UART console backend
├── target.ld.jinja # Linker script template
├── registers/ # Peripheral register crates
│ ├── BUILD.bazel
│ ├── registers.rs
│ └── uart.rs (etc.)
└── threads/kernel/ # Minimal test image
├── BUILD.bazel
├── system.json5
└── target.rs
third_party/crates_io/)[package] name = "rust_crates" version = "0.1.0" edition = "2021" [lib] path = "fake.rs" [dependencies] # Shared embedded deps embedded-io = "0.6.1" panic-halt = "1.0.0" bitflags = "2.9.1" # RISC-V target riscv = "0.12.1" riscv-rt = "0.12.2" # ARM Cortex-M target (uncomment if needed) # cortex-m = "0.7" # cortex-m-rt = "0.7"
# Empty — crate_universe handles everything
cd third_party/crates_io touch fake.rs cargo generate-lockfile
load("@bazel_skylib//rules:native_binary.bzl", "native_binary") # Pigweed CLI launcher native_binary( name = "pw", src = "@pigweed//pw_build/py:workflows_launcher", ) # Code formatter alias( name = "format", actual = "@pigweed//pw_presubmit/py:format", )
See .github/copilot-instructions.md “Integrating a New SoC with Pigweed” section for complete templates of every file below.
target/<soc>/defs.bzlTARGET_COMPATIBLE_WITH = select({ "//target/<soc>:target_<soc>": [], "//conditions:default": ["@platforms//:incompatible"], })
target/<soc>/BUILD.bazel (abbreviated)load("@bazel_skylib//rules:common_settings.bzl", "string_flag") load("@pigweed//pw_build:merge_flags.bzl", "flags_from_dict") load("@pigweed//pw_kernel:flags.bzl", "KERNEL_DEVICE_COMMON_FLAGS") load("@rules_rust//rust:defs.bzl", "rust_library") load("//target/<soc>:defs.bzl", "TARGET_COMPATIBLE_WITH") platform( name = "<soc>", constraint_values = [ ":target_<soc>", "@pigweed//pw_kernel/arch/riscv:timer_mtime", "@pigweed//pw_build/constraints/riscv/extensions:I", "@pigweed//pw_build/constraints/riscv/extensions:M", "@pigweed//pw_build/constraints/riscv/extensions:C", "@pigweed//pw_build/constraints/rust:no_std", "@platforms//cpu:riscv32", "@platforms//os:none", ], flags = flags_from_dict( KERNEL_DEVICE_COMMON_FLAGS | { "@pigweed//pw_kernel/config:kernel_config": ":config", "@pigweed//pw_kernel/subsys/console:console_backend": ":console", "@pigweed//pw_log/rust:pw_log_backend": "@pigweed//pw_kernel:log_backend_basic", }, ), visibility = [":__subpackages__"], ) constraint_value( name = "target_<soc>", constraint_setting = "@pigweed//pw_kernel/target:target", visibility = [":__subpackages__"], ) # string_flag, config_settings, rust_library for entry/console/config... # (see copilot-instructions.md Steps 2a-2d for full templates)
target/<soc>/threads/kernel/BUILD.bazelload("@pigweed//pw_kernel/tooling:system_image.bzl", "system_image") load("@pigweed//pw_kernel/tooling:target_codegen.bzl", "target_codegen") load("@pigweed//pw_kernel/tooling:target_linker_script.bzl", "target_linker_script") load("@rules_rust//rust:defs.bzl", "rust_binary") load("//target/<soc>:defs.bzl", "TARGET_COMPATIBLE_WITH") system_image(name = "threads", kernel = ":target", platform = "//target/<soc>") target_linker_script( name = "linker_script", system_config = ":system_config", tags = ["kernel"], template = "//target/<soc>:linker_script_template", ) filegroup(name = "system_config", srcs = ["system.json5"]) target_codegen( name = "codegen", arch = "@pigweed//pw_kernel/arch/riscv:arch_riscv", system_config = ":system_config", ) rust_binary( name = "target", srcs = ["target.rs"], edition = "2024", target_compatible_with = TARGET_COMPATIBLE_WITH, deps = [ ":codegen", ":linker_script", "//target/<soc>:entry", "@pigweed//pw_kernel/arch/riscv:arch_riscv", "@pigweed//pw_kernel/kernel", "@pigweed//pw_kernel/subsys/console:console_backend", "@pigweed//pw_kernel/target:target_common", "@pigweed//pw_kernel/tests/threads/kernel:threads", "@pigweed//pw_log/rust:pw_log", ], )
# Install bazelisk (if not already) # https://github.com/bazelbuild/bazelisk # Build everything for your SoC bazelisk build //target/<soc>/... # Build just the kernel threads test image bazelisk build //target/<soc>/threads/kernel:threads # VS Code rust-analyzer setup bazelisk run @rules_rust//tools/rust_analyzer:gen_rust_project -- //target/...
When upstream Pigweed has changes you want:
cd /path/to/your/pigweed-fork # Add upstream remote (one-time) git remote add upstream https://pigweed.googlesource.com/pigweed/pigweed # Fetch and merge git fetch upstream git merge upstream/main # or rebase: git rebase upstream/main git push origin main # Then update your project's MODULE.bazel commit hash: # commit = "<new_commit_sha_from_your_fork>"
For local_path_override workflows, just git pull in your local Pigweed checkout — Bazel picks up changes immediately (no commit hash to update).
| Problem | Fix |
|---|---|
ERROR: no such package '@pigweed//pw_kernel/...' | Your fork is missing pw_kernel; merge upstream |
ERROR: invalid registered toolchain | Check register_toolchains() matches your SoC arch |
crate_universe resolution failures | Run cargo generate-lockfile in third_party/crates_io/ |
incompatible target warnings | Verify constraint_values in your platform() |
| Stale Pigweed commit after fork update | Run bazelisk clean --expunge then rebuild |
local_path_override not picking up changes | Bazel caches aggressively; try bazelisk clean |