Bug 1716518 - Upgrade pkg-config to v0.3.19. r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D117833
This commit is contained in:
Mike Hommey 2021-06-15 22:17:26 +00:00
parent e36174a0c3
commit 687ed79ff6
10 changed files with 808 additions and 220 deletions

4
Cargo.lock generated
View File

@ -3858,9 +3858,9 @@ dependencies = [
[[package]]
name = "pkg-config"
version = "0.3.9"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "plain"

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"425339eef4a01cf8773dc69444115e5771c84194f68f1748a22b0c0f200dd475","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"6259efd2a3ba18ea50579f0af2ef3d4e797231522e18bad915ca25901fb31de2","src/lib.rs":"163a48484a96ab21529b42bfe3448753c898a42f4b204aa352bf0f87ff49f30e","tests/foo.pc":"f77712847e77ea81ac6362de5861dc0eddf14b9c07dce1853b3e3e587ffcac5e","tests/framework.pc":"4d02091799252777afb0547321cc04e7df1e017226a6b05630fed5eaf37e0125","tests/test.rs":"09bf2811e3d58432080a76b0d297131e43d215253894a2919626e71b30924b0b"},"package":"3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"}
{"files":{"CHANGELOG.md":"76c3cdc468bd39843de5ed2e5d11cc2de3e2fa3cc099dd3137722105f94c8557","Cargo.toml":"a575e9c8b92c4b29fbf7bb4add0e77ec99464e3ae9248d42c5162e948ffa914d","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"1e22446920533dea171fea3951cb4cf8db34bd4dd6414b688720410a9c414d3c","src/lib.rs":"cecbf5f9c982e21cba0391a4d73ad9aab10a0fbfcfcbb3bab4ecb05ee9467e32","tests/escape.pc":"00caa4136799dbe5bd504239ba90d1156c12def365c8d761da319fe8a83b398e","tests/foo.pc":"4a1c442c5d1c10761ea1644f8fd58f93cc5a706391bc67b04c243bbd35d70d79","tests/framework.pc":"304fdb6cea92973650e410ab1f70ce1ebeb7718af3f139e806efbf182acd565c","tests/test.rs":"5ed13fc28a1853d20d27798819a1228673cb1e57097951dbb892e51327a21adb"},"package":"3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"}

103
third_party/rust/pkg-config/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,103 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [0.3.19] - 2020-10-13
### Added
- Add `README.md` to be displayed on crates.io (#111).
- Support for `-isystem`, `-iquote` and `-idirafter` include flags (#115).
### Changed
- Improve documentation for cross-compilation (#113).
- Allow overriding system root via the `PKG_CONFIG_SYSROOT_DIR` or `SYSROOT`
environment variable (#82).
## [0.3.18] - 2020-07-11
### Fixed
- Use `env::var_os()` almost everywhere to handle non-UTF8 paths in
environment variables, and also improve error handling around environment
variable handling (#106).
### Changed
- Default the `env_metadata` build parameter to `true` instead of `false`.
Whenever a pkg-config related environment variable changes it would make
sense to rebuild crates that use pkg-config, or otherwise changes might not
be picked up. As such the previous default didn't make much sense (#105).
## [0.3.17] - 2019-11-02
### Fixed
- Fix support for multiple version number constraints (#95)
## [0.3.16] - 2019-09-09
### Changed
- Stop using deprecated functions and require Rust 1.30 (#84)
### Fixed
- Fix repository URL in README.md
- Fix various clippy warnings
### Added
- Run `cargo fmt` as part of the CI (#89)
- Derive `Clone` for `Library` and `Debug` for `Config (#91)
- Add support for `PKG_CONFIG_ALLOW_SYSTEM_CFLAGS` and enable by default (#93)
## [0.3.15] - 2019-07-25
### Changed
- Changes minimum documented rust version to 1.28 (#76)
### Fixed
- Fix Travis CI badge url (#78)
- Fix project name in README.md (#81)
### Added
- Support specifying range of versions (#75)
- Allow cross-compilation if pkg-config is customized (#44, #86)
## [0.3.14] - 2018-08-28
### Fixed
- Don't append .lib suffix on MSVC builds (#72)
## [0.3.13] - 2018-08-06
### Fixed
- Fix MSVC support to actually work and consider library paths too (#71)
## [0.3.12] - 2018-06-18
### Added
- Support for MSVC (#70)
- Document and test Rust 1.13 as minimally supported version (#66)
## [0.3.11] - 2018-04-24
### Fixed
- Re-added AsciiExt import (#65)
## [0.3.10] - 2018-04-23
### Added
- Allow static linking of /usr/ on macOS (#42)
- Add support for parsing `-Wl,` style framework flags (#48)
- Parse defines in `pkg-config` output (#49)
- Rerun on `PKG_CONFIG_PATH` changes (#50)
- Introduce target-scoped variables (#58)
- Respect pkg-config escaping rules used with --cflags and --libs (#61)
### Changed
- Use `?` instead of `try!()` in the codebase (#63)

View File

@ -1,16 +1,26 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "pkg-config"
version = "0.3.9"
version = "0.3.19"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
license = "MIT/Apache-2.0"
repository = "https://github.com/alexcrichton/pkg-config-rs"
description = "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n"
documentation = "https://docs.rs/pkg-config"
description = """
A library to run the pkg-config system tool at build time in order to be used in
Cargo build scripts.
"""
readme = "README.md"
keywords = ["build-dependencies"]
[dev-dependencies]
lazy_static = "0.2"
license = "MIT/Apache-2.0"
repository = "https://github.com/rust-lang/pkg-config-rs"
[dev-dependencies.lazy_static]
version = "1"
[badges.travis-ci]
repository = "rust-lang/pkg-config-rs"

View File

@ -1,6 +1,7 @@
# pkg-config-rs
[![Build Status](https://travis-ci.org/alexcrichton/pkg-config-rs.svg?branch=master)](https://travis-ci.org/alexcrichton/pkg-config-rs)
[![Build Status](https://travis-ci.com/rust-lang/pkg-config-rs.svg?branch=master)](https://travis-ci.com/rust-lang/pkg-config-rs)
[![Rust](https://img.shields.io/badge/rust-1.30%2B-blue.svg?maxAge=3600)](https://github.com/rust-lang/pkg-config-rs/)
[Documentation](https://docs.rs/pkg-config)
@ -12,6 +13,8 @@ You can use this crate directly to probe for specific libraries, or use
[metadeps](https://github.com/joshtriplett/metadeps) to declare all your
`pkg-config` dependencies in `Cargo.toml`.
This library requires Rust 1.30+.
# Example
Find the system library named `foo`, with minimum version 1.2.3:
@ -35,10 +38,42 @@ fn main() {
}
```
# External configuration via target-scoped environment variables
In cross-compilation context, it is useful to manage separately `PKG_CONFIG_PATH`
and a few other variables for the `host` and the `target` platform.
The supported variables are: `PKG_CONFIG_PATH`, `PKG_CONFIG_LIBDIR`, and
`PKG_CONFIG_SYSROOT_DIR`.
Each of these variables can also be supplied with certain prefixes and suffixes, in the following prioritized order:
1. `<var>_<target>` - for example, `PKG_CONFIG_PATH_x86_64-unknown-linux-gnu`
2. `<var>_<target_with_underscores>` - for example, `PKG_CONFIG_PATH_x86_64_unknown_linux_gnu`
3. `<build-kind>_<var>` - for example, `HOST_PKG_CONFIG_PATH` or `TARGET_PKG_CONFIG_PATH`
4. `<var>` - a plain `PKG_CONFIG_PATH`
This crate will allow `pkg-config` to be used in cross-compilation
if `PKG_CONFIG_SYSROOT_DIR` or `PKG_CONFIG` is set. You can set `PKG_CONFIG_ALLOW_CROSS=1`
to bypass the compatibility check, but please note that enabling use of `pkg-config` in
cross-compilation without appropriate sysroot and search paths set is likely to break builds.
Some Rust sys crates support building vendored libraries from source, which may be a work
around for lack of cross-compilation support in `pkg-config`.
# License
`pkg-config-rs` is primarily distributed under the terms of both the MIT
license and the Apache License (Version 2.0), with portions covered by various
BSD-like licenses.
This project is licensed under either of
See LICENSE-APACHE, and LICENSE-MIT for details.
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in pkg-config-rs by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.

View File

@ -9,11 +9,16 @@
//! 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`.
//!
//! * `PKG_CONFIG_ALLOW_CROSS` - The `pkg-config` command usually doesn't
//! support cross-compilation, and this crate prevents it from selecting
//! incompatible versions of libraries.
//! Setting `PKG_CONFIG_ALLOW_CROSS=1` disables this protection, which is
//! likely to cause linking errors, unless `pkg-config` has been configured
//! to use appropriate sysroot and search paths for the target platform.
//!
//! 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
@ -62,66 +67,57 @@
//! ```
#![doc(html_root_url = "https://docs.rs/pkg-config/0.3")]
#![cfg_attr(test, deny(warnings))]
use std::ascii::AsciiExt;
use std::collections::HashMap;
use std::env;
use std::error;
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::fs;
use std::io;
use std::path::{PathBuf, Path};
use std::ops::{Bound, RangeBounds};
use std::path::PathBuf;
use std::process::{Command, Output};
use std::str;
pub fn target_supported() -> bool {
let target = env::var("TARGET").unwrap_or(String::new());
let host = env::var("HOST").unwrap_or(String::new());
// Only use pkg-config in host == target situations by default (allowing an
// override) and then also don't use pkg-config on MSVC as it's really not
// meant to work there but when building MSVC code in a MSYS shell we may be
// able to run pkg-config anyway.
(host == target || env::var_os("PKG_CONFIG_ALLOW_CROSS").is_some()) &&
!target.contains("msvc")
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Config {
statik: Option<bool>,
atleast_version: Option<String>,
min_version: Bound<String>,
max_version: Bound<String>,
extra_args: Vec<OsString>,
cargo_metadata: bool,
env_metadata: bool,
print_system_libs: bool,
print_system_cflags: bool,
}
#[derive(Debug)]
#[derive(Clone, 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.
#[derive(Debug)]
pub enum Error {
/// Aborted because of `*_NO_PKG_CONFIG` environment variable.
///
/// Contains the name of the responsible environment variable.
EnvNoPkgConfig(String),
/// Cross compilation detected.
/// Detected cross compilation without a custom sysroot.
///
/// Override with `PKG_CONFIG_ALLOW_CROSS=1`.
/// Ignore the error with `PKG_CONFIG_ALLOW_CROSS=1`,
/// which may let `pkg-config` select libraries
/// for the host's architecture instead of the target's.
CrossCompilation,
/// Attempted to compile using the MSVC ABI build
MSVC,
/// Failed to run `pkg-config`.
///
/// Contains the command and the cause.
@ -137,109 +133,40 @@ pub enum Error {
__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::MSVC => "pkg-config is incompatible with the MSVC ABI build.",
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::MSVC => write!(f, "MSVC"),
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 error::Error for Error {}
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::MSVC => {
write!(f, "MSVC target detected. If you are using the MSVC ABI \
rust build, please use the GNU ABI build instead.")
}
Error::Command { ref command, ref cause } => {
write!(f, "Failed to run `{}`: {}", command, cause)
}
Error::Failure { ref command, ref output } => {
Error::EnvNoPkgConfig(ref name) => write!(f, "Aborted because {} is set", name),
Error::CrossCompilation => f.write_str(
"pkg-config has not been configured to support cross-compilation.
Install a sysroot for the target platform and configure it via
PKG_CONFIG_SYSROOT_DIR and PKG_CONFIG_PATH, or install a
cross-compiling wrapper for pkg-config and set it via
PKG_CONFIG environment variable.",
),
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();
try!(write!(f, "`{}` did not exit successfully: {}", command, output.status));
write!(
f,
"`{}` did not exit successfully: {}",
command, output.status
)?;
if !stdout.is_empty() {
try!(write!(f, "\n--- stdout\n{}", stdout));
write!(f, "\n--- stdout\n{}", stdout)?;
}
if !stderr.is_empty() {
try!(write!(f, "\n--- stderr\n{}", stderr));
write!(f, "\n--- stderr\n{}", stderr)?;
}
Ok(())
}
@ -260,11 +187,17 @@ pub fn probe_library(name: &str) -> Result<Library, Error> {
}
/// Run `pkg-config` to get the value of a variable from a package using
/// --variable.
/// `--variable`.
///
/// The content of `PKG_CONFIG_SYSROOT_DIR` is not injected in paths that are
/// returned by `pkg-config --variable`, which makes them unsuitable to use
/// during cross-compilation unless specifically designed to be used
/// at that time.
pub fn get_variable(package: &str, variable: &str) -> Result<String, Error> {
let arg = format!("--variable={}", variable);
let cfg = Config::new();
Ok(try!(run(cfg.command(package, &[&arg]))).trim_right().to_owned())
let out = run(cfg.command(package, &[&arg]))?;
Ok(str::from_utf8(&out).unwrap().trim_end().to_owned())
}
impl Config {
@ -273,10 +206,13 @@ impl Config {
pub fn new() -> Config {
Config {
statik: None,
atleast_version: None,
min_version: Bound::Unbounded,
max_version: Bound::Unbounded,
extra_args: vec![],
print_system_cflags: true,
print_system_libs: true,
cargo_metadata: true,
env_metadata: true,
}
}
@ -291,7 +227,33 @@ impl Config {
/// 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.min_version = Bound::Included(vers.to_string());
self.max_version = Bound::Unbounded;
self
}
/// Indicate that the library must be equal to version `vers`.
pub fn exactly_version(&mut self, vers: &str) -> &mut Config {
self.min_version = Bound::Included(vers.to_string());
self.max_version = Bound::Included(vers.to_string());
self
}
/// Indicate that the library's version must be in `range`.
pub fn range_version<'a, R>(&mut self, range: R) -> &mut Config
where
R: RangeBounds<&'a str>,
{
self.min_version = match range.start_bound() {
Bound::Included(vers) => Bound::Included(vers.to_string()),
Bound::Excluded(vers) => Bound::Excluded(vers.to_string()),
Bound::Unbounded => Bound::Unbounded,
};
self.max_version = match range.end_bound() {
Bound::Included(vers) => Bound::Included(vers.to_string()),
Bound::Excluded(vers) => Bound::Excluded(vers.to_string()),
Bound::Unbounded => Bound::Unbounded,
};
self
}
@ -310,6 +272,14 @@ impl Config {
self
}
/// Define whether metadata should be emitted for cargo allowing to
/// automatically rebuild when environment variables change. Defaults to
/// `true`.
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.
///
@ -319,6 +289,15 @@ impl Config {
self
}
/// Enable or disable the `PKG_CONFIG_ALLOW_SYSTEM_CFLAGS` environment
/// variable.
///
/// This env var is enabled by default.
pub fn print_system_cflags(&mut self, print: bool) -> &mut Config {
self.print_system_cflags = print;
self
}
/// Deprecated in favor fo the `probe` function
#[doc(hidden)]
pub fn find(&self, name: &str) -> Result<Library, String> {
@ -331,54 +310,131 @@ impl Config {
/// `pkg-config` is run.
pub fn probe(&self, name: &str) -> Result<Library, Error> {
let abort_var_name = format!("{}_NO_PKG_CONFIG", envify(name));
if env::var_os(&abort_var_name).is_some() {
return Err(Error::EnvNoPkgConfig(abort_var_name))
} else if !target_supported() {
if env::var("TARGET").unwrap_or(String::new()).contains("msvc") {
return Err(Error::MSVC);
}
else {
return Err(Error::CrossCompilation);
}
if self.env_var_os(&abort_var_name).is_some() {
return Err(Error::EnvNoPkgConfig(abort_var_name));
} else if !self.target_supported() {
return Err(Error::CrossCompilation);
}
let mut library = Library::new();
let output = try!(run(self.command(name, &["--libs", "--cflags"])));
let output = run(self.command(name, &["--libs", "--cflags"]))?;
library.parse_libs_cflags(name, &output, self);
let output = try!(run(self.command(name, &["--modversion"])));
library.parse_modversion(&output);
let output = run(self.command(name, &["--modversion"]))?;
library.parse_modversion(str::from_utf8(&output).unwrap());
Ok(library)
}
pub fn target_supported(&self) -> bool {
let target = env::var_os("TARGET").unwrap_or_default();
let host = env::var_os("HOST").unwrap_or_default();
// Only use pkg-config in host == target situations by default (allowing an
// override).
if host == target {
return true;
}
// pkg-config may not be aware of cross-compilation, and require
// a wrapper script that sets up platform-specific prefixes.
match self.targetted_env_var("PKG_CONFIG_ALLOW_CROSS") {
// don't use pkg-config if explicitly disabled
Some(ref val) if val == "0" => false,
Some(_) => true,
None => {
// if not disabled, and pkg-config is customized,
// then assume it's prepared for cross-compilation
self.targetted_env_var("PKG_CONFIG").is_some()
|| self.targetted_env_var("PKG_CONFIG_SYSROOT_DIR").is_some()
}
}
}
/// 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) -> Option<OsString> {
match (env::var("TARGET"), env::var("HOST")) {
(Ok(target), Ok(host)) => {
let kind = if host == target { "HOST" } else { "TARGET" };
let target_u = target.replace("-", "_");
self.env_var_os(&format!("{}_{}", var_base, target))
.or_else(|| self.env_var_os(&format!("{}_{}", var_base, target_u)))
.or_else(|| self.env_var_os(&format!("{}_{}", kind, var_base)))
.or_else(|| self.env_var_os(var_base))
}
(Err(env::VarError::NotPresent), _) | (_, Err(env::VarError::NotPresent)) => {
self.env_var_os(var_base)
}
(Err(env::VarError::NotUnicode(s)), _) | (_, Err(env::VarError::NotUnicode(s))) => {
panic!(
"HOST or TARGET environment variable is not valid unicode: {:?}",
s
)
}
}
}
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(|| infer_static(name))
self.statik.unwrap_or_else(|| self.infer_static(name))
}
fn command(&self, name: &str, args: &[&str]) -> Command {
let exe = env::var("PKG_CONFIG").unwrap_or(String::from("pkg-config"));
let exe = self
.env_var_os("PKG_CONFIG")
.unwrap_or_else(|| OsString::from("pkg-config"));
let mut cmd = Command::new(exe);
if self.is_static(name) {
cmd.arg("--static");
}
cmd.args(args)
.args(&self.extra_args);
cmd.args(args).args(&self.extra_args);
if let Some(value) = self.targetted_env_var("PKG_CONFIG_PATH") {
cmd.env("PKG_CONFIG_PATH", value);
}
if let Some(value) = self.targetted_env_var("PKG_CONFIG_LIBDIR") {
cmd.env("PKG_CONFIG_LIBDIR", value);
}
if let Some(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);
if self.print_system_cflags {
cmd.env("PKG_CONFIG_ALLOW_SYSTEM_CFLAGS", "1");
}
cmd.arg(name);
match self.min_version {
Bound::Included(ref version) => {
cmd.arg(&format!("{} >= {}", name, version));
}
Bound::Excluded(ref version) => {
cmd.arg(&format!("{} > {}", name, version));
}
_ => (),
}
match self.max_version {
Bound::Included(ref version) => {
cmd.arg(&format!("{} <= {}", name, version));
}
Bound::Excluded(ref version) => {
cmd.arg(&format!("{} < {}", name, version));
}
_ => (),
}
cmd
}
@ -388,6 +444,37 @@ impl Config {
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
}
}
}
// Implement Default manualy since Bound does not implement Default.
impl Default for Config {
fn default() -> Config {
Config {
statik: None,
min_version: Bound::Unbounded,
max_version: Bound::Unbounded,
extra_args: vec![],
print_system_cflags: false,
print_system_libs: false,
cargo_metadata: false,
env_metadata: false,
}
}
}
impl Library {
@ -398,21 +485,50 @@ impl Library {
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: &str, config: &Config) {
let parts = output.trim_right()
.split(' ')
.filter(|l| l.len() > 2)
.map(|arg| (&arg[0..2], &arg[2..]))
.collect::<Vec<_>>();
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 system_roots = if cfg!(target_os = "macos") {
vec![PathBuf::from("/Library"), PathBuf::from("/System")]
} else {
let sysroot = config
.env_var_os("PKG_CONFIG_SYSROOT_DIR")
.or_else(|| config.env_var_os("SYSROOT"))
.map(PathBuf::from);
if cfg!(target_os = "windows") {
if let Some(sysroot) = sysroot {
vec![sysroot]
} else {
vec![]
}
} else {
vec![sysroot.unwrap_or_else(|| PathBuf::from("/usr"))]
}
};
let mut dirs = Vec::new();
let statik = config.is_static(name);
for &(flag, val) in parts.iter() {
let words = split_flags(output);
// Handle single-character arguments like `-I/usr/include`
let parts = words
.iter()
.filter(|l| l.len() > 2)
.map(|arg| (&arg[0..2], &arg[2..]));
for (flag, val) in parts {
match flag {
"-L" => {
let meta = format!("rustc-link-search=native={}", val);
@ -429,82 +545,187 @@ impl Library {
self.include_paths.push(PathBuf::from(val));
}
"-l" => {
self.libs.push(val.to_string());
if statik && !is_system(val, &dirs) {
// These are provided by the CRT with MSVC
if is_msvc && ["m", "c", "pthread"].contains(&val) {
continue;
}
if statik && is_static_available(val, &system_roots, &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 = output.trim_right().split(' ');
while let Some(part) = iter.next() {
if part != "-framework" {
continue
// Handle multi-character arguments with space-separated value like `-framework foo`
let mut iter = words.iter().flat_map(|arg| {
if arg.starts_with("-Wl,") {
arg[4..].split(',').collect()
} else {
vec![arg.as_ref()]
}
if let Some(lib) = iter.next() {
let meta = format!("rustc-link-lib=framework={}", lib);
config.print_metadata(&meta);
self.frameworks.push(lib.to_string());
});
while let Some(part) = iter.next() {
match part {
"-framework" => {
if let Some(lib) = iter.next() {
let meta = format!("rustc-link-lib=framework={}", lib);
config.print_metadata(&meta);
self.frameworks.push(lib.to_string());
}
}
"-isystem" | "-iquote" | "-idirafter" => {
if let Some(inc) = iter.next() {
self.include_paths.push(PathBuf::from(inc));
}
}
_ => (),
}
}
}
fn parse_modversion(&mut self, output: &str) {
self.version.push_str(output.trim());
}
}
fn infer_static(name: &str) -> bool {
let name = envify(name);
if env::var_os(&format!("{}_STATIC", name)).is_some() {
true
} else if env::var_os(&format!("{}_DYNAMIC", name)).is_some() {
false
} else if env::var_os("PKG_CONFIG_ALL_STATIC").is_some() {
true
} else if env::var_os("PKG_CONFIG_ALL_DYNAMIC").is_some() {
false
} else {
false
self.version.push_str(output.lines().nth(0).unwrap().trim());
}
}
fn envify(name: &str) -> String {
name.chars().map(|c| c.to_ascii_uppercase()).map(|c| {
if c == '-' {'_'} else {c}
}).collect()
name.chars()
.map(|c| c.to_ascii_uppercase())
.map(|c| if c == '-' { '_' } else { c })
.collect()
}
fn is_system(name: &str, dirs: &[PathBuf]) -> bool {
/// System libraries should only be linked dynamically
fn is_static_available(name: &str, system_roots: &[PathBuf], dirs: &[PathBuf]) -> bool {
let libname = format!("lib{}.a", name);
let root = Path::new("/usr");
!dirs.iter().any(|d| {
!d.starts_with(root) && fs::metadata(&d.join(&libname)).is_ok()
dirs.iter().any(|dir| {
!system_roots.iter().any(|sys| dir.starts_with(sys)) && dir.join(&libname).exists()
})
}
fn run(mut cmd: Command) -> Result<String, Error> {
fn run(mut cmd: Command) -> Result<Vec<u8>, Error> {
match cmd.output() {
Ok(output) => {
if output.status.success() {
let stdout = String::from_utf8(output.stdout).unwrap();
Ok(stdout)
Ok(output.stdout)
} else {
Err(Error::Failure {
command: format!("{:?}", cmd),
output: output,
output,
})
}
}
Err(cause) => Err(Error::Command {
command: format!("{:?}", cmd),
cause: 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() {
let system_roots = vec![PathBuf::from("/Library"), PathBuf::from("/System")];
assert!(!is_static_available(
"PluginManager",
system_roots,
&[PathBuf::from("/Library/Frameworks")]
));
assert!(!is_static_available(
"python2.7",
system_roots,
&[PathBuf::from(
"/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config"
)]
));
assert!(!is_static_available(
"ffi_convenience",
system_roots,
&[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",
system_roots,
&[PathBuf::from("/usr/local/lib")]
));
let libpng = Config::new()
.range_version("1".."99")
.probe("libpng16")
.unwrap();
assert!(libpng.version.find('\n').is_none());
}
}
#[test]
#[cfg(target_os = "linux")]
fn system_library_linux_test() {
assert!(!is_static_available(
"util",
&[PathBuf::from("/usr")],
&[PathBuf::from("/usr/lib/x86_64-linux-gnu")]
));
assert!(!is_static_available(
"dialog",
&[PathBuf::from("/usr")],
&[PathBuf::from("/usr/lib")]
));
}

View File

@ -0,0 +1,5 @@
Name: Escape
Version: 4.2.0
Description: Escape utility library
Libs: -Llink\ path\ with\ spaces
Cflags: -Iinclude\ path\ with\ spaces -DA=\"escaped\ string\'\ literal\" -DB=ESCAPED\ IDENTIFIER -DFOX=🦊

View File

@ -12,5 +12,5 @@ Description: A dynamic binary instrumentation framework
Version: 3.10.0.SVN
Requires:
Libs: -L${libdir}/valgrind -lcoregrind-amd64-linux -lvex-amd64-linux -lgcc
Cflags: -I${includedir}
Cflags: -I${includedir} -isystem /usr/foo

View File

@ -11,6 +11,6 @@ Name: Valgrind
Description: A dynamic binary instrumentation framework
Version: 3.10.0.SVN
Requires:
Libs: -F${libdir} -framework foo
Libs: -F${libdir} -framework foo -Wl,-framework,bar -Wl,-framework -Wl,baz -Wl,-framework,foobar,-framework,foobaz
Cflags: -I${includedir}

View File

@ -4,8 +4,8 @@ extern crate lazy_static;
use pkg_config::Error;
use std::env;
use std::sync::Mutex;
use std::path::PathBuf;
use std::sync::Mutex;
lazy_static! {
static ref LOCK: Mutex<()> = Mutex::new(());
@ -13,16 +13,21 @@ lazy_static! {
fn reset() {
for (k, _) in env::vars() {
if k.contains("DYNAMIC") ||
k.contains("STATIC") ||
k.contains("PKG_CONFIG_ALLOW_CROSS") ||
k.contains("FOO_NO_PKG_CONFIG") {
if k.contains("DYNAMIC")
|| k.contains("STATIC")
|| k.contains("PKG_CONFIG_ALLOW_CROSS")
|| k.contains("PKG_CONFIG_SYSROOT_DIR")
|| k.contains("FOO_NO_PKG_CONFIG")
{
env::remove_var(&k);
}
}
env::remove_var("TARGET");
env::remove_var("HOST");
env::set_var("PKG_CONFIG_PATH", &env::current_dir().unwrap().join("tests"));
env::set_var(
"PKG_CONFIG_PATH",
&env::current_dir().unwrap().join("tests"),
);
}
fn find(name: &str) -> Result<pkg_config::Library, Error> {
@ -36,7 +41,7 @@ fn cross_disabled() {
env::set_var("TARGET", "foo");
env::set_var("HOST", "bar");
match find("foo") {
Err(Error::CrossCompilation) => {},
Err(Error::CrossCompilation) => {}
x => panic!("Error::CrossCompilation expected, found `{:?}`", x),
}
}
@ -51,15 +56,37 @@ fn cross_enabled() {
find("foo").unwrap();
}
#[test]
fn cross_enabled_if_customized() {
let _g = LOCK.lock();
reset();
env::set_var("TARGET", "foo");
env::set_var("HOST", "bar");
env::set_var("PKG_CONFIG_SYSROOT_DIR", "/tmp/cross-test");
find("foo").unwrap();
}
#[test]
fn cross_disabled_if_customized() {
let _g = LOCK.lock();
reset();
env::set_var("TARGET", "foo");
env::set_var("HOST", "bar");
env::set_var("PKG_CONFIG_ALLOW_CROSS", "0");
env::set_var("PKG_CONFIG_SYSROOT_DIR", "/tmp/cross-test");
match find("foo") {
Err(Error::CrossCompilation) => {}
_ => panic!("expected CrossCompilation failure"),
}
}
#[test]
fn package_disabled() {
let _g = LOCK.lock();
reset();
env::set_var("FOO_NO_PKG_CONFIG", "1");
match find("foo") {
Err(Error::EnvNoPkgConfig(name)) => {
assert_eq!(name, "FOO_NO_PKG_CONFIG")
}
Err(Error::EnvNoPkgConfig(name)) => assert_eq!(name, "FOO_NO_PKG_CONFIG"),
x => panic!("Error::EnvNoPkgConfig expected, found `{:?}`", x),
}
}
@ -72,6 +99,32 @@ fn output_ok() {
assert!(lib.libs.contains(&"gcc".to_string()));
assert!(lib.libs.contains(&"coregrind-amd64-linux".to_string()));
assert!(lib.link_paths.contains(&PathBuf::from("/usr/lib/valgrind")));
assert!(lib
.include_paths
.contains(&PathBuf::from("/usr/include/valgrind")));
assert!(lib.include_paths.contains(&PathBuf::from("/usr/foo")));
}
#[test]
fn escapes() {
let _g = LOCK.lock();
reset();
let lib = find("escape").unwrap();
assert!(lib
.include_paths
.contains(&PathBuf::from("include path with spaces")));
assert!(lib
.link_paths
.contains(&PathBuf::from("link path with spaces")));
assert_eq!(
lib.defines.get("A"),
Some(&Some("\"escaped string' literal\"".to_owned()))
);
assert_eq!(
lib.defines.get("B"),
Some(&Some("ESCAPED IDENTIFIER".to_owned()))
);
assert_eq!(lib.defines.get("FOX"), Some(&Some("🦊".to_owned())));
}
#[test]
@ -80,6 +133,10 @@ fn framework() {
reset();
let lib = find("framework").unwrap();
assert!(lib.frameworks.contains(&"foo".to_string()));
assert!(lib.frameworks.contains(&"bar".to_string()));
assert!(lib.frameworks.contains(&"baz".to_string()));
assert!(lib.frameworks.contains(&"foobar".to_string()));
assert!(lib.frameworks.contains(&"foobaz".to_string()));
assert!(lib.framework_paths.contains(&PathBuf::from("/usr/lib")));
}
@ -97,3 +154,160 @@ fn version() {
reset();
assert_eq!(&find("foo").unwrap().version[..], "3.10.0.SVN");
}
#[test]
fn atleast_version_ok() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.atleast_version("3.10")
.probe("foo")
.unwrap();
}
#[test]
#[should_panic]
fn atleast_version_ng() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.atleast_version("3.11")
.probe("foo")
.unwrap();
}
#[test]
fn exactly_version_ok() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.exactly_version("3.10.0.SVN")
.probe("foo")
.unwrap();
}
#[test]
#[should_panic]
fn exactly_version_ng() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.exactly_version("3.10.0")
.probe("foo")
.unwrap();
}
#[test]
fn range_version_range_ok() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.range_version("4.2.0".."4.4.0")
.probe("escape")
.unwrap();
}
#[test]
#[should_panic]
fn range_version_range_ng() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.range_version("4.0.0".."4.2.0")
.probe("escape")
.unwrap();
}
#[test]
fn range_version_range_inclusive_ok() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.range_version("4.0.0"..="4.2.0")
.probe("escape")
.unwrap();
}
#[test]
#[should_panic]
fn range_version_range_inclusive_ng() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.range_version("3.8.0"..="4.0.0")
.probe("escape")
.unwrap();
}
#[test]
fn range_version_range_from_ok() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.range_version("4.0.0"..)
.probe("escape")
.unwrap();
}
#[test]
#[should_panic]
fn range_version_range_from_ng() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.range_version("4.4.0"..)
.probe("escape")
.unwrap();
}
#[test]
fn range_version_range_to_ok() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.range_version(.."4.4.0")
.probe("escape")
.unwrap();
}
#[test]
#[should_panic]
fn range_version_range_to_ng() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.range_version(.."4.2.0")
.probe("escape")
.unwrap();
}
#[test]
fn range_version_range_to_inclusive_ok() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.range_version(..="4.2.0")
.probe("escape")
.unwrap();
}
#[test]
#[should_panic]
fn range_version_range_to_inclusive_ng() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.range_version(..="4.0.0")
.probe("escape")
.unwrap();
}
#[test]
fn range_version_full() {
let _g = LOCK.lock();
reset();
pkg_config::Config::new()
.range_version(..)
.probe("escape")
.unwrap();
}