Skip to content

Add support for -Zembed-metadata #15378

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/cargo/core/compiler/build_context/target_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,9 +613,19 @@ impl TargetInfo {
}
}
}
if !result.is_empty() && !crate_types.iter().any(|ct| ct.requires_upstream_objects()) {
// Only add rmeta if pipelining.
result.push(FileType::new_rmeta());
if !result.is_empty() {
if !crate_types.iter().any(|ct| ct.requires_upstream_objects()) {
// Only add rmeta if pipelining...
result.push(FileType::new_rmeta());
} else if crate_types
.iter()
.any(|ct| ct.benefits_from_split_metadata())
{
// ...or when we apply -Zembed-metadata=no to the unit.
// TODO: should we thread through the information if we use
// embed-metadata?
result.push(FileType::new_rmeta());
}
}
Ok((result, unsupported))
}
Expand Down
32 changes: 32 additions & 0 deletions src/cargo/core/compiler/crate_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,38 @@ impl CrateType {
// Everything else, however, is some form of "linkable output" or
// something that requires upstream object files.
}

/// Returns whether production of this crate type could benefit from splitting metadata
/// into a .rmeta file.
///
/// See also [`TargetKind::benefits_from_split_metadata`].
///
/// [`TargetKind::benefits_from_split_metadata`]: crate::core::manifest::TargetKind::benefits_from_split_metadata
pub fn benefits_from_split_metadata(&self) -> bool {
match self {
// rlib/libs generate .rmeta files for pipelined compilation.
// If we also include metadata inside of them, we waste disk space, since the metadata
// will be located both in the lib/rlib and the .rmeta file.
CrateType::Lib |
CrateType::Rlib |
// Dylibs do not have to contain metadata when they are used as a runtime dependency.
// If we split the metadata into a separate .rmeta file, the dylib file (that
// can be shipped as a runtime dependency) can be smaller.
CrateType::Dylib => true,
// Proc macros contains metadata that specifies what macro functions are available in
// it, but the metadata is typically very small. The metadata of proc macros is also
// self-contained (unlike rlibs/dylibs), so let's not unnecessarily split it into
// multiple files.
CrateType::ProcMacro |
// cdylib and staticlib produce artifacts that are used through the C ABI and do not
// contain Rust-specific metadata.
CrateType::Cdylib |
CrateType::Staticlib |
// Binaries also do not contain metadata
CrateType::Bin |
CrateType::Other(_) => false
}
}
}

impl fmt::Display for CrateType {
Expand Down
44 changes: 38 additions & 6 deletions src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1102,15 +1102,34 @@ fn build_base_args(
}
}

let use_embed_metadata = use_embed_metadata(build_runner);
if unit.mode.is_check() {
cmd.arg("--emit=dep-info,metadata");
} else if !unit.requires_upstream_objects() {
// Always produce metadata files for rlib outputs. Metadata may be used
// in this session for a pipelined compilation, or it may be used in a
// future Cargo session as part of a pipelined compile.
cmd.arg("--emit=dep-info,metadata,link");
} else if use_embed_metadata {
// Nightly rustc supports the -Zembed-metadata=no flag, which tells it to avoid including
// full metadata in rlib/dylib artifacts, to save space on disk. In this case, metadata
// will only be stored in .rmeta files.
// When we use this flag, we should also pass --emit=metadata to all artifacts that
// contain useful metadata (rlib/dylib/proc macros), so that a .rmeta file is actually
// generated. If we didn't do this, the full metadata would not get written anywhere.
// However, we do not want to pass --emit=metadata to artifacts that never produce useful
// metadata, such as binaries, because that would just unnecessarily create empty .rmeta
// files on disk.
if unit.benefits_from_split_metadata() {
cmd.arg("--emit=dep-info,metadata,link");
cmd.args(&["-Z", "embed-metadata=no"]);
} else {
cmd.arg("--emit=dep-info,link");
}
} else {
cmd.arg("--emit=dep-info,link");
// If we don't use -Zembed-metadata=no, we emit .rmeta files only for rlib outputs.
// This metadata may be used in this session for a pipelined compilation, or it may
// be used in a future Cargo session as part of a pipelined compile.
if !unit.requires_upstream_objects() {
cmd.arg("--emit=dep-info,metadata,link");
} else {
cmd.arg("--emit=dep-info,link");
}
}

