bazel: build and test bare-metal examples

This configures Bazel targets for the bare-metal examples. We now also
have test targets for them:

    bazel test //:bare_metal_tests

will compile all the code and run a simple test in QEMU (boot the
binaries, send `"q"` to them on stdin).

The Cargo-based workflow has been kept: I expect that students will
keep using it in class, so we should keep testing it in CI.
This commit is contained in:
Martin Geisler
2026-06-20 11:41:46 +02:00
parent 07d3242887
commit ec71ec2040
14 changed files with 3288 additions and 595 deletions
+18
View File
@@ -68,6 +68,24 @@ jobs:
working-directory: ${{ matrix.directory }}
run: cargo build
bare-metal-test:
name: Bare-metal tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Bazel cache
uses: ./.github/workflows/setup-bazel-cache
- name: Install QEMU
uses: ./.github/workflows/apt-get-install
with:
packages: qemu-system-arm
- name: Run QEMU tests
run: bazel test //:bare_metal_tests
find-languages:
runs-on: ubuntu-latest
outputs:
+20
View File
@@ -0,0 +1,20 @@
"""Root BUILD file for Comprehensive Rust workspace.
This file defines a central test_suite for running manual QEMU tests.
"""
package(default_visibility = ["//visibility:public"])
test_suite(
name = "bare_metal_tests",
tags = ["manual"],
tests = [
"//src/bare-metal/aps/examples:improved_test",
"//src/bare-metal/aps/examples:logger_test",
"//src/bare-metal/aps/examples:minimal_test",
"//src/bare-metal/aps/examples:psci_test",
"//src/bare-metal/aps/examples:rt_test",
"//src/bare-metal/aps/examples:safemmio_test",
"//src/exercises/bare-metal/rtc:rtc_test",
],
)
+112
View File
@@ -282,3 +282,115 @@ Use `--mochaOpts.grep` to run a single test within a file:
```bash
npm test -- --spec redbox --mochaOpts.grep "should be hidden by default"
```
## Bazel Bare-Metal and Microcontroller Integration
This project integrates Bazel rules for bare-metal AArch64 and microcontroller
target platforms. Key findings and conventions include:
### Checking Bazel Version
Always check the `.bazelversion` file at the workspace root (e.g., `9.1.1`)
before inspecting the host Bazel version.
### Target Platforms & Constraints
Bare-metal and microcontroller examples specify target CPU/OS constraint
configurations (e.g., `os:none`):
- `aarch64-unknown-none`: Mapped in `platforms/BUILD.bazel` to
`@platforms//os:none` and `@platforms//cpu:aarch64`.
- `thumbv7em-none-eabihf`: Mapped to `@platforms//os:none` and
`@platforms//cpu:armv7e-mf` (matching rules_rust's internal CPU naming
convention).
### Wildcard Host Builds & Compatibility
To prevent raw target compilation failures during wildcard host commands (like
`bazel build //...` or `bazel test //...`), all target-specific `rust_binary`
targets declare compatibility constraints:
```bazel
rust_binary(
name = "my_target",
...
target_compatible_with = ["@platforms//os:none"],
)
```
This causes Bazel to automatically skip building these targets on host
configurations (where OS is Linux/macOS) instead of failing.
### Cargo Universe Imports & Host Splicing
When importing external crates via `rules_rust`'s Cargo Universe
(`crate.from_cargo`), the host platform triple (e.g.,
`x86_64-unknown-linux-gnu`) must be included in `supported_platform_triples`:
```bazel
crate.from_cargo(
...
supported_platform_triples = [
"x86_64-unknown-linux-gnu",
"aarch64-unknown-none",
],
)
```
Even if the imported dependencies are purely for bare-metal targets,
`cargo-bazel` runs on the host during the splicing phase and invokes
`cargo tree` to perform feature and dependency resolution. Excluding the host
execution platform triple prevents Bazel from resolving a host cargo toolchain,
resulting in feature generation failures during the analysis phase.
### Hermetic Binary Extraction (Objcopy)
Since host systems may lack target-specific `rust-objcopy` executables, the
extraction of `.bin` flat images is managed hermetically inside the Bazel
sandbox using a `genrule` target pointing to the nightly compiler's bundled
`rust-objcopy` tool:
```bazel
genrule(
name = "my_target_bin",
srcs = [":my_target"],
outs = ["my_target.bin"],
cmd = "$(execpath @rust_host_tools_nightly//:rust-objcopy) -O binary $(location :my_target) $@",
tools = ["@rust_host_tools_nightly//:rust-objcopy"],
)
```
### Host-Executed QEMU Tests, Runners & Platform Transitions
By default, Bazel test and binary runner targets run on the host system. Since
the target binaries require cross-compiling, we use Starlark configuration
transitions in `platforms/transition.bzl` (via rules like `aarch64_binary` or
`thumbv7em_binary`) to transition the target binary's compilation mode to the
target platform while letting the wrapper `sh_test` or `sh_binary` execute on
the host:
```bazel
# In platforms/transition.bzl:
def _aarch64_transition_impl(settings, attr):
return {"//command_line_option:platforms": ["//platforms:aarch64-unknown-none"]}
# In target BUILD.bazel:
aarch64_binary(
name = "my_target_bin_aarch64",
dep = ":my_target_bin",
)
# Automated host test (non-interactive, pipes 'q'):
sh_test(
name = "my_target_test",
srcs = ["run_test.sh"],
data = [":my_target_bin_aarch64"],
)
# Interactive host runner (run via `bazel run`):
sh_binary(
name = "my_target_run",
srcs = ["run_test.sh"],
data = [":my_target_bin_aarch64"],
)
```
+73
View File
@@ -3,12 +3,26 @@ module(
version = "0.1.0",
)
bazel_dep(name = "platforms", version = "1.1.0")
bazel_dep(name = "rules_rust", version = "0.70.0")
bazel_dep(name = "rules_shell", version = "0.8.0")
bazel_dep(name = "toolchains_llvm", version = "1.7.0")
# Remove when https://github.com/bazelbuild/rules_rust/pull/4019 is
# released and we update past version 0.70.0.
git_override(
module_name = "rules_rust",
commit = "d2d856d13f89f234298db1606ece7afd1c3eee1f",
remote = "https://github.com/bazelbuild/rules_rust.git",
)
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
rust.toolchain(
edition = "2024",
extra_target_triples = [
"aarch64-unknown-none",
"thumbv7em-none-eabihf",
],
)
use_repo(rust, "rust_toolchains")
@@ -87,3 +101,62 @@ crate.from_specs(
host_tools = "@rust_host_tools_nightly",
)
use_repo(crate, "svgbob_plugin")
# Bare-Metal and Microcontroller Examples
#
# Note: Host triple is required in supported_platform_triples below so
# cargo-bazel can invoke cargo tree on the host during splicing.
crate.from_cargo(
name = "bare_metal_alloc_example",
cargo_lockfile = "//src/bare-metal/alloc-example:Cargo.lock",
manifests = ["//src/bare-metal/alloc-example:Cargo.toml"],
supported_platform_triples = [
"x86_64-unknown-linux-gnu",
"aarch64-unknown-none",
],
)
use_repo(crate, "bare_metal_alloc_example")
crate.from_cargo(
name = "bare_metal_aps",
cargo_lockfile = "//src/bare-metal/aps/examples:Cargo.lock",
manifests = ["//src/bare-metal/aps/examples:Cargo.toml"],
supported_platform_triples = [
"x86_64-unknown-linux-gnu",
"aarch64-unknown-none",
],
)
use_repo(crate, "bare_metal_aps")
crate.from_cargo(
name = "bare_metal_microcontrollers",
cargo_lockfile = "//src/bare-metal/microcontrollers/examples:Cargo.lock",
manifests = ["//src/bare-metal/microcontrollers/examples:Cargo.toml"],
supported_platform_triples = [
"x86_64-unknown-linux-gnu",
"thumbv7em-none-eabihf",
],
)
use_repo(crate, "bare_metal_microcontrollers")
crate.from_cargo(
name = "bare_metal_compass",
cargo_lockfile = "//src/exercises/bare-metal/compass:Cargo.lock",
manifests = ["//src/exercises/bare-metal/compass:Cargo.toml"],
supported_platform_triples = [
"x86_64-unknown-linux-gnu",
"thumbv7em-none-eabihf",
],
)
use_repo(crate, "bare_metal_compass")
crate.from_cargo(
name = "bare_metal_rtc",
cargo_lockfile = "//src/exercises/bare-metal/rtc:Cargo.lock",
manifests = ["//src/exercises/bare-metal/rtc:Cargo.toml"],
supported_platform_triples = [
"x86_64-unknown-linux-gnu",
"aarch64-unknown-none",
],
)
use_repo(crate, "bare_metal_rtc")
+2262 -595
View File
File diff suppressed because one or more lines are too long
+33
View File
@@ -0,0 +1,33 @@
# platforms/BUILD.bazel
"""Target Platform Definitions for Embedded Cross-Compilation.
In Bazel, platforms represent execution and target build environments.
We define target platforms by declaring their constraint values (CPU
architecture and Operating System). The registered Rust toolchain
matches these constraint combinations to determine which
compiler/linker options to use:
- `aarch64-unknown-none` represents AArch64 bare-metal (no OS)
environments.
- `thumbv7em-none-eabihf` represents ARM Cortex-M4F microcontroller
environments.
"""
package(default_visibility = ["//visibility:public"])
platform(
name = "aarch64-unknown-none",
constraint_values = [
"@platforms//os:none",
"@platforms//cpu:aarch64",
],
)
platform(
name = "thumbv7em-none-eabihf",
constraint_values = [
"@platforms//os:none",
"@platforms//cpu:armv7e-mf",
],
)
+62
View File
@@ -0,0 +1,62 @@
"""Starlark Configuration Transitions for Cross-Compilation.
In Bazel, test targets (like sh_test) are host-executed. If the global
invocation sets --platforms=//platforms:aarch64-unknown-none, Bazel
attempts to run the test runners themselves on the target bare-metal
platform. Because there are no test execution toolchains for
bare-metal targets, this results in analysis failures.
To resolve this, we keep the global invocation targeting the host
platform, and use these custom rules (aarch64_binary,
thumbv7em_binary) to apply a "transition". A transition modifies the
configuration (the --platforms flag) for the dependency edge to the
target binary. This ensures:
1. The binaries are always compiled for their specific bare-metal
target platform.
2. The parent test targets (sh_test) are built and run on the host
platform.
"""
def _aarch64_transition_impl(settings, attr):
return {"//command_line_option:platforms": ["//platforms:aarch64-unknown-none"]}
_aarch64_transition = transition(
implementation = _aarch64_transition_impl,
inputs = [],
outputs = ["//command_line_option:platforms"],
)
def _transition_rule_impl(ctx):
# Retrieve the files from the transitioned target
return [DefaultInfo(files = ctx.attr.dep[0][DefaultInfo].files)]
aarch64_binary = rule(
implementation = _transition_rule_impl,
attrs = {
"dep": attr.label(cfg = _aarch64_transition),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
},
)
def _thumbv7em_transition_impl(settings, attr):
return {"//command_line_option:platforms": ["//platforms:thumbv7em-none-eabihf"]}
_thumbv7em_transition = transition(
implementation = _thumbv7em_transition_impl,
inputs = [],
outputs = ["//command_line_option:platforms"],
)
thumbv7em_binary = rule(
implementation = _transition_rule_impl,
attrs = {
"dep": attr.label(cfg = _thumbv7em_transition),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
},
)
+28
View File
@@ -0,0 +1,28 @@
"""Bazel BUILD file for AArch64 Bare-Metal Allocator Example.
This package defines the allocator example target. Because this code
targets AArch64 bare-metal platforms and not the host OS:
1. The raw `rust_binary` is constrained using `target_compatible_with`
to run only on `os:none` to prevent compilation errors during
wildcard host builds.
"""
load("@rules_rust//rust:defs.bzl", "rust_binary")
package(default_visibility = ["//visibility:public"])
rust_binary(
name = "alloc-example",
srcs = ["src/main.rs"],
edition = "2024",
rustc_flags = [
"-C",
"linker=rust-lld",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_alloc_example//:buddy_system_allocator",
"@bare_metal_alloc_example//:panic-halt",
],
)
+373
View File
@@ -0,0 +1,373 @@
"""Bazel BUILD file for AArch64 Bare-Metal APS Examples.
This package defines six distinct bare-metal demonstration binaries
along with their binary extraction genrules and host-executed QEMU
testing scripts. Because this code targets AArch64 bare-metal
platforms and not the host OS:
1. The raw `rust_binary` targets are constrained using
`target_compatible_with` to run only on `os:none` to prevent
compilation errors during wildcard host builds.
2. We extract flat binary images via `genrule` targets using the
hermetic host toolchain's `rust-objcopy`.
3. We wrap each binary target in a platform transition
(`aarch64_binary`) so they are built for the AArch64 bare-metal
target platform during host-executed tests.
"""
load("@rules_rust//rust:defs.bzl", "rust_binary")
load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
load("@rules_shell//shell:sh_test.bzl", "sh_test")
load("//platforms:transition.bzl", "aarch64_binary")
package(default_visibility = ["//visibility:public"])
rust_binary(
name = "improved",
srcs = glob(["src/**/*.rs"]),
compile_data = glob(["src/**/*.S"]),
crate_root = "src/main_improved.rs",
edition = "2024",
linker_script = ":image.ld",
rustc_flags = [
"-C",
"linker=rust-lld",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_aps//:aarch64-paging",
"@bare_metal_aps//:aarch64-rt",
"@bare_metal_aps//:arm-pl011-uart",
"@bare_metal_aps//:bitflags",
"@bare_metal_aps//:log",
"@bare_metal_aps//:safe-mmio",
"@bare_metal_aps//:smccc",
"@bare_metal_aps//:spin",
"@bare_metal_aps//:zerocopy",
],
)
rust_binary(
name = "logger",
srcs = glob(["src/**/*.rs"]),
compile_data = glob(["src/**/*.S"]),
crate_root = "src/main_logger.rs",
edition = "2024",
linker_script = ":image.ld",
rustc_flags = [
"-C",
"linker=rust-lld",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_aps//:aarch64-paging",
"@bare_metal_aps//:aarch64-rt",
"@bare_metal_aps//:arm-pl011-uart",
"@bare_metal_aps//:bitflags",
"@bare_metal_aps//:log",
"@bare_metal_aps//:safe-mmio",
"@bare_metal_aps//:smccc",
"@bare_metal_aps//:spin",
"@bare_metal_aps//:zerocopy",
],
)
rust_binary(
name = "minimal",
srcs = glob(["src/**/*.rs"]),
compile_data = glob(["src/**/*.S"]),
crate_root = "src/main_minimal.rs",
edition = "2024",
linker_script = ":image.ld",
rustc_flags = [
"-C",
"linker=rust-lld",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_aps//:aarch64-paging",
"@bare_metal_aps//:aarch64-rt",
"@bare_metal_aps//:arm-pl011-uart",
"@bare_metal_aps//:bitflags",
"@bare_metal_aps//:log",
"@bare_metal_aps//:safe-mmio",
"@bare_metal_aps//:smccc",
"@bare_metal_aps//:spin",
"@bare_metal_aps//:zerocopy",
],
)
rust_binary(
name = "psci",
srcs = glob(["src/**/*.rs"]),
compile_data = glob(["src/**/*.S"]),
crate_root = "src/main_psci.rs",
edition = "2024",
linker_script = ":image.ld",
rustc_flags = [
"-C",
"linker=rust-lld",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_aps//:aarch64-paging",
"@bare_metal_aps//:aarch64-rt",
"@bare_metal_aps//:arm-pl011-uart",
"@bare_metal_aps//:bitflags",
"@bare_metal_aps//:log",
"@bare_metal_aps//:safe-mmio",
"@bare_metal_aps//:smccc",
"@bare_metal_aps//:spin",
"@bare_metal_aps//:zerocopy",
],
)
rust_binary(
name = "rt",
srcs = glob(["src/**/*.rs"]),
compile_data = glob(["src/**/*.S"]),
crate_root = "src/main_rt.rs",
edition = "2024",
linker_script = ":image.ld",
rustc_flags = [
"-C",
"linker=rust-lld",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_aps//:aarch64-paging",
"@bare_metal_aps//:aarch64-rt",
"@bare_metal_aps//:arm-pl011-uart",
"@bare_metal_aps//:bitflags",
"@bare_metal_aps//:log",
"@bare_metal_aps//:safe-mmio",
"@bare_metal_aps//:smccc",
"@bare_metal_aps//:spin",
"@bare_metal_aps//:zerocopy",
],
)
rust_binary(
name = "safemmio",
srcs = glob(["src/**/*.rs"]),
compile_data = glob(["src/**/*.S"]),
crate_root = "src/main_safemmio.rs",
edition = "2024",
linker_script = ":image.ld",
rustc_flags = [
"-C",
"linker=rust-lld",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_aps//:aarch64-paging",
"@bare_metal_aps//:aarch64-rt",
"@bare_metal_aps//:arm-pl011-uart",
"@bare_metal_aps//:bitflags",
"@bare_metal_aps//:log",
"@bare_metal_aps//:safe-mmio",
"@bare_metal_aps//:smccc",
"@bare_metal_aps//:spin",
"@bare_metal_aps//:zerocopy",
],
)
genrule(
name = "improved_bin",
srcs = [":improved"],
outs = ["improved.bin"],
cmd = "$(execpath @rust_host_tools_nightly//:rust-objcopy) -O binary $(location :improved) $@",
tools = ["@rust_host_tools_nightly//:rust-objcopy"],
)
genrule(
name = "logger_bin",
srcs = [":logger"],
outs = ["logger.bin"],
cmd = "$(execpath @rust_host_tools_nightly//:rust-objcopy) -O binary $(location :logger) $@",
tools = ["@rust_host_tools_nightly//:rust-objcopy"],
)
genrule(
name = "minimal_bin",
srcs = [":minimal"],
outs = ["minimal.bin"],
cmd = "$(execpath @rust_host_tools_nightly//:rust-objcopy) -O binary $(location :minimal) $@",
tools = ["@rust_host_tools_nightly//:rust-objcopy"],
)
genrule(
name = "psci_bin",
srcs = [":psci"],
outs = ["psci.bin"],
cmd = "$(execpath @rust_host_tools_nightly//:rust-objcopy) -O binary $(location :psci) $@",
tools = ["@rust_host_tools_nightly//:rust-objcopy"],
)
genrule(
name = "rt_bin",
srcs = [":rt"],
outs = ["rt.bin"],
cmd = "$(execpath @rust_host_tools_nightly//:rust-objcopy) -O binary $(location :rt) $@",
tools = ["@rust_host_tools_nightly//:rust-objcopy"],
)
genrule(
name = "safemmio_bin",
srcs = [":safemmio"],
outs = ["safemmio.bin"],
cmd = "$(execpath @rust_host_tools_nightly//:rust-objcopy) -O binary $(location :safemmio) $@",
tools = ["@rust_host_tools_nightly//:rust-objcopy"],
)
aarch64_binary(
name = "improved_bin_aarch64",
dep = ":improved_bin",
)
aarch64_binary(
name = "logger_bin_aarch64",
dep = ":logger_bin",
)
aarch64_binary(
name = "minimal_bin_aarch64",
dep = ":minimal_bin",
)
aarch64_binary(
name = "psci_bin_aarch64",
dep = ":psci_bin",
)
aarch64_binary(
name = "rt_bin_aarch64",
dep = ":rt_bin",
)
aarch64_binary(
name = "safemmio_bin_aarch64",
dep = ":safemmio_bin",
)
sh_test(
name = "improved_test",
srcs = ["run_test.sh"],
args = ["improved"],
tags = [
"manual",
"qemu",
],
data = [":improved_bin_aarch64"],
)
sh_test(
name = "logger_test",
srcs = ["run_test.sh"],
args = ["logger"],
tags = [
"manual",
"qemu",
],
data = [":logger_bin_aarch64"],
)
sh_test(
name = "minimal_test",
srcs = ["run_test.sh"],
args = ["minimal"],
tags = [
"manual",
"qemu",
],
data = [":minimal_bin_aarch64"],
)
sh_test(
name = "psci_test",
srcs = ["run_test.sh"],
args = ["psci"],
tags = [
"manual",
"qemu",
],
data = [":psci_bin_aarch64"],
)
sh_test(
name = "rt_test",
srcs = ["run_test.sh"],
args = ["rt"],
tags = [
"manual",
"qemu",
],
data = [":rt_bin_aarch64"],
)
sh_test(
name = "safemmio_test",
srcs = ["run_test.sh"],
args = ["safemmio"],
tags = [
"manual",
"qemu",
],
data = [":safemmio_bin_aarch64"],
)
filegroup(
name = "bins",
srcs = [
":improved_bin",
":logger_bin",
":minimal_bin",
":psci_bin",
":rt_bin",
":safemmio_bin",
],
)
sh_binary(
name = "improved_run",
srcs = ["run_test.sh"],
args = ["improved"],
data = [":improved_bin_aarch64"],
)
sh_binary(
name = "logger_run",
srcs = ["run_test.sh"],
args = ["logger"],
data = [":logger_bin_aarch64"],
)
sh_binary(
name = "minimal_run",
srcs = ["run_test.sh"],
args = ["minimal"],
data = [":minimal_bin_aarch64"],
)
sh_binary(
name = "psci_run",
srcs = ["run_test.sh"],
args = ["psci"],
data = [":psci_bin_aarch64"],
)
sh_binary(
name = "rt_run",
srcs = ["run_test.sh"],
args = ["rt"],
data = [":rt_bin_aarch64"],
)
sh_binary(
name = "safemmio_run",
srcs = ["run_test.sh"],
args = ["safemmio"],
data = [":safemmio_bin_aarch64"],
)
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash
# Copyright 2026 Google LLC
# SPDX-License-Identifier: Apache-2.0
set -euo pipefail
BIN_NAME="$1"
BIN_PATH="src/bare-metal/aps/examples/${BIN_NAME}.bin"
# Detect if running as an automated test under 'bazel test'
NON_INTERACTIVE="${TEST_SRCDIR:-}"
if [ ! -f "$BIN_PATH" ]; then
echo "Error: Binary not found at $BIN_PATH" >&2
exit 1
fi
echo "Running ${BIN_NAME}.bin under QEMU..."
if [ -n "$NON_INTERACTIVE" ]; then
echo "q" | qemu-system-aarch64 -machine virt -cpu max -serial mon:stdio -display none -kernel "$BIN_PATH"
else
qemu-system-aarch64 -machine virt -cpu max -serial mon:stdio -display none -kernel "$BIN_PATH"
fi
@@ -0,0 +1,146 @@
"""Bazel BUILD file for ARM Cortex-M Microcontroller Examples.
This package defines board support and hardware abstraction layer
(HAL) binaries for microcontrollers. Because this code targets ARM
Cortex-M microcontrollers and not the host OS:
1. The raw `rust_binary` targets are constrained using
`target_compatible_with` to run only on `os:none` to prevent
compilation errors during wildcard host builds.
"""
load("@rules_rust//rust:defs.bzl", "rust_binary")
package(default_visibility = ["//visibility:public"])
rust_binary(
name = "board_support",
srcs = glob(["src/bin/**/*.rs"]),
crate_root = "src/bin/board_support.rs",
edition = "2024",
rustc_flags = [
"-C",
"linker=rust-lld",
"-C",
"link-arg=-Tlink.x",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_microcontrollers//:cortex-m-rt",
"@bare_metal_microcontrollers//:embedded-hal",
"@bare_metal_microcontrollers//:microbit-v2",
"@bare_metal_microcontrollers//:nrf52833-hal",
"@bare_metal_microcontrollers//:nrf52833-pac",
"@bare_metal_microcontrollers//:panic-halt",
],
)
rust_binary(
name = "hal",
srcs = glob(["src/bin/**/*.rs"]),
crate_root = "src/bin/hal.rs",
edition = "2024",
rustc_flags = [
"-C",
"linker=rust-lld",
"-C",
"link-arg=-Tlink.x",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_microcontrollers//:cortex-m-rt",
"@bare_metal_microcontrollers//:embedded-hal",
"@bare_metal_microcontrollers//:microbit-v2",
"@bare_metal_microcontrollers//:nrf52833-hal",
"@bare_metal_microcontrollers//:nrf52833-pac",
"@bare_metal_microcontrollers//:panic-halt",
],
)
rust_binary(
name = "minimal",
srcs = glob(["src/bin/**/*.rs"]),
crate_root = "src/bin/minimal.rs",
edition = "2024",
rustc_flags = [
"-C",
"linker=rust-lld",
"-C",
"link-arg=-Tlink.x",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_microcontrollers//:cortex-m-rt",
"@bare_metal_microcontrollers//:embedded-hal",
"@bare_metal_microcontrollers//:microbit-v2",
"@bare_metal_microcontrollers//:nrf52833-hal",
"@bare_metal_microcontrollers//:nrf52833-pac",
"@bare_metal_microcontrollers//:panic-halt",
],
)
rust_binary(
name = "mmio",
srcs = glob(["src/bin/**/*.rs"]),
crate_root = "src/bin/mmio.rs",
edition = "2024",
rustc_flags = [
"-C",
"linker=rust-lld",
"-C",
"link-arg=-Tlink.x",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_microcontrollers//:cortex-m-rt",
"@bare_metal_microcontrollers//:embedded-hal",
"@bare_metal_microcontrollers//:microbit-v2",
"@bare_metal_microcontrollers//:nrf52833-hal",
"@bare_metal_microcontrollers//:nrf52833-pac",
"@bare_metal_microcontrollers//:panic-halt",
],
)
rust_binary(
name = "pac",
srcs = glob(["src/bin/**/*.rs"]),
crate_root = "src/bin/pac.rs",
edition = "2024",
rustc_flags = [
"-C",
"linker=rust-lld",
"-C",
"link-arg=-Tlink.x",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_microcontrollers//:cortex-m-rt",
"@bare_metal_microcontrollers//:embedded-hal",
"@bare_metal_microcontrollers//:microbit-v2",
"@bare_metal_microcontrollers//:nrf52833-hal",
"@bare_metal_microcontrollers//:nrf52833-pac",
"@bare_metal_microcontrollers//:panic-halt",
],
)
rust_binary(
name = "typestate",
srcs = glob(["src/bin/**/*.rs"]),
crate_root = "src/bin/typestate.rs",
edition = "2024",
rustc_flags = [
"-C",
"linker=rust-lld",
"-C",
"link-arg=-Tlink.x",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_microcontrollers//:cortex-m-rt",
"@bare_metal_microcontrollers//:embedded-hal",
"@bare_metal_microcontrollers//:microbit-v2",
"@bare_metal_microcontrollers//:nrf52833-hal",
"@bare_metal_microcontrollers//:nrf52833-pac",
"@bare_metal_microcontrollers//:panic-halt",
],
)
@@ -0,0 +1,33 @@
"""Bazel BUILD file for Microcontroller Compass Exercise.
This package defines the compass exercise target. Because this code
targets ARM Cortex-M microcontrollers and not the host OS:
1. The raw `rust_binary` target is constrained using
`target_compatible_with` to run only on `os:none` to prevent
compilation errors during wildcard host builds.
"""
load("@rules_rust//rust:defs.bzl", "rust_binary")
package(default_visibility = ["//visibility:public"])
rust_binary(
name = "compass",
srcs = ["src/main.rs"],
edition = "2024",
rustc_flags = [
"-C",
"linker=rust-lld",
"-C",
"link-arg=-Tlink.x",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_compass//:cortex-m-rt",
"@bare_metal_compass//:embedded-hal",
"@bare_metal_compass//:lsm303agr",
"@bare_metal_compass//:microbit-v2",
"@bare_metal_compass//:panic-halt",
],
)
+85
View File
@@ -0,0 +1,85 @@
"""Bazel BUILD file for PL031 RTC Bare-Metal Exercise.
This package defines the bare-metal binary and its binary
extraction/testing targets. Because this code runs directly on AArch64
hardware (under QEMU) and not the host OS:
1. The raw `rust_binary` is constrained using `target_compatible_with`
to run only on `os:none` to prevent compilation errors during
wildcard host builds.
2. We extract the binary flat image via a `genrule` using the hermetic
host toolchain's `rust-objcopy`.
3. We declare a custom platform transition target (`rtc_bin_aarch64`)
to build the binary for the AArch64 bare-metal target platform,
allowing host-executed tests to depend on it.
"""
load("@rules_rust//rust:defs.bzl", "rust_binary")
load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
load("@rules_shell//shell:sh_test.bzl", "sh_test")
load("//platforms:transition.bzl", "aarch64_binary")
package(default_visibility = ["//visibility:public"])
rust_binary(
name = "rtc",
srcs = [
"src/exceptions.rs",
"src/logger.rs",
"src/main.rs",
"src/pl031.rs",
],
edition = "2024",
linker_script = ":memory.ld",
rustc_flags = [
"-C",
"linker=rust-lld",
"-C",
"link-arg=-Timage.ld",
],
target_compatible_with = ["@platforms//os:none"],
deps = [
"@bare_metal_rtc//:aarch64-paging",
"@bare_metal_rtc//:aarch64-rt",
"@bare_metal_rtc//:arm-gic",
"@bare_metal_rtc//:arm-pl011-uart",
"@bare_metal_rtc//:bitflags",
"@bare_metal_rtc//:chrono",
"@bare_metal_rtc//:log",
"@bare_metal_rtc//:safe-mmio",
"@bare_metal_rtc//:smccc",
"@bare_metal_rtc//:spin",
"@bare_metal_rtc//:zerocopy",
],
)
genrule(
name = "rtc_bin",
srcs = [":rtc"],
outs = ["rtc.bin"],
cmd = "$(execpath @rust_host_tools_nightly//:rust-objcopy) -O binary $(location :rtc) $@",
tools = ["@rust_host_tools_nightly//:rust-objcopy"],
)
aarch64_binary(
name = "rtc_bin_aarch64",
dep = ":rtc_bin",
)
sh_test(
name = "rtc_test",
srcs = ["rtc_test.sh"],
tags = [
"manual",
"qemu",
],
data = [":rtc_bin_aarch64"],
)
sh_binary(
name = "rtc_run",
srcs = ["rtc_test.sh"],
data = [":rtc_bin_aarch64"],
)
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
# Copyright 2026 Google LLC
# SPDX-License-Identifier: Apache-2.0
set -euo pipefail
BIN_PATH="src/exercises/bare-metal/rtc/rtc.bin"
# Detect if running as an automated test under 'bazel test'
NON_INTERACTIVE="${TEST_SRCDIR:-}"
if [ ! -f "$BIN_PATH" ]; then
echo "Error: rtc.bin not found at $BIN_PATH" >&2
exit 1
fi
echo "Running rtc.bin under QEMU..."
if [ -n "$NON_INTERACTIVE" ]; then
echo "q" | qemu-system-aarch64 -machine virt,gic-version=3 -cpu max -serial mon:stdio -display none -kernel "$BIN_PATH"
else
qemu-system-aarch64 -machine virt,gic-version=3 -cpu max -serial mon:stdio -display none -kernel "$BIN_PATH"
fi