crlibm 0.2.0

Binding to CRlibm, a correctly rounded math lib
Documentation

const CRLIBM_INCLUDE_DIRECTORIES: &[&str] =
    &["crlibm", "crlibm/scs_lib"];

const CRLIBM_FILES: &[&str] =
    &["crlibm/crlibm_private.c",
      "crlibm/triple-double.c",
      "crlibm/exp-td.c",
      "crlibm/exp-td-standalone.c",
      "crlibm/expm1-standalone.c",
      "crlibm/expm1.c",
      "crlibm/log1p.c",
      "crlibm/rem_pio2_accurate.c",
      "crlibm/trigo_fast.c",
      "crlibm/trigo_accurate.c",
      "crlibm/trigpi.c",
      "crlibm/asincos.c",
      "crlibm/atan_fast.c",
      "crlibm/atan_accurate.c",
      "crlibm/csh_fast.c",
      "crlibm/scs_lib/scs_private.c",
      "crlibm/scs_lib/addition_scs.c",
      "crlibm/scs_lib/division_scs.c",
      // "crlibm/scs_lib/print_scs.c",
      "crlibm/scs_lib/double2scs.c",
      //"crlibm/scs_lib/zero_scs.c",
      "crlibm/scs_lib/multiplication_scs.c",
      "crlibm/scs_lib/scs2double.c" ];

fn in_appropriate_dir(file: &str) -> std::path::PathBuf {
    match std::env::var("OUT_DIR") {
        Err(_) => std::path::PathBuf::from(file),
        Ok(dir) => {
            use std::path::PathBuf;
            let mut path = PathBuf::new();
            path.push(dir);
            path.push(file);
            path
        }
    }
}

fn add_has_header_flag(build: &mut cc::Build, name: &str)
                       -> std::io::Result<()> {
    let tmp_file = in_appropriate_dir("build_has_header.c");
    use std::fs::File;
    use std::io::Write;
    { let mut file = File::create(&tmp_file)?;
      write!(file, "#include \"{}.h\"\nint main() {{ return 0; }}\n", name)?; }
    let mut local_build = cc::Build::new();
    let header_exists = local_build.warnings(false)
                                   .file(&tmp_file).try_expand().is_ok();
    if header_exists {
        let mut flag = String::from("HAVE_");
        flag.push_str(name.to_uppercase().as_str());
        flag.push_str("_H");
        build.define(flag.as_str(), None);
    }
    std::fs::remove_file(tmp_file).expect("Cannot remove file.");
    Ok(())
}

fn has_fpu_control() -> bool {
    let tmp_file = in_appropriate_dir("has_fpu_control.c");
    use std::fs::File;
    use std::io::Write;
    let file =
        File::create(&tmp_file)
        .and_then(|mut file| {
            write!(file, "#include \"fpu_control.h\"\nint main() {{\n\
                          unsigned long long cw;\n\
                          _FPU_SETCW(cw); return 0; }}\n")
                .and_then(|_| file.flush())
        });
    if file.is_err() { return false }
    let res = cc::Build::new().cargo_warnings(false)
        .file(&tmp_file).try_expand().is_ok();
    std::fs::remove_file(tmp_file).expect("Cannot remove file.");
    res
}

fn main() -> std::io::Result<()> {
    // Compile crlibm
    let mut build = cc::Build::new();
    // See "crlibm/configure.ac"
    let mut has_ia32_de = false;
    // "cygwin" || "mingw" || "mingw64"
    //   build.define("CRLIBM_TYPEOS_CYGWIN", None);
    if cfg!(target_os = "macos") || cfg!(target_os = "freebsd")
        || cfg!(target_os = "openbsd") || cfg!(target_os = "netbsd") {
            build.define("CRLIBM_TYPEOS_BSD", None);
        }
    if cfg!(target_arch = "powerpc") || cfg!(target_arch = "powerpc64") {
        build.define("CRLIBM_TYPECPU_POWERPC", None); }
    if cfg!(target_arch = "x86") {
        build.define("CRLIBM_TYPECPU_X86", None);
        has_ia32_de = true; }
    if cfg!(target_arch = "x86_64") {
        build.define("CRLIBM_TYPECPU_AMD64", None);
        has_ia32_de = true; }
    add_has_header_flag(&mut build, "fenv")?;
    add_has_header_flag(&mut build, "float")?;
    add_has_header_flag(&mut build, "inttypes")?;
    add_has_header_flag(&mut build, "stdlib")?;
    add_has_header_flag(&mut build, "strings")?;
    add_has_header_flag(&mut build, "unistd")?;
    let has_fpu_control = has_fpu_control();
    if has_fpu_control {
        build.define("CRLIBM_HAS_FPU_CONTROL", None);
    }
    build.define("SCS_NB_BITS", "30"); // default value
    build.define("SCS_NB_WORDS", "8"); // default value
    build.includes(CRLIBM_INCLUDE_DIRECTORIES);
    build.files(CRLIBM_FILES);
    // Select which files to compile for log.
    let use_hardware_de =
        cfg!(feature = "log_double_extended") && has_ia32_de && has_fpu_control;
    if use_hardware_de {
        build.files(["crlibm/log-de.c", "crlibm/log2-td.c",
                     "crlibm/log10-td.c"]);
    } else {
        build.file("crlibm/log.c");
    }
    if cfg!(target_arch = "x86") && cfg!(target_os = "linux") /* elf */ {
        build.flag("-znotext");
    }

    // CRlibm produces lots of warnings.
    build.warnings(false);

    build.compile("crlibm");
    Ok(())
}