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
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

use serde_derive::Deserialize;
use toml;

#[derive(Deserialize)]
pub struct Manifest {
    pub package: Package,
}

#[derive(Deserialize)]
pub struct Package {
    pub name: String,
}

fn get_name() -> String {
    let mut s = String::new();
    let manifest_dir: PathBuf = env::var_os("CARGO_MANIFEST_DIR").unwrap().into();
    let path = manifest_dir.join("Cargo.toml");

    File::open(path).unwrap().read_to_string(&mut s).unwrap();

    let manifest: Manifest = toml::from_str(&s).unwrap();

    manifest.package.name
}

pub fn metabuild() {
    let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
    let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

    // We do not care about `_pre` and such.
    let major = env::var("CARGO_PKG_VERSION_MAJOR").unwrap();
    let minor = env::var("CARGO_PKG_VERSION_MINOR").unwrap();
    let micro = env::var("CARGO_PKG_VERSION_PATCH").unwrap();

    let prefix: PathBuf = env::var_os("CARGO_C_PREFIX")
        .unwrap_or("/usr/local".into())
        .into();
    let libdir = env::var_os("CARGO_C_LIBDIR").map_or(prefix.join("lib"), |v| v.into());

    let target_dir = env::var_os("CARGO_TARGET_DIR").map_or(
        {
            let manifest_dir: PathBuf = env::var_os("CARGO_MANIFEST_DIR").unwrap().into();
            manifest_dir
                .join("target")
                .join(std::env::var("PROFILE").unwrap())
        },
        |v| v.into(),
    );

    let name = get_name();

    let lines = shared_object_link_args(
        &name, &major, &minor, &micro, &arch, &os, &env, libdir, target_dir,
    );
    let link = "cargo:rustc-cdylib-link-arg=";

    for line in lines {
        println!("{}{}", link, line);
    }
}

/// Return a list of linker arguments useful to produce a platform-correct dynamic library
pub fn shared_object_link_args(
    name: &str,
    major: &str,
    minor: &str,
    micro: &str,
    _arch: &str,
    os: &str,
    env: &str,
    libdir: PathBuf,
    target_dir: PathBuf,
) -> Vec<String> {
    let mut lines = Vec::new();
    if os == "linux" {
        lines.push(format!("-Wl,-soname,lib{}.so.{}", name, major));
    } else if os == "macos" {
        let line = format!("-Wl,-install_name,{1}/lib{0}.{2}.{3}.{4}.dylib,-current_version,{2}.{3}.{4},-compatibility_version,{2}",
                name, libdir.display(), major, minor, micro);
        lines.push(line)
    } else if os == "windows" && env == "gnu" {
        // This is only set up to work on GNU toolchain versions of Rust
        lines.push(format!(
            "-Wl,--out-implib,{}",
            target_dir.join(format!("{}.dll.a", name)).display()
        ));
        lines.push(format!(
            "-Wl,--output-def,{}",
            target_dir.join(format!("{}.def", name)).display()
        ));
    }

    lines
}