//! Build script (https://doc.rust-lang.org/cargo/reference/build-scripts.html) //! This script is executed as the first step in the compilation process. //! Here we export metadata constants to a `constants/generated.rs` file which is then //! imported and used by the remaining crate. //! //! # Examples //! //! In C you can use the preprocessor macro `__DATE__` to save the compilation date like: //! //! ```c //! #define COMPILATION_DATE __DATE__ //! ``` //! //! Rust does not have such preprocessor macros, so we use this script and do: //! //! ```rust //! let now_utc = chrono::Utc::now(); //! write_str_constant( //! &mut file, //! "COMPILATION_DATE", //! &format!("{}", now_utc.format("%b %d %Y")), //! ); //! ``` #[cfg(not(feature = "gen-mock"))] use built::write_built_file_with_opts; #[cfg(not(feature = "gen-mock"))] use chrono::Utc; #[cfg(not(feature = "gen-mock"))] use regex::Regex; #[cfg(not(feature = "gen-mock"))] use std::{ env, fs::{File, OpenOptions}, io::Write, path::Path, process::Command, str, }; #[cfg(not(feature = "gen-mock"))] const BUILD_OUT_FILE: &str = "build.rs"; #[cfg(not(feature = "gen-mock"))] const BUILD_ALL_OUT_FILE: &str = "_build.rs"; #[cfg(not(feature = "gen-mock"))] const GEN_DIR: &str = "./src/gen"; #[cfg(not(feature = "gen-mock"))] fn main() { // in case we're running under docs.rs then we must return the control // flow immediately as it's not possible to generate files under the // expected read only file system present in `docs.rs` build environment if std::env::var("DOCS_RS").is_ok() { return; } // opens the target destination file panicking with a proper message in // case it was not possible to open it (eg: directory inexistent) let dest_path = Path::new(GEN_DIR).join(Path::new(BUILD_OUT_FILE)); let mut file = OpenOptions::new() .truncate(true) .write(true) .create(true) .open(dest_path) .unwrap_or_else(|_| panic!("Can't open '{BUILD_OUT_FILE}'")); writeln!(file, "//! Global constants, such as compiler version used, features, platform information and others.\n").unwrap(); writeln!(file, "// @generated\n").unwrap(); let now_utc = Utc::now(); write_str_constant( &mut file, "COMPILATION_DATE", &format!("{}", now_utc.format("%b %d %Y")), ); write_str_constant( &mut file, "COMPILATION_TIME", &format!("{}", now_utc.format("%H:%M:%S")), ); write_str_constant( &mut file, "NAME", option_env!("CARGO_PKG_NAME").unwrap_or("UNKNOWN"), ); write_str_constant( &mut file, "VERSION", option_env!("CARGO_PKG_VERSION").unwrap_or("UNKNOWN"), ); write_str_constant(&mut file, "COMPILER", "rustc"); let compiler_version = Command::new("rustc") .arg("--version") .output() .ok() .and_then(|output| String::from_utf8(output.stdout).ok()) .unwrap_or_else(|| "UNKNOWN".to_string()); let re = Regex::new("rustc ([\\d.\\d.\\d]*)").unwrap(); let compiler_version = re .captures(&compiler_version) .unwrap() .get(1) .unwrap() .as_str(); write_str_constant(&mut file, "COMPILER_VERSION", compiler_version); write_str_constant( &mut file, "HOST", &env::var("HOST").unwrap_or_else(|_| String::from("UNKNOWN")), ); write_str_constant( &mut file, "TARGET", &env::var("TARGET").unwrap_or_else(|_| String::from("UNKNOWN")), ); write_str_constant( &mut file, "PROFILE", &env::var("PROFILE").unwrap_or_else(|_| String::from("UNKNOWN")), ); write_str_constant( &mut file, "OPT_LEVEL", &env::var("OPT_LEVEL").unwrap_or_else(|_| String::from("UNKNOWN")), ); write_str_constant( &mut file, "MAKEFLAGS", option_env!("CARGO_MAKEFLAGS").unwrap_or("UNKNOWN"), ); let mut features = vec!["cpu"]; if cfg!(feature = "wasm") { features.push("wasm") } if cfg!(feature = "debug") { features.push("debug") } if cfg!(feature = "pedantic") { features.push("pedantic") } if cfg!(feature = "cpulog") { features.push("cpulog") } write_vec_constant(&mut file, "FEATURES_SEQ", features); write_str_constant( &mut file, "PLATFORM_CPU_BITS", &(std::mem::size_of::() * 8).to_string(), ); write_constant( &mut file, "PLATFORM_CPU_BITS_INT", std::mem::size_of::() * 8, ); let manifest_path = env::var("CARGO_MANIFEST_DIR").unwrap(); let built_path = Path::new(GEN_DIR).join(Path::new(BUILD_ALL_OUT_FILE)); write_built_file_with_opts(Some(manifest_path.as_ref()), &built_path).unwrap(); } #[cfg(feature = "gen-mock")] fn main() {} #[cfg(not(feature = "gen-mock"))] fn write_constant(file: &mut File, key: &str, val: T) where T: std::fmt::Display, { writeln!( file, "pub const {}: {} = {};", key, std::any::type_name::(), val ) .unwrap_or_else(|_| panic!("Failed to write '{key}' to 'build_constants.rs'")); } #[cfg(not(feature = "gen-mock"))] fn write_str_constant(file: &mut File, key: &str, val: &str) { writeln!(file, "pub const {key}: &str = \"{val}\";") .unwrap_or_else(|_| panic!("Failed to write '{key}' to 'build_constants.rs'")); } #[cfg(not(feature = "gen-mock"))] fn write_vec_constant(file: &mut File, key: &str, vec: Vec) where T: std::fmt::Display, { let mut list_str = String::new(); let mut is_first = true; for value in &vec { if is_first { is_first = false; } else { list_str.push_str(", "); } list_str.push_str(format!("\"{value}\"").as_str()); } writeln!( file, "pub const {}: [{}; {}] = [{}];", key, std::any::type_name::(), vec.len(), list_str ) .unwrap_or_else(|_| panic!("Failed to write '{key}' to 'build_constants.rs'")); }