ntru 0.5.6

Implementation of the NTRUEncrypt algorithm. Interface to libntru.
extern crate gcc;

use std::fs::File;
use std::path::Path;
use std::io::Write;
use std::process::Command;
use std::env;

fn main() {
    if cfg!(feature = "no-sse") && cfg!(feature = "sse") {
        panic!("You need to decide if you want SSE support or not. If you have doubts, simply disable both options and let the build script autodetect it.");
    }
    if cfg!(feature = "no-avx2") && cfg!(feature = "avx2") {
        panic!("You need to decide if you want AVX2 support or not. If you have doubts, simply disable both options and let the build script autodetect it.");
    }
    if cfg!(feature = "no-sse") && cfg!(feature = "avx2") {
        panic!("SSE is needed for AVX2 support.");
    }

    if cfg!(target_os = "linux") || cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        env::set_var("CC", "gcc");
        env::set_var("AS", "gcc -c");
        if cfg!(target_os = "linux") {
            env::set_var("AR", "ar");
        }
    } else if cfg!(target_os = "freebsd") || cfg!(target_os = "openbsd") {
        env::set_var("CC", "cc");
        env::set_var("AS", "cc -c");
        env::set_var("AR", "ar");
    }

    let mut avx2 = if cfg!(feature = "no-avx2") { false } else if cfg!(target_os = "windows") {
        cfg!(feature = "avx2")
    } else {
        let output = if cfg!(target_os = "freebsd") || cfg!(target_os = "openbsd") {
            // /usr/bin/grep -o AVX2 /var/run/dmesg.boot | /usr/bin/head -1
            Command::new("/usr/bin/grep")
                .arg("-o")
                .arg("AVX2")
                .arg("/var/run/dmesg.boot")
                .output()
                .unwrap()
        } else if cfg!(target_os = "macos") {
            // /usr/sbin/sysctl machdep.cpu.features | grep -m 1 -ow AVX2
            Command::new("/usr/sbin/sysctl")
                .arg("machdep.cpu.features")
                .output()
                .unwrap()
        } else {
            // /bin/grep -m 1 -o avx2 /proc/cpuinfo
            Command::new("/bin/grep")
                .arg("-m")
                .arg("1")
                .arg("-o")
                .arg("avx2")
                .arg("/proc/cpuinfo")
                .output()
                .unwrap()
        };

        let output = std::str::from_utf8(&output.stdout[..]).unwrap().trim();

        if cfg!(target_os = "freebsd") || cfg!(target_os = "openbsd") || cfg!(target_os = "macos") {
            output.contains("AVX2")
        } else {
            output == "avx2"
        }
    };

    let sse3 = if cfg!(feature = "no-sse3") { false } else if avx2 { true } else if cfg!(target_os = "windows") {
        cfg!(feature = "sse")
    } else {
        let output = if cfg!(target_os = "freebsd") || cfg!(target_os = "openbsd") {
            // /usr/bin/grep -o SSSE3 /var/run/dmesg.boot | /usr/bin/head -1
            Command::new("/usr/bin/grep")
                .arg("-o")
                .arg("SSE3")
                .arg("/var/run/dmesg.boot")
                .output()
                .unwrap()
        } else if cfg!(target_os = "macos") {
            // /usr/sbin/sysctl machdep.cpu.features | grep -m 1 -ow SSSE3
            Command::new("/usr/sbin/sysctl")
                .arg("machdep.cpu.features")
                .output()
                .unwrap()
        } else {
            // /bin/grep -m 1 -o ssse3 /proc/cpuinfo
            Command::new("/bin/grep")
                .arg("-m")
                .arg("1")
                .arg("-o")
                .arg("ssse3")
                .arg("/proc/cpuinfo")
                .output()
                .unwrap()
        };
        let output = std::str::from_utf8(&output.stdout[..]).unwrap().trim();

        if cfg!(target_os = "freebsd") || cfg!(target_os = "openbsd") || cfg!(target_os = "macos") {
            output.contains("SSSE3")
        } else {
            output == "ssse3"
        }
    };

    if !sse3 {
        avx2 = false;
    }

    let mut cflags = "-g -Wall -Wextra -Wno-unused-parameter".to_owned();
    if avx2 {
        cflags = cflags + " -mavx2";
    }
    if sse3 {
        cflags = cflags + " -mssse3";
    } else if cfg!(target_os = "macos") {
        cflags = cflags + " -march=x86-64";
    }
    cflags = cflags + " -O2";

    env::set_var("CFLAGS", cflags);

    let mut config = gcc::Config::new();
    config.file("src/c/src/bitstring.c")
          .file("src/c/src/encparams.c")
          .file("src/c/src/hash.c")
          .file("src/c/src/idxgen.c")
          .file("src/c/src/key.c")
          .file("src/c/src/mgf.c")
          .file("src/c/src/ntru.c")
          .file("src/c/src/poly.c")
          .file("src/c/src/rand.c")
          .file("src/c/src/arith.c")
          .file("src/c/src/sha1.c")
          .file("src/c/src/sha2.c")
          .file("src/c/src/nist_ctr_drbg.c")
          .file("src/c/src/rijndael.c");

    if sse3 &&
       (cfg!(target_pointer_width = "64") || cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")) {
        let out = if cfg!(target_os = "windows") {
            Command::new("c:\\mingw\\msys\\1.0\\bin\\perl")
                .arg("src/c/src/sha1-mb-x86_64.pl")
                .arg("coff")
                .output()
                .unwrap()
        } else if cfg!(target_os = "macos") {
            Command::new("/usr/bin/perl")
                .arg("src/c/src/sha1-mb-x86_64.pl")
                .arg("macosx")
                .output()
                .unwrap()
        } else  {
            Command::new("/usr/bin/perl")
                .arg("src/c/src/sha1-mb-x86_64.pl")
                .arg("elf")
                .output()
                .unwrap()
        };
        let out = std::str::from_utf8(&out.stdout[..]).unwrap().trim();

        let p = Path::new("src/c/src/sha1-mb-x86_64.s");
        let mut f = File::create(&p).unwrap();
        f.write(out.as_bytes()).unwrap();

        Command::new(env::var("CC").unwrap())
            .arg("-c")
            .arg("src/c/src/sha1-mb-x86_64.s")
            .arg("-o")
            .arg("src/c/src/sha1-mb-x86_64.o")
            .output()
            .unwrap();

        let out = if cfg!(target_os = "windows") {
            Command::new("c:\\mingw\\msys\\1.0\\bin\\perl")
                .arg("src/c/src/sha256-mb-x86_64.pl")
                .arg("coff")
                .output()
                .unwrap()
        } else if cfg!(target_os = "macos") {
            Command::new("/usr/bin/perl")
                .arg("src/c/src/sha256-mb-x86_64.pl")
                .arg("macosx")
                .output()
                .unwrap()
        } else {
            Command::new("/usr/bin/perl")
                .arg("src/c/src/sha256-mb-x86_64.pl")
                .arg("elf")
                .output()
                .unwrap()
        };
        let out = std::str::from_utf8(&out.stdout[..]).unwrap().trim();

        let p = Path::new("src/c/src/sha256-mb-x86_64.s");
        let mut f = File::create(&p).unwrap();
        f.write(out.as_bytes()).unwrap();

        Command::new(env::var("CC").unwrap())
            .arg("-c")
            .arg("src/c/src/sha256-mb-x86_64.s")
            .arg("-o")
            .arg("src/c/src/sha256-mb-x86_64.o")
            .output()
            .unwrap();

        config.object("src/c/src/sha1-mb-x86_64.o").object("src/c/src/sha256-mb-x86_64.o");
    }

    config.include("src/c/src").compile("libntru.a");

    if sse3 {
        println!("cargo:rustc-cfg=SSE3")
    }
    if avx2 {
        println!("cargo:rustc-cfg=AVX2")
    }
}