Skip to content

Commit 1921d09

Browse files
committed
Add [hints] table in Cargo.toml, and a hints.mostly-unused hint
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.
1 parent f415c11 commit 1921d09

File tree

8 files changed

+81
-20
lines changed

8 files changed

+81
-20
lines changed

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

Lines changed: 21 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,17 @@
10171027
"$ref": "#/$defs/TomlValue"
10181028
}
10191029
},
1030+
"Hints": {
1031+
"type": "object",
1032+
"properties": {
1033+
"mostly-unused": {
1034+
"type": [
1035+
"boolean",
1036+
"null"
1037+
]
1038+
}
1039+
}
1040+
},
10201041
"TomlWorkspace": {
10211042
"type": "object",
10221043
"properties": {

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

Lines changed: 9 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,13 @@ 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+
pub mostly_unused: Option<bool>,
1654+
}
1655+
16471656
#[derive(Copy, Clone, Debug)]
16481657
pub struct InvalidCargoFeatures {}
16491658

src/cargo/core/compiler/mod.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,7 @@ fn build_base_args(
11381138
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

11431144
cmd.arg("--crate-name").arg(&unit.target.crate_name());
@@ -1326,13 +1327,19 @@ fn build_base_args(
13261327
opt(cmd, "-C", "incremental=", Some(dir));
13271328
}
13281329

1329-
if hint_mostly_unused {
1330+
if hint_mostly_unused.or(hints.mostly_unused).unwrap_or(false) {
13301331
if bcx.gctx.cli_unstable().profile_hint_mostly_unused {
13311332
cmd.arg("-Zhint-mostly-unused");
13321333
} else {
1333-
bcx.gctx
1334-
.shell()
1335-
.warn("ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it")?;
1334+
if hint_mostly_unused.is_some() {
1335+
bcx.gctx
1336+
.shell()
1337+
.warn("ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it")?;
1338+
} else if hints.mostly_unused.is_some() {
1339+
bcx.gctx
1340+
.shell()
1341+
.warn("ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it")?;
1342+
}
13361343
}
13371344
}
13381345

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/util/toml/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ fn normalize_toml(
327327
build_dependencies2: None,
328328
target: None,
329329
lints: None,
330+
hints: None,
330331
workspace: original_toml.workspace.clone().or_else(|| {
331332
// Prevent looking for a workspace by `read_manifest_from_str`
332333
is_embedded.then(manifest::TomlWorkspace::default)
@@ -571,6 +572,8 @@ fn normalize_toml(
571572
lints,
572573
});
573574

575+
normalized_toml.hints = original_toml.hints.clone();
576+
574577
normalized_toml.badges = original_toml.badges.clone();
575578
} else {
576579
if let Some(field) = original_toml.requires_package().next() {
@@ -1628,6 +1631,8 @@ pub fn to_real_manifest(
16281631
.unwrap_or(&default),
16291632
)?;
16301633

1634+
let hints = normalized_toml.hints.clone();
1635+
16311636
let metadata = ManifestMetadata {
16321637
description: normalized_package
16331638
.normalized_description()
@@ -1819,6 +1824,7 @@ pub fn to_real_manifest(
18191824
metabuild,
18201825
resolve_behavior,
18211826
rustflags,
1827+
hints,
18221828
is_embedded,
18231829
);
18241830
if manifest
@@ -3080,6 +3086,7 @@ fn prepare_toml_for_publish(
30803086
None => None,
30813087
},
30823088
lints: me.lints.clone(),
3089+
hints: me.hints.clone(),
30833090
workspace: None,
30843091
profile: me.profile.clone(),
30853092
patch: None,

tests/testsuite/hints.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use cargo_test_support::registry::Package;
55
use cargo_test_support::{project, str};
66

77
#[cargo_test]
8-
fn empty_hints_warn() {
8+
fn empty_hints_no_warn() {
99
let p = project()
1010
.file(
1111
"Cargo.toml",
@@ -22,7 +22,6 @@ fn empty_hints_warn() {
2222
.build();
2323
p.cargo("check -v")
2424
.with_stderr_data(str![[r#"
25-
[WARNING] unused manifest key: hints
2625
[CHECKING] foo v0.0.1 ([ROOT]/foo)
2726
[RUNNING] `rustc --crate-name foo [..]`
2827
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
@@ -50,7 +49,7 @@ fn unknown_hints_warn() {
5049
.build();
5150
p.cargo("check -v")
5251
.with_stderr_data(str![[r#"
53-
[WARNING] unused manifest key: hints
52+
[WARNING] unused manifest key: hints.this-is-an-unknown-hint
5453
[CHECKING] foo v0.0.1 ([ROOT]/foo)
5554
[RUNNING] `rustc --crate-name foo [..]`
5655
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
@@ -92,16 +91,19 @@ fn hint_unknown_type_warn() {
9291
.file("src/main.rs", "fn main() {}")
9392
.build();
9493
p.cargo("check -v")
94+
.with_status(101)
9595
.with_stderr_data(str![[r#"
9696
[UPDATING] `dummy-registry` index
9797
[LOCKING] 1 package to latest compatible version
9898
[DOWNLOADING] crates ...
9999
[DOWNLOADED] bar v1.0.0 (registry `dummy-registry`)
100-
[CHECKING] bar v1.0.0
101-
[RUNNING] `rustc --crate-name bar [..]`
102-
[CHECKING] foo v0.0.1 ([ROOT]/foo)
103-
[RUNNING] `rustc --crate-name foo [..]`
104-
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
100+
[ERROR] invalid type: integer `1`, expected a boolean
101+
--> ../home/.cargo/registry/src/-[HASH]/bar-1.0.0/Cargo.toml:8:29
102+
|
103+
8 | mostly-unused = 1
104+
| ^
105+
|
106+
[ERROR] failed to download replaced source registry `crates-io`
105107
106108
"#]])
107109
.with_stderr_does_not_contain("-Zhint-mostly-unused")
@@ -146,6 +148,7 @@ fn hints_mostly_unused_warn_without_gate() {
146148
[LOCKING] 1 package to latest compatible version
147149
[DOWNLOADING] crates ...
148150
[DOWNLOADED] bar v1.0.0 (registry `dummy-registry`)
151+
[WARNING] ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it
149152
[CHECKING] bar v1.0.0
150153
[RUNNING] `rustc --crate-name bar [..]`
151154
[CHECKING] foo v0.0.1 ([ROOT]/foo)
@@ -197,7 +200,7 @@ fn hints_mostly_unused_nightly() {
197200
[DOWNLOADING] crates ...
198201
[DOWNLOADED] bar v1.0.0 (registry `dummy-registry`)
199202
[CHECKING] bar v1.0.0
200-
[RUNNING] `rustc --crate-name bar [..]`
203+
[RUNNING] `rustc --crate-name bar [..] -Zhint-mostly-unused [..]`
201204
[CHECKING] foo v0.0.1 ([ROOT]/foo)
202205
[RUNNING] `rustc --crate-name foo [..]`
203206
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
@@ -284,7 +287,6 @@ fn mostly_unused_profile_overrides_hints_on_self_nightly() {
284287
.build();
285288
p.cargo("check -v")
286289
.with_stderr_data(str![[r#"
287-
[WARNING] unused manifest key: hints
288290
[CHECKING] foo v0.0.1 ([ROOT]/foo)
289291
[RUNNING] `rustc --crate-name foo [..]`
290292
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s

0 commit comments

Comments
 (0)