rustbuild: Support specifying archiver and linker explicitly

This commit is contained in:
Vadim Petrochenkov
2017-10-10 23:06:22 +03:00
parent 2689fd2402
commit 9e0fc5ccd0
29 changed files with 200 additions and 108 deletions

View File

@@ -300,7 +300,7 @@
# ============================================================================= # =============================================================================
[target.x86_64-unknown-linux-gnu] [target.x86_64-unknown-linux-gnu]
# C compiler to be used to compiler C code and link Rust code. Note that the # C compiler to be used to compiler C code. Note that the
# default value is platform specific, and if not specified it may also depend on # default value is platform specific, and if not specified it may also depend on
# what platform is crossing to what platform. # what platform is crossing to what platform.
#cc = "cc" #cc = "cc"
@@ -309,6 +309,15 @@
# This is only used for host targets. # This is only used for host targets.
#cxx = "c++" #cxx = "c++"
# Archiver to be used to assemble static libraries compiled from C/C++ code.
# Note: an absolute path should be used, otherwise LLVM build will break.
#ar = "ar"
# Linker to be used to link Rust code. Note that the
# default value is platform specific, and if not specified it may also depend on
# what platform is crossing to what platform.
#linker = "cc"
# Path to the `llvm-config` binary of the installation of a custom LLVM to link # Path to the `llvm-config` binary of the installation of a custom LLVM to link
# against. Note that if this is specifed we don't compile LLVM at all for this # against. Note that if this is specifed we don't compile LLVM at all for this
# target. # target.

1
src/Cargo.lock generated
View File

