Skip to content

Commit 6833aa7

Browse files
authored
Add [hints] table in Cargo.toml, and a hints.mostly-unused hint (#15673)
The `[hints]` table in a `Cargo.toml` manifest provides optional information that Cargo can use for building the package, and will use even when using the package as a dependency. All hints can be safely ignored, and Cargo only warns about unknown hints, but does not error. This allows packages to use hints without depending on new Cargo. Add a `mostly-unused` hint, which allows a package to hint that most users of the package will not use most of its items. This is useful for improving the build performance of crates with large dependencies. Crates can override this hint using `hint-mostly-unused = false` in their profile for a dependency. --- In the future, this same mechanism could be used for other hints, such as `min-opt-level`. ### How to test and review this PR? This PR is built atop #15643 . I'd suggest reviewing that PR separately, then just reviewing the new commits in this PR. The new "hints" testsuite module demonstrates the expected behavior of hints. Like #15643 , the nightly-only tests will only pass once rust-lang/rust#135656 has been merged into Rust.
2 parents eabb4cd + 94c27df commit 6833aa7

File tree

16 files changed

+474
-18
lines changed

16 files changed

+474
-18
lines changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ cargo-platform = { path = "crates/cargo-platform", version = "0.3.0" }
3434
cargo-test-macro = { version = "0.4.4", path = "crates/cargo-test-macro" }
3535
cargo-test-support = { version = "0.8.0", path = "crates/cargo-test-support" }
3636
cargo-util = { version = "0.2.22", path = "crates/cargo-util" }
37-
cargo-util-schemas = { version = "0.9.0", path = "crates/cargo-util-schemas" }
37+
cargo-util-schemas = { version = "0.10.0", path = "crates/cargo-util-schemas" }
3838
cargo_metadata = "0.20.0"
3939
clap = "4.5.40"
4040
clap_complete = { version = "4.5.54", features = ["unstable-dynamic"] }

crates/cargo-util-schemas/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cargo-util-schemas"
3-
version = "0.9.1"
3+
version = "0.10.0"
44
rust-version = "1.88" # MSRV:1
55
edition.workspace = true
66
license.workspace = true

crates/cargo-util-schemas/manifest.schema.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,16 @@
167167
}
168168
]
169169
},
170+
"hints": {
171+
"anyOf": [
172+
{
173+
"$ref": "#/$defs/Hints"
174+
},
175+
{
176+
"type": "null"
177+
}
178+
]
179+
},
170180
"workspace": {
171181
"anyOf": [
172182
{
@@ -1017,6 +1027,21 @@
10171027
"$ref": "#/$defs/TomlValue"
10181028
}
10191029
},
1030+
"Hints": {
1031+
"type": "object",
1032+
"properties": {
1033+
"mostly-unused": {
1034+
"anyOf": [
1035+
{
1036+
"$ref": "#/$defs/TomlValue"
1037+
},
1038+
{
1039+
"type": "null"
1040+
}
1041+
]
1042+
}
1043+
}
1044+
},
10201045
"TomlWorkspace": {
10211046
"type": "object",
10221047
"properties": {

crates/cargo-util-schemas/src/manifest/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub struct TomlManifest {
5656
pub build_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
5757
pub target: Option<BTreeMap<String, TomlPlatform>>,
5858
pub lints: Option<InheritableLints>,
59+
pub hints: Option<Hints>,
5960

6061
pub workspace: Option<TomlWorkspace>,
6162
pub profile: Option<TomlProfiles>,
@@ -85,6 +86,7 @@ impl TomlManifest {
8586
.map(|_| "build-dependencies"),
8687
self.target.as_ref().map(|_| "target"),
8788
self.lints.as_ref().map(|_| "lints"),
89+
self.hints.as_ref().map(|_| "hints"),
8890
]
8991
.into_iter()
9092
.flatten()
@@ -1644,6 +1646,17 @@ pub enum TomlLintLevel {
16441646
Allow,
16451647
}
16461648

1649+
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
1650+
#[serde(rename_all = "kebab-case")]
1651+
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1652+
pub struct Hints {
1653+
#[cfg_attr(
1654+
feature = "unstable-schema",
1655+
schemars(with = "Option<TomlValueWrapper>")
1656+
)]
1657+
pub mostly_unused: Option<toml::Value>,
1658+
}
1659+
16471660
#[derive(Copy, Clone, Debug)]
16481661
pub struct InvalidCargoFeatures {}
16491662

src/cargo/core/compiler/mod.rs

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,11 +1135,27 @@ fn build_base_args(
11351135
strip,
11361136
rustflags: profile_rustflags,
11371137
trim_paths,
1138-
hint_mostly_unused,
1138+
hint_mostly_unused: profile_hint_mostly_unused,
11391139
..
11401140
} = unit.profile.clone();
1141+
let hints = unit.pkg.hints().cloned().unwrap_or_default();
11411142
let test = unit.mode.is_any_test();
11421143

1144+
let warn = |msg: &str| {
1145+
bcx.gctx.shell().warn(format!(
1146+
"{}@{}: {msg}",
1147+
unit.pkg.package_id().name(),
1148+
unit.pkg.package_id().version()
1149+
))
1150+
};
1151+
let unit_capped_warn = |msg: &str| {
1152+
if unit.show_warnings(bcx.gctx) {
1153+
warn(msg)
1154+
} else {
1155+
Ok(())
1156+
}
1157+
};
1158+
11431159
cmd.arg("--crate-name").arg(&unit.target.crate_name());
11441160

11451161
let edition = unit.target.edition();
@@ -1326,13 +1342,34 @@ fn build_base_args(
13261342
opt(cmd, "-C", "incremental=", Some(dir));
13271343
}
13281344

1329-
if hint_mostly_unused {
1345+
let pkg_hint_mostly_unused = match hints.mostly_unused {
1346+
None => None,
1347+
Some(toml::Value::Boolean(b)) => Some(b),
1348+
Some(v) => {
1349+
unit_capped_warn(&format!(
1350+
"ignoring unsupported value type ({}) for 'hints.mostly-unused', which expects a boolean",
1351+
v.type_str()
1352+
))?;
1353+
None
1354+
}
1355+
};
1356+
if profile_hint_mostly_unused
1357+
.or(pkg_hint_mostly_unused)
1358+
.unwrap_or(false)
1359+
{
13301360
if bcx.gctx.cli_unstable().profile_hint_mostly_unused {
13311361
cmd.arg("-Zhint-mostly-unused");
13321362
} else {
1333-
bcx.gctx
1334-
.shell()
1335-
.warn("ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it")?;
1363+
if profile_hint_mostly_unused.is_some() {
1364+
// Profiles come from the top-level unit, so we don't use `unit_capped_warn` here.
1365+
warn(
1366+
"ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it",
1367+
)?;
1368+
} else if pkg_hint_mostly_unused.is_some() {
1369+
unit_capped_warn(
1370+
"ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it",
1371+
)?;
1372+
}
13361373
}
13371374
}
13381375

src/cargo/core/manifest.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::sync::Arc;
88

99
use anyhow::Context as _;
1010
use cargo_util_schemas::manifest::RustVersion;
11-
use cargo_util_schemas::manifest::{TomlManifest, TomlProfiles};
11+
use cargo_util_schemas::manifest::{Hints, TomlManifest, TomlProfiles};
1212
use semver::Version;
1313
use serde::Serialize;
1414
use serde::ser;
@@ -90,6 +90,7 @@ pub struct Manifest {
9090
metabuild: Option<Vec<String>>,
9191
resolve_behavior: Option<ResolveBehavior>,
9292
lint_rustflags: Vec<String>,
93+
hints: Option<Hints>,
9394
embedded: bool,
9495
}
9596

@@ -521,6 +522,7 @@ impl Manifest {
521522
metabuild: Option<Vec<String>>,
522523
resolve_behavior: Option<ResolveBehavior>,
523524
lint_rustflags: Vec<String>,
525+
hints: Option<Hints>,
524526
embedded: bool,
525527
) -> Manifest {
526528
Manifest {
@@ -551,6 +553,7 @@ impl Manifest {
551553
metabuild,
552554
resolve_behavior,
553555
lint_rustflags,
556+
hints,
554557
embedded,
555558
}
556559
}
@@ -668,6 +671,10 @@ impl Manifest {
668671
self.lint_rustflags.as_slice()
669672
}
670673

674+
pub fn hints(&self) -> Option<&Hints> {
675+
self.hints.as_ref()
676+
}
677+
671678
pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Manifest {
672679
Manifest {
673680
summary: self.summary.map_source(to_replace, replace_with),

src/cargo/core/package.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::rc::Rc;
99
use std::time::{Duration, Instant};
1010

1111
use anyhow::Context as _;
12-
use cargo_util_schemas::manifest::RustVersion;
12+
use cargo_util_schemas::manifest::{Hints, RustVersion};
1313
use curl::easy::Easy;
1414
use curl::multi::{EasyHandle, Multi};
1515
use lazycell::LazyCell;
@@ -95,6 +95,8 @@ pub struct SerializedPackage {
9595
metabuild: Option<Vec<String>>,
9696
default_run: Option<String>,
9797
rust_version: Option<RustVersion>,
98+
#[serde(skip_serializing_if = "Option::is_none")]
99+
hints: Option<Hints>,
98100
}
99101

100102
impl Package {
@@ -172,6 +174,11 @@ impl Package {
172174
self.manifest().rust_version()
173175
}
174176

177+
/// Gets the package's hints.
178+
pub fn hints(&self) -> Option<&Hints> {
179+
self.manifest().hints()
180+
}
181+
175182
/// Returns `true` if the package uses a custom build script for any target.
176183
pub fn has_custom_build(&self) -> bool {
177184
self.targets().iter().any(|t| t.is_custom_build())
@@ -241,6 +248,7 @@ impl Package {
241248
publish: self.publish().as_ref().cloned(),
242249
default_run: self.manifest().default_run().map(|s| s.to_owned()),
243250
rust_version: self.rust_version().cloned(),
251+
hints: self.hints().cloned(),
244252
}
245253
}
246254
}

src/cargo/core/profiles.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ fn merge_profile(profile: &mut Profile, toml: &TomlProfile) {
578578
profile.trim_paths = Some(trim_paths.clone());
579579
}
580580
if let Some(hint_mostly_unused) = toml.hint_mostly_unused {
581-
profile.hint_mostly_unused = hint_mostly_unused;
581+
profile.hint_mostly_unused = Some(hint_mostly_unused);
582582
}
583583
profile.strip = match toml.strip {
584584
Some(StringOrBool::Bool(true)) => Strip::Resolved(StripInner::Named("symbols".into())),
@@ -629,8 +629,8 @@ pub struct Profile {
629629
// remove when `-Ztrim-paths` is stablized
630630
#[serde(skip_serializing_if = "Option::is_none")]
631631
pub trim_paths: Option<TomlTrimPaths>,
632-
#[serde(skip_serializing_if = "std::ops::Not::not")]
633-
pub hint_mostly_unused: bool,
632+
#[serde(skip_serializing_if = "Option::is_none")]
633+
pub hint_mostly_unused: Option<bool>,
634634
}
635635

636636
impl Default for Profile {
@@ -652,7 +652,7 @@ impl Default for Profile {
652652
strip: Strip::Deferred(StripInner::None),
653653
rustflags: vec![],
654654
trim_paths: None,
655-
hint_mostly_unused: false,
655+
hint_mostly_unused: None,
656656
}
657657
}
658658
}

src/cargo/sources/registry/index/cache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ impl<'a> SummariesCache<'a> {
204204
let size = self
205205
.versions
206206
.iter()
207-
.map(|(_version, data)| (10 + data.len()))
207+
.map(|(_version, data)| 10 + data.len())
208208
.sum();
209209
let mut contents = Vec::with_capacity(size);
210210
contents.push(CURRENT_CACHE_VERSION);

0 commit comments

Comments
 (0)