Skip to content

Commit e75ca1b

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

File tree

7 files changed

+185
-9
lines changed

7 files changed

+185
-9
lines changed

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

+13-3
Original file line numberDiff line numberDiff line change
@@ -613,9 +613,19 @@ impl TargetInfo {
613613
}
614614
}
615615
}
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());
616+
if !result.is_empty() {
617+
if !crate_types.iter().any(|ct| ct.requires_upstream_objects()) {
618+
// Only add rmeta if pipelining...
619+
result.push(FileType::new_rmeta());
620+
} else if crate_types
621+
.iter()
622+
.any(|ct| ct.benefits_from_no_embed_metadata())
623+
{
624+
// ...or when we apply -Zembed-metadata=no to the unit.
625+
// TODO: should we thread through the information if we use
626+
// embed-metadata?
627+
result.push(FileType::new_rmeta());
628+
}
619629
}
620630
Ok((result, unsupported))
621631
}

src/cargo/core/compiler/crate_type.rs

+32
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

+32-6
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

+7
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

+2
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

+11
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 {

tests/testsuite/build.rs

+88
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+
}

0 commit comments

Comments
 (0)