Skip to content

Commit d5dfd7a

Browse files
committed
Auto merge of #122362 - Zoxc:rustc_driver_static_std, r=oli-obk,lqd,bjorn3,Kobzol
Link `std` statically in `rustc_driver` This makes `rustc_driver` statically link to `std`. This is done by not passing `-C prefer-dynamic` when building `rustc_driver`. However building `rustc-main` won't work currently as it tries to dynamically link to both `rustc_driver` and `std` resulting in a crate graph with `std` duplicated. To fix that new command line option `-Z prefer_deps_of_dynamic` is added which prevents linking to a dylib if there's a static variant of it already statically linked into another dylib dependency. The main motivation for this change is to enable `#[global_allocator]` to be used in `rustc_driver` allowing overriding the allocator used in rustc on all platforms. --- Instead of adding `-Z prefer_deps_of_dynamic`, this PR is changed to crate opt-in to the linking change via the `rustc_private` feature instead, as that would be typically needed to link to `rustc_driver` anyway. --- try-job: aarch64-apple try-job: x86_64-msvc try-job: i686-mingw try-job: dist-x86_64-msvc try-job: aarch64-gnu
2 parents c9bd03c + b22253c commit d5dfd7a

File tree

12 files changed

+113
-17
lines changed

12 files changed

+113
-17
lines changed

compiler/rustc/src/main.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// We need this feature as it changes `dylib` linking behavior and allows us to link to `rustc_driver`.
2+
#![feature(rustc_private)]
3+
14
// A note about jemalloc: rustc uses jemalloc when built for CI and
25
// distribution. The obvious way to do this is with the `#[global_allocator]`
36
// mechanism. However, for complicated reasons (see

compiler/rustc_metadata/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ metadata_crate_dep_multiple =
4141
metadata_crate_dep_not_static =
4242
`{$crate_name}` was unavailable as a static crate, preventing fully static linking
4343
44+
metadata_crate_dep_rustc_driver =
45+
`feature(rustc_private)` is needed to link to the compiler's `rustc_driver` library
46+
4447
metadata_crate_location_unknown_type =
4548
extern location for {$crate_name} is of an unknown type: {$path}
4649

compiler/rustc_metadata/src/dependency_format.rs

+42-13
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,22 @@
5151
//! Additionally, the algorithm is geared towards finding *any* solution rather
5252
//! than finding a number of solutions (there are normally quite a few).
5353
54-
use rustc_data_structures::fx::FxHashMap;
54+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
5555
use rustc_hir::def_id::CrateNum;
5656
use rustc_middle::bug;
5757
use rustc_middle::middle::dependency_format::{Dependencies, DependencyList, Linkage};
5858
use rustc_middle::ty::TyCtxt;
5959
use rustc_session::config::CrateType;
6060
use rustc_session::cstore::CrateDepKind;
6161
use rustc_session::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic};
62+
use rustc_span::sym;
6263
use tracing::info;
6364

6465
use crate::creader::CStore;
6566
use crate::errors::{
6667
BadPanicStrategy, CrateDepMultiple, IncompatiblePanicInDropStrategy, LibRequired,
67-
NonStaticCrateDep, RequiredPanicStrategy, RlibRequired, RustcLibRequired, TwoPanicRuntimes,
68+
NonStaticCrateDep, RequiredPanicStrategy, RlibRequired, RustcDriverHelp, RustcLibRequired,
69+
TwoPanicRuntimes,
6870
};
6971

