rivet-rtos 0.2.0

Rivet RTOS: zero-allocation async RTOS kernel — arch/board-independent, see docs/porting.md
Documentation
//! Compile-time kernel configuration (plan.md §4.1).
//!
//! Reads environment variables at build time and generates
//! `$OUT_DIR/config.rs` with the kernel's capacity constants. Same pattern
//! Embassy uses for its arena size; works on stable Rust. Every value is
//! bounds-checked with `compile_error!`.
//!
//! Variables (defaults in parentheses):
//! - `RIVET_MAX_PTASKS` (16, max 32) — preemptive task slots.
//! - `RIVET_MAX_TIMERS` (16) — outstanding cooperative sleep timers.
//! - `RIVET_MAX_COOP_TASKS` (16, max 32 — hard-capped by the u32
//!   per-priority bitmap).
//! - `RIVET_PRIORITIES` (32, max 32) — priority levels.
//! - `RIVET_TICK_HZ` (1000) — tick rate the arch layer programs.
//! - `RIVET_MAX_HELD_MUTEXES` (4) — nested mutexes per task.
//! - `RIVET_MAX_IRQS` (32, max 240 — ARMv7-M's architectural ceiling) —
//!   registrable external-interrupt slots (plan.md Phase 13).
//! - `RIVET_MAX_HARTS` (1, max 8) — number of harts the kernel's
//!   per-hart scheduler state is sized for (plan.md Phase 19). `1` (the
//!   default) is the single-hart control-flow path every board except
//!   RISC-V `virt` under `-smp` uses; only a board that actually calls
//!   `rivet::preempt::start_secondary_hart` from more than one hart
//!   should ever set this above `1`.

use std::env;
use std::fmt::Write;
use std::fs;
use std::path::PathBuf;

fn cfg_usize(name: &str, default: usize, min: usize, max: usize) -> usize {
    let raw = env::var(name);
    let value = match raw {
        Ok(v) => v.parse::<usize>().unwrap_or_else(|_| {
            panic!("{name}: expected an integer, got {v:?}");
        }),
        Err(_) => default,
    };
    if !(min..=max).contains(&value) {
        panic!(
            "{name}={value} is out of range; must be in [{min}, {max}] \
             (re-run cargo with the variable unset or corrected)"
        );
    }
    value
}

fn main() {
    println!("cargo:rerun-if-env-changed=RIVET_MAX_PTASKS");
    println!("cargo:rerun-if-env-changed=RIVET_MAX_TIMERS");
    println!("cargo:rerun-if-env-changed=RIVET_MAX_COOP_TASKS");
    println!("cargo:rerun-if-env-changed=RIVET_PRIORITIES");
    println!("cargo:rerun-if-env-changed=RIVET_TICK_HZ");
    println!("cargo:rerun-if-env-changed=RIVET_MAX_HELD_MUTEXES");
    println!("cargo:rerun-if-env-changed=RIVET_MAX_IRQS");
    println!("cargo:rerun-if-env-changed=RIVET_MAX_HARTS");

    let max_ptasks = cfg_usize("RIVET_MAX_PTASKS", 16, 1, 32);
    let max_timers = cfg_usize("RIVET_MAX_TIMERS", 16, 1, 64);
    let max_coop = cfg_usize("RIVET_MAX_COOP_TASKS", 16, 1, 32);
    let priorities = cfg_usize("RIVET_PRIORITIES", 32, 1, 32);
    let tick_hz = cfg_usize("RIVET_TICK_HZ", 1000, 1, 1_000_000);
    let max_held = cfg_usize("RIVET_MAX_HELD_MUTEXES", 4, 1, 16);
    let max_irqs = cfg_usize("RIVET_MAX_IRQS", 32, 1, 240);
    let max_harts = cfg_usize("RIVET_MAX_HARTS", 1, 1, 8);

    let mut out = String::new();
    let _ = writeln!(out, "// Generated by rivet/build.rs — do not edit.");
    let _ = writeln!(
        out,
        "pub const MAX_PTASKS: usize = {max_ptasks}; // RIVET_MAX_PTASKS"
    );
    let _ = writeln!(
        out,
        "pub const MAX_TIMERS: usize = {max_timers}; // RIVET_MAX_TIMERS"
    );
    let _ = writeln!(
        out,
        "pub const MAX_TASKS: usize = {max_coop}; // RIVET_MAX_COOP_TASKS (<= 32)"
    );
    let _ = writeln!(
        out,
        "pub const PRIORITY_LEVELS: usize = {priorities}; // RIVET_PRIORITIES (<= 32)"
    );
    let _ = writeln!(out, "pub const TICK_HZ: u32 = {tick_hz}; // RIVET_TICK_HZ");
    let _ = writeln!(
        out,
        "pub const MAX_HELD: usize = {max_held}; // RIVET_MAX_HELD_MUTEXES"
    );
    let _ = writeln!(
        out,
        "pub const MAX_IRQS: usize = {max_irqs}; // RIVET_MAX_IRQS"
    );
    let _ = writeln!(
        out,
        "pub const MAX_HARTS: usize = {max_harts}; // RIVET_MAX_HARTS"
    );

    let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR"));
    fs::write(out_dir.join("config.rs"), out).expect("write config.rs");
}