mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-09 13:33:43 +00:00
Auto merge of #116278 - Kobzol:bootstrap-lld-mode, r=albertlarsan68,petrochenkov
Generalize LLD usage in bootstrap The current usage of using LLD (`rust.use-lld = true`) in bootstrap is a bit messy. What it claimed: > Indicates whether LLD will be used to link Rust crates during bootstrap on > supported platforms. The LLD from the bootstrap distribution will be used > and not the LLD compiled during the bootstrap. What it did: 1) On MSVC, it did indeed use the snapshot compiler's `rust-lld`, but at the same time it was invoking a global `lld` binary (since https://github.com/rust-lang/rust/pull/102101), therefore it wouldn't work if `lld` wasn't available. 2) On other targets, it was just straight up using a global `lld` linker. If it wasn't available, it would fail. This PR (hopefully) cleans up handling of LLD in bootstrap. It introduces a new enum called `LldMode`, which explicitly distinguishes between no LLD, external LLD and self-contained LLD. Since it's non-trivial to provide a custom path to LLD, if an external `lld` is used, the linker binary has to be named exactly `lld` and it has to be available in PATH. In addition, this PR also dog-foods [MCP510](https://github.com/rust-lang/compiler-team/issues/510) in bootstrap. To keep backwards compatibility somewhat, I kept the original `use-lld` flag and mapped the `true` value to `"external"`, which is how it behaved before on Linux and other non-MSVC targets. Having the option to use an external `lld` on Linux should come in handy for testing on CI once MCP510 sets the default linker on Linux to `lld`. Note that thanks to MCP510, currently "self-contained" means that `lld` is used from the stage N-1 compiler (before, we always used `lld` from the snapshot/stage0 compiler). Best reviewed commit by commit. CC `@petrochenkov`
This commit is contained in:
commit
befd1eb4ec
@ -648,10 +648,12 @@ change-id = 117813
|
||||
#lld = false
|
||||
|
||||
# Indicates whether LLD will be used to link Rust crates during bootstrap on
|
||||
# supported platforms. The LLD from the bootstrap distribution will be used
|
||||
# and not the LLD compiled during the bootstrap.
|
||||
# supported platforms.
|
||||
# If set to `true` or `"external"`, a global `lld` binary that has to be in $PATH
|
||||
# will be used.
|
||||
# If set to `"self-contained"`, rust-lld from the snapshot compiler will be used.
|
||||
#
|
||||
# LLD will not be used if we're cross linking.
|
||||
# On MSVC, LLD will not be used if we're cross linking.
|
||||
#
|
||||
# Explicitly setting the linker for a target will override this option when targeting MSVC.
|
||||
#use-lld = false
|
||||
|
@ -870,7 +870,7 @@ impl Step for Rustc {
|
||||
// is already on by default in MSVC optimized builds, which is interpreted as --icf=all:
|
||||
// https://github.com/llvm/llvm-project/blob/3329cec2f79185bafd678f310fafadba2a8c76d2/lld/COFF/Driver.cpp#L1746
|
||||
// https://github.com/rust-lang/rust/blob/f22819bcce4abaff7d1246a56eec493418f9f4ee/compiler/rustc_codegen_ssa/src/back/linker.rs#L827
|
||||
if builder.config.use_lld && !compiler.host.contains("msvc") {
|
||||
if builder.config.lld_mode.is_used() && !compiler.host.is_msvc() {
|
||||
cargo.rustflag("-Clink-args=-Wl,--icf=all");
|
||||
}
|
||||
|
||||
@ -1089,7 +1089,7 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect
|
||||
// found. This is to avoid the linker errors about undefined references to
|
||||
// `__llvm_profile_instrument_memop` when linking `rustc_driver`.
|
||||
let mut llvm_linker_flags = String::new();
|
||||
if builder.config.llvm_profile_generate && target.contains("msvc") {
|
||||
if builder.config.llvm_profile_generate && target.is_msvc() {
|
||||
if let Some(ref clang_cl_path) = builder.config.llvm_clang_cl {
|
||||
// Add clang's runtime library directory to the search path
|
||||
let clang_rt_dir = get_clang_cl_resource_dir(clang_cl_path);
|
||||
@ -1114,7 +1114,7 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect
|
||||
// not for MSVC or macOS
|
||||
if builder.config.llvm_static_stdcpp
|
||||
&& !target.contains("freebsd")
|
||||
&& !target.contains("msvc")
|
||||
&& !target.is_msvc()
|
||||
&& !target.contains("apple")
|
||||
&& !target.contains("solaris")
|
||||
{
|
||||
|
@ -98,7 +98,7 @@ pub fn prebuilt_llvm_config(
|
||||
let out_dir = builder.llvm_out(target);
|
||||
|
||||
let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build);
|
||||
if !builder.config.build.contains("msvc") || builder.ninja() {
|
||||
if !builder.config.build.is_msvc() || builder.ninja() {
|
||||
llvm_config_ret_dir.push("build");
|
||||
}
|
||||
llvm_config_ret_dir.push("bin");
|
||||
@ -411,7 +411,7 @@ impl Step for Llvm {
|
||||
ldflags.shared.push(" -latomic");
|
||||
}
|
||||
|
||||
if target.contains("msvc") {
|
||||
if target.is_msvc() {
|
||||
cfg.define("LLVM_USE_CRT_DEBUG", "MT");
|
||||
cfg.define("LLVM_USE_CRT_RELEASE", "MT");
|
||||
cfg.define("LLVM_USE_CRT_RELWITHDEBINFO", "MT");
|
||||
@ -644,7 +644,7 @@ fn configure_cmake(
|
||||
}
|
||||
|
||||
let sanitize_cc = |cc: &Path| {
|
||||
if target.contains("msvc") {
|
||||
if target.is_msvc() {
|
||||
OsString::from(cc.to_str().unwrap().replace("\\", "/"))
|
||||
} else {
|
||||
cc.as_os_str().to_owned()
|
||||
@ -654,7 +654,7 @@ fn configure_cmake(
|
||||
// MSVC with CMake uses msbuild by default which doesn't respect these
|
||||
// vars that we'd otherwise configure. In that case we just skip this
|
||||
// entirely.
|
||||
if target.contains("msvc") && !builder.ninja() {
|
||||
if target.is_msvc() && !builder.ninja() {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -664,7 +664,7 @@ fn configure_cmake(
|
||||
};
|
||||
|
||||
// Handle msvc + ninja + ccache specially (this is what the bots use)
|
||||
if target.contains("msvc") && builder.ninja() && builder.config.ccache.is_some() {
|
||||
if target.is_msvc() && builder.ninja() && builder.config.ccache.is_some() {
|
||||
let mut wrap_cc = env::current_exe().expect("failed to get cwd");
|
||||
wrap_cc.set_file_name("sccache-plus-cl.exe");
|
||||
|
||||
@ -768,7 +768,7 @@ fn configure_cmake(
|
||||
// For distribution we want the LLVM tools to be *statically* linked to libstdc++.
|
||||
// We also do this if the user explicitly requested static libstdc++.
|
||||
if builder.config.llvm_static_stdcpp
|
||||
&& !target.contains("msvc")
|
||||
&& !target.is_msvc()
|
||||
&& !target.contains("netbsd")
|
||||
&& !target.contains("solaris")
|
||||
{
|
||||
@ -874,7 +874,7 @@ impl Step for Lld {
|
||||
// when doing PGO on CI, cmake or clang-cl don't automatically link clang's
|
||||
// profiler runtime in. In that case, we need to manually ask cmake to do it, to avoid
|
||||
// linking errors, much like LLVM's cmake setup does in that situation.
|
||||
if builder.config.llvm_profile_generate && target.contains("msvc") {
|
||||
if builder.config.llvm_profile_generate && target.is_msvc() {
|
||||
if let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref() {
|
||||
// Find clang's runtime library directory and push that as a search path to the
|
||||
// cmake linker flags.
|
||||
|
@ -29,8 +29,9 @@ use crate::utils;
|
||||
use crate::utils::cache::{Interned, INTERNER};
|
||||
use crate::utils::exec::BootstrapCommand;
|
||||
use crate::utils::helpers::{
|
||||
self, add_link_lib_path, add_rustdoc_cargo_lld_flags, add_rustdoc_lld_flags, dylib_path,
|
||||
dylib_path_var, output, t, target_supports_cranelift_backend, up_to_date, LldThreads,
|
||||
self, add_link_lib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var,
|
||||
linker_args, linker_flags, output, t, target_supports_cranelift_backend, up_to_date,
|
||||
LldThreads,
|
||||
};
|
||||
use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests};
|
||||
use crate::{envify, CLang, DocTests, GitRepo, Mode};
|
||||
@ -277,7 +278,7 @@ impl Step for Cargotest {
|
||||
.args(builder.config.test_args())
|
||||
.env("RUSTC", builder.rustc(compiler))
|
||||
.env("RUSTDOC", builder.rustdoc(compiler));
|
||||
add_rustdoc_cargo_lld_flags(&mut cmd, builder, compiler.host, LldThreads::No);
|
||||
add_rustdoc_cargo_linker_args(&mut cmd, builder, compiler.host, LldThreads::No);
|
||||
builder.run_delaying_failure(cmd);
|
||||
}
|
||||
}
|
||||
@ -863,7 +864,7 @@ impl Step for RustdocTheme {
|
||||
.env("CFG_RELEASE_CHANNEL", &builder.config.channel)
|
||||
.env("RUSTDOC_REAL", builder.rustdoc(self.compiler))
|
||||
.env("RUSTC_BOOTSTRAP", "1");
|
||||
add_rustdoc_lld_flags(&mut cmd, builder, self.compiler.host, LldThreads::No);
|
||||
cmd.args(linker_args(builder, self.compiler.host, LldThreads::No));
|
||||
|
||||
builder.run_delaying_failure(&mut cmd);
|
||||
}
|
||||
@ -1039,7 +1040,7 @@ impl Step for RustdocGUI {
|
||||
cmd.env("RUSTDOC", builder.rustdoc(self.compiler))
|
||||
.env("RUSTC", builder.rustc(self.compiler));
|
||||
|
||||
add_rustdoc_cargo_lld_flags(&mut cmd, builder, self.compiler.host, LldThreads::No);
|
||||
add_rustdoc_cargo_linker_args(&mut cmd, builder, self.compiler.host, LldThreads::No);
|
||||
|
||||
for path in &builder.paths {
|
||||
if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) {
|
||||
@ -1746,14 +1747,14 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
|
||||
|
||||
let mut hostflags = flags.clone();
|
||||
hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display()));
|
||||
hostflags.extend(builder.lld_flags(compiler.host));
|
||||
hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No));
|
||||
for flag in hostflags {
|
||||
cmd.arg("--host-rustcflags").arg(flag);
|
||||
}
|
||||
|
||||
let mut targetflags = flags;
|
||||
targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display()));
|
||||
targetflags.extend(builder.lld_flags(target));
|
||||
targetflags.extend(linker_flags(builder, compiler.host, LldThreads::No));
|
||||
for flag in targetflags {
|
||||
cmd.arg("--target-rustcflags").arg(flag);
|
||||
}
|
||||
@ -1931,7 +1932,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
|
||||
//
|
||||
// Note that if we encounter `PATH` we make sure to append to our own `PATH`
|
||||
// rather than stomp over it.
|
||||
if !builder.config.dry_run() && target.contains("msvc") {
|
||||
if !builder.config.dry_run() && target.is_msvc() {
|
||||
for &(ref k, ref v) in builder.cc.borrow()[&target].env() {
|
||||
if k != "PATH" {
|
||||
cmd.env(k, v);
|
||||
@ -3070,7 +3071,7 @@ impl Step for TestHelpers {
|
||||
// We may have found various cross-compilers a little differently due to our
|
||||
// extra configuration, so inform cc of these compilers. Note, though, that
|
||||
// on MSVC we still need cc's detection of env vars (ugh).
|
||||
if !target.contains("msvc") {
|
||||
if !target.is_msvc() {
|
||||
if let Some(ar) = builder.ar(target) {
|
||||
cfg.archiver(ar);
|
||||
}
|
||||
|
@ -833,7 +833,7 @@ impl<'a> Builder<'a> {
|
||||
// On MSVC a tool may invoke a C compiler (e.g., compiletest in run-make
|
||||
// mode) and that C compiler may need some extra PATH modification. Do
|
||||
// so here.
|
||||
if compiler.host.contains("msvc") {
|
||||
if compiler.host.is_msvc() {
|
||||
let curpaths = env::var_os("PATH").unwrap_or_default();
|
||||
let curpaths = env::split_paths(&curpaths).collect::<Vec<_>>();
|
||||
for &(ref k, ref v) in self.cc.borrow()[&compiler.host].env() {
|
||||
|
@ -19,8 +19,8 @@ use crate::core::build_steps::{check, clean, compile, dist, doc, install, run, s
|
||||
use crate::core::config::flags::{Color, Subcommand};
|
||||
use crate::core::config::{DryRun, SplitDebuginfo, TargetSelection};
|
||||
use crate::utils::cache::{Cache, Interned, INTERNER};
|
||||
use crate::utils::helpers::{self, add_dylib_path, add_link_lib_path, add_rustdoc_lld_flags, exe};
|
||||
use crate::utils::helpers::{libdir, output, t, LldThreads};
|
||||
use crate::utils::helpers::{self, add_dylib_path, add_link_lib_path, exe, linker_args};
|
||||
use crate::utils::helpers::{libdir, linker_flags, output, t, LldThreads};
|
||||
use crate::Crate;
|
||||
use crate::EXTRA_CHECK_CFGS;
|
||||
use crate::{Build, CLang, DocTests, GitRepo, Mode};
|
||||
@ -1174,7 +1174,7 @@ impl<'a> Builder<'a> {
|
||||
cmd.env_remove("MAKEFLAGS");
|
||||
cmd.env_remove("MFLAGS");
|
||||
|
||||
add_rustdoc_lld_flags(&mut cmd, self, compiler.host, LldThreads::Yes);
|
||||
cmd.args(linker_args(self, compiler.host, LldThreads::Yes));
|
||||
cmd
|
||||
}
|
||||
|
||||
@ -1673,23 +1673,22 @@ impl<'a> Builder<'a> {
|
||||
rustflags.arg(&format!("-Zstack-protector={stack_protector}"));
|
||||
}
|
||||
|
||||
if let Some(host_linker) = self.linker(compiler.host) {
|
||||
hostflags.arg(format!("-Clinker={}", host_linker.display()));
|
||||
}
|
||||
if self.is_fuse_ld_lld(compiler.host) {
|
||||
hostflags.arg("-Clink-args=-fuse-ld=lld");
|
||||
for arg in linker_args(self, compiler.host, LldThreads::Yes) {
|
||||
hostflags.arg(&arg);
|
||||
}
|
||||
|
||||
if let Some(target_linker) = self.linker(target) {
|
||||
let target = crate::envify(&target.triple);
|
||||
cargo.env(&format!("CARGO_TARGET_{target}_LINKER"), target_linker);
|
||||
}
|
||||
if self.is_fuse_ld_lld(target) {
|
||||
rustflags.arg("-Clink-args=-fuse-ld=lld");
|
||||
// We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not
|
||||
// `linker_args` here.
|
||||
for flag in linker_flags(self, target, LldThreads::Yes) {
|
||||
rustflags.arg(&flag);
|
||||
}
|
||||
for arg in linker_args(self, target, LldThreads::Yes) {
|
||||
rustdocflags.arg(&arg);
|
||||
}
|
||||
self.lld_flags(target).for_each(|flag| {
|
||||
rustdocflags.arg(&flag);
|
||||
});
|
||||
|
||||
if !(["build", "check", "clippy", "fix", "rustc"].contains(&cmd)) && want_rustdoc {
|
||||
cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));
|
||||
@ -1729,8 +1728,7 @@ impl<'a> Builder<'a> {
|
||||
|
||||
let split_debuginfo_is_stable = target.contains("linux")
|
||||
|| target.contains("apple")
|
||||
|| (target.contains("msvc")
|
||||
&& self.config.rust_split_debuginfo == SplitDebuginfo::Packed)
|
||||
|| (target.is_msvc() && self.config.rust_split_debuginfo == SplitDebuginfo::Packed)
|
||||
|| (target.contains("windows")
|
||||
&& self.config.rust_split_debuginfo == SplitDebuginfo::Off);
|
||||
|
||||
@ -1909,7 +1907,7 @@ impl<'a> Builder<'a> {
|
||||
// the options through environment variables that are fetched and understood by both.
|
||||
//
|
||||
// FIXME: the guard against msvc shouldn't need to be here
|
||||
if target.contains("msvc") {
|
||||
if target.is_msvc() {
|
||||
if let Some(ref cl) = self.config.llvm_clang_cl {
|
||||
cargo.env("CC", cl).env("CXX", cl);
|
||||
}
|
||||
|
@ -105,6 +105,39 @@ impl Display for DebuginfoLevel {
|
||||
}
|
||||
}
|
||||
|
||||
/// LLD in bootstrap works like this:
|
||||
/// - Self-contained lld: use `rust-lld` from the compiler's sysroot
|
||||
/// - External: use an external `lld` binary
|
||||
///
|
||||
/// It is configured depending on the target:
|
||||
/// 1) Everything except MSVC
|
||||
/// - Self-contained: `-Clinker-flavor=gnu-lld-cc -Clink-self-contained=+linker`
|
||||
/// - External: `-Clinker-flavor=gnu-lld-cc`
|
||||
/// 2) MSVC
|
||||
/// - Self-contained: `-Clinker=<path to rust-lld>`
|
||||
/// - External: `-Clinker=lld`
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub enum LldMode {
|
||||
/// Do not use LLD
|
||||
#[default]
|
||||
Unused,
|
||||
/// Use `rust-lld` from the compiler's sysroot
|
||||
SelfContained,
|
||||
/// Use an externally provided `lld` binary.
|
||||
/// Note that the linker name cannot be overridden, the binary has to be named `lld` and it has
|
||||
/// to be in $PATH.
|
||||
External,
|
||||
}
|
||||
|
||||
impl LldMode {
|
||||
pub fn is_used(&self) -> bool {
|
||||
match self {
|
||||
LldMode::SelfContained | LldMode::External => true,
|
||||
LldMode::Unused => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Global configuration for the entire build and/or bootstrap.
|
||||
///
|
||||
/// This structure is parsed from `config.toml`, and some of the fields are inferred from `git` or build-time parameters.
|
||||
@ -199,7 +232,7 @@ pub struct Config {
|
||||
pub llvm_from_ci: bool,
|
||||
pub llvm_build_config: HashMap<String, String>,
|
||||
|
||||
pub use_lld: bool,
|
||||
pub lld_mode: LldMode,
|
||||
pub lld_enabled: bool,
|
||||
pub llvm_tools_enabled: bool,
|
||||
|
||||
@ -489,6 +522,10 @@ impl TargetSelection {
|
||||
pub fn is_synthetic(&self) -> bool {
|
||||
self.synthetic
|
||||
}
|
||||
|
||||
pub fn is_msvc(&self) -> bool {
|
||||
self.contains("msvc")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetSelection {
|
||||
@ -977,6 +1014,44 @@ enum StringOrInt<'a> {
|
||||
String(&'a str),
|
||||
Int(i64),
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for LldMode {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct LldModeVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for LldModeVisitor {
|
||||
type Value = LldMode;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("one of true, 'self-contained' or 'external'")
|
||||
}
|
||||
|
||||
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(if v { LldMode::External } else { LldMode::Unused })
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
match v {
|
||||
"external" => Ok(LldMode::External),
|
||||
"self-contained" => Ok(LldMode::SelfContained),
|
||||
_ => Err(E::custom("unknown mode {v}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(LldModeVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
define_config! {
|
||||
/// TOML representation of how the Rust build is configured.
|
||||
struct Rust {
|
||||
@ -1014,7 +1089,7 @@ define_config! {
|
||||
save_toolstates: Option<String> = "save-toolstates",
|
||||
codegen_backends: Option<Vec<String>> = "codegen-backends",
|
||||
lld: Option<bool> = "lld",
|
||||
use_lld: Option<bool> = "use-lld",
|
||||
lld_mode: Option<LldMode> = "use-lld",
|
||||
llvm_tools: Option<bool> = "llvm-tools",
|
||||
deny_warnings: Option<bool> = "deny-warnings",
|
||||
backtrace_on_ice: Option<bool> = "backtrace-on-ice",
|
||||
@ -1442,8 +1517,18 @@ impl Config {
|
||||
if let Some(true) = rust.incremental {
|
||||
config.incremental = true;
|
||||
}
|
||||
set(&mut config.use_lld, rust.use_lld);
|
||||
set(&mut config.lld_mode, rust.lld_mode);
|
||||
set(&mut config.lld_enabled, rust.lld);
|
||||
|
||||
if matches!(config.lld_mode, LldMode::SelfContained)
|
||||
&& !config.lld_enabled
|
||||
&& flags.stage.unwrap_or(0) > 0
|
||||
{
|
||||
panic!(
|
||||
"Trying to use self-contained lld as a linker, but LLD is not being added to the sysroot. Enable it with rust.lld = true."
|
||||
);
|
||||
}
|
||||
|
||||
set(&mut config.llvm_tools_enabled, rust.llvm_tools);
|
||||
config.rustc_parallel = rust
|
||||
.parallel_compiler
|
||||
|
@ -237,7 +237,7 @@ than building it.
|
||||
}
|
||||
}
|
||||
|
||||
if need_cmake && target.contains("msvc") {
|
||||
if need_cmake && target.is_msvc() {
|
||||
// There are three builds of cmake on windows: MSVC, MinGW, and
|
||||
// Cygwin. The Cygwin build does not have generators for Visual
|
||||
// Studio, so detect that here and error.
|
||||
|
@ -37,7 +37,7 @@ use utils::channel::GitInfo;
|
||||
|
||||
use crate::core::builder;
|
||||
use crate::core::builder::Kind;
|
||||
use crate::core::config::flags;
|
||||
use crate::core::config::{flags, LldMode};
|
||||
use crate::core::config::{DryRun, Target};
|
||||
use crate::core::config::{LlvmLibunwind, TargetSelection};
|
||||
use crate::utils::cache::{Interned, INTERNER};
|
||||
@ -862,7 +862,7 @@ impl Build {
|
||||
}
|
||||
} else {
|
||||
let base = self.llvm_out(target).join("build");
|
||||
let base = if !self.ninja() && target.contains("msvc") {
|
||||
let base = if !self.ninja() && target.is_msvc() {
|
||||
if self.config.llvm_optimize {
|
||||
if self.config.llvm_release_debuginfo {
|
||||
base.join("RelWithDebInfo")
|
||||
@ -1255,35 +1255,27 @@ impl Build {
|
||||
Some(self.cxx.borrow()[&target].path().into())
|
||||
} else if target != self.config.build
|
||||
&& helpers::use_host_linker(target)
|
||||
&& !target.contains("msvc")
|
||||
&& !target.is_msvc()
|
||||
{
|
||||
Some(self.cc(target))
|
||||
} else if self.config.use_lld && !self.is_fuse_ld_lld(target) && self.build == target {
|
||||
Some(self.initial_lld.clone())
|
||||
} else if self.config.lld_mode.is_used()
|
||||
&& self.is_lld_direct_linker(target)
|
||||
&& self.build == target
|
||||
{
|
||||
match self.config.lld_mode {
|
||||
LldMode::SelfContained => Some(self.initial_lld.clone()),
|
||||
LldMode::External => Some("lld".into()),
|
||||
LldMode::Unused => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// LLD is used through `-fuse-ld=lld` rather than directly.
|
||||
// Is LLD configured directly through `-Clinker`?
|
||||
// Only MSVC targets use LLD directly at the moment.
|
||||
fn is_fuse_ld_lld(&self, target: TargetSelection) -> bool {
|
||||
self.config.use_lld && !target.contains("msvc")
|
||||
}
|
||||
|
||||
fn lld_flags(&self, target: TargetSelection) -> impl Iterator<Item = String> {
|
||||
let mut options = [None, None];
|
||||
|
||||
if self.config.use_lld {
|
||||
if self.is_fuse_ld_lld(target) {
|
||||
options[0] = Some("-Clink-arg=-fuse-ld=lld".to_string());
|
||||
}
|
||||
|
||||
let no_threads = helpers::lld_flag_no_threads(target.contains("windows"));
|
||||
options[1] = Some(format!("-Clink-arg=-Wl,{no_threads}"));
|
||||
}
|
||||
|
||||
IntoIterator::into_iter(options).flatten()
|
||||
fn is_lld_direct_linker(&self, target: TargetSelection) -> bool {
|
||||
target.is_msvc()
|
||||
}
|
||||
|
||||
/// Returns if this target should statically link the C runtime, if specified
|
||||
@ -1764,7 +1756,7 @@ to download LLVM rather than building it.
|
||||
// In these cases we automatically enable Ninja if we find it in the
|
||||
// environment.
|
||||
if !self.config.ninja_in_file
|
||||
&& self.config.build.contains("msvc")
|
||||
&& self.config.build.is_msvc()
|
||||
&& cmd_finder.maybe_have("ninja").is_some()
|
||||
{
|
||||
return true;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::core::config::TomlConfig;
|
||||
use super::{Config, Flags};
|
||||
use crate::core::config::{LldMode, TomlConfig};
|
||||
|
||||
use clap::CommandFactory;
|
||||
use serde::Deserialize;
|
||||
@ -217,3 +217,12 @@ fn verify_file_integrity() {
|
||||
|
||||
remove_file(tempfile).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rust_lld() {
|
||||
assert!(matches!(parse("").lld_mode, LldMode::Unused));
|
||||
assert!(matches!(parse("rust.use-lld = \"self-contained\"").lld_mode, LldMode::SelfContained));
|
||||
assert!(matches!(parse("rust.use-lld = \"external\"").lld_mode, LldMode::External));
|
||||
assert!(matches!(parse("rust.use-lld = true").lld_mode, LldMode::External));
|
||||
assert!(matches!(parse("rust.use-lld = false").lld_mode, LldMode::Unused));
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ fn cc2ar(cc: &Path, target: TargetSelection) -> Option<PathBuf> {
|
||||
Some(PathBuf::from(ar))
|
||||
} else if let Some(ar) = env::var_os("AR") {
|
||||
Some(PathBuf::from(ar))
|
||||
} else if target.contains("msvc") {
|
||||
} else if target.is_msvc() {
|
||||
None
|
||||
} else if target.contains("musl") {
|
||||
Some(PathBuf::from("ar"))
|
||||
@ -78,7 +78,7 @@ fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build {
|
||||
cfg.static_crt(a);
|
||||
}
|
||||
None => {
|
||||
if target.contains("msvc") {
|
||||
if target.is_msvc() {
|
||||
cfg.static_crt(true);
|
||||
}
|
||||
if target.contains("musl") {
|
||||
|
@ -86,4 +86,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
|
||||
severity: ChangeSeverity::Info,
|
||||
summary: "Use of the `if-available` value for `download-ci-llvm` is deprecated; prefer using the new `if-unchanged` value.",
|
||||
},
|
||||
ChangeInfo {
|
||||
change_id: 116278,
|
||||
severity: ChangeSeverity::Info,
|
||||
summary: "The `rust.use-lld` configuration now has different options ('external'/true or 'self-contained'), and its behaviour has changed.",
|
||||
},
|
||||
];
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
use build_helper::util::fail;
|
||||
use std::env;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
@ -15,7 +15,7 @@ use std::sync::OnceLock;
|
||||
use std::time::{Instant, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::core::builder::Builder;
|
||||
use crate::core::config::{Config, TargetSelection};
|
||||
use crate::core::config::{Config, LldMode, TargetSelection};
|
||||
|
||||
pub use crate::utils::dylib::{dylib_path, dylib_path_var};
|
||||
|
||||
@ -378,6 +378,7 @@ fn absolute_unix(path: &Path) -> io::Result<PathBuf> {
|
||||
|
||||
#[cfg(windows)]
|
||||
fn absolute_windows(path: &std::path::Path) -> std::io::Result<std::path::PathBuf> {
|
||||
use std::ffi::OsString;
|
||||
use std::io::Error;
|
||||
use std::os::windows::ffi::{OsStrExt, OsStringExt};
|
||||
use std::ptr::null_mut;
|
||||
@ -443,17 +444,29 @@ pub fn get_clang_cl_resource_dir(clang_cl_path: &str) -> PathBuf {
|
||||
clang_rt_dir.to_path_buf()
|
||||
}
|
||||
|
||||
pub fn lld_flag_no_threads(is_windows: bool) -> &'static str {
|
||||
/// Returns a flag that configures LLD to use only a single thread.
|
||||
/// If we use an external LLD, we need to find out which version is it to know which flag should we
|
||||
/// pass to it (LLD older than version 10 had a different flag).
|
||||
fn lld_flag_no_threads(lld_mode: LldMode, is_windows: bool) -> &'static str {
|
||||
static LLD_NO_THREADS: OnceLock<(&'static str, &'static str)> = OnceLock::new();
|
||||
let (windows, other) = LLD_NO_THREADS.get_or_init(|| {
|
||||
let out = output(Command::new("lld").arg("-flavor").arg("ld").arg("--version"));
|
||||
let newer = match (out.find(char::is_numeric), out.find('.')) {
|
||||
(Some(b), Some(e)) => out.as_str()[b..e].parse::<i32>().ok().unwrap_or(14) > 10,
|
||||
|
||||
let new_flags = ("/threads:1", "--threads=1");
|
||||
let old_flags = ("/no-threads", "--no-threads");
|
||||
|
||||
let (windows_flag, other_flag) = LLD_NO_THREADS.get_or_init(|| {
|
||||
let newer_version = match lld_mode {
|
||||
LldMode::External => {
|
||||
let out = output(Command::new("lld").arg("-flavor").arg("ld").arg("--version"));
|
||||
match (out.find(char::is_numeric), out.find('.')) {
|
||||
(Some(b), Some(e)) => out.as_str()[b..e].parse::<i32>().ok().unwrap_or(14) > 10,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
if newer { ("/threads:1", "--threads=1") } else { ("/no-threads", "--no-threads") }
|
||||
if newer_version { new_flags } else { old_flags }
|
||||
});
|
||||
if is_windows { windows } else { other }
|
||||
if is_windows { windows_flag } else { other_flag }
|
||||
}
|
||||
|
||||
pub fn dir_is_empty(dir: &Path) -> bool {
|
||||
@ -476,22 +489,58 @@ pub enum LldThreads {
|
||||
No,
|
||||
}
|
||||
|
||||
pub fn add_rustdoc_lld_flags(
|
||||
cmd: &mut Command,
|
||||
/// Returns the linker arguments for rustc/rustdoc for the given builder and target.
|
||||
pub fn linker_args(
|
||||
builder: &Builder<'_>,
|
||||
target: TargetSelection,
|
||||
lld_threads: LldThreads,
|
||||
) {
|
||||
cmd.args(build_rustdoc_lld_flags(builder, target, lld_threads));
|
||||
) -> Vec<String> {
|
||||
let mut args = linker_flags(builder, target, lld_threads);
|
||||
|
||||
if let Some(linker) = builder.linker(target) {
|
||||
args.push(format!("-Clinker={}", linker.display()));
|
||||
}
|
||||
|
||||
args
|
||||
}
|
||||
|
||||
pub fn add_rustdoc_cargo_lld_flags(
|
||||
/// Returns the linker arguments for rustc/rustdoc for the given builder and target, without the
|
||||
/// -Clinker flag.
|
||||
pub fn linker_flags(
|
||||
builder: &Builder<'_>,
|
||||
target: TargetSelection,
|
||||
lld_threads: LldThreads,
|
||||
) -> Vec<String> {
|
||||
let mut args = vec![];
|
||||
if !builder.is_lld_direct_linker(target) && builder.config.lld_mode.is_used() {
|
||||
match builder.config.lld_mode {
|
||||
LldMode::External => {
|
||||
args.push("-Clinker-flavor=gnu-lld-cc".to_string());
|
||||
}
|
||||
LldMode::SelfContained => {
|
||||
args.push("-Clinker-flavor=gnu-lld-cc".to_string());
|
||||
args.push("-Clink-self-contained=+linker".to_string());
|
||||
}
|
||||
LldMode::Unused => {}
|
||||
}
|
||||
|
||||
if matches!(lld_threads, LldThreads::No) {
|
||||
args.push(format!(
|
||||
"-Clink-arg=-Wl,{}",
|
||||
lld_flag_no_threads(builder.config.lld_mode, target.is_msvc())
|
||||
));
|
||||
}
|
||||
}
|
||||
args
|
||||
}
|
||||
|
||||
pub fn add_rustdoc_cargo_linker_args(
|
||||
cmd: &mut Command,
|
||||
builder: &Builder<'_>,
|
||||
target: TargetSelection,
|
||||
lld_threads: LldThreads,
|
||||
) {
|
||||
let args = build_rustdoc_lld_flags(builder, target, lld_threads);
|
||||
let args = linker_args(builder, target, lld_threads);
|
||||
let mut flags = cmd
|
||||
.get_envs()
|
||||
.find_map(|(k, v)| if k == OsStr::new("RUSTDOCFLAGS") { v } else { None })
|
||||
@ -507,27 +556,3 @@ pub fn add_rustdoc_cargo_lld_flags(
|
||||
cmd.env("RUSTDOCFLAGS", flags);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_rustdoc_lld_flags(
|
||||
builder: &Builder<'_>,
|
||||
target: TargetSelection,
|
||||
lld_threads: LldThreads,
|
||||
) -> Vec<OsString> {
|
||||
let mut args = vec![];
|
||||
|
||||
if let Some(linker) = builder.linker(target) {
|
||||
let mut flag = std::ffi::OsString::from("-Clinker=");
|
||||
flag.push(linker);
|
||||
args.push(flag);
|
||||
}
|
||||
if builder.is_fuse_ld_lld(target) {
|
||||
args.push(OsString::from("-Clink-arg=-fuse-ld=lld"));
|
||||
if matches!(lld_threads, LldThreads::No) {
|
||||
args.push(OsString::from(format!(
|
||||
"-Clink-arg=-Wl,{}",
|
||||
lld_flag_no_threads(target.contains("windows"))
|
||||
)));
|
||||
}
|
||||
}
|
||||
args
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user