blob: 332c5a897efe4e6ae71cb5debabf33a426394303 [file] [view] [edit]
# 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` |