@@ -1994,7 +1994,6 @@ dependencies = [
"alloc_jemalloc 0.0.0", "alloc_jemalloc 0.0.0",
"alloc_system 0.0.0", "alloc_system 0.0.0",
"build_helper 0.1.0", "build_helper 0.1.0",
"cc 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"collections 0.0.0", "collections 0.0.0",
"compiler_builtins 0.0.0", "compiler_builtins 0.0.0",
"core 0.0.0", "core 0.0.0",

View File

@@ -120,12 +120,6 @@ fn main() {
cmd.arg("-L").arg(&root); cmd.arg("-L").arg(&root);
} }
// Pass down extra flags, commonly used to configure `-Clinker` when
// cross compiling.
if let Ok(s) = env::var("RUSTC_FLAGS") {
cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::<Vec<_>>());
}
// Pass down incremental directory, if any. // Pass down incremental directory, if any.
if let Ok(dir) = env::var("RUSTC_INCREMENTAL") { if let Ok(dir) = env::var("RUSTC_INCREMENTAL") {
cmd.arg(format!("-Zincremental={}", dir)); cmd.arg(format!("-Zincremental={}", dir));
@@ -254,6 +248,13 @@ fn main() {
} }
} }
// Pass down extra flags, commonly used to configure `-Clinker`.
// Linker options should be set for build scripts as well,
// can't link a build script executable without a linker!
if let Ok(s) = env::var("RUSTC_FLAGS") {
cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::<Vec<_>>());
}
let color = match env::var("RUSTC_COLOR") { let color = match env::var("RUSTC_COLOR") {
Ok(s) => usize::from_str(&s).expect("RUSTC_COLOR should be an integer"), Ok(s) => usize::from_str(&s).expect("RUSTC_COLOR should be an integer"),
Err(_) => 0, Err(_) => 0,

View File

@@ -47,6 +47,9 @@ fn main() {
if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() { if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() {
cmd.arg("-Z").arg("force-unstable-if-unmarked"); cmd.arg("-Z").arg("force-unstable-if-unmarked");
} }
if let Some(linker) = env::var_os("RUSTDOC_LINKER") {
cmd.arg("--linker").arg(linker).arg("-Z").arg("unstable-options");
}
// Bootstrap's Cargo-command builder sets this variable to the current Rust version; let's pick // Bootstrap's Cargo-command builder sets this variable to the current Rust version; let's pick
// it up so we can make rustdoc print this into the docs // it up so we can make rustdoc print this into the docs

View File

@@ -413,13 +413,15 @@ impl<'a> Builder<'a> {
pub fn rustdoc_cmd(&self, host: Interned<String>) -> Command { pub fn rustdoc_cmd(&self, host: Interned<String>) -> Command {
let mut cmd = Command::new(&self.out.join("bootstrap/debug/rustdoc")); let mut cmd = Command::new(&self.out.join("bootstrap/debug/rustdoc"));
let compiler = self.compiler(self.top_stage, host); let compiler = self.compiler(self.top_stage, host);
cmd cmd.env("RUSTC_STAGE", compiler.stage.to_string())
.env("RUSTC_STAGE", compiler.stage.to_string())
.env("RUSTC_SYSROOT", self.sysroot(compiler)) .env("RUSTC_SYSROOT", self.sysroot(compiler))
.env("RUSTC_LIBDIR", self.sysroot_libdir(compiler, self.build.build)) .env("RUSTC_LIBDIR", self.sysroot_libdir(compiler, self.build.build))
.env("CFG_RELEASE_CHANNEL", &self.build.config.channel) .env("CFG_RELEASE_CHANNEL", &self.build.config.channel)
.env("RUSTDOC_REAL", self.rustdoc(host)) .env("RUSTDOC_REAL", self.rustdoc(host))
.env("RUSTDOC_CRATE_VERSION", self.build.rust_version()); .env("RUSTDOC_CRATE_VERSION", self.build.rust_version());
if let Some(linker) = self.build.linker(host) {
cmd.env("RUSTDOC_LINKER", linker);
}
cmd cmd
} }
@@ -485,6 +487,10 @@ impl<'a> Builder<'a> {
.env("TEST_MIRI", self.config.test_miri.to_string()) .env("TEST_MIRI", self.config.test_miri.to_string())
.env("RUSTC_FLAGS", self.rustc_flags(target).join(" ")); .env("RUSTC_FLAGS", self.rustc_flags(target).join(" "));
if let Some(linker) = self.build.linker(target) {
cargo.env("RUSTDOC_LINKER", linker);
}
if mode != Mode::Tool { if mode != Mode::Tool {
// Tools don't get debuginfo right now, e.g. cargo and rls don't // Tools don't get debuginfo right now, e.g. cargo and rls don't
// get compiled with debuginfo. // get compiled with debuginfo.
@@ -557,17 +563,35 @@ impl<'a> Builder<'a> {
cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity)); cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity));
// Specify some various options for build scripts used throughout // Throughout the build Cargo can execute a number of build scripts
// the build. // compiling C/C++ code and we need to pass compilers, archivers, flags, etc
// obtained previously to those build scripts.
// Build scripts use either the `cc` crate or `configure/make` so we pass
// the options through environment variables that are fetched and understood by both.
// //
// FIXME: the guard against msvc shouldn't need to be here // FIXME: the guard against msvc shouldn't need to be here
if !target.contains("msvc") { if !target.contains("msvc") {
cargo.env(format!("CC_{}", target), self.cc(target)) let cc = self.cc(target);
.env(format!("AR_{}", target), self.ar(target).unwrap()) // only msvc is None cargo.env(format!("CC_{}", target), cc)
.env(format!("CFLAGS_{}", target), self.cflags(target).join(" ")); .env("CC", cc);
let cflags = self.cflags(target).join(" ");
cargo.env(format!("CFLAGS_{}", target), cflags.clone())
.env("CFLAGS", cflags.clone());
if let Some(ar) = self.ar(target) {
let ranlib = format!("{} s", ar.display());
cargo.env(format!("AR_{}", target), ar)
.env("AR", ar)
.env(format!("RANLIB_{}", target), ranlib.clone())
.env("RANLIB", ranlib);
}
if let Ok(cxx) = self.cxx(target) { if let Ok(cxx) = self.cxx(target) {
cargo.env(format!("CXX_{}", target), cxx); cargo.env(format!("CXX_{}", target), cxx)
.env("CXX", cxx)
.env(format!("CXXFLAGS_{}", target), cflags.clone())
.env("CXXFLAGS", cflags);
} }
} }

View File

@@ -31,20 +31,51 @@
//! ever be probed for. Instead the compilers found here will be used for //! ever be probed for. Instead the compilers found here will be used for
//! everything. //! everything.
use std::collections::HashSet;
use std::{env, iter};
use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use std::iter;
use build_helper::{cc2ar, output}; use build_helper::output;
use cc; use cc;
use Build; use Build;
use config::Target; use config::Target;
use cache::Interned; use cache::Interned;
// The `cc` crate doesn't provide a way to obtain a path to the detected archiver,
// so use some simplified logic here. First we respect the environment variable `AR`, then
// try to infer the archiver path from the C compiler path.
// In the future this logic should be replaced by calling into the `cc` crate.
fn cc2ar(cc: &Path, target: &str) -> Option<PathBuf> {
if let Some(ar) = env::var_os("AR") {
Some(PathBuf::from(ar))
} else if target.contains("msvc") {
None
} else if target.contains("musl") {
Some(PathBuf::from("ar"))
} else if target.contains("openbsd") {
Some(PathBuf::from("ar"))
} else {
let parent = cc.parent().unwrap();
let file = cc.file_name().unwrap().to_str().unwrap();
for suffix in &["gcc", "cc", "clang"] {
if let Some(idx) = file.rfind(suffix) {
let mut file = file[..idx].to_owned();
file.push_str("ar");
return Some(parent.join(&file));
}
}
Some(parent.join(file))
}
}
pub fn find(build: &mut Build) { pub fn find(build: &mut Build) {
// For all targets we're going to need a C compiler for building some shims // For all targets we're going to need a C compiler for building some shims
// and such as well as for being a linker for Rust code. // and such as well as for being a linker for Rust code.
for target in build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build)) { let targets = build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build))
.collect::<HashSet<_>>();
for target in targets.into_iter() {
let mut cfg = cc::Build::new(); let mut cfg = cc::Build::new();
cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false) cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false)
.target(&target).host(&build.build); .target(&target).host(&build.build);
@@ -57,16 +88,23 @@ pub fn find(build: &mut Build) {
} }
let compiler = cfg.get_compiler(); let compiler = cfg.get_compiler();
let ar = cc2ar(compiler.path(), &target); let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) {
ar
} else {
cc2ar(compiler.path(), &target)
};
build.verbose(&format!("CC_{} = {:?}", &target, compiler.path())); build.verbose(&format!("CC_{} = {:?}", &target, compiler.path()));
if let Some(ref ar) = ar { build.cc.insert(target, compiler);
if let Some(ar) = ar {
build.verbose(&format!("AR_{} = {:?}", &target, ar)); build.verbose(&format!("AR_{} = {:?}", &target, ar));
build.ar.insert(target, ar);
} }
build.cc.insert(target, (compiler, ar));
} }
// For all host triples we need to find a C++ compiler as well // For all host triples we need to find a C++ compiler as well
for host in build.hosts.iter().cloned().chain(iter::once(build.build)) { let hosts = build.hosts.iter().cloned().chain(iter::once(build.build)).collect::<HashSet<_>>();
for host in hosts.into_iter() {
let mut cfg = cc::Build::new(); let mut cfg = cc::Build::new();
cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false).cpp(true) cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false).cpp(true)
.target(&host).host(&build.build); .target(&host).host(&build.build);

View File