let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
Expand Down Expand Up @@ -1609,6 +1628,8 @@ pub fn extern_args(
let mut result = Vec::new();
let deps = build_runner.unit_deps(unit);

let use_embed_metadata = use_embed_metadata(build_runner);

// Closure to add one dependency to `result`.
let mut link_to =
|dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
Expand Down Expand Up @@ -1658,6 +1679,12 @@ pub fn extern_args(
if output.flavor == FileFlavor::Linkable {
pass(&output.path);
}
// If we use -Zembed-metadata=no, we also need to pass the path to the
// corresponding .rmeta file to the linkable artifact, because the
// normal dependency (rlib) doesn't contain the full metadata.
else if use_embed_metadata && output.flavor == FileFlavor::Rmeta {
pass(&output.path);
}
}
}
Ok(())
Expand All @@ -1684,6 +1711,11 @@ fn envify(s: &str) -> String {
.collect()
}

/// Returns true if -Zembed-metadata=no mode should be used when compiling Rust artifacts.
fn use_embed_metadata(build_runner: &BuildRunner<'_, '_>) -> bool {
build_runner.bcx.gctx.nightly_features_allowed
}

/// Configuration of the display of messages emitted by the compiler,
/// e.g. diagnostics, warnings, errors, and message caching.
struct OutputOptions {
Expand Down
6 changes: 6 additions & 0 deletions src/cargo/core/compiler/unit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ impl UnitInner {
self.mode.is_any_test() || self.target.kind().requires_upstream_objects()
}

/// Returns whether compilation of this unit could benefit from splitting metadata
/// into a .rmeta file.
pub fn benefits_from_split_metadata(&self) -> bool {
matches!(self.mode, CompileMode::Build) && self.target.kind().benefits_from_split_metadata()
}

/// Returns whether or not this is a "local" package.
///
/// A "local" package is one that the user can likely edit, or otherwise
Expand Down
11 changes: 11 additions & 0 deletions src/cargo/core/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,17 @@ impl TargetKind {
}
}

/// Returns whether production of this artifact could benefit from splitting metadata
/// into a .rmeta file.
pub fn benefits_from_split_metadata(&self) -> bool {
match self {
TargetKind::Lib(kinds) | TargetKind::ExampleLib(kinds) => {
kinds.iter().any(|k| k.benefits_from_split_metadata())
}
_ => false,
}
}

/// Returns the arguments suitable for `--crate-type` to pass to rustc.
pub fn rustc_crate_types(&self) -> Vec<CrateType> {
match self {
Expand Down
88 changes: 88 additions & 0 deletions tests/testsuite/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6749,3 +6749,91 @@ fn renamed_uplifted_artifact_remains_unmodified_after_rebuild() {
let not_the_same = !same_file::is_same_file(bin, renamed_bin).unwrap();
assert!(not_the_same, "renamed uplifted artifact must be unmodified");
}

#[cargo_test(nightly, reason = "-Zembed-metadata is nightly only")]
fn embed_metadata() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]

name = "foo"
version = "0.5.0"
edition = "2015"

[dependencies.bar]
path = "bar"
"#,
)
.file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &[]))
.file("bar/Cargo.toml", &basic_lib_manifest("bar"))
.file(
"bar/src/bar.rs",
r#"
pub fn gimme() -> &'static str {
"test passed"
}
"#,
)
.build();

p.cargo("build")
.masquerade_as_nightly_cargo(&["-Zembed-metadata"])
.arg("-v")
.with_stderr_contains("[RUNNING] `[..]-Z embed-metadata=no[..]`")
.with_stderr_contains(
"[RUNNING] `[..]--extern bar=[ROOT]/foo/target/debug/deps/libbar-[HASH].rmeta[..]`",
)
.run();
}

// Make sure that cargo passes --extern=<dep>.rmeta even if <dep>
// is compiled as a dylib.
#[cargo_test(nightly, reason = "-Zembed-metadata is nightly only")]
fn embed_metadata_dylib_dep() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.5.0"
edition = "2015"

[dependencies.bar]
path = "bar"
"#,
)
.file("src/main.rs", &main_file(r#""{}", bar::gimme()"#, &[]))
.file(
"bar/Cargo.toml",
r#"
[package]
name = "bar"
version = "0.5.0"
edition = "2015"

[lib]
crate-type = ["dylib"]
"#,
)
.file(
"bar/src/lib.rs",
r#"
pub fn gimme() -> &'static str {
"test passed"
}
"#,
)
.build();

p.cargo("build")
.masquerade_as_nightly_cargo(&["-Zembed-metadata"])
.arg("-v")
.with_stderr_contains("[RUNNING] `[..]-Z embed-metadata=no[..]`")
.with_stderr_contains(
"[RUNNING] `[..]--extern bar=[ROOT]/foo/target/debug/deps/libbar.rmeta[..]`",
)
.run();
}
Loading