refactor: build system auto-discovery, updated docs
- build.rs discovers boards by content (implements LoRaBoard), not exclusion lists - build.rs auto-discovers helper modules by scanning board files for use super::<helper> - Jinja template dynamically generates cfg gates for helpers - Updated PORTING.md for new architecture (hal/, driver/, BoardParts, associated types) - Zero manual template editing needed when adding boards to existing MCU families
This commit is contained in:
3 files changed
+94
-46
No files matched your search
+65
-22
@@ -1,32 +1,48 @@
|
||||
use minijinja::{context, Environment};
|
||||
use owo_colors::OwoColorize;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
|
||||
fn main() {
|
||||
// Protocol tests run on the host without a board feature. Cargo doesn't
|
||||
// expose `cfg(test)` to build scripts, so we rely on an env var set by
|
||||
// the justfile `test` recipe. See: `just test`
|
||||
// Protocol tests run on the host without a board feature.
|
||||
if std::env::var_os("DONGLORA_HOST_TEST").is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let board_dir = "src/board";
|
||||
let boards: Vec<String> = fs::read_dir(board_dir)
|
||||
.expect("failed to read src/board directory")
|
||||
.filter_map(|e| {
|
||||
let p = e.ok()?.path();
|
||||
let stem = p.file_stem()?.to_str()?;
|
||||
// Exclude non-board files: mod.rs, traits.rs, and platform helpers (esp32s3.rs).
|
||||
if p.is_file()
|
||||
&& p.extension()? == "rs"
|
||||
&& !matches!(stem, "mod" | "traits" | "esp32s3")
|
||||
{
|
||||
return Some(p.file_stem()?.to_str()?.to_string());
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect();
|
||||
|
||||
// ── Discover boards vs helpers by content ───────────────────────
|
||||
//
|
||||
// A .rs file in src/board/ that implements LoRaBoard is a board.
|
||||
// Everything else (traits.rs, esp32s3.rs, etc.) is a helper module.
|
||||
// No exclusion lists — detection is based on what the file contains.
|
||||
let mut boards: Vec<String> = Vec::new();
|
||||
|
||||
for entry in fs::read_dir(board_dir).expect("failed to read src/board directory") {
|
||||
let p = match entry {
|
||||
Ok(e) => e.path(),
|
||||
Err(_) => continue,
|
||||
};
|
||||
if !p.is_file() || p.extension().and_then(|e| e.to_str()) != Some("rs") {
|
||||
continue;
|
||||
}
|
||||
let stem = match p.file_stem().and_then(|s| s.to_str()) {
|
||||
Some("mod") => continue, // Skip generated file
|
||||
Some(s) => s.to_string(),
|
||||
None => continue,
|
||||
};
|
||||
let content = match fs::read_to_string(&p) {
|
||||
Ok(c) => c,
|
||||
Err(_) => continue,
|
||||
};
|
||||
if content.contains("LoRaBoard for Board") {
|
||||
boards.push(stem);
|
||||
}
|
||||
}
|
||||
|
||||
boards.sort(); // Deterministic ordering
|
||||
|
||||
// ── Validate exactly one board feature is enabled ───────────────
|
||||
let enabled: Vec<_> = std::env::vars()
|
||||
.filter(|(k, _)| k.starts_with("CARGO_FEATURE_"))
|
||||
.map(|(k, _)| k.replace("CARGO_FEATURE_", "").to_lowercase())
|
||||
@@ -49,6 +65,35 @@ fn main() {
|
||||
),
|
||||
}
|
||||
|
||||
// ── Auto-discover helper modules used by board files ────────────
|
||||
//
|
||||
// Scan each board file for `use super::<name>` to find which helper
|
||||
// modules they depend on. Generate cfg gates so helpers compile only
|
||||
// when a dependent board is selected.
|
||||
let mut helper_boards: HashMap<String, Vec<String>> = HashMap::new();
|
||||
|
||||
for board in &boards {
|
||||
let path = format!("{}/{}.rs", board_dir, board);
|
||||
if let Ok(content) = fs::read_to_string(&path) {
|
||||
for line in content.lines() {
|
||||
let trimmed = line.trim();
|
||||
if let Some(rest) = trimmed.strip_prefix("use super::") {
|
||||
let helper = rest
|
||||
.split(|c: char| !c.is_alphanumeric() && c != '_')
|
||||
.next()
|
||||
.unwrap_or("");
|
||||
if !helper.is_empty() && helper != "traits" {
|
||||
helper_boards
|
||||
.entry(helper.to_string())
|
||||
.or_default()
|
||||
.push(board.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Render board module template ────────────────────────────────
|
||||
let mut env = Environment::new();
|
||||
let template = fs::read_to_string("src/board/mod.rs.j2").expect("missing src/board/mod.rs.j2");
|
||||
env.add_template("mod", &template)
|
||||
@@ -56,7 +101,7 @@ fn main() {
|
||||
let rendered = env
|
||||
.get_template("mod")
|
||||
.expect("template not found")
|
||||
.render(context!(boards => boards))
|
||||
.render(context!(boards => boards, helpers => helper_boards))
|
||||
.expect("failed to render board module");
|
||||
fs::write("src/board/mod.rs", rendered).expect("failed to write src/board/mod.rs");
|
||||
|
||||
@@ -64,9 +109,7 @@ fn main() {
|
||||
println!("cargo:rerun-if-changed={}", board_dir);
|
||||
|
||||
// cortex-m-rt's link.x does `INCLUDE memory.x`. Copy our nRF layout into
|
||||
// OUT_DIR (which cortex-m-rt already adds to the linker search path) so it's
|
||||
// only visible to ARM builds. Keeping it out of the project root prevents
|
||||
// the linker from shadowing esp-hal's generated memory.x on Xtensa builds.
|
||||
// OUT_DIR so it's only visible to ARM builds.
|
||||
let target = std::env::var("TARGET").unwrap_or_default();
|
||||
if !target.starts_with("xtensa") {
|
||||
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||
|
||||
Reference in new issue
Block a user