Skip to content
Snippets Groups Projects
build.rs 4.61 KiB
Newer Older
  • Learn to ignore specific revisions
  • #![allow(clippy::uninlined_format_args)]
    
    
    /// 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")),
    /// );
    /// ```
    use chrono::Utc;
    use regex::Regex;
    use std::fs::{File, OpenOptions};
    use std::io::Write;
    use std::path::Path;
    use std::process::Command;
    use std::str;
    
    
    const BUILD_OUT_FILE: &str = "build.rs";
    const SOURCE_DIR: &str = "./src/gen";
    
    
    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(SOURCE_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));
    
        let module_doc_string = "//! Global constants, such as compiler version used, algorithms, compression and filters supported and others\n";
        writeln!(file, "{}", module_doc_string).unwrap();
    
        let generated_annotation = "// @generated\n";
        writeln!(file, "{}", generated_annotation).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,
            "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);
    
        let mut features = vec!["cpu"];
    
        if cfg!(feature = "wasm-extension") {
            features.push("wasm")
        }
    
        if cfg!(feature = "python-extension") {
            features.push("python")
        }
    
        write_vec_constant(&mut file, "FEATURES", features);
    
        write_str_constant(
            &mut file,
            "PLATFORM_CPU_BITS",
            &(std::mem::size_of::<usize>() * 8).to_string(),
        );
    
        write_constant(&mut file, "DEFAULT_THREAD_POOL_SIZE", num_cpus::get());
        write_constant(&mut file, "MAX_THREAD_POOL_SIZE", num_cpus::get() * 10);
    }
    
    fn write_constant<T>(file: &mut File, key: &str, val: T)
    where
        T: std::fmt::Display,
    {
        writeln!(
            file,
            "pub const {}: {} = {};",
            key,
            std::any::type_name::<T>(),
            val
        )
        .unwrap_or_else(|_| panic!("Failed to write '{}' to 'build_constants.rs'", key));
    }
    
    fn write_str_constant(file: &mut File, key: &str, val: &str) {
        writeln!(file, "pub const {}: &str = \"{}\";", key, val)
            .unwrap_or_else(|_| panic!("Failed to write '{}' to 'build_constants.rs'", key));
    }
    
    fn write_vec_constant<T>(file: &mut File, key: &str, vec: Vec<T>)
    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::<T>(),
            vec.len(),
            list_str
        )
        .unwrap_or_else(|_| panic!("Failed to write '{}' to 'build_constants.rs'", key));
    }