Skip to content

Commit 78c1671

Browse files
committed
Auto merge of #8323 - naerbnic:add_workspace_metadata_table, r=alexcrichton
Add support for `workspace.metadata` table Implements feature request #8309 Additionally includes the information in the output of "cargo metadata" through a new top-level field `metadata`, similar to the per-package `metadata` field
2 parents 10ae887 + 16f3b8d commit 78c1671

File tree

11 files changed

+200
-50
lines changed

11 files changed

+200
-50
lines changed

src/cargo/core/compiler/standard_lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pub fn resolve_std<'cfg>(
6565
&Some(members),
6666
/*default_members*/ &None,
6767
/*exclude*/ &None,
68+
/*custom_metadata*/ &None,
6869
));
6970
let virtual_manifest = crate::core::VirtualManifest::new(
7071
/*replace*/ Vec::new(),

src/cargo/core/workspace.rs

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ pub struct Workspace<'cfg> {
8888

8989
/// The resolver behavior specified with the `resolver` field.
9090
resolve_behavior: Option<ResolveBehavior>,
91+
92+
/// Workspace-level custom metadata
93+
custom_metadata: Option<toml::Value>,
9194
}
9295

9396
// Separate structure for tracking loaded packages (to avoid loading anything
@@ -126,6 +129,7 @@ pub struct WorkspaceRootConfig {
126129
members: Option<Vec<String>>,
127130
default_members: Option<Vec<String>>,
128131
exclude: Vec<String>,
132+
custom_metadata: Option<toml::Value>,
129133
}
130134

131135
/// An iterator over the member packages of a workspace, returned by
@@ -155,6 +159,9 @@ impl<'cfg> Workspace<'cfg> {
155159
ws.root_manifest = ws.find_root(manifest_path)?;
156160
}
157161

162+
ws.custom_metadata = ws
163+
.load_workspace_config()?
164+
.and_then(|cfg| cfg.custom_metadata);
158165
ws.find_members()?;
159166
ws.resolve_behavior = match ws.root_maybe() {
160167
MaybePackage::Package(p) => p.manifest().resolve_behavior(),
@@ -182,6 +189,7 @@ impl<'cfg> Workspace<'cfg> {
182189
loaded_packages: RefCell::new(HashMap::new()),
183190
ignore_lock: false,
184191
resolve_behavior: None,
192+
custom_metadata: None,
185193
}
186194
}
187195

@@ -395,6 +403,30 @@ impl<'cfg> Workspace<'cfg> {
395403
self
396404
}
397405

