| # Out-of-Tree Pigweed Kernel Build (from a Fork) | |
| 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. | |
| ## 1. Fork Pigweed | |
| ```sh | |
| # Fork https://pigweed.googlesource.com/pigweed/pigweed to your GitHub/GitLab | |
| # Example: https://github.com/<your-org>/pigweed | |
| ``` | |
| ## 2. Point MODULE.bazel at Your Fork | |
| In the root `MODULE.bazel`, change the `git_override` for Pigweed: | |
| ```starlark | |
| # ── 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): | |
| ```starlark | |
| # 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_override` is not reproducible across machines. | |
| > Always switch back to `git_override` with a pinned commit for CI/production. | |
| ## 3. Full MODULE.bazel Template | |
| ```starlark | |
| 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://...", | |
| # ) | |
| ``` | |
| ## 4. Required Project Structure | |
| ``` | |
| 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 | |
| ``` | |
| ## 5. Third-Party Rust Crates (`third_party/crates_io/`) | |
| ### Cargo.toml | |
| ```toml | |
| [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" | |
| ``` | |
| ### BUILD.bazel | |
| ```starlark | |
| # Empty — crate_universe handles everything | |
| ``` | |
| ### Generate the lockfile | |
| ```sh | |
| cd third_party/crates_io | |
| touch fake.rs | |
| cargo generate-lockfile | |
| ``` | |
| ## 6. Root BUILD.bazel | |
| ```starlark | |
| 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", | |
| ) | |
| ``` | |
| ## 7. SoC Target — Key Files | |
| See `.github/copilot-instructions.md` "Integrating a New SoC with Pigweed" | |
| section for complete templates of every file below. | |
| ### 7a. `target/<soc>/defs.bzl` | |
| ```starlark | |
| TARGET_COMPATIBLE_WITH = select({ | |
| "//target/<soc>:target_<soc>": [], | |
| "//conditions:default": ["@platforms//:incompatible"], | |
| }) | |
| ``` | |
| ### 7b. `target/<soc>/BUILD.bazel` (abbreviated) | |
| ```starlark | |
| 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) | |
| ``` | |
| ### 7c. Test Image — `target/<soc>/threads/kernel/BUILD.bazel` | |
| ```starlark | |
| load("@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", | |
| ], | |
| ) | |
| ``` | |
| ## 8. Building | |
| ```sh | |
| # 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/... | |
| ``` | |
| ## 9. Syncing with Upstream Pigweed | |
| When upstream Pigweed has changes you want: | |
| ```sh | |
| 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). | |
| ## 10. Troubleshooting | |
| | 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` | |