Auto merge of #39523 - alexcrichton:fpic, r=aturon

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:
bors 2017-02-08 20:49:24 +00:00
commit 29dece1c8b
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]]
@ -133,7 +133,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]]
@ -142,7 +142,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]]
@ -406,7 +406,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",
]
@ -549,7 +549,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",
@ -581,7 +581,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",
@ -671,7 +671,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 @@
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 @@ pub fn cpp(&mut self, cpp: bool) -> &mut 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 @@ pub fn cpp_link_stdlib(&mut self, cpp_link_stdlib: Option<&str>)
/// 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 @@ pub fn pic(&mut self, pic: bool) -> &mut 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 @@ pub fn compile(&self, output: &str) {
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 @@ fn compile_objects(&self, objs: &[(PathBuf, PathBuf)]) {
}
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 @@ fn compile_object(&self, file: &Path, dst: &Path) {
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 @@ fn compile_object(&self, file: &Path, dst: &Path) {
} 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 @@ fn compile_object(&self, file: &Path, dst: &Path) {
/// 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 @@ pub fn get_compiler(&self) -> Tool {
}
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 @@ pub fn get_compiler(&self) -> Tool {
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 @@ fn assemble(&self, lib_name: &str, dst: &Path, objects: &[PathBuf]) {
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 @@ enum ArchSpec {
"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 @@ enum ArchSpec {
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 @@ fn cmd<P: AsRef<OsStr>>(&self, prog: P) -> Command {
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 @@ fn get_base_compiler(&self) -> Tool {
} 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 @@ fn get_var(&self, var_base: &str) -> Result<String, String> {
}
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 @@ fn env_tool(&self, name: &str) -> Option<(String, Vec<String>)> {
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 @@ fn get_cpp_link_stdlib(&self) -> Option<String> {
}
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 @@ fn get_host(&self) -> String {
}
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 @@ fn get_debug(&self) -> bool {
}
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 @@ fn print(&self, s: &str) {
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 @@ pub fn to_command(&self) -> Command {
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 @@ pub fn env(&self) -> &[(OsString, OsString)] {
}
}
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 @@ fn RegOpenKeyExW(key: HKEY,
lpSubKey: LPCWSTR,
ulOptions: DWORD,
samDesired: REGSAM,
phkResult: PHKEY) -> LONG;
phkResult: PHKEY)
-> LONG;
fn RegEnumKeyExW(key: HKEY,
dwIndex: DWORD,
lpName: LPWSTR,
@ -48,13 +49,15 @@ fn RegEnumKeyExW(key: HKEY,
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 @@ pub fn open(&self, key: &OsStr) -> io::Result<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 @@ pub fn open(&self, key: &OsStr) -> io::Result<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 @@ pub fn query_str(&self, name: &str) -> io::Result<OsString> {
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 @@ pub fn query_str(&self, name: &str) -> io::Result<OsString> {
impl Drop for OwnedKey {
fn drop(&mut self) {
unsafe { RegCloseKey(self.0); }
unsafe {
RegCloseKey(self.0);
}
}
}
@ -153,8 +169,13 @@ fn next(&mut self) -> Option<io::Result<OsString>> {
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 @@ fn into_tool(self) -> 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 @@ fn into_tool(self) -> 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 @@ fn find_msvc_latest(tool: &str, target: &str, ver: &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 @@ fn add_env(tool: &mut Tool, env: &str, paths: Vec<PathBuf>) {
// 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 @@ fn get_ucrt_dir() -> Option<(PathBuf, String)> {
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 @@ fn get_sdk10_dir() -> Option<(PathBuf, String)> {
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 @@ fn get_sdk8_dir() -> Option<PathBuf> {
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 @@ fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> {
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 @@ fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> {
}
}
}
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 @@ pub fn new() -> 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 @@ pub fn gcc(&self) -> gcc::Config {
"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 @@ pub fn must_not_have<P: AsRef<OsStr>>(&self, p: P) -> &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 @@
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");
}