406+
pub fn custom_metadata(&self) -> Option<&toml::Value> {
407+
self.custom_metadata.as_ref()
408+
}
409+
410+
pub fn load_workspace_config(&mut self) -> CargoResult<Option<WorkspaceRootConfig>> {
411+
// If we didn't find a root, it must mean there is no [workspace] section, and thus no
412+
// metadata.
413+
if let Some(root_path) = &self.root_manifest {
414+
let root_package = self.packages.load(root_path)?;
415+
match root_package.workspace_config() {
416+
WorkspaceConfig::Root(ref root_config) => {
417+
return Ok(Some(root_config.clone()));
418+
}
419+
420+
_ => anyhow::bail!(
421+
"root of a workspace inferred but wasn't a root: {}",
422+
root_path.display()
423+
),
424+
}
425+
}
426+
427+
Ok(None)
428+
}
429+
398430
/// Finds the root of a workspace for the crate whose manifest is located
399431
/// at `manifest_path`.
400432
///
@@ -476,8 +508,8 @@ impl<'cfg> Workspace<'cfg> {
476508
/// will transitively follow all `path` dependencies looking for members of
477509
/// the workspace.
478510
fn find_members(&mut self) -> CargoResult<()> {
479-
let root_manifest_path = match self.root_manifest {
480-
Some(ref path) => path.clone(),
511+
let workspace_config = match self.load_workspace_config()? {
512+
Some(workspace_config) => workspace_config,
481513
None => {
482514
debug!("find_members - only me as a member");
483515
self.members.push(self.current_manifest.clone());
@@ -490,30 +522,20 @@ impl<'cfg> Workspace<'cfg> {
490522
}
491523
};
492524

493-
let members_paths;
494-
let default_members_paths;
495-
{
496-
let root_package = self.packages.load(&root_manifest_path)?;
497-
match *root_package.workspace_config() {
498-
WorkspaceConfig::Root(ref root_config) => {
499-
members_paths = root_config
500-
.members_paths(root_config.members.as_ref().unwrap_or(&vec![]))?;
501-
default_members_paths = if root_manifest_path == self.current_manifest {
502-
if let Some(ref default) = root_config.default_members {
503-
Some(root_config.members_paths(default)?)
504-
} else {
505-
None
506-
}
507-
} else {
508-
None
509-
};
510-
}
511-
_ => anyhow::bail!(
512-
"root of a workspace inferred but wasn't a root: {}",
513-
root_manifest_path.display()
514-
),
525+
// self.root_manifest must be Some to have retrieved workspace_config
526+
let root_manifest_path = self.root_manifest.clone().unwrap();
527+
528+
let members_paths =
529+
workspace_config.members_paths(workspace_config.members.as_ref().unwrap_or(&vec![]))?;
530+
let default_members_paths = if root_manifest_path == self.current_manifest {
531+
if let Some(ref default) = workspace_config.default_members {
532+
Some(workspace_config.members_paths(default)?)
533+
} else {
534+
None
515535
}
516-
}
536+
} else {
537+
None
538+
};
517539

518540
for path in members_paths {
519541
self.find_path_deps(&path.join("Cargo.toml"), &root_manifest_path, false)?;
@@ -1129,12 +1151,14 @@ impl WorkspaceRootConfig {
11291151
members: &Option<Vec<String>>,
11301152
default_members: &Option<Vec<String>>,
11311153
exclude: &Option<Vec<String>>,
1154+
custom_metadata: &Option<toml::Value>,
11321155
) -> WorkspaceRootConfig {
11331156
WorkspaceRootConfig {
11341157
root_dir: root_dir.to_path_buf(),
11351158
members: members.clone(),
11361159
default_members: default_members.clone(),
11371160
exclude: exclude.clone().unwrap_or_default(),
1161+
custom_metadata: custom_metadata.clone(),
11381162
}
11391163
}
11401164

src/cargo/ops/cargo_output_metadata.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub fn output_metadata(ws: &Workspace<'_>, opt: &OutputMetadataOptions) -> Cargo
4646
target_directory: ws.target_dir().into_path_unlocked(),
4747
version: VERSION,
4848
workspace_root: ws.root().to_path_buf(),
49+
metadata: ws.custom_metadata().cloned(),
4950
})
5051
}
5152

@@ -60,6 +61,7 @@ pub struct ExportInfo {
6061
target_directory: PathBuf,
6162
version: u32,
6263
workspace_root: PathBuf,
64+
metadata: Option<toml::Value>,
6365
}
6466

6567
#[derive(Serialize)]

src/cargo/util/toml/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,7 @@ pub struct TomlWorkspace {
825825
#[serde(rename = "default-members")]
826826
default_members: Option<Vec<String>>,
827827
exclude: Option<Vec<String>>,
828+
metadata: Option<toml::Value>,
828829
resolver: Option<String>,
829830
}
830831

@@ -1230,6 +1231,7 @@ impl TomlManifest {
12301231
&config.members,
12311232
&config.default_members,
12321233
&config.exclude,
1234+
&config.metadata,
12331235
)),
12341236
(None, root) => WorkspaceConfig::Member {
12351237
root: root.cloned(),
@@ -1414,6 +1416,7 @@ impl TomlManifest {
14141416
&config.members,
14151417
&config.default_members,
14161418
&config.exclude,
1419+
&config.metadata,
14171420
)),
14181421
None => {
14191422
bail!("virtual manifests must be configured with [workspace]");

src/doc/man/cargo-metadata.adoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,15 @@ The output has the following format:
268268
"version": 1,
269269
/* The absolute path to the root of the workspace. */
270270
"workspace_root": "/path/to/my-package"
271+
/* Workspace metadata.
272+
This is null if no metadata is specified. */
273+
"metadata": {
274+
"docs": {
275+
"rs": {
276+
"all-features": true
277+
}
278+
}
279+
}
271280
}
272281
----
273282

src/doc/src/reference/manifest.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,15 @@ package-name = "my-awesome-android-app"
426426
assets = "path/to/static"
427427
```
428428

429+
There is a similar table at the workspace level at
430+
[`workspace.metadata`][workspace-metadata]. While cargo does not specify a
431+
format for the content of either of these tables, it is suggested that
432+
external tools may wish to use them in a consistent fashion, such as referring
433+
to the data in `workspace.metadata` if data is missing from `package.metadata`,
434+
if that makes sense for the tool in question.
435+
436+
[workspace-metadata]: workspaces.md#the-workspacemetadata-table
437+
429438
#### The `default-run` field
430439

431440
The `default-run` field in the `[package]` section of the manifest can be used

src/doc/src/reference/workspaces.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,31 @@ default-members = ["path/to/member2", "path/to/member3/foo"]
8282

8383
When specified, `default-members` must expand to a subset of `members`.
8484

85+
### The `workspace.metadata` table
86+
87+
The `workspace.metadata` table is ignored by Cargo and will not be warned
88+
about. This section can be used for tools that would like to store workspace
89+
configuration in `Cargo.toml`. For example:
90+
91+
```toml
92+
[workspace]
93+
members = ["member1", "member2"]
94+
95+
[workspace.metadata.webcontents]
96+
root = "path/to/webproject"
97+
tool = ["npm", "run", "build"]
98+
# ...
99+
```
100+
101+
There is a similar set of tables at the package level at
102+
[`package.metadata`][package-metadata]. While cargo does not specify a
103+
format for the content of either of these tables, it is suggested that
104+
external tools may wish to use them in a consistent fashion, such as referring
105+
to the data in `workspace.metadata` if data is missing from `package.metadata`,
106+
if that makes sense for the tool in question.
107+
85108
[package]: manifest.md#the-package-section
109+
[package-metadata]: manifest.md#the-metadata-table
86110
[output directory]: ../guide/build-cache.md
87111
[patch]: overriding-dependencies.md#the-patch-section
88112
[replace]: overriding-dependencies.md#the-replace-section

tests/testsuite/alt_registry.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,8 @@ fn alt_reg_metadata() {
824824
"resolve": null,
825825
"target_directory": "[..]/foo/target",
826826
"version": 1,
827-
"workspace_root": "[..]/foo"
827+
"workspace_root": "[..]/foo",
828+
"metadata": null
828829
}"#,
829830
)
830831
.run();
@@ -1003,7 +1004,8 @@ fn alt_reg_metadata() {
10031004
"resolve": "{...}",
10041005
"target_directory": "[..]/foo/target",
10051006
"version": 1,
1006-
"workspace_root": "[..]/foo"
1007+
"workspace_root": "[..]/foo",
1008+
"metadata": null
10071009
}"#,
10081010
)
10091011
.run();
@@ -1152,7 +1154,8 @@ fn unknown_registry() {
11521154
"resolve": "{...}",
11531155
"target_directory": "[..]/foo/target",
11541156
"version": 1,
1155-
"workspace_root": "[..]/foo"
1157+
"workspace_root": "[..]/foo",
1158+
"metadata": null
11561159
}
11571160
"#,
11581161
)

tests/testsuite/build.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3310,6 +3310,43 @@ fn no_warn_about_package_metadata() {
33103310
.run();
33113311
}
33123312

3313+
#[cargo_test]
3314+
fn no_warn_about_workspace_metadata() {
3315+
let p = project()
3316+
.file(
3317+
"Cargo.toml",
3318+
r#"
3319+
[workspace]
3320+
members = ["foo"]
3321+
3322+
[workspace.metadata]
3323+
something = "something_else"
3324+
x = 1
3325+
y = 2
3326+
3327+
[workspace.metadata.another]
3328+
bar = 12
3329+
"#,
3330+
)
3331+
.file(
3332+
"foo/Cargo.toml",
3333+
r#"
3334+
[package]
3335+
name = "foo"
3336+
version = "0.0.1"
3337+
"#,
3338+
)
3339+
.file("foo/src/lib.rs", "")
3340+
.build();
3341+
3342+
p.cargo("build")
3343+
.with_stderr(
3344+
"[..] foo v0.0.1 ([..])\n\
3345+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n",
3346+
)
3347+
.run();
3348+
}
3349+
33133350
#[cargo_test]
33143351
fn cargo_build_empty_target() {
33153352
let p = project()

0 commit comments

Comments
 (0)