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
//! Helpers for build.rs scripts.
//!
//! This library is meant to be used in build.rs scripts context.
//!
//! It contains a set of standalone functions that encodes some of the
//! shared wisdom and conventions across build.rs scripts, cargo, dinghy,
//! cc-rs, pkg-config-rs, bindgen, and others. It also helps providing
//! cross-compilation arguments to autotools `./configure` scripts.

#[macro_use]
extern crate error_chain;
extern crate cc;
#[macro_use]
extern crate log;

mod bindgen_macros;
pub mod build;
pub mod build_env;
pub mod utils;

use crate::build::is_cross_compiling;
use crate::build_env::sysroot_path;
use crate::build_env::target_env;
use crate::utils::path_between;
use crate::utils::path_to_str;
use std::env;
use std::ffi::OsStr;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;

error_chain! {
    foreign_links {
        Io(::std::io::Error);
        EnvVar(::std::env::VarError);
        StringFromUtf8(::std::string::FromUtf8Error);
    }
}

/// Decorator for the std::process::Command adding a some chainable helpers.
///
/// Mostly useful for calling `./configure` scripts.
pub trait CommandExt {
    /// Add this argument to the commands, but only on macos.
    fn arg_for_macos<S: AsRef<OsStr>>(&mut self, arg: S) -> Result<&mut Command>;

    /// Add a `--prefix` to point to a toolchain sysroot or the /, depending on
    /// dinghy environment.
    fn configure_prefix<P: AsRef<Path>>(&mut self, path: P) -> Result<&mut Command>;

    /// Adds pkgconfig environment variables to point to an eventual cross compiling sysroot.
    ///
    /// Usefull for compatibilty with pkg-config-rs up to 0.3.9 or to deal with
    /// `./configure` scripts.
    fn with_pkgconfig(&mut self) -> Result<&mut Command>;

    /// Propagate TARGET, TARGET_CC, TARGET_AR and TARGET_SYSROOT to a
    /// `./configure` script.
    fn with_toolchain(&mut self) -> Result<&mut Command>;
}

impl CommandExt for Command {
    fn arg_for_macos<S: AsRef<OsStr>>(&mut self, arg: S) -> Result<&mut Command> {
        if env::var("TARGET")
            .map(|target| target.contains("-apple-darwin"))
            .unwrap_or(false)
        {
            self.arg(arg.as_ref());
        }
        Ok(self)
    }

    fn configure_prefix<P: AsRef<Path>>(&mut self, prefix_dir: P) -> Result<&mut Command> {
        self.args(&[
            "--prefix",
            path_to_str(&path_between(
                sysroot_path().unwrap_or(PathBuf::from("/")),
                prefix_dir,
            ))?,
        ]);
        Ok(self)
    }

    fn with_pkgconfig(&mut self) -> Result<&mut Command> {
        if is_cross_compiling()? {
            if let Ok(value) = target_env("PKG_CONFIG_PATH") {
                info!("Running command with PKG_CONFIG_PATH:{:?}", value);
                self.env("PKG_CONFIG_PATH", value);
            }
            if let Ok(value) = target_env("PKG_CONFIG_LIBDIR") {
                info!("Running command with PKG_CONFIG_LIBDIR:{:?}", value);
                self.env("PKG_CONFIG_LIBDIR", value);
            }
            if let Ok(value) = target_env("PKG_CONFIG_SYSROOT_DIR") {
                info!("Running command with PKG_CONFIG_SYSROOT_DIR:{:?}", value);
                self.env("PKG_CONFIG_SYSROOT_DIR", value);
            }
        }
        Ok(self)
    }

    fn with_toolchain(&mut self) -> Result<&mut Command> {
        if is_cross_compiling()? {
            if let Ok(target) = env::var("TARGET") {
                self.arg(format!("--host={}", target));
            }
            if let Ok(cc) = env::var("TARGET_CC") {
                self.arg(format!("CC={}", cc));
            }
            if let Ok(ar) = env::var("TARGET_AR") {
                self.arg(format!("AR={}", ar));
            }
            if let Ok(sysroot) = env::var("TARGET_SYSROOT") {
                self.arg(format!("--with-sysroot={}", &sysroot));
            }
        }
        Ok(self)
    }
}