Pass -fPIC to native compiles on 32-bit

This is apparently a regression from 1.14.0 to 1.15.0. Previously we
passed `-fPIC` to C compilers on i686 targets, but the `gcc` crate
apparently [explicitly] didn't do this. I don't recall why that was
avoided but it was [previously passed by the makefiles][mk] and this
seems to have [caused a regression][regression] in Firefox, so this
commit reverts back to passing `-fPIC`.

[explicitly]: https://github.com/alexcrichton/gcc-rs/commit/362bdf20
[mk]: https://github.com/rust-lang/rust/blob/c781fc4a/mk/cfg/i686-unknown-linux-gnu.mk#L11
[regression]: https://bugzilla.mozilla.org/show_bug.cgi?id=1336155
This commit is contained in:
Alex Crichton 2017-02-03 22:18:32 -08:00
parent c781fc4a6a
commit f98f6c717a
12 changed files with 601 additions and 429 deletions

20
src/Cargo.lock generated
View File

@ -19,7 +19,7 @@ version = "0.0.0"
dependencies = [
"build_helper 0.1.0",
"core 0.0.0",
"gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.0.0",
]
@ -42,7 +42,7 @@ dependencies = [
"build_helper 0.1.0",
"cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
@ -74,7 +74,7 @@ name = "cmake"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -91,7 +91,7 @@ name = "compiler_builtins"
version = "0.0.0"
dependencies = [
"core 0.0.0",
"gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -132,7 +132,7 @@ name = "flate"
version = "0.0.0"
dependencies = [
"build_helper 0.1.0",
"gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -141,7 +141,7 @@ version = "0.0.0"
[[package]]
name = "gcc"
version = "0.3.40"
version = "0.3.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -396,7 +396,7 @@ name = "rustc_llvm"
version = "0.0.0"
dependencies = [
"build_helper 0.1.0",
"gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_bitflags 0.0.0",
]
@ -539,7 +539,7 @@ version = "0.0.0"
dependencies = [
"arena 0.0.0",
"build_helper 0.1.0",
"gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.0.0",
"rustc 0.0.0",
"rustc_back 0.0.0",
@ -571,7 +571,7 @@ dependencies = [
"collections 0.0.0",
"compiler_builtins 0.0.0",
"core 0.0.0",
"gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.0.0",
"panic_abort 0.0.0",
"panic_unwind 0.0.0",
@ -661,7 +661,7 @@ dependencies = [
"checksum cmake 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0e5bcf27e097a184c1df4437654ed98df3d7a516e8508a6ba45d8b092bbdf283"
"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f"
"checksum filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5363ab8e4139b8568a6237db5248646e5a8a2f89bd5ccb02092182b11fd3e922"
"checksum gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "872db9e59486ef2b14f8e8c10e9ef02de2bccef6363d7f34835dedb386b3d950"
"checksum gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)" = "c07c758b972368e703a562686adb39125707cc1ef3399da8c019fc6c2498a75d"
"checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685"
"checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8"
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"

View File

@ -1 +1 @@
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"f9b1ca6ae27d1c18215265024629a8960c31379f206d9ed20f64e0b2dcf79805",".travis.yml":"675ffe583db77282d010306f29e6d81e5070ab081deddd0300137dfbd2cb83de","Cargo.toml":"19bb617b74de761515ef5d087fd0e30912fda1d7c22fd04fa211236dab99a509","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"ecb2d93f4c81edbd48d8742ff7887dc0a4530a5890967839090bbc972d49bebe","appveyor.yml":"46c77d913eaa45871296942c2cd96ef092c9dcaf19201cb5c500a5107faeb06f","src/bin/gcc-shim.rs":"11edfe1fc6f932bd42ffffda5145833302bc163e0b87dc0d54f4bd0997ad4708","src/lib.rs":"7e7c60beccfdd145e876da81bb07dd09c5248dab0b26d93190bab4242799d51a","src/registry.rs":"3e2a42581ebb82e325dd5600c6571cef937b35003b2927dc618967f5238a2058","src/windows_registry.rs":"1f4211caec5a192b5f05c8a47efb27aa6a0ab976c659b9318a0cf603a28d6746","tests/cc_env.rs":"d92c5e3d3d43ac244e63b2cd2c93a521fcf124bf1ccf8d4c6bfa7f8333d88976","tests/support/mod.rs":"f4dad5a8133c3dd6678d9a3de057b82e624ef547b9b3e4ac9508a48962fc387b","tests/test.rs":"164220f11be2eebc20315826513999970660a82feff8cc4b15b4e9d73d98324e"},"package":"872db9e59486ef2b14f8e8c10e9ef02de2bccef6363d7f34835dedb386b3d950"}
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"4cc6445feac7e9a1f8f1e1c51cc3afd0cf7bb931e3c5a6f18c41258401652702",".travis.yml":"e68f9d10a8e367890cf734239c39952ee480cf0e8da9520b377df4a2b8ccc9e8","Cargo.toml":"4c5eb683d4c57fff819ebf564a8db93b5c87284993def6bc066ba1e311d5b090","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"b1a639560fd536f2c3ab708a8e1066b675edd4d018dfa4e5e18d0d7327d81c15","appveyor.yml":"46c77d913eaa45871296942c2cd96ef092c9dcaf19201cb5c500a5107faeb06f","src/bin/gcc-shim.rs":"d6be9137cb48b86891e7b263adbf492e1193ffe682db9ba4a88eb1079b874b58","src/lib.rs":"eb4ca086dd2ffa5e30f022f556d0def6d1142160da392afb328393b3f435e8f7","src/registry.rs":"3876ef9573e3bbc050aef41a684b9a510cc1a91b15ae874fe032cf4377b4d116","src/windows_registry.rs":"36c6a7f8322407faff2dcfd4789d0876d034885944bc0340ac7c1f7cbfc307f1","tests/cc_env.rs":"d92c5e3d3d43ac244e63b2cd2c93a521fcf124bf1ccf8d4c6bfa7f8333d88976","tests/support/mod.rs":"56bcfd1e2ff5ae8e581c71229444a3d96094bf689808808dd80e315bd6632083","tests/test.rs":"b63e74d571e7d585edc53693bcf0caae88fc040613ace91e32437d4a62cddb6a"},"package":"c07c758b972368e703a562686adb39125707cc1ef3399da8c019fc6c2498a75d"}

View File

@ -1,2 +1,4 @@
target
Cargo.lock
.idea
*.iml

View File

@ -3,6 +3,13 @@ rust:
- stable
- beta
- nightly
matrix:
include:
# Minimum version supported
- rust: 1.6.0
install:
script: cargo build
sudo: false
install:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then OS=unknown-linux-gnu; else OS=apple-darwin; fi

View File

@ -1,11 +1,11 @@
[package]
name = "gcc"
version = "0.3.40"
version = "0.3.43"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
license = "MIT/Apache-2.0"
repository = "https://github.com/alexcrichton/gcc-rs"
documentation = "http://alexcrichton.com/gcc-rs"
documentation = "https://docs.rs/gcc"
description = """
A build-time dependency for Cargo build scripts to assist in invoking the native
C compiler to compile native C code into a static archive to be linked into Rust
@ -13,8 +13,12 @@ code.
"""
keywords = ["build-dependencies"]
[badges]
travis-ci = { repository = "alexcrichton/gcc-rs" }
appveyor = { repository = "alexcrichton/gcc-rs" }
[dependencies]
rayon = { version = "0.4", optional = true }
rayon = { version = "0.6", optional = true }
[features]
parallel = ["rayon"]

View File

@ -5,7 +5,7 @@ A library to compile C/C++ code into a Rust library/application.
[![Build Status](https://travis-ci.org/alexcrichton/gcc-rs.svg?branch=master)](https://travis-ci.org/alexcrichton/gcc-rs)
[![Build status](https://ci.appveyor.com/api/projects/status/onu270iw98h81nwv?svg=true)](https://ci.appveyor.com/project/alexcrichton/gcc-rs)
[Documentation](http://alexcrichton.com/gcc-rs)
[Documentation](https://docs.rs/gcc)
A simple library meant to be used as a build dependency with Cargo packages in
order to build a set of C/C++ files into a static archive. Note that while this
@ -106,7 +106,9 @@ gcc = { version = "0.3", features = ["parallel"] }
```
By default gcc-rs will limit parallelism to `$NUM_JOBS`, or if not present it
will limit it to the number of cpus on the machine.
will limit it to the number of cpus on the machine. If you are using cargo,
use `-jN` option of `build`, `test` and `run` commands as `$NUM_JOBS`
is supplied by cargo.
## Compile-time Requirements

View File

@ -10,7 +10,7 @@ fn main() {
for i in 0.. {
let candidate = out_dir.join(format!("out{}", i));
if candidate.exists() {
continue
continue;
}
let mut f = File::create(candidate).unwrap();
for arg in env::args().skip(1) {
@ -18,6 +18,6 @@ fn main() {
}
File::create(out_dir.join("libfoo.a")).unwrap();
break
break;
}
}

View File

@ -42,7 +42,7 @@
//! }
//! ```
#![doc(html_root_url = "http://alexcrichton.com/gcc-rs")]
#![doc(html_root_url = "https://docs.rs/gcc/0.3")]
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
@ -52,10 +52,10 @@ extern crate rayon;
use std::env;
use std::ffi::{OsString, OsStr};
use std::fs;
use std::io;
use std::path::{PathBuf, Path};
use std::process::{Command, Stdio};
use std::io::{BufReader, BufRead, Write};
use std::io::{self, BufReader, BufRead, Read, Write};
use std::thread;
#[cfg(windows)]
mod registry;
@ -94,6 +94,52 @@ pub struct Tool {
path: PathBuf,
args: Vec<OsString>,
env: Vec<(OsString, OsString)>,
family: ToolFamily
}
/// Represents the family of tools this tool belongs to.
///
/// Each family of tools differs in how and what arguments they accept.
///
/// Detection of a family is done on best-effort basis and may not accurately reflect the tool.
#[derive(Copy, Clone, Debug)]
enum ToolFamily {
/// Tool is GNU Compiler Collection-like.
Gnu,
/// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags
/// and its cross-compilation approach is different.
Clang,
/// Tool is the MSVC cl.exe.
Msvc,
}
impl ToolFamily {
/// What the flag to request debug info for this family of tools look like
fn debug_flag(&self) -> &'static str {
match *self {
ToolFamily::Msvc => "/Z7",
ToolFamily::Gnu |
ToolFamily::Clang => "-g",
}
}
/// What the flag to include directories into header search path looks like
fn include_flag(&self) -> &'static str {
match *self {
ToolFamily::Msvc => "/I",
ToolFamily::Gnu |
ToolFamily::Clang => "-I",
}
}
/// What the flag to request macro-expanded source output looks like
fn expand_flag(&self) -> &'static str {
match *self {
ToolFamily::Msvc => "/E",
ToolFamily::Gnu |
ToolFamily::Clang => "-E",
}
}
}
/// Compile a library from the given set of input C files.
@ -114,7 +160,7 @@ pub fn compile_library(output: &str, files: &[&str]) {
for f in files.iter() {
c.file(*f);
}
c.compile(output)
c.compile(output);
}
impl Config {
@ -194,8 +240,7 @@ impl Config {
/// otherwise cargo will link against the specified library.
///
/// The given library name must not contain the `lib` prefix.
pub fn cpp_link_stdlib(&mut self, cpp_link_stdlib: Option<&str>)
-> &mut Config {
pub fn cpp_link_stdlib(&mut self, cpp_link_stdlib: Option<&str>) -> &mut Config {
self.cpp_link_stdlib = Some(cpp_link_stdlib.map(|s| s.into()));
self
}
@ -220,8 +265,7 @@ impl Config {
/// be used, otherwise `-stdlib` is added to the compile invocation.
///
/// The given library name must not contain the `lib` prefix.
pub fn cpp_set_stdlib(&mut self, cpp_set_stdlib: Option<&str>)
-> &mut Config {
pub fn cpp_set_stdlib(&mut self, cpp_set_stdlib: Option<&str>) -> &mut Config {
self.cpp_set_stdlib = cpp_set_stdlib.map(|s| s.into());
self.cpp_link_stdlib(cpp_set_stdlib);
self
@ -322,7 +366,8 @@ impl Config {
#[doc(hidden)]
pub fn __set_env<A, B>(&mut self, a: A, b: B) -> &mut Config
where A: AsRef<OsStr>, B: AsRef<OsStr>
where A: AsRef<OsStr>,
B: AsRef<OsStr>
{
self.env.push((a.as_ref().to_owned(), b.as_ref().to_owned()));
self
@ -355,18 +400,18 @@ impl Config {
if self.get_target().contains("msvc") {
let compiler = self.get_base_compiler();
let atlmfc_lib = compiler.env().iter().find(|&&(ref var, _)| {
var == OsStr::new("LIB")
}).and_then(|&(_, ref lib_paths)| {
env::split_paths(lib_paths).find(|path| {
let sub = Path::new("atlmfc/lib");
path.ends_with(sub) || path.parent().map_or(false, |p| p.ends_with(sub))
})
});
let atlmfc_lib = compiler.env()
.iter()
.find(|&&(ref var, _)| var.as_os_str() == OsStr::new("LIB"))
.and_then(|&(_, ref lib_paths)| {
env::split_paths(lib_paths).find(|path| {
let sub = Path::new("atlmfc/lib");
path.ends_with(sub) || path.parent().map_or(false, |p| p.ends_with(sub))
})
});
if let Some(atlmfc_lib) = atlmfc_lib {
self.print(&format!("cargo:rustc-link-search=native={}",
atlmfc_lib.display()));
self.print(&format!("cargo:rustc-link-search=native={}", atlmfc_lib.display()));
}
}
@ -394,9 +439,7 @@ impl Config {
}
drop(rayon::initialize(cfg));
objs.par_iter().weight_max().for_each(|&(ref src, ref dst)| {
self.compile_object(src, dst)
})
objs.par_iter().weight_max().for_each(|&(ref src, ref dst)| self.compile_object(src, dst));
}
#[cfg(not(feature = "parallel"))]
@ -417,8 +460,12 @@ impl Config {
for &(ref a, ref b) in self.env.iter() {
cmd.env(a, b);
}
(cmd, compiler.path.file_name().unwrap()
.to_string_lossy().into_owned())
(cmd,
compiler.path
.file_name()
.unwrap()
.to_string_lossy()
.into_owned())
};
if msvc && is_asm {
cmd.arg("/Fo").arg(dst);
@ -429,12 +476,35 @@ impl Config {
} else {
cmd.arg("-o").arg(&dst);
}
cmd.arg(if msvc {"/c"} else {"-c"});
cmd.arg(if msvc { "/c" } else { "-c" });
cmd.arg(file);
run(&mut cmd, &name);
}
/// Run the compiler, returning the macro-expanded version of the input files.
///
/// This is only relevant for C and C++ files.
pub fn expand(&self) -> Vec<u8> {
let compiler = self.get_compiler();
let mut cmd = compiler.to_command();
for &(ref a, ref b) in self.env.iter() {
cmd.env(a, b);
}
cmd.arg(compiler.family.expand_flag());
for file in self.files.iter() {
cmd.arg(file);
}
let name = compiler.path
.file_name()
.unwrap()
.to_string_lossy()
.into_owned();
run(&mut cmd, &name)
}
/// Get the compiler that's in use for this configuration.
///
/// This function will return a `Tool` which represents the culmination
@ -451,136 +521,159 @@ impl Config {
/// falling back to the default configuration.
pub fn get_compiler(&self) -> Tool {
let opt_level = self.get_opt_level();
let debug = self.get_debug();
let target = self.get_target();
let msvc = target.contains("msvc");
self.print(&format!("debug={} opt-level={}", debug, opt_level));
let mut cmd = self.get_base_compiler();
let nvcc = cmd.path.to_str()
.map(|path| path.contains("nvcc"))
let nvcc = cmd.path.file_name()
.and_then(|p| p.to_str()).map(|p| p.contains("nvcc"))
.unwrap_or(false);
if msvc {
cmd.args.push("/nologo".into());
let features = env::var("CARGO_CFG_TARGET_FEATURE")
.unwrap_or(String::new());
if features.contains("crt-static") {
cmd.args.push("/MT".into());
} else {
cmd.args.push("/MD".into());
// Non-target flags
// If the flag is not conditioned on target variable, it belongs here :)
match cmd.family {
ToolFamily::Msvc => {
cmd.args.push("/nologo".into());
let features = env::var("CARGO_CFG_TARGET_FEATURE")
.unwrap_or(String::new());
if features.contains("crt-static") {
cmd.args.push("/MT".into());
} else {
cmd.args.push("/MD".into());
}
match &opt_level[..] {
"z" | "s" => cmd.args.push("/Os".into()),
"1" => cmd.args.push("/O1".into()),
// -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2.
"2" | "3" => cmd.args.push("/O2".into()),
_ => {}
}
}
match &opt_level[..] {
"z" | "s" => cmd.args.push("/Os".into()),
"2" => cmd.args.push("/O2".into()),
"1" => cmd.args.push("/O1".into()),
_ => {}
ToolFamily::Gnu |
ToolFamily::Clang => {
cmd.args.push(format!("-O{}", opt_level).into());
if !nvcc {
cmd.args.push("-ffunction-sections".into());
cmd.args.push("-fdata-sections".into());
if self.pic.unwrap_or(!target.contains("windows-gnu")) {
cmd.args.push("-fPIC".into());
}
} else if self.pic.unwrap_or(false) {
cmd.args.push("-Xcompiler".into());
cmd.args.push("\'-fPIC\'".into());
}
}
if target.contains("i586") {
cmd.args.push("/ARCH:IA32".into());
}
} else if nvcc {
cmd.args.push(format!("-O{}", opt_level).into());
} else {
cmd.args.push(format!("-O{}", opt_level).into());
cmd.args.push("-ffunction-sections".into());
cmd.args.push("-fdata-sections".into());
}
for arg in self.envflags(if self.cpp {"CXXFLAGS"} else {"CFLAGS"}) {
cmd.args.push(arg.into());
}
if debug {
cmd.args.push(if msvc {"/Z7"} else {"-g"}.into());
if self.get_debug() {
cmd.args.push(cmd.family.debug_flag().into());
}
// Target flags
match cmd.family {
ToolFamily::Clang => {
cmd.args.push(format!("--target={}", target).into());
}
ToolFamily::Msvc => {
if target.contains("i586") {
cmd.args.push("/ARCH:IA32".into());
}
}
ToolFamily::Gnu => {
if target.contains("i686") || target.contains("i586") {
cmd.args.push("-m32".into());
} else if target.contains("x86_64") || target.contains("powerpc64") {
cmd.args.push("-m64".into());
}
if target.contains("musl") {
cmd.args.push("-static".into());
}
// armv7 targets get to use armv7 instructions
if target.starts_with("armv7-unknown-linux-") {
cmd.args.push("-march=armv7-a".into());
}
// On android we can guarantee some extra float instructions
// (specified in the android spec online)
if target.starts_with("armv7-linux-androideabi") {
cmd.args.push("-march=armv7-a".into());
cmd.args.push("-mfpu=vfpv3-d16".into());
}
// For us arm == armv6 by default
if target.starts_with("arm-unknown-linux-") {
cmd.args.push("-march=armv6".into());
cmd.args.push("-marm".into());
}
// Turn codegen down on i586 to avoid some instructions.
if target.starts_with("i586-unknown-linux-") {
cmd.args.push("-march=pentium".into());
}
// Set codegen level for i686 correctly
if target.starts_with("i686-unknown-linux-") {
cmd.args.push("-march=i686".into());
}
// Looks like `musl-gcc` makes is hard for `-m32` to make its way
// all the way to the linker, so we need to actually instruct the
// linker that we're generating 32-bit executables as well. This'll
// typically only be used for build scripts which transitively use
// these flags that try to compile executables.
if target == "i686-unknown-linux-musl" {
cmd.args.push("-Wl,-melf_i386".into());
}
if target.starts_with("thumb") {
cmd.args.push("-mthumb".into());
if target.ends_with("eabihf") {
cmd.args.push("-mfloat-abi=hard".into())
}
}
if target.starts_with("thumbv6m") {
cmd.args.push("-march=armv6s-m".into());
}
if target.starts_with("thumbv7em") {
cmd.args.push("-march=armv7e-m".into());
if target.ends_with("eabihf") {
cmd.args.push("-mfpu=fpv4-sp-d16".into())
}
}
if target.starts_with("thumbv7m") {
cmd.args.push("-march=armv7-m".into());
}
}
}
if target.contains("-ios") {
// FIXME: potential bug. iOS is always compiled with Clang, but Gcc compiler may be
// detected instead.
self.ios_flags(&mut cmd);
} else if !msvc {
if target.contains("i686") || target.contains("i586") {
cmd.args.push("-m32".into());
} else if target.contains("x86_64") || target.contains("powerpc64") {
cmd.args.push("-m64".into());
}
if !nvcc && self.pic.unwrap_or(!target.contains("i686") && !target.contains("windows-gnu")) {
cmd.args.push("-fPIC".into());
} else if nvcc && self.pic.unwrap_or(false) {
cmd.args.push("-Xcompiler".into());
cmd.args.push("\'-fPIC\'".into());
}
if target.contains("musl") {
cmd.args.push("-static".into());
}
// armv7 targets get to use armv7 instructions
if target.starts_with("armv7-unknown-linux-") {
cmd.args.push("-march=armv7-a".into());
}
// On android we can guarantee some extra float instructions
// (specified in the android spec online)
if target.starts_with("armv7-linux-androideabi") {
cmd.args.push("-march=armv7-a".into());
cmd.args.push("-mfpu=vfpv3-d16".into());
}
// For us arm == armv6 by default
if target.starts_with("arm-unknown-linux-") {
cmd.args.push("-march=armv6".into());
cmd.args.push("-marm".into());
}
// Turn codegen down on i586 to avoid some instructions.
if target.starts_with("i586-unknown-linux-") {
cmd.args.push("-march=pentium".into());
}
// Set codegen level for i686 correctly
if target.starts_with("i686-unknown-linux-") {
cmd.args.push("-march=i686".into());
}
// Looks like `musl-gcc` makes is hard for `-m32` to make its way
// all the way to the linker, so we need to actually instruct the
// linker that we're generating 32-bit executables as well. This'll
// typically only be used for build scripts which transitively use
// these flags that try to compile executables.
if target == "i686-unknown-linux-musl" {
cmd.args.push("-Wl,-melf_i386".into());
}
if target.starts_with("thumb") {
cmd.args.push("-mthumb".into());
if target.ends_with("eabihf") {
cmd.args.push("-mfloat-abi=hard".into())
}
}
if target.starts_with("thumbv6m") {
cmd.args.push("-march=armv6s-m".into());
}
if target.starts_with("thumbv7em") {
cmd.args.push("-march=armv7e-m".into());
if target.ends_with("eabihf") {
cmd.args.push("-mfpu=fpv4-sp-d16".into())
}
}
if target.starts_with("thumbv7m") {
cmd.args.push("-march=armv7-m".into());
}
}
if self.cpp && !msvc {
if let Some(ref stdlib) = self.cpp_set_stdlib {
cmd.args.push(format!("-stdlib=lib{}", stdlib).into());
if self.cpp {
match (self.cpp_set_stdlib.as_ref(), cmd.family) {
(None, _) => { }
(Some(stdlib), ToolFamily::Gnu) |
(Some(stdlib), ToolFamily::Clang) => {
cmd.args.push(format!("-stdlib=lib{}", stdlib).into());
}
_ => {
println!("cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \
does not support this option, ignored", cmd.family);
}
}
}
for directory in self.include_directories.iter() {
cmd.args.push(if msvc {"/I"} else {"-I"}.into());
cmd.args.push(cmd.family.include_flag().into());
cmd.args.push(directory.into());
}
@ -589,7 +682,7 @@ impl Config {
}
for &(ref key, ref value) in self.definitions.iter() {
let lead = if msvc {"/"} else {"-"};
let lead = if let ToolFamily::Msvc = cmd.family {"/"} else {"-"};
if let &Some(ref value) = value {
cmd.args.push(format!("{}D{}={}", lead, key, value).into());
} else {
@ -601,10 +694,12 @@ impl Config {
fn msvc_macro_assembler(&self) -> (Command, String) {
let target = self.get_target();
let tool = if target.contains("x86_64") {"ml64.exe"} else {"ml.exe"};
let mut cmd = windows_registry::find(&target, tool).unwrap_or_else(|| {
self.cmd(tool)
});
let tool = if target.contains("x86_64") {
"ml64.exe"
} else {
"ml.exe"
};
let mut cmd = windows_registry::find(&target, tool).unwrap_or_else(|| self.cmd(tool));
for directory in self.include_directories.iter() {
cmd.arg("/I").arg(directory);
}
@ -635,31 +730,37 @@ impl Config {
if target.contains("msvc") {
let mut cmd = match self.archiver {
Some(ref s) => self.cmd(s),
None => windows_registry::find(&target, "lib.exe")
.unwrap_or(self.cmd("lib.exe")),
None => windows_registry::find(&target, "lib.exe").unwrap_or(self.cmd("lib.exe")),
};
let mut out = OsString::from("/OUT:");
out.push(dst);
run(cmd.arg(out).arg("/nologo")
.args(objects)
.args(&self.objects), "lib.exe");
run(cmd.arg(out)
.arg("/nologo")
.args(objects)
.args(&self.objects),
"lib.exe");
// The Rust compiler will look for libfoo.a and foo.lib, but the
// MSVC linker will also be passed foo.lib, so be sure that both
// exist for now.
let lib_dst = dst.with_file_name(format!("{}.lib", lib_name));
let _ = fs::remove_file(&lib_dst);
fs::hard_link(&dst, &lib_dst).or_else(|_| {
//if hard-link fails, just copy (ignoring the number of bytes written)
fs::copy(&dst, &lib_dst).map(|_| ())
}).ok().expect("Copying from {:?} to {:?} failed.");;
fs::hard_link(&dst, &lib_dst)
.or_else(|_| {
// if hard-link fails, just copy (ignoring the number of bytes written)
fs::copy(&dst, &lib_dst).map(|_| ())
})
.ok()
.expect("Copying from {:?} to {:?} failed.");;
} else {
let ar = self.get_ar();
let cmd = ar.file_name().unwrap().to_string_lossy();
run(self.cmd(&ar).arg("crs")
.arg(dst)
.args(objects)
.args(&self.objects), &cmd);
run(self.cmd(&ar)
.arg("crs")
.arg(dst)
.args(objects)
.args(&self.objects),
&cmd);
}
}
@ -677,7 +778,7 @@ impl Config {
"arm64" | "aarch64" => ArchSpec::Device("arm64"),
"i386" | "i686" => ArchSpec::Simulator("-m32"),
"x86_64" => ArchSpec::Simulator("-m64"),
_ => fail("Unknown arch for iOS target")
_ => fail("Unknown arch for iOS target"),
};
let sdk = match arch {
@ -686,7 +787,7 @@ impl Config {
cmd.args.push(arch.into());
cmd.args.push("-miphoneos-version-min=7.0".into());
"iphoneos"
},
}
ArchSpec::Simulator(arch) => {
cmd.args.push(arch.into());
cmd.args.push("-mios-simulator-version-min=7.0".into());
@ -715,12 +816,12 @@ impl Config {
for &(ref a, ref b) in self.env.iter() {
cmd.env(a, b);
}
return cmd
return cmd;
}
fn get_base_compiler(&self) -> Tool {
if let Some(ref c) = self.compiler {
return Tool::new(c.clone())
return Tool::new(c.clone());
}
let host = self.get_host();
let target = self.get_target();
@ -729,87 +830,88 @@ impl Config {
} else {
("CC", "cl.exe", "gcc", "cc")
};
self.env_tool(env).map(|(tool, args)| {
let mut t = Tool::new(PathBuf::from(tool));
for arg in args {
t.args.push(arg.into());
}
return t
}).or_else(|| {
if target.contains("emscripten") {
if self.cpp {
Some(Tool::new(PathBuf::from("em++")))
self.env_tool(env)
.map(|(tool, args)| {
let mut t = Tool::new(PathBuf::from(tool));
for arg in args {
t.args.push(arg.into());
}
return t;
})
.or_else(|| {
if target.contains("emscripten") {
if self.cpp {
Some(Tool::new(PathBuf::from("em++")))
} else {
Some(Tool::new(PathBuf::from("emcc")))
}
} else {
Some(Tool::new(PathBuf::from("emcc")))
None
}
} else {
None
}
}).or_else(|| {
windows_registry::find_tool(&target, "cl.exe")
}).unwrap_or_else(|| {
let compiler = if host.contains("windows") &&
target.contains("windows") {
if target.contains("msvc") {
msvc.to_string()
})
.or_else(|| windows_registry::find_tool(&target, "cl.exe"))
.unwrap_or_else(|| {
let compiler = if host.contains("windows") && target.contains("windows") {
if target.contains("msvc") {
msvc.to_string()
} else {
format!("{}.exe", gnu)
}
} else if target.contains("android") {
format!("{}-{}", target.replace("armv7", "arm"), gnu)
} else if self.get_host() != target {
// CROSS_COMPILE is of the form: "arm-linux-gnueabi-"
let cc_env = self.getenv("CROSS_COMPILE");
let cross_compile = cc_env.as_ref().map(|s| s.trim_right_matches('-'));
let prefix = cross_compile.or(match &target[..] {
"aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"),
"arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
"arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
"arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"),
"arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
"arm-unknown-netbsdelf-eabi" => Some("arm--netbsdelf-eabi"),
"armv6-unknown-netbsdelf-eabihf" => Some("armv6--netbsdelf-eabihf"),
"armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
"armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
"armv7-unknown-netbsdelf-eabihf" => Some("armv7--netbsdelf-eabihf"),
"i686-pc-windows-gnu" => Some("i686-w64-mingw32"),
"i686-unknown-linux-musl" => Some("musl"),
"i686-unknown-netbsdelf" => Some("i486--netbsdelf"),
"mips-unknown-linux-gnu" => Some("mips-linux-gnu"),
"mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"),
"mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"),
"mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"),
"powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
"powerpc-unknown-netbsd" => Some("powerpc--netbsd"),
"powerpc64-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
"powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"),
"s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"),
"sparc64-unknown-netbsd" => Some("sparc64--netbsd"),
"thumbv6m-none-eabi" => Some("arm-none-eabi"),
"thumbv7em-none-eabi" => Some("arm-none-eabi"),
"thumbv7em-none-eabihf" => Some("arm-none-eabi"),
"thumbv7m-none-eabi" => Some("arm-none-eabi"),
"x86_64-pc-windows-gnu" => Some("x86_64-w64-mingw32"),
"x86_64-rumprun-netbsd" => Some("x86_64-rumprun-netbsd"),
"x86_64-unknown-linux-musl" => Some("musl"),
"x86_64-unknown-netbsd" => Some("x86_64--netbsd"),
_ => None,
});
match prefix {
Some(prefix) => format!("{}-{}", prefix, gnu),
None => default.to_string(),
}
} else {
format!("{}.exe", gnu)
}
} else if target.contains("android") {
format!("{}-{}", target, gnu)
} else if self.get_host() != target {
// CROSS_COMPILE is of the form: "arm-linux-gnueabi-"
let cc_env = self.getenv("CROSS_COMPILE");
let cross_compile = cc_env.as_ref().map(|s| s.trim_right_matches('-'));
let prefix = cross_compile.or(match &target[..] {
"aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"),
"arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
"arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
"arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"),
"arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
"arm-unknown-netbsdelf-eabi" => Some("arm--netbsdelf-eabi"),
"armv6-unknown-netbsdelf-eabihf" => Some("armv6--netbsdelf-eabihf"),
"armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
"armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
"armv7-unknown-netbsdelf-eabihf" => Some("armv7--netbsdelf-eabihf"),
"i686-pc-windows-gnu" => Some("i686-w64-mingw32"),
"i686-unknown-linux-musl" => Some("musl"),
"i686-unknown-netbsdelf" => Some("i486--netbsdelf"),
"mips-unknown-linux-gnu" => Some("mips-linux-gnu"),
"mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"),
"mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"),
"mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"),
"powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
"powerpc-unknown-netbsd" => Some("powerpc--netbsd"),
"powerpc64-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
"powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"),
"s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"),
"sparc64-unknown-netbsd" => Some("sparc64--netbsd"),
"thumbv6m-none-eabi" => Some("arm-none-eabi"),
"thumbv7em-none-eabi" => Some("arm-none-eabi"),
"thumbv7em-none-eabihf" => Some("arm-none-eabi"),
"thumbv7m-none-eabi" => Some("arm-none-eabi"),
"x86_64-pc-windows-gnu" => Some("x86_64-w64-mingw32"),
"x86_64-rumprun-netbsd" => Some("x86_64-rumprun-netbsd"),
"x86_64-unknown-linux-musl" => Some("musl"),
"x86_64-unknown-netbsd" => Some("x86_64--netbsd"),
_ => None,
});
match prefix {
Some(prefix) => format!("{}-{}", prefix, gnu),
None => default.to_string(),
}
} else {
default.to_string()
};
Tool::new(PathBuf::from(compiler))
})
default.to_string()
};
Tool::new(PathBuf::from(compiler))
})
}
fn get_var(&self, var_base: &str) -> Result<String, String> {
let target = self.get_target();
let host = self.get_host();
let kind = if host == target {"HOST"} else {"TARGET"};
let kind = if host == target { "HOST" } else { "TARGET" };
let target_u = target.replace("-", "_");
let res = self.getenv(&format!("{}_{}", var_base, target))
.or_else(|| self.getenv(&format!("{}_{}", var_base, target_u)))
@ -823,8 +925,10 @@ impl Config {
}
fn envflags(&self, name: &str) -> Vec<String> {
self.get_var(name).unwrap_or(String::new())
.split(|c: char| c.is_whitespace()).filter(|s| !s.is_empty())
self.get_var(name)
.unwrap_or(String::new())
.split(|c: char| c.is_whitespace())
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.collect()
}
@ -834,8 +938,7 @@ impl Config {
let whitelist = ["ccache", "distcc"];
for t in whitelist.iter() {
if tool.starts_with(t) && tool[t.len()..].starts_with(" ") {
return (t.to_string(),
vec![tool[t.len()..].trim_left().to_string()])
return (t.to_string(), vec![tool[t.len()..].trim_left().to_string()]);
}
}
(tool, Vec::new())
@ -860,17 +963,18 @@ impl Config {
}
fn get_ar(&self) -> PathBuf {
self.archiver.clone().or_else(|| {
self.get_var("AR").map(PathBuf::from).ok()
}).unwrap_or_else(|| {
if self.get_target().contains("android") {
PathBuf::from(format!("{}-ar", self.get_target()))
} else if self.get_target().contains("emscripten") {
PathBuf::from("emar")
} else {
PathBuf::from("ar")
}
})
self.archiver
.clone()
.or_else(|| self.get_var("AR").map(PathBuf::from).ok())
.unwrap_or_else(|| {
if self.get_target().contains("android") {
PathBuf::from(format!("{}-ar", self.get_target().replace("armv7", "arm")))
} else if self.get_target().contains("emscripten") {
PathBuf::from("emar")
} else {
PathBuf::from("ar")
}
})
}
fn get_target(&self) -> String {
@ -882,9 +986,7 @@ impl Config {
}
fn get_opt_level(&self) -> String {
self.opt_level.as_ref().cloned().unwrap_or_else(|| {
self.getenv_unwrap("OPT_LEVEL")
})
self.opt_level.as_ref().cloned().unwrap_or_else(|| self.getenv_unwrap("OPT_LEVEL"))
}
fn get_debug(&self) -> bool {
@ -892,9 +994,7 @@ impl Config {
}
fn get_out_dir(&self) -> PathBuf {
self.out_dir.clone().unwrap_or_else(|| {
env::var_os("OUT_DIR").map(PathBuf::from).unwrap()
})
self.out_dir.clone().unwrap_or_else(|| env::var_os("OUT_DIR").map(PathBuf::from).unwrap())
}
fn getenv(&self, v: &str) -> Option<String> {
@ -919,10 +1019,23 @@ impl Config {
impl Tool {
fn new(path: PathBuf) -> Tool {
// Try to detect family of the tool from its name, falling back to Gnu.
let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
if fname.contains("clang") {
ToolFamily::Clang
} else if fname.contains("cl") {
ToolFamily::Msvc
} else {
ToolFamily::Gnu
}
} else {
ToolFamily::Gnu
};
Tool {
path: path,
args: Vec::new(),
env: Vec::new(),
family: family
}
}
@ -937,7 +1050,7 @@ impl Tool {
for &(ref k, ref v) in self.env.iter() {
cmd.env(k, v);
}
return cmd
cmd
}
/// Returns the path for this compiler.
@ -963,23 +1076,27 @@ impl Tool {
}
}
fn run(cmd: &mut Command, program: &str) {
fn run(cmd: &mut Command, program: &str) -> Vec<u8> {
println!("running: {:?}", cmd);
// Capture the standard error coming from these programs, and write it out
// with cargo:warning= prefixes. Note that this is a bit wonky to avoid
// requiring the output to be UTF-8, we instead just ship bytes from one
// location to another.
let spawn_result = match cmd.stderr(Stdio::piped()).spawn() {
let (spawn_result, stdout) = match cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() {
Ok(mut child) => {
let stderr = BufReader::new(child.stderr.take().unwrap());
for line in stderr.split(b'\n').filter_map(|l| l.ok()) {
print!("cargo:warning=");
std::io::stdout().write_all(&line).unwrap();
println!("");
}
child.wait()
thread::spawn(move || {
for line in stderr.split(b'\n').filter_map(|l| l.ok()) {
print!("cargo:warning=");
std::io::stdout().write_all(&line).unwrap();
println!("");
}
});
let mut stdout = vec![];
child.stdout.take().unwrap().read_to_end(&mut stdout).unwrap();
(child.wait(), stdout)
}
Err(e) => Err(e),
Err(e) => (Err(e), vec![]),
};
let status = match spawn_result {
Ok(status) => status,
@ -991,7 +1108,10 @@ fn run(cmd: &mut Command, program: &str) {
""
};
fail(&format!("failed to execute command: {}\nIs `{}` \
not installed?{}", e, program, extra));
not installed?{}",
e,
program,
extra));
}
Err(e) => fail(&format!("failed to execute command: {}", e)),
};
@ -999,6 +1119,7 @@ fn run(cmd: &mut Command, program: &str) {
if !status.success() {
fail(&format!("command did not execute successfully, got: {}", status));
}
stdout
}
fn fail(s: &str) -> ! {

View File

@ -40,7 +40,8 @@ extern "system" {
lpSubKey: LPCWSTR,
ulOptions: DWORD,
samDesired: REGSAM,
phkResult: PHKEY) -> LONG;
phkResult: PHKEY)
-> LONG;
fn RegEnumKeyExW(key: HKEY,
dwIndex: DWORD,
lpName: LPWSTR,
@ -48,13 +49,15 @@ extern "system" {
lpReserved: LPDWORD,
lpClass: LPWSTR,
lpcClass: LPDWORD,
lpftLastWriteTime: PFILETIME) -> LONG;
lpftLastWriteTime: PFILETIME)
-> LONG;
fn RegQueryValueExW(hKey: HKEY,
lpValueName: LPCWSTR,
lpReserved: LPDWORD,
lpType: LPDWORD,
lpData: LPBYTE,
lpcbData: LPDWORD) -> LONG;
lpcbData: LPDWORD)
-> LONG;
fn RegCloseKey(hKey: HKEY) -> LONG;
}
@ -73,8 +76,7 @@ pub struct Iter<'a> {
unsafe impl Sync for Repr {}
unsafe impl Send for Repr {}
pub static LOCAL_MACHINE: RegistryKey =
RegistryKey(Repr::Const(HKEY_LOCAL_MACHINE));
pub static LOCAL_MACHINE: RegistryKey = RegistryKey(Repr::Const(HKEY_LOCAL_MACHINE));
impl RegistryKey {
fn raw(&self) -> HKEY {
@ -88,8 +90,11 @@ impl RegistryKey {
let key = key.encode_wide().chain(Some(0)).collect::<Vec<_>>();
let mut ret = 0 as *mut _;
let err = unsafe {
RegOpenKeyExW(self.raw(), key.as_ptr(), 0,
KEY_READ | KEY_WOW64_32KEY, &mut ret)
RegOpenKeyExW(self.raw(),
key.as_ptr(),
0,
KEY_READ | KEY_WOW64_32KEY,
&mut ret)
};
if err == ERROR_SUCCESS as LONG {
Ok(RegistryKey(Repr::Owned(OwnedKey(ret))))
@ -99,7 +104,10 @@ impl RegistryKey {
}
pub fn iter(&self) -> Iter {
Iter { idx: 0.., key: self }
Iter {
idx: 0..,
key: self,
}
}
pub fn query_str(&self, name: &str) -> io::Result<OsString> {
@ -108,25 +116,31 @@ impl RegistryKey {
let mut len = 0;
let mut kind = 0;
unsafe {
let err = RegQueryValueExW(self.raw(), name.as_ptr(), 0 as *mut _,
&mut kind, 0 as *mut _, &mut len);
let err = RegQueryValueExW(self.raw(),
name.as_ptr(),
0 as *mut _,
&mut kind,
0 as *mut _,
&mut len);
if err != ERROR_SUCCESS as LONG {
return Err(io::Error::from_raw_os_error(err as i32))
return Err(io::Error::from_raw_os_error(err as i32));
}
if kind != REG_SZ {
return Err(io::Error::new(io::ErrorKind::Other,
"registry key wasn't a string"))
return Err(io::Error::new(io::ErrorKind::Other, "registry key wasn't a string"));
}
// The length here is the length in bytes, but we're using wide
// characters so we need to be sure to halve it for the capacity
// passed in.
let mut v = Vec::with_capacity(len as usize / 2);
let err = RegQueryValueExW(self.raw(), name.as_ptr(), 0 as *mut _,
0 as *mut _, v.as_mut_ptr() as *mut _,
let err = RegQueryValueExW(self.raw(),
name.as_ptr(),
0 as *mut _,
0 as *mut _,
v.as_mut_ptr() as *mut _,
&mut len);
if err != ERROR_SUCCESS as LONG {
return Err(io::Error::from_raw_os_error(err as i32))
return Err(io::Error::from_raw_os_error(err as i32));
}
v.set_len(len as usize / 2);
@ -142,7 +156,9 @@ impl RegistryKey {
impl Drop for OwnedKey {
fn drop(&mut self) {
unsafe { RegCloseKey(self.0); }
unsafe {
RegCloseKey(self.0);
}
}
}
@ -153,8 +169,13 @@ impl<'a> Iterator for Iter<'a> {
self.idx.next().and_then(|i| unsafe {
let mut v = Vec::with_capacity(256);
let mut len = v.capacity() as DWORD;
let ret = RegEnumKeyExW(self.key.raw(), i, v.as_mut_ptr(), &mut len,
0 as *mut _, 0 as *mut _, 0 as *mut _,
let ret = RegEnumKeyExW(self.key.raw(),
i,
v.as_mut_ptr(),
&mut len,
0 as *mut _,
0 as *mut _,
0 as *mut _,
0 as *mut _);
if ret == ERROR_NO_MORE_ITEMS as LONG {
None

View File

@ -78,31 +78,29 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
add_env(&mut tool, "LIB", libs);
add_env(&mut tool, "PATH", path);
add_env(&mut tool, "INCLUDE", include);
return tool
tool
}
}
// This logic is all tailored for MSVC, if we're not that then bail out
// early.
if !target.contains("msvc") {
return None
return None;
}
// Looks like msbuild isn't located in the same location as other tools like
// cl.exe and lib.exe. To handle this we probe for it manually with
// dedicated registry keys.
if tool.contains("msbuild") {
return find_msbuild(target)
return find_msbuild(target);
}
// If VCINSTALLDIR is set, then someone's probably already run vcvars and we
// should just find whatever that indicates.
if env::var_os("VCINSTALLDIR").is_some() {
return env::var_os("PATH").and_then(|path| {
env::split_paths(&path).map(|p| p.join(tool)).find(|p| p.exists())
}).map(|path| {
Tool::new(path.into())
})
return env::var_os("PATH")
.and_then(|path| env::split_paths(&path).map(|p| p.join(tool)).find(|p| p.exists()))
.map(|path| Tool::new(path.into()));
}
// Ok, if we're here, now comes the fun part of the probing. Default shells
@ -112,13 +110,10 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
// environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that
// the tool is actually usable.
return find_msvc_latest(tool, target, "15.0").or_else(|| {
find_msvc_latest(tool, target, "14.0")
}).or_else(|| {
find_msvc_12(tool, target)
}).or_else(|| {
find_msvc_11(tool, target)
});
return find_msvc_latest(tool, target, "15.0")
.or_else(|| find_msvc_latest(tool, target, "14.0"))
.or_else(|| find_msvc_12(tool, target))
.or_else(|| find_msvc_11(tool, target));
// For MSVC 14 or newer we need to find the Universal CRT as well as either
// the Windows 10 SDK or Windows 8.1 SDK.
@ -151,7 +146,7 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
tool.include.push(sdk_include.join("winrt"));
tool.include.push(sdk_include.join("shared"));
} else {
return None
return None;
}
Some(tool.into_tool())
}
@ -198,26 +193,27 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
// Given a possible MSVC installation directory, we look for the linker and
// then add the MSVC library path.
fn get_tool(tool: &str, path: &Path, target: &str) -> Option<MsvcTool> {
bin_subdir(target).into_iter().map(|(sub, host)| {
(path.join("bin").join(sub).join(tool),
path.join("bin").join(host))
}).filter(|&(ref path, _)| {
path.is_file()
}).map(|(path, host)| {
let mut tool = MsvcTool::new(path);
tool.path.push(host);
tool
}).filter_map(|mut tool| {
let sub = otry!(vc_lib_subdir(target));
tool.libs.push(path.join("lib").join(sub));
tool.include.push(path.join("include"));
let atlmfc_path = path.join("atlmfc");
if atlmfc_path.exists() {
tool.libs.push(atlmfc_path.join("lib").join(sub));
tool.include.push(atlmfc_path.join("include"));
}
Some(tool)
}).next()
bin_subdir(target)
.into_iter()
.map(|(sub, host)| (path.join("bin").join(sub).join(tool), path.join("bin").join(host)))
.filter(|&(ref path, _)| path.is_file())
.map(|(path, host)| {
let mut tool = MsvcTool::new(path);
tool.path.push(host);
tool
})
.filter_map(|mut tool| {
let sub = otry!(vc_lib_subdir(target));
tool.libs.push(path.join("lib").join(sub));
tool.include.push(path.join("include"));
let atlmfc_path = path.join("atlmfc");
if atlmfc_path.exists() {
tool.libs.push(atlmfc_path.join("lib").join(sub));
tool.include.push(atlmfc_path.join("include"));
}
Some(tool)
})
.next()
}
// To find MSVC we look in a specific registry key for the version we are
@ -240,17 +236,16 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
let key = otry!(LOCAL_MACHINE.open(key.as_ref()).ok());
let root = otry!(key.query_str("KitsRoot10").ok());
let readdir = otry!(Path::new(&root).join("lib").read_dir().ok());
let max_libdir = otry!(readdir.filter_map(|dir| {
dir.ok()
}).map(|dir| {
dir.path()
}).filter(|dir| {
dir.components().last().and_then(|c| {
c.as_os_str().to_str()
}).map(|c| {
c.starts_with("10.") && dir.join("ucrt").is_dir()
}).unwrap_or(false)
}).max());
let max_libdir = otry!(readdir.filter_map(|dir| dir.ok())
.map(|dir| dir.path())
.filter(|dir| {
dir.components()
.last()
.and_then(|c| c.as_os_str().to_str())
.map(|c| c.starts_with("10.") && dir.join("ucrt").is_dir())
.unwrap_or(false)
})
.max());
let version = max_libdir.components().last().unwrap();
let version = version.as_os_str().to_str().unwrap().to_string();
Some((root.into(), version))
@ -270,12 +265,13 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
let root = otry!(key.query_str("InstallationFolder").ok());
let readdir = otry!(Path::new(&root).join("lib").read_dir().ok());
let mut dirs = readdir.filter_map(|dir| dir.ok())
.map(|dir| dir.path())
.collect::<Vec<_>>();
.map(|dir| dir.path())
.collect::<Vec<_>>();
dirs.sort();
let dir = otry!(dirs.into_iter().rev().filter(|dir| {
dir.join("um").join("x64").join("kernel32.lib").is_file()
}).next());
let dir = otry!(dirs.into_iter()
.rev()
.filter(|dir| dir.join("um").join("x64").join("kernel32.lib").is_file())
.next());
let version = dir.components().last().unwrap();
let version = version.as_os_str().to_str().unwrap().to_string();
Some((root.into(), version))
@ -319,10 +315,8 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
fn bin_subdir(target: &str) -> Vec<(&'static str, &'static str)> {
let arch = target.split('-').next().unwrap();
match (arch, host_arch()) {
("i586", X86) |
("i686", X86) => vec![("", "")],
("i586", X86_64) |
("i686", X86_64) => vec![("amd64_x86", "amd64"), ("", "")],
("i586", X86) | ("i686", X86) => vec![("", "")],
("i586", X86_64) | ("i686", X86_64) => vec![("amd64_x86", "amd64"), ("", "")],
("x86_64", X86) => vec![("x86_amd64", "")],
("x86_64", X86_64) => vec![("amd64", "amd64"), ("x86_amd64", "")],
("arm", X86) => vec![("x86_arm", "")],
@ -393,9 +387,8 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
let mut max_vers = 0;
let mut max_key = None;
for subkey in key.iter().filter_map(|k| k.ok()) {
let val = subkey.to_str().and_then(|s| {
s.trim_left_matches("v").replace(".", "").parse().ok()
});
let val = subkey.to_str()
.and_then(|s| s.trim_left_matches("v").replace(".", "").parse().ok());
let val = match val {
Some(s) => s,
None => continue,
@ -407,24 +400,25 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
}
}
}
return max_key
max_key
}
// see http://stackoverflow.com/questions/328017/path-to-msbuild
fn find_msbuild(target: &str) -> Option<Tool> {
let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
LOCAL_MACHINE.open(key.as_ref()).ok().and_then(|key| {
max_version(&key).and_then(|(_vers, key)| {
key.query_str("MSBuildToolsPath").ok()
LOCAL_MACHINE.open(key.as_ref())
.ok()
.and_then(|key| {
max_version(&key).and_then(|(_vers, key)| key.query_str("MSBuildToolsPath").ok())
})
.map(|path| {
let mut path = PathBuf::from(path);
path.push("MSBuild.exe");
let mut tool = Tool::new(path);
if target.contains("x86_64") {
tool.env.push(("Platform".into(), "X64".into()));
}
tool
})
}).map(|path| {
let mut path = PathBuf::from(path);
path.push("MSBuild.exe");
let mut tool = Tool::new(path);
if target.contains("x86_64") {
tool.env.push(("Platform".into(), "X64".into()));
}
tool
})
}
}

View File

@ -37,28 +37,27 @@ impl Test {
pub fn gnu() -> Test {
let t = Test::new();
t.shim("cc").shim("ar");
return t
t
}
pub fn msvc() -> Test {
let mut t = Test::new();
t.shim("cl").shim("lib.exe");
t.msvc = true;
return t
t
}
pub fn shim(&self, name: &str) -> &Test {
let fname = format!("{}{}", name, env::consts::EXE_SUFFIX);
fs::hard_link(&self.gcc, self.td.path().join(&fname)).or_else(|_| {
fs::copy(&self.gcc, self.td.path().join(&fname)).map(|_| ())
}).unwrap();
fs::hard_link(&self.gcc, self.td.path().join(&fname))
.or_else(|_| fs::copy(&self.gcc, self.td.path().join(&fname)).map(|_| ()))
.unwrap();
self
}
pub fn gcc(&self) -> gcc::Config {
let mut cfg = gcc::Config::new();
let mut path = env::split_paths(&env::var_os("PATH").unwrap())
.collect::<Vec<_>>();
let mut path = env::split_paths(&env::var_os("PATH").unwrap()).collect::<Vec<_>>();
path.insert(0, self.td.path().to_owned());
let target = if self.msvc {
"x86_64-pc-windows-msvc"
@ -66,26 +65,27 @@ impl Test {
"x86_64-unknown-linux-gnu"
};
cfg.target(target).host(target)
.opt_level(2)
.debug(false)
.out_dir(self.td.path())
.__set_env("PATH", env::join_paths(path).unwrap())
.__set_env("GCCTEST_OUT_DIR", self.td.path());
cfg.target(target)
.host(target)
.opt_level(2)
.debug(false)
.out_dir(self.td.path())
.__set_env("PATH", env::join_paths(path).unwrap())
.__set_env("GCCTEST_OUT_DIR", self.td.path());
if self.msvc {
cfg.compiler(self.td.path().join("cl"));
cfg.archiver(self.td.path().join("lib.exe"));
}
return cfg
cfg
}
pub fn cmd(&self, i: u32) -> Execution {
let mut s = String::new();
File::open(self.td.path().join(format!("out{}", i))).unwrap()
.read_to_string(&mut s).unwrap();
Execution {
args: s.lines().map(|s| s.to_string()).collect(),
}
File::open(self.td.path().join(format!("out{}", i)))
.unwrap()
.read_to_string(&mut s)
.unwrap();
Execution { args: s.lines().map(|s| s.to_string()).collect() }
}
}
@ -107,8 +107,6 @@ impl Execution {
}
pub fn has(&self, p: &OsStr) -> bool {
self.args.iter().any(|arg| {
OsStr::new(arg) == p
})
self.args.iter().any(|arg| OsStr::new(arg) == p)
}
}

View File

@ -9,14 +9,16 @@ mod support;
fn gnu_smoke() {
let test = Test::gnu();
test.gcc()
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_have("-O2")
.must_have("foo.c")
.must_not_have("-g")
.must_have("-c")
.must_have("-ffunction-sections")
.must_have("-fdata-sections");
test.cmd(0)
.must_have("-O2")
.must_have("foo.c")
.must_not_have("-g")
.must_have("-c")
.must_have("-ffunction-sections")
.must_have("-fdata-sections");
test.cmd(1).must_have(test.td.path().join("foo.o"));
}
@ -25,10 +27,12 @@ fn gnu_opt_level_1() {
let test = Test::gnu();
test.gcc()
.opt_level(1)
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_have("-O1")
.must_not_have("-O2");
test.cmd(0)
.must_have("-O1")
.must_not_have("-O2");
}
#[test]
@ -36,13 +40,15 @@ fn gnu_opt_level_s() {
let test = Test::gnu();
test.gcc()
.opt_level_str("s")
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_have("-Os")
.must_not_have("-O1")
.must_not_have("-O2")
.must_not_have("-O3")
.must_not_have("-Oz");
test.cmd(0)
.must_have("-Os")
.must_not_have("-O1")
.must_not_have("-O2")
.must_not_have("-O3")
.must_not_have("-Oz");
}
#[test]
@ -50,7 +56,8 @@ fn gnu_debug() {
let test = Test::gnu();
test.gcc()
.debug(true)
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_have("-g");
}
@ -62,10 +69,12 @@ fn gnu_x86_64() {
test.gcc()
.target(&target)
.host(&target)
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_have("-fPIC")
.must_have("-m64");
test.cmd(0)
.must_have("-fPIC")
.must_have("-m64");
}
}
@ -78,7 +87,8 @@ fn gnu_x86_64_no_pic() {
.pic(false)
.target(&target)
.host(&target)
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_not_have("-fPIC");
}
@ -92,10 +102,12 @@ fn gnu_i686() {
test.gcc()
.target(&target)
.host(&target)
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_not_have("-fPIC")
.must_have("-m32");
test.cmd(0)
.must_not_have("-fPIC")
.must_have("-m32");
}
}
@ -108,7 +120,8 @@ fn gnu_i686_pic() {
.pic(true)
.target(&target)
.host(&target)
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_have("-fPIC");
}
@ -119,7 +132,8 @@ fn gnu_set_stdlib() {
let test = Test::gnu();
test.gcc()
.cpp_set_stdlib(Some("foo"))
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_not_have("-stdlib=foo");
}
@ -129,7 +143,8 @@ fn gnu_include() {
let test = Test::gnu();
test.gcc()
.include("foo/bar")
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_have("-I").must_have("foo/bar");
}
@ -140,7 +155,8 @@ fn gnu_define() {
test.gcc()
.define("FOO", Some("bar"))
.define("BAR", None)
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_have("-DFOO=bar").must_have("-DBAR");
}
@ -149,7 +165,8 @@ fn gnu_define() {
fn gnu_compile_assembly() {
let test = Test::gnu();
test.gcc()
.file("foo.S").compile("libfoo.a");
.file("foo.S")
.compile("libfoo.a");
test.cmd(0).must_have("foo.S");
}
@ -157,12 +174,14 @@ fn gnu_compile_assembly() {
fn msvc_smoke() {
let test = Test::msvc();
test.gcc()
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_have("/O2")
.must_have("foo.c")
.must_not_have("/Z7")
.must_have("/c");
test.cmd(0)
.must_have("/O2")
.must_have("foo.c")
.must_not_have("/Z7")
.must_have("/c");
test.cmd(1).must_have(test.td.path().join("foo.o"));
}
@ -171,7 +190,8 @@ fn msvc_opt_level_0() {
let test = Test::msvc();
test.gcc()
.opt_level(0)
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_not_have("/O2");
}
@ -181,7 +201,8 @@ fn msvc_debug() {
let test = Test::msvc();
test.gcc()
.debug(true)
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_have("/Z7");
}
@ -190,7 +211,8 @@ fn msvc_include() {
let test = Test::msvc();
test.gcc()
.include("foo/bar")
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_have("/I").must_have("foo/bar");
}
@ -201,7 +223,8 @@ fn msvc_define() {
test.gcc()
.define("FOO", Some("bar"))
.define("BAR", None)
.file("foo.c").compile("libfoo.a");
.file("foo.c")
.compile("libfoo.a");
test.cmd(0).must_have("/DFOO=bar").must_have("/DBAR");
}