Skip to content

Commit 9223c39

Browse files
committed
Auto merge of #45191 - petrochenkov:yesar, r=Mark-Simulacrum
rustbuild: Support specifying archiver and linker explicitly With this patch `x.py test` passes without toolchain being in `PATH` if `cc`, `cxx`, `ar`, `linker` and `gdb` are specified in `config.toml` (except for a few `run-make` tests using `nm`). Fixes #41821 r? @Mark-Simulacrum
2 parents 29ed49f + 0577b60 commit 9223c39

File tree

29 files changed

+213
-125
lines changed

29 files changed

+213
-125
lines changed

config.toml.example

+10-1
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@
300300
# =============================================================================
301301
[target.x86_64-unknown-linux-gnu]
302302

303-
# C compiler to be used to compiler C code and link Rust code. Note that the
303+
# C compiler to be used to compiler C code. Note that the
304304
# default value is platform specific, and if not specified it may also depend on
305305
# what platform is crossing to what platform.
306306
#cc = "cc"
@@ -309,6 +309,15 @@
309309
# This is only used for host targets.
310310
#cxx = "c++"
311311

312+
# Archiver to be used to assemble static libraries compiled from C/C++ code.
313+
# Note: an absolute path should be used, otherwise LLVM build will break.
314+
#ar = "ar"
315+
316+
# Linker to be used to link Rust code. Note that the
317+
# default value is platform specific, and if not specified it may also depend on
318+
# what platform is crossing to what platform.
319+
#linker = "cc"
320+
312321
# Path to the `llvm-config` binary of the installation of a custom LLVM to link
313322
# against. Note that if this is specifed we don't compile LLVM at all for this
314323
# target.

src/Cargo.lock

-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/bootstrap/bin/rustc.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,9 @@ fn main() {
120120
cmd.arg("-L").arg(&root);
121121
}
122122

123-
// Pass down extra flags, commonly used to configure `-Clinker` when
124-
// cross compiling.
125-
if let Ok(s) = env::var("RUSTC_FLAGS") {
126-
cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::<Vec<_>>());
123+
// Override linker if necessary.
124+
if let Ok(target_linker) = env::var("RUSTC_TARGET_LINKER") {
125+
cmd.arg(format!("-Clinker={}", target_linker));
127126
}
128127

129128
// Pass down incremental directory, if any.
@@ -252,6 +251,11 @@ fn main() {
252251
if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() {
253252
cmd.arg("-Z").arg("force-unstable-if-unmarked");
254253
}
254+
} else {
255+
// Override linker if necessary.
256+
if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
257+
cmd.arg(format!("-Clinker={}", host_linker));
258+
}
255259
}
256260

