Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 53 additions & 19 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,21 @@ ci-nosetup:

# Run the fast jobs.
# This is designed for developers, to be run often and before submitting code upstream.
#
# These jobs write to separate target-triple subdirectories, so run them
# concurrently once format-check (cheap, fails fast) passes on its own.
# Only default our own -j if the user didn't pass one -- a submake's own
# -j always overrides a parent's instead of merging with it. --output-sync
# needs make >= 4.0 (e.g. macOS's bundled make is 3.81), so only pass it
# if supported, since an unknown flag is a hard error, not a graceful skip.
NPROC := $(or $(shell nproc 2>/dev/null),$(shell sysctl -n hw.ncpu 2>/dev/null),4)
JOBS := $(if $(filter -j% --jobserver%,$(MAKEFLAGS)),,-j$(NPROC))
OSYNC := $(if $(filter-out 0 1 2 3,$(firstword $(subst ., ,$(MAKE_VERSION)))),--output-sync=target,)
.PHONY: prepush
prepush:\
format-check\
ci-job-clippy\
ci-job-syntax\
licensecheck
prepush:
@$(MAKE) format-check
@$(MAKE) $(OSYNC) $(JOBS) \
ci-job-clippy ci-job-syntax licensecheck
$(call banner,Pre-Push checks all passed!)
# Note: Tock runs additional and more intense CI checks on all PRs.
# If one of these error, you can run `make ci-job-NAME` to test locally.
Expand Down Expand Up @@ -401,21 +410,42 @@ ci-job-readme-check:


### ci-runner-github-clippy jobs:
#
# One board per `arch/*` crate actually in use (tools/build/list_arch_boards.sh),
# so clippy checks every architecture's target-specific code without
# checking every board.
#
# Split into sub-targets so `make -j` can run them concurrently instead of
# back-to-back. They share one workspace `target/` dir, so on a clean
# checkout they still partly serialize on `-Zbuild-std`'s lock; cached
# (normal) runs don't.
#
# $(shell) doesn't fail the build on a nonzero exit, so an empty result
# (the script errored, or a future change left it with nothing to say)
# would otherwise silently generate zero board-specific clippy rules
# below instead of failing loudly. Checked in ci-job-clippy's own recipe,
# not a top-level $(error): this assignment runs on every `make`
# invocation regardless of target (Make expands all rule headers up
# front), so failing here would take down unrelated targets like `make
# clean` whenever this script breaks.
CLIPPY_ARCH_BOARDS := $(shell ./tools/build/list_arch_boards.sh)

.PHONY: ci-job-clippy
ci-job-clippy:
$(call banner,CI-Job: Clippy)
ci-job-clippy: ci-job-clippy-workspace $(foreach b,$(CLIPPY_ARCH_BOARDS),ci-job-clippy-$(subst /,-,$(b)))
@test -n "$(CLIPPY_ARCH_BOARDS)" || (echo "error: list_arch_boards.sh returned no boards -- check it succeeded" >&2; exit 1)

.PHONY: ci-job-clippy-workspace
ci-job-clippy-workspace:
$(call banner,CI-Job: Clippy (workspace))
@cargo clippy -- -D warnings
# Run `cargo clippy` in select boards so we run clippy with targets that
# actually check the arch-specific functions.
#
# - nrf52840dk: cortex-m4
# - raspberry_pi_pico: cortex-m0
# - hifive1: riscv
# - qemu_i486_q35: x86
@cd boards/nordic/nrf52840dk && cargo clippy -- -D warnings
@cd boards/raspberry_pi_pico && cargo clippy -- -D warnings
@cd boards/hifive1 && cargo clippy -- -D warnings
@cd boards/qemu_i486_q35 && cargo clippy -Zjson-target-spec -- -D warnings

define clippy_arch_board_rule
.PHONY: ci-job-clippy-$(subst /,-,$(1))
ci-job-clippy-$(subst /,-,$(1)):
$$(call banner,CI-Job: Clippy ($(1)))
@cd boards/$(1) && cargo clippy -- -D warnings
endef
$(foreach b,$(CLIPPY_ARCH_BOARDS),$(eval $(call clippy_arch_board_rule,$(b))))



Expand All @@ -427,10 +457,14 @@ ci-job-clippy:
# `rustflags` a crate already sets via its own `.cargo/config.toml`.
DENY_WARNINGS_CARGO_CONFIG := $(CURDIR)/boards/cargo/deny_warnings.toml

