1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#[cfg(feature = "parallel")]
extern crate rayon;
#[cfg(feature = "parallel")]
use rayon::prelude::*;

use std::env;
use std::process::Command;
use std::process::Stdio;
use std::path::{Path, PathBuf};

fn x86_triple(os: &str) -> &'static str {
    match os {
        "linux" => "-felf32",
        "darwin" => "-fmacho32",
        "windows" => "-fwin32",
        _ => ""
    }
}

fn x86_64_triple(os: &str) -> &'static str {
    match os {
        "linux" => "-felf64",
        "darwin" => "-fmacho64",
        "windows" => "-fwin64",
        _ => ""
    }
}

fn parse_triple(trip: &str) -> &'static str {
    let parts = trip.split('-').collect::<Vec<_>>();
    // ARCH-VENDOR-OS-ENVIRONMENT
    // or ARCH-VENDOR-OS
    // we don't care about environ so doesn't matter if triple doesn't have it
    if parts.len() < 3 {
        return ""
    }

    match parts[0] {
        "x86_64" => x86_64_triple(parts[2]),
        "x86" | "i386" | "i586" | "i686" => x86_triple(parts[2]),
        _ => ""
    }
}

/// # Example
///
/// ```no_run
/// nasm_rs::compile_library("libfoo.a", &["foo.s", "bar.s"]);
/// ```
pub fn compile_library(output: &str, files: &[&str]) {
    compile_library_args(output, files, &[])
}

/// # Example
///
/// ```no_run
/// nasm_rs::compile_library_args("libfoo.a", &["foo.s", "bar.s"], &["-Fdwarf"]);
/// ```
pub fn compile_library_args(output: &str, files: &[&str], args: &[&str]) {
    #[cfg(not(target_env = "msvc"))]
    assert!(output.starts_with("lib"));

    #[cfg(not(target_env = "msvc"))]
    assert!(output.ends_with(".a"));

    #[cfg(target_env = "msvc")]
    assert!(output.ends_with(".lib"));

    let target = env::var("TARGET").unwrap();

    let cargo_manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let out_dir = env::var("OUT_DIR").unwrap();

    let mut new_args: Vec<&str> = vec![];
    new_args.push(parse_triple(&target));

    if env::var_os("DEBUG").is_some() {
        new_args.push("-g");
    }

    new_args.extend(args);

    let src = Path::new(&cargo_manifest_dir);

    let dst = Path::new(&out_dir);

    let objects = make_iter(files).map(|file| {
        compile_file(file, &new_args, src, dst)
    }).collect::<Vec<_>>();

    run(Command::new(ar()).arg("crus").arg(dst.join(output)).args(&objects[..]));

    println!("cargo:rustc-flags=-L {}",
             dst.display());
}

#[cfg(feature = "parallel")]
fn make_iter<'a, 'b>(files: &'a [&'b str]) -> rayon::slice::Iter<'a, &'b str> {
    files.par_iter()
}

#[cfg(not(feature = "parallel"))]
fn make_iter<'a, 'b>(files: &'a [&'b str]) -> std::slice::Iter<'a, &'b str> {
    files.iter()
}

fn compile_file(file: &str, new_args: &[&str], src: &Path, dst: &Path) -> PathBuf {
    let obj = dst.join(file).with_extension("o");
    let mut cmd = Command::new("nasm");
    cmd.args(&new_args[..]);
    std::fs::create_dir_all(&obj.parent().unwrap()).unwrap();

    run(cmd.arg(src.join(file)).arg("-o").arg(&obj));
    obj
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);

    let status = match cmd.stdout(Stdio::inherit()).stderr(Stdio::inherit()).status() {
        Ok(status) => status,

        Err(e) => panic!("failed to spawn process: {}", e),
    };

    if !status.success() {
        panic!("nonzero exit status: {}", status);
    }
}

fn ar() -> String {
    env::var("AR").unwrap_or("ar".to_string())
}