diff options
Diffstat (limited to 'pkg-config/src')
| -rw-r--r-- | pkg-config/src/lib.rs | 630 | 
1 files changed, 0 insertions, 630 deletions
diff --git a/pkg-config/src/lib.rs b/pkg-config/src/lib.rs deleted file mode 100644 index 88dd310..0000000 --- a/pkg-config/src/lib.rs +++ /dev/null @@ -1,630 +0,0 @@ -//! A build dependency for Cargo libraries to find system artifacts through the -//! `pkg-config` utility. -//! -//! This library will shell out to `pkg-config` as part of build scripts and -//! probe the system to determine how to link to a specified library. The -//! `Config` structure serves as a method of configuring how `pkg-config` is -//! invoked in a builder style. -//! -//! A number of environment variables are available to globally configure how -//! this crate will invoke `pkg-config`: -//! -//! * `PKG_CONFIG_ALLOW_CROSS` - if this variable is not set, then `pkg-config` -//!   will automatically be disabled for all cross compiles. -//! * `FOO_NO_PKG_CONFIG` - if set, this will disable running `pkg-config` when -//!   probing for the library named `foo`. -//! -//! There are also a number of environment variables which can configure how a -//! library is linked to (dynamically vs statically). These variables control -//! whether the `--static` flag is passed. Note that this behavior can be -//! overridden by configuring explicitly on `Config`. The variables are checked -//! in the following order: -//! -//! * `FOO_STATIC` - pass `--static` for the library `foo` -//! * `FOO_DYNAMIC` - do not pass `--static` for the library `foo` -//! * `PKG_CONFIG_ALL_STATIC` - pass `--static` for all libraries -//! * `PKG_CONFIG_ALL_DYNAMIC` - do not pass `--static` for all libraries -//! -//! After running `pkg-config` all appropriate Cargo metadata will be printed on -//! stdout if the search was successful. -//! -//! # Example -//! -//! Find the system library named `foo`, with minimum version 1.2.3: -//! -//! ```no_run -//! extern crate pkg_config; -//! -//! fn main() { -//!     pkg_config::Config::new().atleast_version("1.2.3").probe("foo").unwrap(); -//! } -//! ``` -//! -//! Find the system library named `foo`, with no version requirement (not -//! recommended): -//! -//! ```no_run -//! extern crate pkg_config; -//! -//! fn main() { -//!     pkg_config::probe_library("foo").unwrap(); -//! } -//! ``` -//! -//! Configure how library `foo` is linked to. -//! -//! ```no_run -//! extern crate pkg_config; -//! -//! fn main() { -//!     pkg_config::Config::new().atleast_version("1.2.3").statik(true).probe("foo").unwrap(); -//! } -//! ``` - -#![doc(html_root_url = "https://docs.rs/pkg-config/0.3")] - -#[allow(unused_imports)] // Required for Rust <1.23 -use std::ascii::AsciiExt; -use std::collections::HashMap; -use std::env; -use std::error; -use std::ffi::{OsStr, OsString}; -use std::fmt; -use std::io; -use std::path::{PathBuf, Path}; -use std::process::{Command, Output}; -use std::str; - -pub fn target_supported() -> bool { -    let target = env::var("TARGET").unwrap_or_else(|_| String::new()); -    let host = env::var("HOST").unwrap_or_else(|_| String::new()); - -    // Only use pkg-config in host == target situations by default (allowing an -    // override). -    (host == target || env::var_os("PKG_CONFIG_ALLOW_CROSS").is_some()) -} - -#[derive(Clone, Default)] -pub struct Config { -    statik: Option<bool>, -    atleast_version: Option<String>, -    extra_args: Vec<OsString>, -    cargo_metadata: bool, -    env_metadata: bool, -    print_system_libs: bool, -} - -#[derive(Debug)] -pub struct Library { -    pub libs: Vec<String>, -    pub link_paths: Vec<PathBuf>, -    pub frameworks: Vec<String>, -    pub framework_paths: Vec<PathBuf>, -    pub include_paths: Vec<PathBuf>, -    pub defines: HashMap<String, Option<String>>, -    pub version: String, -    _priv: (), -} - -/// Represents all reasons `pkg-config` might not succeed or be run at all. -pub enum Error { -    /// Aborted because of `*_NO_PKG_CONFIG` environment variable. -    /// -    /// Contains the name of the responsible environment variable. -    EnvNoPkgConfig(String), - -    /// Cross compilation detected. -    /// -    /// Override with `PKG_CONFIG_ALLOW_CROSS=1`. -    CrossCompilation, - -    /// Failed to run `pkg-config`. -    /// -    /// Contains the command and the cause. -    Command { command: String, cause: io::Error }, - -    /// `pkg-config` did not exit sucessfully. -    /// -    /// Contains the command and output. -    Failure { command: String, output: Output }, - -    #[doc(hidden)] -    // please don't match on this, we're likely to add more variants over time -    __Nonexhaustive, -} - -impl error::Error for Error { -    fn description(&self) -> &str { -        match *self { -            Error::EnvNoPkgConfig(_) => "pkg-config requested to be aborted", -            Error::CrossCompilation => { -                "pkg-config doesn't handle cross compilation. \ -                 Use PKG_CONFIG_ALLOW_CROSS=1 to override" -            } -            Error::Command { .. } => "failed to run pkg-config", -            Error::Failure { .. } => "pkg-config did not exit sucessfully", -            Error::__Nonexhaustive => panic!(), -        } -    } - -    fn cause(&self) -> Option<&error::Error> { -        match *self { -            Error::Command { ref cause, .. } => Some(cause), -            _ => None, -        } -    } -} - -// Workaround for temporary lack of impl Debug for Output in stable std -struct OutputDebugger<'a>(&'a Output); - -// Lifted from 1.7 std -impl<'a> fmt::Debug for OutputDebugger<'a> { -    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { -        let stdout_utf8 = str::from_utf8(&self.0.stdout); -        let stdout_debug: &fmt::Debug = match stdout_utf8 { -            Ok(ref str) => str, -            Err(_) => &self.0.stdout -        }; - -        let stderr_utf8 = str::from_utf8(&self.0.stderr); -        let stderr_debug: &fmt::Debug = match stderr_utf8 { -            Ok(ref str) => str, -            Err(_) => &self.0.stderr -        }; - -        fmt.debug_struct("Output") -           .field("status", &self.0.status) -           .field("stdout", stdout_debug) -           .field("stderr", stderr_debug) -           .finish() -    } -} - -// Workaround for temporary lack of impl Debug for Output in stable std, continued -impl fmt::Debug for Error { -    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { -        match *self { -            Error::EnvNoPkgConfig(ref name) => { -                f.debug_tuple("EnvNoPkgConfig") -                 .field(name) -                 .finish() -            } -            Error::CrossCompilation => write!(f, "CrossCompilation"), -            Error::Command { ref command, ref cause } => { -                f.debug_struct("Command") -                 .field("command", command) -                 .field("cause", cause) -                 .finish() -            } -            Error::Failure { ref command, ref output } => { -                f.debug_struct("Failure") -                 .field("command", command) -                 .field("output", &OutputDebugger(output)) -                 .finish() -            } -            Error::__Nonexhaustive => panic!(), -        } -    } -} - -impl fmt::Display for Error { -    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { -        match *self { -            Error::EnvNoPkgConfig(ref name) => { -                write!(f, "Aborted because {} is set", name) -            } -            Error::CrossCompilation => { -                write!(f, "Cross compilation detected. \ -                       Use PKG_CONFIG_ALLOW_CROSS=1 to override") -            } -            Error::Command { ref command, ref cause } => { -                write!(f, "Failed to run `{}`: {}", command, cause) -            } -            Error::Failure { ref command, ref output } => { -                let stdout = str::from_utf8(&output.stdout).unwrap(); -                let stderr = str::from_utf8(&output.stderr).unwrap(); -                write!(f, "`{}` did not exit successfully: {}", command, output.status)?; -                if !stdout.is_empty() { -                    write!(f, "\n--- stdout\n{}", stdout)?; -                } -                if !stderr.is_empty() { -                    write!(f, "\n--- stderr\n{}", stderr)?; -                } -                Ok(()) -            } -            Error::__Nonexhaustive => panic!(), -        } -    } -} - -/// Deprecated in favor of the probe_library function -#[doc(hidden)] -pub fn find_library(name: &str) -> Result<Library, String> { -    probe_library(name).map_err(|e| e.to_string()) -} - -/// Simple shortcut for using all default options for finding a library. -pub fn probe_library(name: &str) -> Result<Library, Error> { -    Config::new().probe(name) -} - -/// Run `pkg-config` to get the value of a variable from a package using -/// --variable. -pub fn get_variable(package: &str, variable: &str) -> Result<String, Error> { -    let arg = format!("--variable={}", variable); -    let cfg = Config::new(); -    let out = run(cfg.command(package, &[&arg]))?; -    Ok(str::from_utf8(&out).unwrap().trim_right().to_owned()) -} - -impl Config { -    /// Creates a new set of configuration options which are all initially set -    /// to "blank". -    pub fn new() -> Config { -        Config { -            statik: None, -            atleast_version: None, -            extra_args: vec![], -            print_system_libs: true, -            cargo_metadata: true, -            env_metadata: false, -        } -    } - -    /// Indicate whether the `--static` flag should be passed. -    /// -    /// This will override the inference from environment variables described in -    /// the crate documentation. -    pub fn statik(&mut self, statik: bool) -> &mut Config { -        self.statik = Some(statik); -        self -    } - -    /// Indicate that the library must be at least version `vers`. -    pub fn atleast_version(&mut self, vers: &str) -> &mut Config { -        self.atleast_version = Some(vers.to_string()); -        self -    } - -    /// Add an argument to pass to pkg-config. -    /// -    /// It's placed after all of the arguments generated by this library. -    pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Config { -        self.extra_args.push(arg.as_ref().to_os_string()); -        self -    } - -    /// Define whether metadata should be emitted for cargo allowing it to -    /// automatically link the binary. Defaults to `true`. -    pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Config { -        self.cargo_metadata = cargo_metadata; -        self -    } - -    /// Define whether metadata should be emitted for cargo allowing to -    /// automatically rebuild when environment variables change. Defaults to -    /// `false`. -    pub fn env_metadata(&mut self, env_metadata: bool) -> &mut Config { -        self.env_metadata = env_metadata; -        self -    } - -    /// Enable or disable the `PKG_CONFIG_ALLOW_SYSTEM_LIBS` environment -    /// variable. -    /// -    /// This env var is enabled by default. -    pub fn print_system_libs(&mut self, print: bool) -> &mut Config { -        self.print_system_libs = print; -        self -    } - -    /// Deprecated in favor fo the `probe` function -    #[doc(hidden)] -    pub fn find(&self, name: &str) -> Result<Library, String> { -        self.probe(name).map_err(|e| e.to_string()) -    } - -    /// Run `pkg-config` to find the library `name`. -    /// -    /// This will use all configuration previously set to specify how -    /// `pkg-config` is run. -    pub fn probe(&self, name: &str) -> Result<Library, Error> { -        let abort_var_name = format!("{}_NO_PKG_CONFIG", envify(name)); -        if self.env_var_os(&abort_var_name).is_some() { -            return Err(Error::EnvNoPkgConfig(abort_var_name)) -        } else if !target_supported() { -            return Err(Error::CrossCompilation); -        } - -        let mut library = Library::new(); - -        let output = run(self.command(name, &["--libs", "--cflags"]))?; -        library.parse_libs_cflags(name, &output, self); - -        let output = run(self.command(name, &["--modversion"]))?; -        library.parse_modversion(str::from_utf8(&output).unwrap()); - -        Ok(library) -    } - -    /// Deprecated in favor of the top level `get_variable` function -    #[doc(hidden)] -    pub fn get_variable(package: &str, variable: &str) -> Result<String, String> { -        get_variable(package, variable).map_err(|e| e.to_string()) -    } - -    fn targetted_env_var(&self, var_base: &str) -> Result<String, env::VarError> { -        if let Ok(target) = env::var("TARGET") { -            let host = env::var("HOST")?; -            let kind = if host == target { "HOST" } else { "TARGET" }; -            let target_u = target.replace("-", "_"); - -            self.env_var(&format!("{}_{}", var_base, target)) -                .or_else(|_| self.env_var(&format!("{}_{}", var_base, target_u))) -                .or_else(|_| self.env_var(&format!("{}_{}", kind, var_base))) -                .or_else(|_| self.env_var(var_base)) -        } else { -            self.env_var(var_base) -        } -    } - -    fn env_var(&self, name: &str) -> Result<String, env::VarError> { -        if self.env_metadata { -            println!("cargo:rerun-if-env-changed={}", name); -        } -        env::var(name) -    } - -    fn env_var_os(&self, name: &str) -> Option<OsString> { -        if self.env_metadata { -            println!("cargo:rerun-if-env-changed={}", name); -        } -        env::var_os(name) -    } - -    fn is_static(&self, name: &str) -> bool { -        self.statik.unwrap_or_else(|| self.infer_static(name)) -    } - -    fn command(&self, name: &str, args: &[&str]) -> Command { -        let exe = self.env_var("PKG_CONFIG").unwrap_or_else(|_| String::from("pkg-config")); -        let mut cmd = Command::new(exe); -        if self.is_static(name) { -            cmd.arg("--static"); -        } -        cmd.args(args) -           .args(&self.extra_args); - -        if let Ok(value) = self.targetted_env_var("PKG_CONFIG_PATH") { -            cmd.env("PKG_CONFIG_PATH", value); -        } -        if let Ok(value) = self.targetted_env_var("PKG_CONFIG_LIBDIR") { -            cmd.env("PKG_CONFIG_LIBDIR", value); -        } -        if let Ok(value) = self.targetted_env_var("PKG_CONFIG_SYSROOT_DIR") { -            cmd.env("PKG_CONFIG_SYSROOT_DIR", value); -        } -        if self.print_system_libs { -            cmd.env("PKG_CONFIG_ALLOW_SYSTEM_LIBS", "1"); -        } -        if let Some(ref version) = self.atleast_version { -            cmd.arg(&format!("{} >= {}", name, version)); -        } else { -            cmd.arg(name); -        } -        cmd -    } - -    fn print_metadata(&self, s: &str) { -        if self.cargo_metadata { -            println!("cargo:{}", s); -        } -    } - -    fn infer_static(&self, name: &str) -> bool { -        let name = envify(name); -        if self.env_var_os(&format!("{}_STATIC", name)).is_some() { -            true -        } else if self.env_var_os(&format!("{}_DYNAMIC", name)).is_some() { -            false -        } else if self.env_var_os("PKG_CONFIG_ALL_STATIC").is_some() { -            true -        } else if self.env_var_os("PKG_CONFIG_ALL_DYNAMIC").is_some() { -            false -        } else { -            false -        } -    } -} - -impl Library { -    fn new() -> Library { -        Library { -            libs: Vec::new(), -            link_paths: Vec::new(), -            include_paths: Vec::new(), -            frameworks: Vec::new(), -            framework_paths: Vec::new(), -            defines: HashMap::new(), -            version: String::new(), -            _priv: (), -        } -    } - -    fn parse_libs_cflags(&mut self, name: &str, output: &[u8], config: &Config) { -        let mut is_msvc = false; -        if let Ok(target) = env::var("TARGET") { -            if target.contains("msvc") { -                is_msvc = true; -            } -        } - -        let words = split_flags(output); -        let parts = words.iter() -                          .filter(|l| l.len() > 2) -                          .map(|arg| (&arg[0..2], &arg[2..])) -                          .collect::<Vec<_>>(); - -        let mut dirs = Vec::new(); -        let statik = config.is_static(name); -        for &(flag, val) in &parts { -            match flag { -                "-L" => { -                    let meta = format!("rustc-link-search=native={}", val); -                    config.print_metadata(&meta); -                    dirs.push(PathBuf::from(val)); -                    self.link_paths.push(PathBuf::from(val)); -                } -                "-F" => { -                    let meta = format!("rustc-link-search=framework={}", val); -                    config.print_metadata(&meta); -                    self.framework_paths.push(PathBuf::from(val)); -                } -                "-I" => { -                    self.include_paths.push(PathBuf::from(val)); -                } -                "-l" => { -                    // These are provided by the CRT with MSVC -                    if is_msvc && ["m", "c", "pthread"].contains(&val) { -                        continue; -                    } - -                    if statik && is_static_available(val, &dirs) { -                        let meta = format!("rustc-link-lib=static={}", val); -                        config.print_metadata(&meta); -                    } else { -                        let meta = format!("rustc-link-lib={}", val); -                        config.print_metadata(&meta); -                    } - -                    self.libs.push(val.to_string()); -                } -                "-D" => { -                    let mut iter = val.split("="); -                    self.defines.insert(iter.next().unwrap().to_owned(), iter.next().map(|s| s.to_owned())); -                } -                _ => {} -            } -        } - -        let mut iter = words.iter() -                            .flat_map(|arg| if arg.starts_with("-Wl,") { -                                 arg[4..].split(',').collect() -                             } else { -                                 vec![arg.as_ref()] -                             }); -        while let Some(part) = iter.next() { -            if part != "-framework" { -                continue -            } -            if let Some(lib) = iter.next() { -                let meta = format!("rustc-link-lib=framework={}", lib); -                config.print_metadata(&meta); -                self.frameworks.push(lib.to_string()); -            } -        } -    } - -    fn parse_modversion(&mut self, output: &str) { -        self.version.push_str(output.trim()); -    } -} - -fn envify(name: &str) -> String { -    name.chars().map(|c| c.to_ascii_uppercase()).map(|c| { -        if c == '-' {'_'} else {c} -    }).collect() -} - -/// System libraries should only be linked dynamically -fn is_static_available(name: &str, dirs: &[PathBuf]) -> bool { -    let libname = format!("lib{}.a", name); -    let system_roots = if cfg!(target_os = "macos") { -        vec![Path::new("/Library"), Path::new("/System")] -    } else { -        vec![Path::new("/usr")] -    }; - -    dirs.iter().any(|dir| { -        !system_roots.iter().any(|sys| dir.starts_with(sys)) && -        dir.join(&libname).exists() -    }) -} - -fn run(mut cmd: Command) -> Result<Vec<u8>, Error> { -    match cmd.output() { -        Ok(output) => { -            if output.status.success() { -                Ok(output.stdout) -            } else { -                Err(Error::Failure { -                    command: format!("{:?}", cmd), -                    output: output, -                }) -            } -        } -        Err(cause) => Err(Error::Command { -            command: format!("{:?}", cmd), -            cause: cause, -        }), -    } -} - -/// Split output produced by pkg-config --cflags and / or --libs into separate flags. -/// -/// Backslash in output is used to preserve literal meaning of following byte.  Different words are -/// separated by unescaped space. Other whitespace characters generally should not occur unescaped -/// at all, apart from the newline at the end of output. For compatibility with what others -/// consumers of pkg-config output would do in this scenario, they are used here for splitting as -/// well. -fn split_flags(output: &[u8]) -> Vec<String> { -    let mut word = Vec::new(); -    let mut words = Vec::new(); -    let mut escaped = false; - -    for &b in output { -        match b { -            _ if escaped => { -                escaped = false; -                word.push(b); -            } -            b'\\' => { -                escaped = true -            } -            b'\t' | b'\n' | b'\r' | b' ' => { -                if !word.is_empty() { -                    words.push(String::from_utf8(word).unwrap()); -                    word = Vec::new(); -                } -            } -            _ => word.push(b), -        } -    } - -    if !word.is_empty() { -        words.push(String::from_utf8(word).unwrap()); -    } - -    words -} - -#[test] -#[cfg(target_os = "macos")] -fn system_library_mac_test() { -    assert!(!is_static_available("PluginManager", &[PathBuf::from("/Library/Frameworks")])); -    assert!(!is_static_available("python2.7", &[PathBuf::from("/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config")])); -    assert!(!is_static_available("ffi_convenience", &[PathBuf::from("/Library/Ruby/Gems/2.0.0/gems/ffi-1.9.10/ext/ffi_c/libffi-x86_64/.libs")])); - -    // Homebrew is in /usr/local, and it's not a part of the OS -    if Path::new("/usr/local/lib/libpng16.a").exists() { -        assert!(is_static_available("png16", &[PathBuf::from("/usr/local/lib")])); -    } -} - -#[test] -#[cfg(target_os = "linux")] -fn system_library_linux_test() { -    assert!(!is_static_available("util", &[PathBuf::from("/usr/lib/x86_64-linux-gnu")])); -    assert!(!is_static_available("dialog", &[PathBuf::from("/usr/lib")])); -}  | 