7072
pub(crate) fn calculate(tcx: TyCtxt<'_>) -> Dependencies {
@@ -160,25 +162,49 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList {
160162
Linkage::Dynamic | Linkage::IncludedFromDylib => {}
161163
}
162164

165+
let all_dylibs = || {
166+
tcx.crates(()).iter().filter(|&&cnum| {
167+
!tcx.dep_kind(cnum).macros_only() && tcx.used_crate_source(cnum).dylib.is_some()
168+
})
169+
};
170+
171+
let mut upstream_in_dylibs = FxHashSet::default();
172+
173+
if tcx.features().rustc_private {
174+
// We need this to prevent users of `rustc_driver` from linking dynamically to `std`
175+
// which does not work as `std` is also statically linked into `rustc_driver`.
176+
177+
// Find all libraries statically linked to upstream dylibs.
178+
for &cnum in all_dylibs() {
179+
let deps = tcx.dylib_dependency_formats(cnum);
180+
for &(depnum, style) in deps.iter() {
181+
if let RequireStatic = style {
182+
upstream_in_dylibs.insert(depnum);
183+
}
184+
}
185+
}
186+
}
187+
163188
let mut formats = FxHashMap::default();
164189

165190
// Sweep all crates for found dylibs. Add all dylibs, as well as their
166191
// dependencies, ensuring there are no conflicts. The only valid case for a
167192
// dependency to be relied upon twice is for both cases to rely on a dylib.
168-
for &cnum in tcx.crates(()).iter() {
169-
if tcx.dep_kind(cnum).macros_only() {
193+
for &cnum in all_dylibs() {
194+
if upstream_in_dylibs.contains(&cnum) {
195+
info!("skipping dylib: {}", tcx.crate_name(cnum));
196+
// If this dylib is also available statically linked to another dylib
197+
// we try to use that instead.
170198
continue;
171199
}
200+
172201
let name = tcx.crate_name(cnum);
173-
let src = tcx.used_crate_source(cnum);
174-
if src.dylib.is_some() {
175-
info!("adding dylib: {}", name);
176-
add_library(tcx, cnum, RequireDynamic, &mut formats, &mut unavailable_as_static);
177-
let deps = tcx.dylib_dependency_formats(cnum);
178-
for &(depnum, style) in deps.iter() {
179-
info!("adding {:?}: {}", style, tcx.crate_name(depnum));
180-
add_library(tcx, depnum, style, &mut formats, &mut unavailable_as_static);
181-
}
202+
info!("adding dylib: {}", name);
203+
add_library(tcx, cnum, RequireDynamic, &mut formats, &mut unavailable_as_static);
204+
let deps = tcx.dylib_dependency_formats(cnum);
205+
for &(depnum, style) in deps.iter() {
206+
info!("adding {:?}: {}", style, tcx.crate_name(depnum));
207+
add_library(tcx, depnum, style, &mut formats, &mut unavailable_as_static);
182208
}
183209
}
184210

@@ -268,12 +294,15 @@ fn add_library(
268294
// This error is probably a little obscure, but I imagine that it
269295
// can be refined over time.
270296
if link2 != link || link == RequireStatic {
297+
let linking_to_rustc_driver = tcx.sess.psess.unstable_features.is_nightly_build()
298+
&& tcx.crates(()).iter().any(|&cnum| tcx.crate_name(cnum) == sym::rustc_driver);
271299
tcx.dcx().emit_err(CrateDepMultiple {
272300
crate_name: tcx.crate_name(cnum),
273301
non_static_deps: unavailable_as_static
274302
.drain(..)
275303
.map(|cnum| NonStaticCrateDep { crate_name: tcx.crate_name(cnum) })
276304
.collect(),
305+
rustc_driver_help: linking_to_rustc_driver.then_some(RustcDriverHelp),
277306
});
278307
}
279308
}

compiler/rustc_metadata/src/errors.rs

+6
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ pub struct CrateDepMultiple {
3838
pub crate_name: Symbol,
3939
#[subdiagnostic]
4040
pub non_static_deps: Vec<NonStaticCrateDep>,
41+
#[subdiagnostic]
42+
pub rustc_driver_help: Option<RustcDriverHelp>,
4143
}
4244

4345
#[derive(Subdiagnostic)]
@@ -46,6 +48,10 @@ pub struct NonStaticCrateDep {
4648
pub crate_name: Symbol,
4749
}
4850

51+
#[derive(Subdiagnostic)]
52+
#[help(metadata_crate_dep_rustc_driver)]
53+
pub struct RustcDriverHelp;
54+
4955
#[derive(Diagnostic)]
5056
#[diag(metadata_two_panic_runtimes)]
5157
pub struct TwoPanicRuntimes {

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1619,6 +1619,7 @@ symbols! {
16191619
rustc_dirty,
16201620
rustc_do_not_const_check,
16211621
rustc_doc_primitive,
1622+
rustc_driver,
16221623
rustc_dummy,
16231624
rustc_dump_def_parents,
16241625
rustc_dump_item_bounds,

src/bootstrap/src/bin/rustc.rs

+19-3
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,25 @@ fn main() {
8989
rustc_real
9090
};
9191

92+
// Get the name of the crate we're compiling, if any.
93+
let crate_name = parse_value_from_args(&orig_args, "--crate-name");
94+
95+
// When statically linking `std` into `rustc_driver`, remove `-C prefer-dynamic`
96+
if env::var("RUSTC_LINK_STD_INTO_RUSTC_DRIVER").unwrap() == "1"
97+
&& crate_name == Some("rustc_driver")
98+
&& stage != "0"
99+
{
100+
if let Some(pos) = args.iter().enumerate().position(|(i, a)| {
101+
a == "-C" && args.get(i + 1).map(|a| a == "prefer-dynamic").unwrap_or(false)
102+
}) {
103+
args.remove(pos);
104+
args.remove(pos);
105+
}
106+
if let Some(pos) = args.iter().position(|a| a == "-Cprefer-dynamic") {
107+
args.remove(pos);
108+
}
109+
}
110+
92111
let mut cmd = match env::var_os("RUSTC_WRAPPER_REAL") {
93112
Some(wrapper) if !wrapper.is_empty() => {
94113
let mut cmd = Command::new(wrapper);
@@ -99,9 +118,6 @@ fn main() {
99118
};
100119
cmd.args(&args).env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
101120

102-
// Get the name of the crate we're compiling, if any.
103-
let crate_name = parse_value_from_args(&orig_args, "--crate-name");
104-
105121
if let Some(crate_name) = crate_name {
106122
if let Some(target) = env::var_os("RUSTC_TIME") {
107123
if target == "all"

src/bootstrap/src/core/builder.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,12 @@ impl<'a> Builder<'a> {
11061106
StepDescription::run(v, self, paths);
11071107
}
11081108

1109+
/// Returns if `std` should be statically linked into `rustc_driver`.
1110+
/// It's currently not done on `windows-gnu` due to linker bugs.
1111+
pub fn link_std_into_rustc_driver(&self, target: TargetSelection) -> bool {
1112+
!target.triple.ends_with("-windows-gnu")
1113+
}
1114+
11091115
/// Obtain a compiler at a given stage and for a given host (i.e., this is the target that the
11101116
/// compiler will run on, *not* the target it will build code for). Explicitly does not take
11111117
/// `Compiler` since all `Compiler` instances are meant to be obtained through this function,
@@ -2162,10 +2168,18 @@ impl<'a> Builder<'a> {
21622168
// When we build Rust dylibs they're all intended for intermediate
21632169
// usage, so make sure we pass the -Cprefer-dynamic flag instead of
21642170
// linking all deps statically into the dylib.
2165-
if matches!(mode, Mode::Std | Mode::Rustc) {
2171+
if matches!(mode, Mode::Std) {
2172+
rustflags.arg("-Cprefer-dynamic");
2173+
}
2174+
if matches!(mode, Mode::Rustc) && !self.link_std_into_rustc_driver(target) {
21662175
rustflags.arg("-Cprefer-dynamic");
21672176
}
21682177

2178+
cargo.env(
2179+
"RUSTC_LINK_STD_INTO_RUSTC_DRIVER",
2180+
if self.link_std_into_rustc_driver(target) { "1" } else { "0" },
2181+
);
2182+
21692183
// When building incrementally we default to a lower ThinLTO import limit
21702184
// (unless explicitly specified otherwise). This will produce a somewhat
21712185
// slower code but give way better compile times.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# `rustc_private`
2+
3+
The tracking issue for this feature is: [#27812]
4+
5+
[#27812]: https://github.com/rust-lang/rust/issues/27812
6+
7+
------------------------
8+
9+
This feature allows access to unstable internal compiler crates.
10+
11+
Additionally it changes the linking behavior of crates which have this feature enabled. It will prevent linking to a dylib if there's a static variant of it already statically linked into another dylib dependency. This is required to successfully link to `rustc_driver`.

src/tools/clippy/src/main.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// We need this feature as it changes `dylib` linking behavior and allows us to link to
2+
// `rustc_driver`.
3+
#![feature(rustc_private)]
14
// warn on lints, that are included in `rust-lang/rust`s bootstrap
25
#![warn(rust_2018_idioms, unused_lifetimes)]
36

src/tools/clippy/tests/compile-test.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// We need this feature as it changes `dylib` linking behavior and allows us to link to
2+
// `rustc_driver`.
3+
#![feature(rustc_private)]
14
#![warn(rust_2018_idioms, unused_lifetimes)]
25
#![allow(unused_extern_crates)]
36

src/tools/rustdoc/main.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// We need this feature as it changes `dylib` linking behavior and allows us to link to `rustc_driver`.
2+
#![feature(rustc_private)]
3+
14
fn main() {
25
rustdoc::main()
36
}

src/tools/rustfmt/src/git-rustfmt/main.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// We need this feature as it changes `dylib` linking behavior and allows us to link to
2+
// `rustc_driver`.
3+
#![feature(rustc_private)]
4+
15
#[macro_use]
26
extern crate tracing;
37

0 commit comments

Comments
 (0)