Skip to content

Commit af5c9db

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 8b944d4 commit af5c9db

File tree

8 files changed

+258
-13
lines changed

8 files changed

+258
-13
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
{
@@ -998,6 +1008,17 @@
9981008
"$ref": "#/$defs/TomlValue"
9991009
}
10001010
},
1011+
"Hints": {
1012+
"type": "object",
1013+
"properties": {
1014+
"mostly-unused": {
1015+
"type": [
1016+
"boolean",
1017+
"null"
1018+
]
1019+
}
1020+
}
1021+
},
10011022
"TomlWorkspace": {
10021023
"type": "object",
10031024
"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()
@@ -1641,6 +1643,13 @@ pub enum TomlLintLevel {
16411643
Allow,
16421644
}
16431645

1646+
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
1647+
#[serde(rename_all = "kebab-case")]
1648+
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1649+
pub struct Hints {
1650+
pub mostly_unused: Option<bool>,
1651+
}
1652+
16441653
#[derive(Copy, Clone, Debug)]
16451654
pub struct InvalidCargoFeatures {}
16461655

src/cargo/core/compiler/mod.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,6 +1133,7 @@ fn build_base_args(
11331133
hint_mostly_unused,
11341134
..
11351135
} = unit.profile.clone();
1136+
let hints = unit.pkg.hints().cloned().unwrap_or_default();
11361137
let test = unit.mode.is_any_test();
11371138

11381139
cmd.arg("--crate-name").arg(&unit.target.crate_name());
@@ -1321,13 +1322,19 @@ fn build_base_args(
13211322
opt(cmd, "-C", "incremental=", Some(dir));
13221323
}
13231324

1324-
if hint_mostly_unused {
1325+
if hint_mostly_unused.or(hints.mostly_unused).unwrap_or(false) {
13251326
if bcx.gctx.cli_unstable().profile_hint_mostly_unused {
13261327
cmd.arg("-Zhint-mostly-unused");
13271328
} else {
1328-
bcx.gctx
1329-
.shell()
1330-
.warn("ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it")?;
1329+
if hint_mostly_unused.is_some() {
1330+
bcx.gctx
1331+
.shell()
1332+
.warn("ignoring 'hint-mostly-unused' profile option, pass `-Zprofile-hint-mostly-unused` to enable it")?;
1333+
} else if hints.mostly_unused.is_some() {
1334+
bcx.gctx
1335+
.shell()
1336+
.warn("ignoring 'hints.mostly-unused', pass `-Zprofile-hint-mostly-unused` to enable it")?;
1337+
}
13311338
}
13321339
}
13331340

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::ser;
1414
use serde::Serialize;
@@ -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
@@ -316,6 +316,7 @@ fn normalize_toml(
316316
build_dependencies2: None,
317317
target: None,
318318
lints: None,
319+
hints: None,
319320
workspace: original_toml.workspace.clone().or_else(|| {
320321
// Prevent looking for a workspace by `read_manifest_from_str`
321322
is_embedded.then(manifest::TomlWorkspace::default)
@@ -559,6 +560,8 @@ fn normalize_toml(
559560
lints,
560561
});
561562

563+
normalized_toml.hints = original_toml.hints.clone();
564+
562565
normalized_toml.badges = original_toml.badges.clone();
563566
} else {
564567
if let Some(field) = original_toml.requires_package().next() {
@@ -1608,6 +1611,8 @@ pub fn to_real_manifest(
16081611
.unwrap_or(&default),
16091612
)?;
16101613

1614+
let hints = normalized_toml.hints.clone();
1615+
16111616
let metadata = ManifestMetadata {
16121617
description: normalized_package
16131618
.normalized_description()
@@ -1799,6 +1804,7 @@ pub fn to_real_manifest(
17991804
metabuild,
18001805
resolve_behavior,
18011806
rustflags,
1807+
hints,
18021808
is_embedded,
18031809
);
18041810
if manifest
@@ -3050,6 +3056,7 @@ fn prepare_toml_for_publish(
30503056
None => None,
30513057
},
30523058
lints: me.lints.clone(),
3059+
hints: me.hints.clone(),
30533060
workspace: None,
30543061
profile: me.profile.clone(),
30553062
patch: None,

0 commit comments

Comments
 (0)