# Calls `cargo check` directly rather than via `$(MAKE) allcheck`: a
# recursive $(MAKE) here bypasses --output-sync=target's buffering (the
# child manages its own output), so under a parallel `prepush` its
# compile output could land in the middle of another job's block.
.PHONY: ci-job-syntax
ci-job-syntax:
$(call banner,CI-Job: Syntax)
@TOCK_CARGO_FLAGS="--config $(DENY_WARNINGS_CARGO_CONFIG)" $(MAKE) allcheck
@cargo check --config $(DENY_WARNINGS_CARGO_CONFIG)

.PHONY: ci-job-compilation
ci-job-compilation:
Expand Down
13 changes: 8 additions & 5 deletions boards/qemu_i486_q35/.cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ include = [
"../../cargo/unstable_flags.toml",
]

[env]
# Relative to crate root, not this file
# TODO: where is the best place to store this config file?
RUST_TARGET_PATH = "."

[build]
target = "i486-unknown-none.json"

[target.i486-unknown-none]
runner = "qemu-system-i386 -cpu 486 -machine q35 -net none -device isa-debug-exit,iobase=0xf4,iosize=0x04 -device virtio-rng-pci,disable-legacy=on -serial stdio -kernel"

# `target` above is a bare filename, not a path -- cargo only resolves that
# as a custom target spec (rather than requiring a builtin triple name) with
# this enabled. Same mechanism ../../cargo/riscv_flags.toml uses for the
# RISC-V boards' JSON specs, just needed explicitly here since those are
# referenced by relative path instead.
[unstable]
json-target-spec = true
4 changes: 0 additions & 4 deletions boards/qemu_i486_q35/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@
# Skip auto-installing targets with rustup, since we are using a custom target
NO_RUSTUP := 1

# Because we use a custom target with a .json file, we must pass
# `-Zjson-target-spec` to cargo as of roughly January 2026.
CARGO = cargo -Zjson-target-spec

include ../Makefile.common

QEMU_CMD := qemu-system-i386
Expand Down
67 changes: 67 additions & 0 deletions tools/build/list_arch_boards.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env bash

# Licensed under the Apache License, Version 2.0 or the MIT License.
# SPDX-License-Identifier: Apache-2.0 OR MIT
# Copyright Tock Contributors 2026.

# For each arch/* crate, print one representative board: the
# alphabetically-first board that depends on it, directly or via its chip.
#
# Reasonably aware of arch dependencies (i.e., `cortexm` picked up by `cortexm7`), and
# avoids duplicates where unneeded (but, e.g., `cortexm4` also necessarily repeats `cortexm`).
#
# Variants of another board (extra feature/policy configs, tutorial
# copies) are skipped, same as boards/README.md's own tooling, since
# they're not independent ports and would just be redundant picks.
# Sorted once so the loop below can stop at the first (alphabetical) match.
boards=($(./tools/build/list_boards.sh | sort | grep -vE '^(configurations|tutorials)/'))

# True if $board depends on arch/$arch, directly or via its chip.
board_depends_on_arch() {
local board="$1" arch="$2"
local board_toml="boards/$board/Cargo.toml"
# Matches a `path = "…/arch/$arch"` dependency line; some Cargo.toml
# files omit the whitespace around `=`.
local pattern="path[[:space:]]*=[[:space:]]*\"[^\"]*/arch/$arch\""

grep -qE "$pattern" "$board_toml" && return 0

local chip_path chip_toml
# Pulls the path out of each `chips/*` dependency the board declares.
for chip_path in $(grep -oE 'path[[:space:]]*=[[:space:]]*"[^"]*/chips/[^"]*"' "$board_toml" | sed -E 's/.*"(.*)"/\1/'); do
chip_toml="boards/$board/$chip_path/Cargo.toml"
[ -f "$chip_toml" ] && grep -qE "$pattern" "$chip_toml" && return 0
done
return 1
}

# Collected rather than printed as we go, so a failure partway through
# (see below) leaves nothing on stdout instead of an incomplete board list
# -- callers using `$(shell ...)` can't see a nonzero exit status, only
# empty-vs-nonempty output.
found_boards=""

for arch in $(./tools/build/list_archs.sh); do
found=""
for board in "${boards[@]}"; do
if board_depends_on_arch "$board" "$arch"; then
found="$board"
break
fi
done
if [ -n "$found" ]; then
found_boards="$found_boards$found
"
continue
fi

# No board uses this arch directly -- fine if another arch crate does
# (e.g. cortex-m, picked up by cortex-m4), since it then gets compiled,
# and so checked, as a side effect of building that arch's own board.
grep -qE "path[[:space:]]*=[[:space:]]*\"\.\./$arch\"" arch/*/Cargo.toml 2>/dev/null && continue

echo "error: no board or arch crate depends on arch/$arch" >&2
exit 1
done

printf '%s' "$found_boards" | sort -u