@@ -725,6 +725,9 @@ impl Step for Compiletest {
// Avoid depending on rustdoc when we don't need it. // Avoid depending on rustdoc when we don't need it.
if mode == "rustdoc" || mode == "run-make" { if mode == "rustdoc" || mode == "run-make" {
cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler.host)); cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler.host));
if let Some(linker) = build.linker(target) {
cmd.arg("--linker").arg(linker);
}
} }
cmd.arg("--src-base").arg(build.src.join("src/test").join(suite)); cmd.arg("--src-base").arg(build.src.join("src/test").join(suite));
@@ -806,6 +809,9 @@ impl Step for Compiletest {
.arg("--cflags").arg(build.cflags(target).join(" ")) .arg("--cflags").arg(build.cflags(target).join(" "))
.arg("--llvm-components").arg(llvm_components.trim()) .arg("--llvm-components").arg(llvm_components.trim())
.arg("--llvm-cxxflags").arg(llvm_cxxflags.trim()); .arg("--llvm-cxxflags").arg(llvm_cxxflags.trim());
if let Some(ar) = build.ar(target) {
cmd.arg("--ar").arg(ar);
}
} }
} }
if suite == "run-make" && !build.config.llvm_enabled { if suite == "run-make" && !build.config.llvm_enabled {
@@ -831,7 +837,7 @@ impl Step for Compiletest {
// Note that if we encounter `PATH` we make sure to append to our own `PATH` // Note that if we encounter `PATH` we make sure to append to our own `PATH`
// rather than stomp over it. // rather than stomp over it.
if target.contains("msvc") { if target.contains("msvc") {
for &(ref k, ref v) in build.cc[&target].0.env() { for &(ref k, ref v) in build.cc[&target].env() {
if k != "PATH" { if k != "PATH" {
cmd.env(k, v); cmd.env(k, v);
} }

View File

@@ -143,6 +143,8 @@ pub struct Target {
pub jemalloc: Option<PathBuf>, pub jemalloc: Option<PathBuf>,
pub cc: Option<PathBuf>, pub cc: Option<PathBuf>,
pub cxx: Option<PathBuf>, pub cxx: Option<PathBuf>,
pub ar: Option<PathBuf>,
pub linker: Option<PathBuf>,
pub ndk: Option<PathBuf>, pub ndk: Option<PathBuf>,
pub crt_static: Option<bool>, pub crt_static: Option<bool>,
pub musl_root: Option<PathBuf>, pub musl_root: Option<PathBuf>,
@@ -282,6 +284,8 @@ struct TomlTarget {
jemalloc: Option<String>, jemalloc: Option<String>,
cc: Option<String>, cc: Option<String>,
cxx: Option<String>, cxx: Option<String>,
ar: Option<String>,
linker: Option<String>,
android_ndk: Option<String>, android_ndk: Option<String>,
crt_static: Option<bool>, crt_static: Option<bool>,
musl_root: Option<String>, musl_root: Option<String>,
@@ -484,8 +488,10 @@ impl Config {
if let Some(ref s) = cfg.android_ndk { if let Some(ref s) = cfg.android_ndk {
target.ndk = Some(env::current_dir().unwrap().join(s)); target.ndk = Some(env::current_dir().unwrap().join(s));
} }
target.cxx = cfg.cxx.clone().map(PathBuf::from);
target.cc = cfg.cc.clone().map(PathBuf::from); target.cc = cfg.cc.clone().map(PathBuf::from);
target.cxx = cfg.cxx.clone().map(PathBuf::from);
target.ar = cfg.ar.clone().map(PathBuf::from);
target.linker = cfg.linker.clone().map(PathBuf::from);
target.crt_static = cfg.crt_static.clone(); target.crt_static = cfg.crt_static.clone();
target.musl_root = cfg.musl_root.clone().map(PathBuf::from); target.musl_root = cfg.musl_root.clone().map(PathBuf::from);
target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from); target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from);

View File

@@ -240,10 +240,11 @@ pub struct Build {
lldb_python_dir: Option<String>, lldb_python_dir: Option<String>,
// Runtime state filled in later on // Runtime state filled in later on
// target -> (cc, ar) // C/C++ compilers and archiver for all targets
cc: HashMap<Interned<String>, (cc::Tool, Option<PathBuf>)>, cc: HashMap<Interned<String>, cc::Tool>,
// host -> (cc, ar)
cxx: HashMap<Interned<String>, cc::Tool>, cxx: HashMap<Interned<String>, cc::Tool>,
ar: HashMap<Interned<String>, PathBuf>,
// Misc
crates: HashMap<Interned<String>, Crate>, crates: HashMap<Interned<String>, Crate>,
is_sudo: bool, is_sudo: bool,
ci_env: CiEnv, ci_env: CiEnv,
@@ -324,6 +325,7 @@ impl Build {
rls_info, rls_info,
cc: HashMap::new(), cc: HashMap::new(),
cxx: HashMap::new(), cxx: HashMap::new(),
ar: HashMap::new(),
crates: HashMap::new(), crates: HashMap::new(),
lldb_version: None, lldb_version: None,
lldb_python_dir: None, lldb_python_dir: None,
@@ -612,7 +614,7 @@ impl Build {
/// Returns the path to the C compiler for the target specified. /// Returns the path to the C compiler for the target specified.
fn cc(&self, target: Interned<String>) -> &Path { fn cc(&self, target: Interned<String>) -> &Path {
self.cc[&target].0.path() self.cc[&target].path()
} }
/// Returns a list of flags to pass to the C compiler for the target /// Returns a list of flags to pass to the C compiler for the target
@@ -620,7 +622,7 @@ impl Build {
fn cflags(&self, target: Interned<String>) -> Vec<String> { fn cflags(&self, target: Interned<String>) -> Vec<String> {
// Filter out -O and /O (the optimization flags) that we picked up from // Filter out -O and /O (the optimization flags) that we picked up from
// cc-rs because the build scripts will determine that for themselves. // cc-rs because the build scripts will determine that for themselves.
let mut base = self.cc[&target].0.args().iter() let mut base = self.cc[&target].args().iter()
.map(|s| s.to_string_lossy().into_owned()) .map(|s| s.to_string_lossy().into_owned())
.filter(|s| !s.starts_with("-O") && !s.starts_with("/O")) .filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@@ -644,7 +646,11 @@ impl Build {
/// Returns the path to the `ar` archive utility for the target specified. /// Returns the path to the `ar` archive utility for the target specified.
fn ar(&self, target: Interned<String>) -> Option<&Path> { fn ar(&self, target: Interned<String>) -> Option<&Path> {
self.cc[&target].1.as_ref().map(|p| &**p) self.ar.get(&target).map(|p| &**p)
}
fn linker(&self, target: Interned<String>) -> Option<&Path> {
self.config.target_config.get(&target).and_then(|c| c.linker.as_ref().map(|p| &**p))
} }
/// Returns the path to the C++ compiler for the target specified. /// Returns the path to the C++ compiler for the target specified.
@@ -667,7 +673,10 @@ impl Build {
// than an entry here. // than an entry here.
let mut base = Vec::new(); let mut base = Vec::new();
if target != self.config.build && !target.contains("msvc") && if let Some(linker) = self.linker(target) {
// If linker was explictly provided, force it on all the compiled Rust code.
base.push(format!("-Clinker={}", linker.display()));
} else if target != self.config.build && !target.contains("msvc") &&
!target.contains("emscripten") { !target.contains("emscripten") {
base.push(format!("-Clinker={}", self.cc(target).display())); base.push(format!("-Clinker={}", self.cc(target).display()));
} }

View File

@@ -227,6 +227,13 @@ impl Step for Llvm {
cfg.build_arg("-j").build_arg(build.jobs().to_string()); cfg.build_arg("-j").build_arg(build.jobs().to_string());
cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" ")); cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" "));
cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" ")); cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" "));
if let Some(ar) = build.ar(target) {
if ar.is_absolute() {
// LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it
// tries to resolve this path in the LLVM build directory.
cfg.define("CMAKE_AR", sanitize_cc(ar));
}
}
}; };
configure_compilers(&mut cfg); configure_compilers(&mut cfg);

View File

@@ -561,7 +561,7 @@ impl<'a> Builder<'a> {
if compiler.host.contains("msvc") { if compiler.host.contains("msvc") {
let curpaths = env::var_os("PATH").unwrap_or_default(); let curpaths = env::var_os("PATH").unwrap_or_default();
let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>(); let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>();
for &(ref k, ref v) in self.cc[&compiler.host].0.env() { for &(ref k, ref v) in self.cc[&compiler.host].env() {
if k != "PATH" { if k != "PATH" {
continue continue
} }

View File

@@ -138,27 +138,6 @@ pub fn gnu_target(target: &str) -> String {
} }
} }
pub fn cc2ar(cc: &Path, target: &str) -> Option<PathBuf> {
if target.contains("msvc") {
None
} else if target.contains("musl") {
Some(PathBuf::from("ar"))
} else if target.contains("openbsd") {
Some(PathBuf::from("ar"))
} else {
let parent = cc.parent().unwrap();
let file = cc.file_name().unwrap().to_str().unwrap();
for suffix in &["gcc", "cc", "clang"] {
if let Some(idx) = file.rfind(suffix) {
let mut file = file[..idx].to_owned();
file.push_str("ar");
return Some(parent.join(&file));
}
}
Some(parent.join(file))
}
}
pub fn make(host: &str) -> PathBuf { pub fn make(host: &str) -> PathBuf {
if host.contains("bitrig") || host.contains("dragonfly") || if host.contains("bitrig") || host.contains("dragonfly") ||
host.contains("freebsd") || host.contains("netbsd") || host.contains("freebsd") || host.contains("netbsd") ||

View File

@@ -63,15 +63,6 @@ fn main() {
_ => return, _ => return,
}; };
let compiler = cc::Build::new().get_compiler();
// only msvc returns None for ar so unwrap is okay
let ar = build_helper::cc2ar(compiler.path(), &target).unwrap();
let cflags = compiler.args()
.iter()
.map(|s| s.to_str().unwrap())
.collect::<Vec<_>>()
.join(" ");
let mut cmd = Command::new("sh"); let mut cmd = Command::new("sh");
cmd.arg(native.src_dir.join("configure") cmd.arg(native.src_dir.join("configure")
.to_str() .to_str()
@@ -79,8 +70,6 @@ fn main() {
.replace("C:\\", "/c/") .replace("C:\\", "/c/")
.replace("\\", "/")) .replace("\\", "/"))
.current_dir(&native.out_dir) .current_dir(&native.out_dir)
.env("CC", compiler.path())
.env("EXTRA_CFLAGS", cflags.clone())
// jemalloc generates Makefile deps using GCC's "-MM" flag. This means // jemalloc generates Makefile deps using GCC's "-MM" flag. This means
// that GCC will run the preprocessor, and only the preprocessor, over // that GCC will run the preprocessor, and only the preprocessor, over
// jemalloc's source files. If we don't specify CPPFLAGS, then at least // jemalloc's source files. If we don't specify CPPFLAGS, then at least
@@ -89,9 +78,7 @@ fn main() {
// passed to GCC, and then GCC won't define the // passed to GCC, and then GCC won't define the
// "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" macro that jemalloc needs to // "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" macro that jemalloc needs to
// select an atomic operation implementation. // select an atomic operation implementation.
.env("CPPFLAGS", cflags.clone()) .env("CPPFLAGS", env::var_os("CFLAGS").unwrap_or_default());
.env("AR", &ar)
.env("RANLIB", format!("{} s", ar.display()));
if target.contains("ios") { if target.contains("ios") {
cmd.arg("--disable-tls"); cmd.arg("--disable-tls");

View File

@@ -246,6 +246,9 @@ pub fn opts() -> Vec<RustcOptGroup> {
unstable("crate-version", |o| { unstable("crate-version", |o| {
o.optopt("", "crate-version", "crate version to print into documentation", "VERSION") o.optopt("", "crate-version", "crate version to print into documentation", "VERSION")
}), }),
unstable("linker", |o| {
o.optopt("", "linker", "linker used for building executable test code", "PATH")
}),
] ]
} }
@@ -357,15 +360,16 @@ pub fn main_args(args: &[String]) -> isize {
let playground_url = matches.opt_str("playground-url"); let playground_url = matches.opt_str("playground-url");
let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from); let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
let display_warnings = matches.opt_present("display-warnings"); let display_warnings = matches.opt_present("display-warnings");
let linker = matches.opt_str("linker");
match (should_test, markdown_input) { match (should_test, markdown_input) {
(true, true) => { (true, true) => {
return markdown::test(input, cfgs, libs, externs, test_args, maybe_sysroot, render_type, return markdown::test(input, cfgs, libs, externs, test_args, maybe_sysroot, render_type,
display_warnings) display_warnings, linker)
} }
(true, false) => { (true, false) => {
return test::run(input, cfgs, libs, externs, test_args, crate_name, maybe_sysroot, return test::run(input, cfgs, libs, externs, test_args, crate_name, maybe_sysroot,
render_type, display_warnings) render_type, display_warnings, linker)
} }
(false, true) => return markdown::render(input, (false, true) => return markdown::render(input,
output.unwrap_or(PathBuf::from("doc")), output.unwrap_or(PathBuf::from("doc")),

View File

@@ -142,7 +142,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches,
/// Run any tests/code examples in the markdown file `input`. /// Run any tests/code examples in the markdown file `input`.
pub fn test(input: &str, cfgs: Vec<String>, libs: SearchPaths, externs: Externs, pub fn test(input: &str, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
mut test_args: Vec<String>, maybe_sysroot: Option<PathBuf>, mut test_args: Vec<String>, maybe_sysroot: Option<PathBuf>,
render_type: RenderType, display_warnings: bool) -> isize { render_type: RenderType, display_warnings: bool, linker: Option<String>) -> isize {
let input_str = match load_string(input) { let input_str = match load_string(input) {
Ok(s) => s, Ok(s) => s,
Err(LoadStringError::ReadFail) => return 1, Err(LoadStringError::ReadFail) => return 1,
@@ -154,7 +154,7 @@ pub fn test(input: &str, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
let mut collector = Collector::new(input.to_string(), cfgs, libs, externs, let mut collector = Collector::new(input.to_string(), cfgs, libs, externs,
true, opts, maybe_sysroot, None, true, opts, maybe_sysroot, None,
Some(input.to_owned()), Some(input.to_owned()),
render_type); render_type, linker);
if render_type == RenderType::Pulldown { if render_type == RenderType::Pulldown {
old_find_testable_code(&input_str, &mut collector, DUMMY_SP); old_find_testable_code(&input_str, &mut collector, DUMMY_SP);
find_testable_code(&input_str, &mut collector, DUMMY_SP); find_testable_code(&input_str, &mut collector, DUMMY_SP);

View File

@@ -61,7 +61,8 @@ pub fn run(input: &str,
crate_name: Option<String>, crate_name: Option<String>,
maybe_sysroot: Option<PathBuf>, maybe_sysroot: Option<PathBuf>,
render_type: RenderType, render_type: RenderType,
display_warnings: bool) display_warnings: bool,
linker: Option<String>)
-> isize { -> isize {
let input_path = PathBuf::from(input); let input_path = PathBuf::from(input);
let input = config::Input::File(input_path.clone()); let input = config::Input::File(input_path.clone());
@@ -121,7 +122,8 @@ pub fn run(input: &str,
maybe_sysroot, maybe_sysroot,
Some(codemap), Some(codemap),
None, None,
render_type); render_type,
linker);
{ {
let map = hir::map::map_crate(&sess, &*cstore, &mut hir_forest, &defs); let map = hir::map::map_crate(&sess, &*cstore, &mut hir_forest, &defs);
@@ -180,7 +182,8 @@ fn run_test(test: &str, cratename: &str, filename: &str, cfgs: Vec<String>, libs
externs: Externs, externs: Externs,
should_panic: bool, no_run: bool, as_test_harness: bool, should_panic: bool, no_run: bool, as_test_harness: bool,
compile_fail: bool, mut error_codes: Vec<String>, opts: &TestOptions, compile_fail: bool, mut error_codes: Vec<String>, opts: &TestOptions,
maybe_sysroot: Option<PathBuf>) { maybe_sysroot: Option<PathBuf>,
linker: Option<String>) {
// the test harness wants its own `main` & top level functions, so // the test harness wants its own `main` & top level functions, so
// never wrap the test in `fn main() { ... }` // never wrap the test in `fn main() { ... }`
let test = make_test(test, Some(cratename), as_test_harness, opts); let test = make_test(test, Some(cratename), as_test_harness, opts);
@@ -201,6 +204,7 @@ fn run_test(test: &str, cratename: &str, filename: &str, cfgs: Vec<String>, libs
externs, externs,
cg: config::CodegenOptions { cg: config::CodegenOptions {
prefer_dynamic: true, prefer_dynamic: true,
linker,
.. config::basic_codegen_options() .. config::basic_codegen_options()
}, },
test: as_test_harness, test: as_test_harness,
@@ -442,13 +446,14 @@ pub struct Collector {
filename: Option<String>, filename: Option<String>,
// to be removed when hoedown will be removed as well // to be removed when hoedown will be removed as well
pub render_type: RenderType, pub render_type: RenderType,
linker: Option<String>,
} }
impl Collector { impl Collector {
pub fn new(cratename: String, cfgs: Vec<String>, libs: SearchPaths, externs: Externs, pub fn new(cratename: String, cfgs: Vec<String>, libs: SearchPaths, externs: Externs,
use_headers: bool, opts: TestOptions, maybe_sysroot: Option<PathBuf>, use_headers: bool, opts: TestOptions, maybe_sysroot: Option<PathBuf>,
codemap: Option<Rc<CodeMap>>, filename: Option<String>, codemap: Option<Rc<CodeMap>>, filename: Option<String>,
render_type: RenderType) -> Collector { render_type: RenderType, linker: Option<String>) -> Collector {
Collector { Collector {
tests: Vec::new(), tests: Vec::new(),
old_tests: HashMap::new(), old_tests: HashMap::new(),
@@ -464,6 +469,7 @@ impl Collector {
codemap, codemap,
filename, filename,
render_type, render_type,
linker,
} }
} }
@@ -510,6 +516,7 @@ impl Collector {
let cratename = self.cratename.to_string(); let cratename = self.cratename.to_string();
let opts = self.opts.clone(); let opts = self.opts.clone();
let maybe_sysroot = self.maybe_sysroot.clone(); let maybe_sysroot = self.maybe_sysroot.clone();
let linker = self.linker.clone();
debug!("Creating test {}: {}", name, test); debug!("Creating test {}: {}", name, test);
self.tests.push(testing::TestDescAndFn { self.tests.push(testing::TestDescAndFn {
desc: testing::TestDesc { desc: testing::TestDesc {
@@ -538,7 +545,8 @@ impl Collector {
compile_fail, compile_fail,
error_codes, error_codes,
&opts, &opts,
maybe_sysroot) maybe_sysroot,
linker)
}) })
} { } {
Ok(()) => (), Ok(()) => (),

View File

@@ -36,7 +36,6 @@ rustc_tsan = { path = "../librustc_tsan" }
[build-dependencies] [build-dependencies]
build_helper = { path = "../build_helper" } build_helper = { path = "../build_helper" }
cc = "1.0.1"
[features] [features]
backtrace = [] backtrace = []

View File

@@ -11,7 +11,6 @@
#![deny(warnings)] #![deny(warnings)]
extern crate build_helper; extern crate build_helper;
extern crate cc;
use std::env; use std::env;
use std::process::Command; use std::process::Command;
@@ -77,12 +76,6 @@ fn main() {
fn build_libbacktrace(host: &str, target: &str) -> Result<(), ()> { fn build_libbacktrace(host: &str, target: &str) -> Result<(), ()> {
let native = native_lib_boilerplate("libbacktrace", "libbacktrace", "backtrace", ".libs")?; let native = native_lib_boilerplate("libbacktrace", "libbacktrace", "backtrace", ".libs")?;
let compiler = cc::Build::new().get_compiler();
// only msvc returns None for ar so unwrap is okay
let ar = build_helper::cc2ar(compiler.path(), target).unwrap();
let mut cflags = compiler.args().iter().map(|s| s.to_str().unwrap())
.collect::<Vec<_>>().join(" ");
cflags.push_str(" -fvisibility=hidden");
run(Command::new("sh") run(Command::new("sh")
.current_dir(&native.out_dir) .current_dir(&native.out_dir)
.arg(native.src_dir.join("configure").to_str().unwrap() .arg(native.src_dir.join("configure").to_str().unwrap()
@@ -94,10 +87,7 @@ fn build_libbacktrace(host: &str, target: &str) -> Result<(), ()> {
.arg("--disable-host-shared") .arg("--disable-host-shared")
.arg(format!("--host={}", build_helper::gnu_target(target))) .arg(format!("--host={}", build_helper::gnu_target(target)))
.arg(format!("--build={}", build_helper::gnu_target(host))) .arg(format!("--build={}", build_helper::gnu_target(host)))
.env("CC", compiler.path()) .env("CFLAGS", env::var("CFLAGS").unwrap_or_default() + " -fvisibility=hidden"),
.env("AR", &ar)
.env("RANLIB", format!("{} s", ar.display()))
.env("CFLAGS", cflags),
BuildExpectation::None); BuildExpectation::None);
run(Command::new(build_helper::make(host)) run(Command::new(build_helper::make(host))

View File

@@ -5,7 +5,7 @@ all:
mkdir $(TMPDIR)/b mkdir $(TMPDIR)/b
$(call COMPILE_OBJ,$(TMPDIR)/a/foo.o,foo.c) $(call COMPILE_OBJ,$(TMPDIR)/a/foo.o,foo.c)
$(call COMPILE_OBJ,$(TMPDIR)/b/foo.o,bar.c) $(call COMPILE_OBJ,$(TMPDIR)/b/foo.o,bar.c)
ar crus $(TMPDIR)/libfoo.a $(TMPDIR)/a/foo.o $(TMPDIR)/b/foo.o $(AR) crus $(TMPDIR)/libfoo.a $(TMPDIR)/a/foo.o $(TMPDIR)/b/foo.o
$(RUSTC) foo.rs $(RUSTC) foo.rs
$(RUSTC) bar.rs $(RUSTC) bar.rs
$(call RUN,bar) $(call RUN,bar)

View File

@@ -3,8 +3,8 @@
ifneq (,$(findstring MINGW,$(UNAME))) ifneq (,$(findstring MINGW,$(UNAME)))
ifndef IS_MSVC ifndef IS_MSVC
all: all:
g++ foo.cpp -c -o $(TMPDIR)/foo.o $(CXX) foo.cpp -c -o $(TMPDIR)/foo.o
ar crus $(TMPDIR)/libfoo.a $(TMPDIR)/foo.o $(AR) crus $(TMPDIR)/libfoo.a $(TMPDIR)/foo.o
$(RUSTC) foo.rs -lfoo -lstdc++ $(RUSTC) foo.rs -lfoo -lstdc++
$(call RUN,foo) $(call RUN,foo)
else else

View File

@@ -2,5 +2,5 @@
all: all:
touch $(TMPDIR)/rust.metadata.bin touch $(TMPDIR)/rust.metadata.bin
ar crus $(TMPDIR)/libfoo-ffffffff-1.0.rlib $(TMPDIR)/rust.metadata.bin $(AR) crus $(TMPDIR)/libfoo-ffffffff-1.0.rlib $(TMPDIR)/rust.metadata.bin
$(RUSTC) foo.rs 2>&1 | grep "can't find crate for" $(RUSTC) foo.rs 2>&1 | grep "can't find crate for"

View File

@@ -18,7 +18,6 @@ extern crate rustc_errors;
extern crate rustc_trans; extern crate rustc_trans;
extern crate syntax; extern crate syntax;
use rustc::dep_graph::DepGraph;
use rustc::session::{build_session, Session}; use rustc::session::{build_session, Session};
use rustc::session::config::{basic_options, build_configuration, Input, use rustc::session::config::{basic_options, build_configuration, Input,
OutputType, OutputTypes}; OutputType, OutputTypes};
@@ -56,6 +55,9 @@ fn basic_sess(sysroot: PathBuf) -> (Session, Rc<CStore>) {
let mut opts = basic_options(); let mut opts = basic_options();
opts.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); opts.output_types = OutputTypes::new(&[(OutputType::Exe, None)]);
opts.maybe_sysroot = Some(sysroot); opts.maybe_sysroot = Some(sysroot);
if let Ok(linker) = std::env::var("RUSTC_LINKER") {
opts.cg.linker = Some(linker);
}
let descriptions = Registry::new(&rustc::DIAGNOSTICS); let descriptions = Registry::new(&rustc::DIAGNOSTICS);
let cstore = Rc::new(CStore::new(Box::new(rustc_trans::LlvmMetadataLoader))); let cstore = Rc::new(CStore::new(Box::new(rustc_trans::LlvmMetadataLoader)));
@@ -67,8 +69,7 @@ fn basic_sess(sysroot: PathBuf) -> (Session, Rc<CStore>) {
fn compile(code: String, output: PathBuf, sysroot: PathBuf) { fn compile(code: String, output: PathBuf, sysroot: PathBuf) {
let (sess, cstore) = basic_sess(sysroot); let (sess, cstore) = basic_sess(sysroot);
let cfg = build_configuration(&sess, HashSet::new());
let control = CompileController::basic(); let control = CompileController::basic();
let input = Input::Str { name: anon_src(), input: code }; let input = Input::Str { name: anon_src(), input: code };
compile_input(&sess, &cstore, &input, &None, &Some(output), None, &control); let _ = compile_input(&sess, &cstore, &input, &None, &Some(output), None, &control);
} }

View File

@@ -2,6 +2,6 @@
all: foo.rs all: foo.rs
$(RUSTC) --cfg 'feature="bar"' --crate-type lib foo.rs $(RUSTC) --cfg 'feature="bar"' --crate-type lib foo.rs
$(HOST_RPATH_ENV) '$(RUSTDOC)' --test --cfg 'feature="bar"' \ $(RUSTDOC) --test --cfg 'feature="bar"' \
-L $(TMPDIR) foo.rs |\ -L $(TMPDIR) foo.rs |\
grep -q 'foo.rs - foo (line 11) ... ok' grep -q 'foo.rs - foo (line 11) ... ok'

View File

@@ -1,4 +1,4 @@
-include ../tools.mk -include ../tools.mk
all: all:
$(HOST_RPATH_ENV) '$(RUSTDOC)' -o "$(TMPDIR)/foo/bar/doc" foo.rs $(RUSTDOC) -o "$(TMPDIR)/foo/bar/doc" foo.rs

View File

@@ -1,6 +1,6 @@
-include ../tools.mk -include ../tools.mk
all: all:
ar crus $(TMPDIR)/libfoo.a foo.rs $(AR) crus $(TMPDIR)/libfoo.a foo.rs
ar d $(TMPDIR)/libfoo.a foo.rs $(AR) d $(TMPDIR)/libfoo.a foo.rs
$(RUSTC) foo.rs $(RUSTC) foo.rs

View File

@@ -7,7 +7,13 @@ TARGET_RPATH_ENV = \
RUSTC_ORIGINAL := $(RUSTC) RUSTC_ORIGINAL := $(RUSTC)
BARE_RUSTC := $(HOST_RPATH_ENV) '$(RUSTC)' BARE_RUSTC := $(HOST_RPATH_ENV) '$(RUSTC)'
BARE_RUSTDOC := $(HOST_RPATH_ENV) '$(RUSTDOC)'
RUSTC := $(BARE_RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR) $(RUSTFLAGS) RUSTC := $(BARE_RUSTC) --out-dir $(TMPDIR) -L $(TMPDIR) $(RUSTFLAGS)
RUSTDOC := $(BARE_RUSTDOC)
ifdef RUSTC_LINKER
RUSTC := $(RUSTC) -Clinker=$(RUSTC_LINKER)
RUSTDOC := $(RUSTDOC) --linker $(RUSTC_LINKER) -Z unstable-options
endif
#CC := $(CC) -L $(TMPDIR) #CC := $(CC) -L $(TMPDIR)
HTMLDOCCK := $(PYTHON) $(S)/src/etc/htmldocck.py HTMLDOCCK := $(PYTHON) $(S)/src/etc/htmldocck.py
@@ -102,13 +108,13 @@ REMOVE_DYLIBS = rm $(TMPDIR)/$(call DYLIB_GLOB,$(1))
REMOVE_RLIBS = rm $(TMPDIR)/$(call RLIB_GLOB,$(1)) REMOVE_RLIBS = rm $(TMPDIR)/$(call RLIB_GLOB,$(1))
%.a: %.o %.a: %.o
ar crus $@ $< $(AR) crus $@ $<
ifdef IS_MSVC ifdef IS_MSVC
%.lib: lib%.o %.lib: lib%.o
$(MSVC_LIB) -out:`cygpath -w $@` $< $(MSVC_LIB) -out:`cygpath -w $@` $<
else else
%.lib: lib%.o %.lib: lib%.o
ar crus $@ $< $(AR) crus $@ $<
endif endif
%.dylib: %.o %.dylib: %.o
$(CC) -dynamiclib -Wl,-dylib -o $@ $< $(CC) -dynamiclib -Wl,-dylib -o $@ $<

View File

@@ -201,6 +201,8 @@ pub struct Config {
pub cc: String, pub cc: String,
pub cxx: String, pub cxx: String,
pub cflags: String, pub cflags: String,
pub ar: String,
pub linker: Option<String>,
pub llvm_components: String, pub llvm_components: String,
pub llvm_cxxflags: String, pub llvm_cxxflags: String,
pub nodejs: Option<String>, pub nodejs: Option<String>,

View File

@@ -102,6 +102,8 @@ pub fn parse_config(args: Vec<String> ) -> Config {
.reqopt("", "cc", "path to a C compiler", "PATH") .reqopt("", "cc", "path to a C compiler", "PATH")
.reqopt("", "cxx", "path to a C++ compiler", "PATH") .reqopt("", "cxx", "path to a C++ compiler", "PATH")
.reqopt("", "cflags", "flags for the C compiler", "FLAGS") .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
.optopt("", "ar", "path to an archiver", "PATH")
.optopt("", "linker", "path to a linker", "PATH")
.reqopt("", "llvm-components", "list of LLVM components built in", "LIST") .reqopt("", "llvm-components", "list of LLVM components built in", "LIST")
.reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS") .reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS")
.optopt("", "nodejs", "the name of nodejs", "PATH") .optopt("", "nodejs", "the name of nodejs", "PATH")
@@ -198,6 +200,8 @@ pub fn parse_config(args: Vec<String> ) -> Config {
cc: matches.opt_str("cc").unwrap(), cc: matches.opt_str("cc").unwrap(),
cxx: matches.opt_str("cxx").unwrap(), cxx: matches.opt_str("cxx").unwrap(),
cflags: matches.opt_str("cflags").unwrap(), cflags: matches.opt_str("cflags").unwrap(),
ar: matches.opt_str("ar").unwrap_or("ar".into()),
linker: matches.opt_str("linker"),
llvm_components: matches.opt_str("llvm-components").unwrap(), llvm_components: matches.opt_str("llvm-components").unwrap(),
llvm_cxxflags: matches.opt_str("llvm-cxxflags").unwrap(), llvm_cxxflags: matches.opt_str("llvm-cxxflags").unwrap(),
nodejs: matches.opt_str("nodejs"), nodejs: matches.opt_str("nodejs"),
@@ -234,6 +238,8 @@ pub fn log_config(config: &Config) {
logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir)); logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
logv(c, format!("adb_device_status: {}", logv(c, format!("adb_device_status: {}",
config.adb_device_status)); config.adb_device_status));
logv(c, format!("ar: {}", config.ar));
logv(c, format!("linker: {:?}", config.linker));
logv(c, format!("verbose: {}", config.verbose)); logv(c, format!("verbose: {}", config.verbose));
logv(c, format!("quiet: {}", config.quiet)); logv(c, format!("quiet: {}", config.quiet));
logv(c, "\n".to_string()); logv(c, "\n".to_string());

View File

@@ -1155,6 +1155,9 @@ actual:\n\
.arg("-o").arg(out_dir) .arg("-o").arg(out_dir)
.arg(&self.testpaths.file) .arg(&self.testpaths.file)
.args(&self.props.compile_flags); .args(&self.props.compile_flags);
if let Some(ref linker) = self.config.linker {
rustdoc.arg("--linker").arg(linker).arg("-Z").arg("unstable-options");
}
self.compose_and_run_compiler(rustdoc, None) self.compose_and_run_compiler(rustdoc, None)
} }
@@ -2101,6 +2104,10 @@ actual:\n\
.env("LLVM_COMPONENTS", &self.config.llvm_components) .env("LLVM_COMPONENTS", &self.config.llvm_components)
.env("LLVM_CXXFLAGS", &self.config.llvm_cxxflags); .env("LLVM_CXXFLAGS", &self.config.llvm_cxxflags);
if let Some(ref linker) = self.config.linker {
cmd.env("RUSTC_LINKER", linker);
}
// We don't want RUSTFLAGS set from the outside to interfere with // We don't want RUSTFLAGS set from the outside to interfere with
// compiler flags set in the test cases: // compiler flags set in the test cases:
cmd.env_remove("RUSTFLAGS"); cmd.env_remove("RUSTFLAGS");
@@ -2123,7 +2130,8 @@ actual:\n\
.env("CXX", &self.config.cxx); .env("CXX", &self.config.cxx);
} else { } else {
cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags)) cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
.env("CXX", format!("{} {}", self.config.cxx, self.config.cflags)); .env("CXX", format!("{} {}", self.config.cxx, self.config.cflags))
.env("AR", &self.config.ar);
if self.config.target.contains("windows") { if self.config.target.contains("windows") {
cmd.env("IS_WINDOWS", "1"); cmd.env("IS_WINDOWS", "1");