257261
let color = match env::var("RUSTC_COLOR") {

src/bootstrap/bin/rustdoc.rs

+3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ fn main() {
4747
if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() {
4848
cmd.arg("-Z").arg("force-unstable-if-unmarked");
4949
}
50+
if let Some(linker) = env::var_os("RUSTC_TARGET_LINKER") {
51+
cmd.arg("--linker").arg(linker).arg("-Z").arg("unstable-options");
52+
}
5053

5154
// Bootstrap's Cargo-command builder sets this variable to the current Rust version; let's pick
5255
// it up so we can make rustdoc print this into the docs

src/bootstrap/builder.rs

+41-15
Original file line numberDiff line numberDiff line change
@@ -413,13 +413,15 @@ impl<'a> Builder<'a> {
413413
pub fn rustdoc_cmd(&self, host: Interned<String>) -> Command {
414414
let mut cmd = Command::new(&self.out.join("bootstrap/debug/rustdoc"));
415415
let compiler = self.compiler(self.top_stage, host);
416-
cmd
417-
.env("RUSTC_STAGE", compiler.stage.to_string())
418-
.env("RUSTC_SYSROOT", self.sysroot(compiler))
419-
.env("RUSTC_LIBDIR", self.sysroot_libdir(compiler, self.build.build))
420-
.env("CFG_RELEASE_CHANNEL", &self.build.config.channel)
421-
.env("RUSTDOC_REAL", self.rustdoc(host))
422-
.env("RUSTDOC_CRATE_VERSION", self.build.rust_version());
416+
cmd.env("RUSTC_STAGE", compiler.stage.to_string())
417+
.env("RUSTC_SYSROOT", self.sysroot(compiler))
418+
.env("RUSTC_LIBDIR", self.sysroot_libdir(compiler, self.build.build))
419+
.env("CFG_RELEASE_CHANNEL", &self.build.config.channel)
420+
.env("RUSTDOC_REAL", self.rustdoc(host))
421+
.env("RUSTDOC_CRATE_VERSION", self.build.rust_version());
422+
if let Some(linker) = self.build.linker(host) {
423+
cmd.env("RUSTC_TARGET_LINKER", linker);
424+
}
423425
cmd
424426
}
425427

@@ -482,8 +484,14 @@ impl<'a> Builder<'a> {
482484
} else {
483485
PathBuf::from("/path/to/nowhere/rustdoc/not/required")
484486
})
485-
.env("TEST_MIRI", self.config.test_miri.to_string())
486-
.env("RUSTC_FLAGS", self.rustc_flags(target).join(" "));
487+
.env("TEST_MIRI", self.config.test_miri.to_string());
488+
489+
if let Some(host_linker) = self.build.linker(compiler.host) {
490+
cargo.env("RUSTC_HOST_LINKER", host_linker);
491+
}
492+
if let Some(target_linker) = self.build.linker(target) {
493+
cargo.env("RUSTC_TARGET_LINKER", target_linker);
494+
}
487495

488496
if mode != Mode::Tool {
489497
// Tools don't get debuginfo right now, e.g. cargo and rls don't
@@ -557,17 +565,35 @@ impl<'a> Builder<'a> {
557565

558566
cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity));
559567

560-
// Specify some various options for build scripts used throughout
561-
// the build.
568+
// Throughout the build Cargo can execute a number of build scripts
569+
// compiling C/C++ code and we need to pass compilers, archivers, flags, etc
570+
// obtained previously to those build scripts.
571+
// Build scripts use either the `cc` crate or `configure/make` so we pass
572+
// the options through environment variables that are fetched and understood by both.
562573
//
563574
// FIXME: the guard against msvc shouldn't need to be here
564575
if !target.contains("msvc") {
565-
cargo.env(format!("CC_{}", target), self.cc(target))
566-
.env(format!("AR_{}", target), self.ar(target).unwrap()) // only msvc is None
567-
.env(format!("CFLAGS_{}", target), self.cflags(target).join(" "));
576+
let cc = self.cc(target);
577+
cargo.env(format!("CC_{}", target), cc)
578+
.env("CC", cc);
579+
580+
let cflags = self.cflags(target).join(" ");
581+
cargo.env(format!("CFLAGS_{}", target), cflags.clone())
582+
.env("CFLAGS", cflags.clone());
583+
584+
if let Some(ar) = self.ar(target) {
585+
let ranlib = format!("{} s", ar.display());
586+
cargo.env(format!("AR_{}", target), ar)
587+
.env("AR", ar)
588+
.env(format!("RANLIB_{}", target), ranlib.clone())
589+
.env("RANLIB", ranlib);
590+
}
568591

569592
if let Ok(cxx) = self.cxx(target) {
570-
cargo.env(format!("CXX_{}", target), cxx);
593+
cargo.env(format!("CXX_{}", target), cxx)
594+
.env("CXX", cxx)
595+
.env(format!("CXXFLAGS_{}", target), cflags.clone())
596+
.env("CXXFLAGS", cflags);
571597
}
572598
}
573599

src/bootstrap/cc_detect.rs

+45-7
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,51 @@
3131
//! ever be probed for. Instead the compilers found here will be used for
3232
//! everything.
3333
34+
use std::collections::HashSet;
35+
use std::{env, iter};
36+
use std::path::{Path, PathBuf};
3437
use std::process::Command;
35-
use std::iter;
3638

37-
use build_helper::{cc2ar, output};
39+
use build_helper::output;
3840
use cc;
3941

4042
use Build;
4143
use config::Target;
4244
use cache::Interned;
4345

46+
// The `cc` crate doesn't provide a way to obtain a path to the detected archiver,
47+
// so use some simplified logic here. First we respect the environment variable `AR`, then
48+
// try to infer the archiver path from the C compiler path.
49+
// In the future this logic should be replaced by calling into the `cc` crate.
50+
fn cc2ar(cc: &Path, target: &str) -> Option<PathBuf> {
51+
if let Some(ar) = env::var_os("AR") {
52+
Some(PathBuf::from(ar))
53+
} else if target.contains("msvc") {
54+
None
55+
} else if target.contains("musl") {
56+
Some(PathBuf::from("ar"))
57+
} else if target.contains("openbsd") {
58+
Some(PathBuf::from("ar"))
59+
} else {
60+
let parent = cc.parent().unwrap();
61+
let file = cc.file_name().unwrap().to_str().unwrap();
62+
for suffix in &["gcc", "cc", "clang"] {
63+
if let Some(idx) = file.rfind(suffix) {
64+
let mut file = file[..idx].to_owned();
65+
file.push_str("ar");
66+
return Some(parent.join(&file));
67+
}
68+
}
69+
Some(parent.join(file))
70+
}
71+
}
72+
4473
pub fn find(build: &mut Build) {
4574
// For all targets we're going to need a C compiler for building some shims
4675
// and such as well as for being a linker for Rust code.
47-
for target in build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build)) {
76+
let targets = build.targets.iter().chain(&build.hosts).cloned().chain(iter::once(build.build))
77+
.collect::<HashSet<_>>();
78+
for target in targets.into_iter() {
4879
let mut cfg = cc::Build::new();
4980
cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false)
5081
.target(&target).host(&build.build);
@@ -57,16 +88,23 @@ pub fn find(build: &mut Build) {
5788
}
5889

5990
let compiler = cfg.get_compiler();
60-
let ar = cc2ar(compiler.path(), &target);
91+
let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) {
92+
ar
93+
} else {
94+
cc2ar(compiler.path(), &target)
95+
};
96+
6197
build.verbose(&format!("CC_{} = {:?}", &target, compiler.path()));
62-
if let Some(ref ar) = ar {
98+
build.cc.insert(target, compiler);
99+
if let Some(ar) = ar {
63100
build.verbose(&format!("AR_{} = {:?}", &target, ar));
101+
build.ar.insert(target, ar);
64102
}
65-
build.cc.insert(target, (compiler, ar));
66103
}
67104

68105
// For all host triples we need to find a C++ compiler as well
69-
for host in build.hosts.iter().cloned().chain(iter::once(build.build)) {
106+
let hosts = build.hosts.iter().cloned().chain(iter::once(build.build)).collect::<HashSet<_>>();
107+
for host in hosts.into_iter() {
70108
let mut cfg = cc::Build::new();
71109
cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false).cpp(true)
72110
.target(&host).host(&build.build);

src/bootstrap/check.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -747,12 +747,14 @@ impl Step for Compiletest {
747747
flags.push("-g".to_string());
748748
}
749749

750-
let mut hostflags = build.rustc_flags(compiler.host);
751-
hostflags.extend(flags.clone());
750+
if let Some(linker) = build.linker(target) {
751+
cmd.arg("--linker").arg(linker);
752+
}
753+
754+
let hostflags = flags.clone();
752755
cmd.arg("--host-rustcflags").arg(hostflags.join(" "));
753756

754-
let mut targetflags = build.rustc_flags(target);
755-
targetflags.extend(flags);
757+
let mut targetflags = flags.clone();
756758
targetflags.push(format!("-Lnative={}",
757759
build.test_helpers_out(target).display()));
758760
cmd.arg("--target-rustcflags").arg(targetflags.join(" "));
@@ -806,6 +808,9 @@ impl Step for Compiletest {
806808
.arg("--cflags").arg(build.cflags(target).join(" "))
807809
.arg("--llvm-components").arg(llvm_components.trim())
808810
.arg("--llvm-cxxflags").arg(llvm_cxxflags.trim());
811+
if let Some(ar) = build.ar(target) {
812+
cmd.arg("--ar").arg(ar);
813+
}
809814
}
810815
}
811816
if suite == "run-make" && !build.config.llvm_enabled {
@@ -831,7 +836,7 @@ impl Step for Compiletest {
831836
// Note that if we encounter `PATH` we make sure to append to our own `PATH`
832837
// rather than stomp over it.
833838
if target.contains("msvc") {
834-
for &(ref k, ref v) in build.cc[&target].0.env() {
839+
for &(ref k, ref v) in build.cc[&target].env() {
835840
if k != "PATH" {
836841
cmd.env(k, v);
837842
}

src/bootstrap/config.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ pub struct Target {
143143
pub jemalloc: Option<PathBuf>,
144144
pub cc: Option<PathBuf>,
145145
pub cxx: Option<PathBuf>,
146+
pub ar: Option<PathBuf>,
147+
pub linker: Option<PathBuf>,
146148
pub ndk: Option<PathBuf>,
147149
pub crt_static: Option<bool>,
148150
pub musl_root: Option<PathBuf>,
@@ -282,6 +284,8 @@ struct TomlTarget {
282284
jemalloc: Option<String>,
283285
cc: Option<String>,
284286
cxx: Option<String>,
287+
ar: Option<String>,
288+
linker: Option<String>,
285289
android_ndk: Option<String>,
286290
crt_static: Option<bool>,
287291
musl_root: Option<String>,
@@ -484,8 +488,10 @@ impl Config {
484488
if let Some(ref s) = cfg.android_ndk {
485489
target.ndk = Some(env::current_dir().unwrap().join(s));
486490
}
487-
target.cxx = cfg.cxx.clone().map(PathBuf::from);
488491
target.cc = cfg.cc.clone().map(PathBuf::from);
492+
target.cxx = cfg.cxx.clone().map(PathBuf::from);
493+
target.ar = cfg.ar.clone().map(PathBuf::from);
494+
target.linker = cfg.linker.clone().map(PathBuf::from);
489495
target.crt_static = cfg.crt_static.clone();
490496
target.musl_root = cfg.musl_root.clone().map(PathBuf::from);
491497
target.qemu_rootfs = cfg.qemu_rootfs.clone().map(PathBuf::from);

src/bootstrap/lib.rs

+18-20
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,11 @@ pub struct Build {
240240
lldb_python_dir: Option<String>,
241241

242242
// Runtime state filled in later on
243-
// target -> (cc, ar)
244-
cc: HashMap<Interned<String>, (cc::Tool, Option<PathBuf>)>,
245-
// host -> (cc, ar)
243+
// C/C++ compilers and archiver for all targets
244+
cc: HashMap<Interned<String>, cc::Tool>,
246245
cxx: HashMap<Interned<String>, cc::Tool>,
246+
ar: HashMap<Interned<String>, PathBuf>,
247+
// Misc
247248
crates: HashMap<Interned<String>, Crate>,
248249
is_sudo: bool,
249250
ci_env: CiEnv,
@@ -324,6 +325,7 @@ impl Build {
324325
rls_info,
325326
cc: HashMap::new(),
326327
cxx: HashMap::new(),
328+
ar: HashMap::new(),
327329
crates: HashMap::new(),
328330
lldb_version: None,
329331
lldb_python_dir: None,
@@ -612,15 +614,15 @@ impl Build {
612614

613615
/// Returns the path to the C compiler for the target specified.
614616
fn cc(&self, target: Interned<String>) -> &Path {
615-
self.cc[&target].0.path()
617+
self.cc[&target].path()
616618
}
617619

618620
/// Returns a list of flags to pass to the C compiler for the target
619621
/// specified.
620622
fn cflags(&self, target: Interned<String>) -> Vec<String> {
621623
// Filter out -O and /O (the optimization flags) that we picked up from
622624
// cc-rs because the build scripts will determine that for themselves.
623-
let mut base = self.cc[&target].0.args().iter()
625+
let mut base = self.cc[&target].args().iter()
624626
.map(|s| s.to_string_lossy().into_owned())
625627
.filter(|s| !s.starts_with("-O") && !s.starts_with("/O"))
626628
.collect::<Vec<_>>();
@@ -644,7 +646,7 @@ impl Build {
644646

645647
/// Returns the path to the `ar` archive utility for the target specified.
646648
fn ar(&self, target: Interned<String>) -> Option<&Path> {
647-
self.cc[&target].1.as_ref().map(|p| &**p)
649+
self.ar.get(&target).map(|p| &**p)
648650
}
649651

650652
/// Returns the path to the C++ compiler for the target specified.
@@ -657,21 +659,17 @@ impl Build {
657659
}
658660
}
659661

660-
/// Returns flags to pass to the compiler to generate code for `target`.
661-
fn rustc_flags(&self, target: Interned<String>) -> Vec<String> {
662-
// New flags should be added here with great caution!
663-
//
664-
// It's quite unfortunate to **require** flags to generate code for a
665-
// target, so it should only be passed here if absolutely necessary!
666-
// Most default configuration should be done through target specs rather
667-
// than an entry here.
668-
669-
let mut base = Vec::new();
670-
if target != self.config.build && !target.contains("msvc") &&
671-
!target.contains("emscripten") {
672-
base.push(format!("-Clinker={}", self.cc(target).display()));
662+
/// Returns the path to the linker for the given target if it needs to be overriden.
663+
fn linker(&self, target: Interned<String>) -> Option<&Path> {
664+
if let Some(linker) = self.config.target_config.get(&target)
665+
.and_then(|c| c.linker.as_ref()) {
666+
Some(linker)
667+
} else if target != self.config.build &&
668+
!target.contains("msvc") && !target.contains("emscripten") {
669+
Some(self.cc(target))
670+
} else {
671+
None
673672
}
674-
base
675673
}
676674

677675
/// Returns if this target should statically link the C runtime, if specified

src/bootstrap/native.rs

+7
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,13 @@ impl Step for Llvm {
227227
cfg.build_arg("-j").build_arg(build.jobs().to_string());
228228
cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" "));
229229
cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" "));
230+
if let Some(ar) = build.ar(target) {
231+
if ar.is_absolute() {
232+
// LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it
233+
// tries to resolve this path in the LLVM build directory.
234+
cfg.define("CMAKE_AR", sanitize_cc(ar));
235+
}
236+
}
230237
};
231238

232239
configure_compilers(&mut cfg);

0 commit comments

Comments
 (0)