Skip to content

Commit 8250a90

Browse files
committed
Add -Zno-embed-metadata unstable flag
1 parent 872b92e commit 8250a90

File tree

10 files changed

+214
-34
lines changed

10 files changed

+214
-34
lines changed

src/cargo/core/compiler/build_context/target_info.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -570,9 +570,10 @@ impl TargetInfo {
570570
mode: CompileMode,
571571
target_kind: &TargetKind,
572572
target_triple: &str,
573+
gctx: &GlobalContext,
573574
) -> CargoResult<(Vec<FileType>, Vec<CrateType>)> {
574575
match mode {
575-
CompileMode::Build => self.calc_rustc_outputs(target_kind, target_triple),
576+
CompileMode::Build => self.calc_rustc_outputs(target_kind, target_triple, gctx),
576577
CompileMode::Test | CompileMode::Bench => {
577578
match self.file_types(&CrateType::Bin, FileFlavor::Normal, target_triple)? {
578579
Some(fts) => Ok((fts, Vec::new())),
@@ -593,6 +594,7 @@ impl TargetInfo {
593594
&self,
594595
target_kind: &TargetKind,
595596
target_triple: &str,
597+
gctx: &GlobalContext,
596598
) -> CargoResult<(Vec<FileType>, Vec<CrateType>)> {
597599
let mut unsupported = Vec::new();
598600
let mut result = Vec::new();
@@ -613,9 +615,18 @@ impl TargetInfo {
613615
}
614616
}
615617
}
616-
if !result.is_empty() && !crate_types.iter().any(|ct| ct.requires_upstream_objects()) {
617-
// Only add rmeta if pipelining.
618-
result.push(FileType::new_rmeta());
618+
if !result.is_empty() {
619+
if gctx.cli_unstable().no_embed_metadata
620+
&& crate_types
621+
.iter()
622+
.any(|ct| ct.benefits_from_no_embed_metadata())
623+
{
624+
// Add .rmeta when we apply -Zembed-metadata=no to the unit.
625+
result.push(FileType::new_rmeta());
626+
} else if !crate_types.iter().any(|ct| ct.requires_upstream_objects()) {
627+
// Only add rmeta if pipelining
628+
result.push(FileType::new_rmeta());
629+
}
619630
}
620631
Ok((result, unsupported))
621632
}

src/cargo/core/compiler/build_runner/compilation_files.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
363363
CompileMode::Build,
364364
&TargetKind::Bin,
365365
bcx.target_data.short_name(&kind),
366+
bcx.gctx,
366367
)
367368
.expect("target must support `bin`");
368369

@@ -540,7 +541,7 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
540541
let info = bcx.target_data.info(unit.kind);
541542
let triple = bcx.target_data.short_name(&unit.kind);
542543
let (file_types, unsupported) =
543-
info.rustc_outputs(unit.mode, unit.target.kind(), triple)?;
544+
info.rustc_outputs(unit.mode, unit.target.kind(), triple, bcx.gctx)?;
544545
if file_types.is_empty() {
545546
if !unsupported.is_empty() {
546547
let unsupported_strs: Vec<_> = unsupported.iter().map(|ct| ct.as_str()).collect();

src/cargo/core/compiler/crate_type.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,38 @@ impl CrateType {
7676
// Everything else, however, is some form of "linkable output" or
7777
// something that requires upstream object files.
7878
}
79+
80+
/// Returns whether production of this crate type could benefit from splitting metadata
81+
/// into a .rmeta file.
82+
///
83+
/// See also [`TargetKind::benefits_from_no_embed_metadata`].
84+
///
85+
/// [`TargetKind::benefits_from_no_embed_metadata`]: crate::core::manifest::TargetKind::benefits_from_no_embed_metadata
86+
pub fn benefits_from_no_embed_metadata(&self) -> bool {
87+
match self {
88+
// rlib/libs generate .rmeta files for pipelined compilation.
89+
// If we also include metadata inside of them, we waste disk space, since the metadata
90+
// will be located both in the lib/rlib and the .rmeta file.
91+
CrateType::Lib |
92+
CrateType::Rlib |
93+
// Dylibs do not have to contain metadata when they are used as a runtime dependency.
94+
// If we split the metadata into a separate .rmeta file, the dylib file (that
95+
// can be shipped as a runtime dependency) can be smaller.
96+
CrateType::Dylib => true,
97+
// Proc macros contain metadata that specifies what macro functions are available in
98+
// it, but the metadata is typically very small. The metadata of proc macros is also
99+
// self-contained (unlike rlibs/dylibs), so let's not unnecessarily split it into
100+
// multiple files.
101+
CrateType::ProcMacro |
102+
// cdylib and staticlib produce artifacts that are used through the C ABI and do not
103+
// contain Rust-specific metadata.
104+
CrateType::Cdylib |
105+
CrateType::Staticlib |
106+
// Binaries also do not contain metadata
107+
CrateType::Bin |
108+
CrateType::Other(_) => false
109+
}
110+
}
79111
}
80112

81113
impl fmt::Display for CrateType {

src/cargo/core/compiler/mod.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,13 +1131,31 @@ fn build_base_args(
11311131

11321132
if unit.mode.is_check() {
11331133
cmd.arg("--emit=dep-info,metadata");
1134-
} else if !unit.requires_upstream_objects() {
1135-
// Always produce metadata files for rlib outputs. Metadata may be used
1136-
// in this session for a pipelined compilation, or it may be used in a
1137-
// future Cargo session as part of a pipelined compile.
1138-
cmd.arg("--emit=dep-info,metadata,link");
1134+
} else if build_runner.bcx.gctx.cli_unstable().no_embed_metadata {
1135+
// Nightly rustc supports the -Zembed-metadata=no flag, which tells it to avoid including
1136+
// full metadata in rlib/dylib artifacts, to save space on disk. In this case, metadata
1137+
// will only be stored in .rmeta files.
1138+
// When we use this flag, we should also pass --emit=metadata to all artifacts that
1139+
// contain useful metadata (rlib/dylib/proc macros), so that a .rmeta file is actually
1140+
// generated. If we didn't do this, the full metadata would not get written anywhere.
1141+
// However, we do not want to pass --emit=metadata to artifacts that never produce useful
1142+
// metadata, such as binaries, because that would just unnecessarily create empty .rmeta
1143+
// files on disk.
1144+
if unit.benefits_from_no_embed_metadata() {
1145+
cmd.arg("--emit=dep-info,metadata,link");
1146+
cmd.args(&["-Z", "embed-metadata=no"]);
1147+
} else {
1148+
cmd.arg("--emit=dep-info,link");
1149+
}
11391150
} else {
1140-
cmd.arg("--emit=dep-info,link");
1151+
// If we don't use -Zembed-metadata=no, we emit .rmeta files only for rlib outputs.
1152+
// This metadata may be used in this session for a pipelined compilation, or it may
1153+
// be used in a future Cargo session as part of a pipelined compile.
1154+
if !unit.requires_upstream_objects() {
1155+
cmd.arg("--emit=dep-info,metadata,link");
1156+
} else {
1157+
cmd.arg("--emit=dep-info,link");
1158+
}
11411159
}
11421160

11431161
let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
@@ -1636,6 +1654,8 @@ pub fn extern_args(
16361654
let mut result = Vec::new();
16371655
let deps = build_runner.unit_deps(unit);
16381656

1657+
let no_embed_metadata = build_runner.bcx.gctx.cli_unstable().no_embed_metadata;
1658+
16391659
// Closure to add one dependency to `result`.
16401660
let mut link_to =
16411661
|dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
@@ -1685,6 +1705,12 @@ pub fn extern_args(
16851705
if output.flavor == FileFlavor::Linkable {
16861706
pass(&output.path);
16871707
}
1708+
// If we use -Zembed-metadata=no, we also need to pass the path to the
1709+
// corresponding .rmeta file to the linkable artifact, because the
1710+
// normal dependency (rlib) doesn't contain the full metadata.
1711+
else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1712+
pass(&output.path);
1713+
}
16881714
}
16891715
}
16901716
Ok(())

src/cargo/core/compiler/unit.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ impl UnitInner {
125125
self.mode.is_any_test() || self.target.kind().requires_upstream_objects()
126126
}
127127

128+
/// Returns whether compilation of this unit could benefit from splitting metadata
129+
/// into a .rmeta file.
130+
pub fn benefits_from_no_embed_metadata(&self) -> bool {
131+
matches!(self.mode, CompileMode::Build)
132+
&& self.target.kind().benefits_from_no_embed_metadata()
133+
}
134+
128135
/// Returns whether or not this is a "local" package.
129136
///
130137
/// A "local" package is one that the user can likely edit, or otherwise

src/cargo/core/features.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,7 @@ unstable_cli_options!(
783783
msrv_policy: bool = ("Enable rust-version aware policy within cargo"),
784784
mtime_on_use: bool = ("Configure Cargo to update the mtime of used files"),
785785
next_lockfile_bump: bool,
786+
no_embed_metadata: bool = ("Avoid embedding metadata in library artifacts"),
786787
no_index_update: bool = ("Do not update the registry index even if the cache is outdated"),
787788
package_workspace: bool = ("Handle intra-workspace dependencies when packaging"),
788789
panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"),
@@ -1294,6 +1295,7 @@ impl CliUnstable {
12941295
"msrv-policy" => self.msrv_policy = parse_empty(k, v)?,
12951296
// can also be set in .cargo/config or with and ENV
12961297
"mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?,
1298+
"no-embed-metadata" => self.no_embed_metadata = parse_empty(k, v)?,
12971299
"no-index-update" => self.no_index_update = parse_empty(k, v)?,
12981300
"package-workspace" => self.package_workspace = parse_empty(k, v)?,
12991301
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,

src/cargo/core/manifest.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,17 @@ impl TargetKind {
279279
}
280280
}
281281

282+
/// Returns whether production of this artifact could benefit from splitting metadata
283+
/// into a .rmeta file.
284+
pub fn benefits_from_no_embed_metadata(&self) -> bool {
285+
match self {
286+
TargetKind::Lib(kinds) | TargetKind::ExampleLib(kinds) => {
287+
kinds.iter().any(|k| k.benefits_from_no_embed_metadata())
288+
}
289+
_ => false,
290+
}
291+
}
292+
282293
/// Returns the arguments suitable for `--crate-type` to pass to rustc.
283294
pub fn rustc_crate_types(&self) -> Vec<CrateType> {
284295
match self {

src/cargo/ops/cargo_clean.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ fn clean_specs(
230230

231231
let (file_types, _unsupported) = target_data
232232
.info(*compile_kind)
233-
.rustc_outputs(mode, target.kind(), triple)?;
233+
.rustc_outputs(mode, target.kind(), triple, clean_ctx.gctx)?;
234234
let (dir, uplift_dir) = match target.kind() {
235235
TargetKind::ExampleBin | TargetKind::ExampleLib(..) => {
236236
(layout.build_examples(), Some(layout.examples()))

tests/testsuite/build.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6749,3 +6749,91 @@ fn renamed_uplifted_artifact_remains_unmodified_after_rebuild() {
67496749
let not_the_same = !same_file::is_same_file(bin, renamed_bin).unwrap();
67506750
assert!(not_the_same, "renamed uplifted artifact must be unmodified");
67516751
}
6752+
6753+
#[cargo_test(nightly, reason = "-Zembed-metadata is nightly only")]
6754+
fn embed_metadata() {
6755+
let p = project()
6756+
.file(
6757+
"Cargo.toml",
6758+
r#"
6759+
[package]
6760+
6761+
name = "foo"
6762+
version = "0.5.0"
6763+
edition = "2015"
6764+
6765+
[dependencies.bar]
6766+
path = "bar"
6767+
"#,
6768+
)
6769+
.file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &[]))
6770+
.file("bar/Cargo.toml", &basic_lib_manifest("bar"))
6771+
.file(
6772+
"bar/src/bar.rs",
6773+
r#"
6774+
pub fn gimme() -> &'static str {
6775+
"test passed"
6776+
}
6777+
"#,
6778+
)
6779+
.build();
6780+
6781+
p.cargo("build -Z no-embed-metadata")
6782+
.masquerade_as_nightly_cargo(&["-Z no-embed-metadata"])
6783+
.arg("-v")
6784+
.with_stderr_contains("[RUNNING] `[..]-Z embed-metadata=no[..]`")
6785+
.with_stderr_contains(
6786+
"[RUNNING] `[..]--extern bar=[ROOT]/foo/target/debug/deps/libbar-[HASH].rmeta[..]`",
6787+
)
6788+
.run();
6789+
}
6790+
6791+
// Make sure that cargo passes --extern=<dep>.rmeta even if <dep>
6792+
// is compiled as a dylib.
6793+
#[cargo_test(nightly, reason = "-Zembed-metadata is nightly only")]
6794+
fn embed_metadata_dylib_dep() {
6795+
let p = project()
6796+
.file(
6797+
"Cargo.toml",
6798+
r#"
6799+
[package]
6800+
name = "foo"
6801+
version = "0.5.0"
6802+
edition = "2015"
6803+
6804+
[dependencies.bar]
6805+
path = "bar"
6806+
"#,
6807+
)
6808+
.file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &[]))
6809+
.file(
6810+
"bar/Cargo.toml",
6811+
r#"
6812+
[package]
6813+
name = "bar"
6814+
version = "0.5.0"
6815+
edition = "2015"
6816+
6817+
[lib]
6818+
crate-type = ["dylib"]
6819+
"#,
6820+
)
6821+
.file(
6822+
"bar/src/lib.rs",
6823+
r#"
6824+
pub fn gimme() -> &'static str {
6825+
"test passed"
6826+
}
6827+
"#,
6828+
)
6829+
.build();
6830+
6831+
p.cargo("build -Z no-embed-metadata")
6832+
.masquerade_as_nightly_cargo(&["-Z no-embed-metadata"])
6833+
.arg("-v")
6834+
.with_stderr_contains("[RUNNING] `[..]-Z embed-metadata=no[..]`")
6835+
.with_stderr_contains(
6836+
"[RUNNING] `[..]--extern bar=[ROOT]/foo/target/debug/deps/libbar.rmeta[..]`",
6837+
)
6838+
.run();
6839+
}

tests/testsuite/cargo/z_help/stdout.term.svg

Lines changed: 24 additions & 22 deletions
Loading

0 commit comments

Comments
 (0)