xous-core/utralib/build.rs
bunnie ee36a25acd add check for soc.svd file
in simulation some extra registers are made available for helping
to manipulate the simulation. map them in if they are around.
2025-09-20 22:11:22 +08:00

199 lines
7.9 KiB
Rust

use std::env;
use std::fs::OpenOptions;
#[cfg(not(any(feature = "hosted", feature = "hosted-baosec")))]
use std::io::Read;
use std::io::Write;
use std::path::PathBuf;
fn out_dir() -> PathBuf { PathBuf::from(env::var_os("OUT_DIR").unwrap()) }
/// Helper macro that returns a constant number of features enabled among specified list.
macro_rules! count_enabled_features {
($($feature:literal),*) => {
{
let mut enabled_features = 0;
$(
enabled_features += cfg!(feature = $feature) as u32;
)*
enabled_features
}
}
}
/// Helper macro that returns a compile-time error if multiple or none of the
/// features of some category are defined.
///
/// # Example
///
/// Given the following code:
///
/// ```
/// allow_single_feature!("feature-category-name", "a", "b", "c");
/// ```
///
/// These runs fail compilation check:
/// $ cargo check --features a,b # error msg: 'Multiple feature-category-name specified. Only one is allowed.
/// $ cargo check # error msg: 'None of the feature-category-name specified. Pick one.'
///
/// This compiles:
/// $ cargo check --feature a
macro_rules! allow_single_feature {
($name:literal, $($feature:literal),*) => {
const _: () = {
const MSG_MULTIPLE: &str = concat!("\nMultiple ", $name, " specified. Only one is allowed.");
const MSG_NONE: &str = concat!("\nNone of the ", $name, " specified. Pick one.");
match count_enabled_features!($($feature),*) {
0 => std::panic!("{}", MSG_NONE),
1 => {}
2.. => std::panic!("{}", MSG_MULTIPLE),
}
};
}
}
macro_rules! allow_single_target_feature {
($($args:tt)+) => {
allow_single_feature!("targets", $($args)+);
}
}
#[cfg(feature = "precursor")] // Gitrevs are only relevant for Precursor target
macro_rules! allow_single_gitrev_feature {
($($args:tt)+) => {
allow_single_feature!("gitrevs", $($args)+);
}
}
fn main() {
// ------ check that the feature flags are sane -----
// note on selecting "hosted" mode. An explicit "hosted" flag is provided to clarify
// the build system's intent. However, in general, most packages prefer to use this idiom:
//
// #[cfg(not(target_os = "xous"))]
//
// This flag is synonymous with feature = "hosted", and it also makes "hosted" mode the
// "default" package in the case that the code is being built in CI or in external
// packages that don't know to configure the "hosted" feature flag.
//
// This idiom breaks if Xous ever gets to the point of compiling and running code on
// its own platform; but generally, if the target binary is running on e.g. windows/linux/non-xous
// target triples, the user's intent was "hosted" mode.
//
// This script retains the use of an explicit "hosted" flag because we want to catch
// unintentional build system misconfigurations that meant to build for a target other
// than "hosted", rather than just falling back silently to defaults.
allow_single_target_feature!(
"precursor",
"hosted",
"renode",
"atsama5d27",
"bao1x",
"artybio",
"artyvexii"
);
#[cfg(feature = "precursor")]
allow_single_gitrev_feature!("precursor-perflib", "precursor-dvt", "precursor-pvt");
// ----- select an SVD file based on a specific revision -----
#[cfg(feature = "precursor-perflib")]
let svd_filenames = vec!["precursor/soc-perf.svd"];
#[cfg(feature = "precursor-perflib")]
let generated_filename = "src/generated/precursor_perf.rs";
#[cfg(feature = "renode")]
let svd_filenames = vec!["renode/renode.svd"];
#[cfg(feature = "renode")]
let generated_filename = "src/generated/renode.rs";
#[cfg(feature = "precursor-dvt")]
let svd_filenames = vec!["precursor/soc-dvt.svd"];
#[cfg(feature = "precursor-dvt")]
let generated_filename = "src/generated/precursor_dvt.rs";
#[cfg(feature = "precursor-pvt")]
let svd_filenames = vec!["precursor/soc-pvt.svd"];
#[cfg(feature = "precursor")]
let generated_filename = "src/generated/precursor_pvt.rs";
#[cfg(feature = "atsama5d27")]
let svd_filenames = vec!["atsama5d/ATSAMA5D27.svd"];
#[cfg(feature = "atsama5d27")]
let generated_filename = "src/generated/atsama5d27.rs";
#[cfg(feature = "bao1x")]
let mut svd_filenames = vec!["bao1x/core.svd", "bao1x/bao1x_peri.svd"];
#[cfg(feature = "bao1x")]
if std::path::Path::new("bao1x/soc.svd").exists() {
svd_filenames.push("bao1x/soc.svd");
}
#[cfg(feature = "bao1x")]
let generated_filename = "src/generated/bao1x.rs";
#[cfg(feature = "artybio")]
let svd_filenames = vec!["artybio/soc.svd", "artybio/bio.svd"];
#[cfg(feature = "artybio")]
let generated_filename = "src/generated/artybio.rs";
#[cfg(feature = "artyvexii")]
let svd_filenames = vec!["artyvexii/soc.svd"];
#[cfg(feature = "artyvexii")]
let generated_filename = "src/generated/artyvexii.rs";
// ----- control file generation and rebuild sequence -----
// check and see if the configuration has changed since the last build. This should be
// passed by the build system (e.g. xtask) if the feature is used.
//
// Debug this using:
// $env:CARGO_LOG="cargo::core::compiler::fingerprint=info"
#[cfg(not(any(feature = "hosted", feature = "hosted-baosec")))]
{
let mut svd_files = Vec::new();
for svd in svd_filenames.iter() {
let svd_file_path = std::path::Path::new(&svd);
println!("cargo:rerun-if-changed={}", svd_file_path.canonicalize().unwrap().display());
svd_files.push(std::fs::File::open(svd_file_path).unwrap());
}
// Regenerate the utra file in RAM.
let mut dest_vec = vec![];
svd2utra::generate(svd_files, &mut dest_vec).unwrap();
// If the file exists, check to see if it is different from what we just generated.
// If not, skip writing the new file.
// If the file doesn't exist, or if it's different, write out a new utra file.
let should_write = if let Ok(mut existing_file) = std::fs::File::open(generated_filename) {
let mut existing_file_contents = vec![];
existing_file
.read_to_end(&mut existing_file_contents)
.expect("couldn't read existing utra generated file");
existing_file_contents != dest_vec
} else {
true
};
if should_write {
let mut dest_file = std::fs::File::create(generated_filename).expect("couldn't open dest file");
dest_file.write_all(&dest_vec).expect("couldn't write contents to utra file");
}
// ----- feedback SVD path to build framework -----
// pass the computed SVD filename back to the build system, so that we can pass this
// on to the image creation program. This is necessary so we can extract all the memory
// regions and create the whitelist of memory pages allowed to the kernel; any page not
// explicitly used by the hardware model is ineligible for mapping and allocation by any
// process. This helps to prevent memory aliasing attacks by hardware blocks that partially
// decode their addresses (this would be in anticipation of potential hardware bugs; ideally
// this isn't ever a problem).
let svd_path = out_dir().join("../../SVD_PATH");
let mut svd_file = OpenOptions::new().create(true).write(true).truncate(true).open(svd_path).unwrap();
for svd in svd_filenames.iter() {
writeln!(svd_file, "utralib/{}", svd).unwrap();
}
}
#[cfg(any(feature = "hosted", feature = "hosted-baosec"))]
{
let svd_path = out_dir().join("../../SVD_PATH");
let mut svd_file = OpenOptions::new().create(true).write(true).truncate(true).open(svd_path).unwrap();
write!(svd_file, "").unwrap(); // there is no SVD file for hosted mode
}
}