Skip to content

Commit 77f2da7

Browse files
committed
Auto merge of #12914 - epage:metadata, r=weihanglo
fix(metadata): Stabilize id format as PackageIDSpec ### What does this PR try to resolve? For tools integrating with cargo, `cargo metadata` is the primary interface. Limitations include: - There isn't an unambiguous way to map a package entry from `cargo metadata` to a parameter to pass to other `cargo` commands. An `id` field exists but it is documented as an opaque string, useful only for comparisons with other `id`s within the document. - There isn't an unambiguous way of taking user parameters (`--package`) and mapping them to `cargo metadata` entries. `cargo pkgid` could help but it returns a `PackageIdSpec` which doesn't exist within the `cargo metadata` output. This attempts to solve these problems by switching the `id` field from `PackageId` to `PackageIdSpec` which is a [publicly documented format](https://doc.rust-lang.org/cargo/reference/pkgid-spec.html), can be generated by `cargo pkgid`, and is accepted by most commands via the `--package` flag. As the `"id"` field is documented as opaque, this technically isn't a breaking change though people could be parsing it. For `cargo_metadata` they do [use a new type that documents it as opaque but publicly expose the inner `String`](https://docs.rs/cargo_metadata/latest/cargo_metadata/struct.PackageId.html). The `String` wasn't publicly exposed due to a request by users but instead their `PackageId` type replaced using `String`s in the API in oli-obk/cargo_metadata#59 with no indication given as to why the `String` was still exposed. However, you'll note that before that PR, they had `WorkspaceMember` that parsed `PackageId`. This was introduced in oli-obk/cargo_metadata#26 without a motivation given. **Note that `PackageIdSpec` has multiple representation that might uniquely identify a package and we can return any one of them.** Fixes #7267 ### How should we test and review this PR? ### Additional information cc `@oli-obk`
2 parents 19eb0f0 + 63bb70d commit 77f2da7

File tree

13 files changed

+353
-319
lines changed

13 files changed

+353
-319
lines changed

src/cargo/core/package.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::core::compiler::{CompileKind, RustcTargetData};
2222
use crate::core::dependency::DepKind;
2323
use crate::core::resolver::features::ForceAllTargets;
2424
use crate::core::resolver::{HasDevUnits, Resolve};
25-
use crate::core::{Dependency, Manifest, PackageId, SourceId, Target};
25+
use crate::core::{Dependency, Manifest, PackageId, PackageIdSpec, SourceId, Target};
2626
use crate::core::{Summary, Workspace};
2727
use crate::sources::source::{MaybePackage, SourceMap};
2828
use crate::util::cache_lock::{CacheLock, CacheLockMode};
@@ -82,7 +82,7 @@ impl PartialOrd for Package {
8282
pub struct SerializedPackage {
8383
name: InternedString,
8484
version: Version,
85-
id: PackageId,
85+
id: PackageIdSpec,
8686
license: Option<String>,
8787
license_file: Option<String>,
8888
description: Option<String>,
@@ -239,7 +239,7 @@ impl Package {
239239
SerializedPackage {
240240
name: package_id.name(),
241241
version: package_id.version().clone(),
242-
id: package_id,
242+
id: package_id.to_spec(),
243243
license: manmeta.license.clone(),
244244
license_file: manmeta.license_file.clone(),
245245
description: manmeta.description.clone(),

src/cargo/ops/cargo_output_metadata.rs

+23-17
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::core::compiler::{CompileKind, RustcTargetData};
33
use crate::core::dependency::DepKind;
44
use crate::core::package::SerializedPackage;
55
use crate::core::resolver::{features::CliFeatures, HasDevUnits, Resolve};
6-
use crate::core::{Package, PackageId, Workspace};
6+
use crate::core::{Package, PackageId, PackageIdSpec, Workspace};
77
use crate::ops::{self, Packages};
88
use crate::util::interning::InternedString;
99
use crate::util::CargoResult;
@@ -42,8 +42,11 @@ pub fn output_metadata(ws: &Workspace<'_>, opt: &OutputMetadataOptions) -> Cargo
4242

4343
Ok(ExportInfo {
4444
packages,
45-
workspace_members: ws.members().map(|pkg| pkg.package_id()).collect(),
46-
workspace_default_members: ws.default_members().map(|pkg| pkg.package_id()).collect(),
45+
workspace_members: ws.members().map(|pkg| pkg.package_id().to_spec()).collect(),
46+
workspace_default_members: ws
47+
.default_members()
48+
.map(|pkg| pkg.package_id().to_spec())
49+
.collect(),
4750
resolve,
4851
target_directory: ws.target_dir().into_path_unlocked(),
4952
version: VERSION,
@@ -58,8 +61,8 @@ pub fn output_metadata(ws: &Workspace<'_>, opt: &OutputMetadataOptions) -> Cargo
5861
#[derive(Serialize)]
5962
pub struct ExportInfo {
6063
packages: Vec<SerializedPackage>,
61-
workspace_members: Vec<PackageId>,
62-
workspace_default_members: Vec<PackageId>,
64+
workspace_members: Vec<PackageIdSpec>,
65+
workspace_default_members: Vec<PackageIdSpec>,
6366
resolve: Option<MetadataResolve>,
6467
target_directory: PathBuf,
6568
version: u32,
@@ -70,13 +73,13 @@ pub struct ExportInfo {
7073
#[derive(Serialize)]
7174
struct MetadataResolve {
7275
nodes: Vec<MetadataResolveNode>,
73-
root: Option<PackageId>,
76+
root: Option<PackageIdSpec>,
7477
}
7578

7679
#[derive(Serialize)]
7780
struct MetadataResolveNode {
78-
id: PackageId,
79-
dependencies: Vec<PackageId>,
81+
id: PackageIdSpec,
82+
dependencies: Vec<PackageIdSpec>,
8083
deps: Vec<Dep>,
8184
features: Vec<InternedString>,
8285
}
@@ -86,7 +89,9 @@ struct Dep {
8689
// TODO(bindeps): after -Zbindeps gets stabilized,
8790
// mark this field as deprecated in the help manual of cargo-metadata
8891
name: InternedString,
89-
pkg: PackageId,
92+
pkg: PackageIdSpec,
93+
#[serde(skip)]
94+
pkg_id: PackageId,
9095
dep_kinds: Vec<DepKindInfo>,
9196
}
9297

@@ -179,7 +184,7 @@ fn build_resolve_graph(
179184

180185
let mr = MetadataResolve {
181186
nodes: node_map.into_iter().map(|(_pkg_id, node)| node).collect(),
182-
root: ws.current_opt().map(|pkg| pkg.package_id()),
187+
root: ws.current_opt().map(|pkg| pkg.package_id().to_spec()),
183188
};
184189
Ok((actual_packages, mr))
185190
}
@@ -301,18 +306,20 @@ fn build_resolve_graph_r(
301306

302307
dep_kinds.sort();
303308

304-
let pkg = normalize_id(dep_id);
309+
let pkg_id = normalize_id(dep_id);
305310

306311
let dep = match (lib_target, dep_kinds.len()) {
307312
(Some(target), _) => Dep {
308313
name: extern_name(target)?,
309-
pkg,
314+
pkg: pkg_id.to_spec(),
315+
pkg_id,
310316
dep_kinds,
311317
},
312318
// No lib target exists but contains artifact deps.
313319
(None, 1..) => Dep {
314320
name: InternedString::new(""),
315-
pkg,
321+
pkg: pkg_id.to_spec(),
322+
pkg_id,
316323
dep_kinds,
317324
},
318325
// No lib or artifact dep exists.
@@ -325,11 +332,10 @@ fn build_resolve_graph_r(
325332
dep_metadatas
326333
};
327334

328-
let dumb_deps: Vec<PackageId> = deps.iter().map(|dep| dep.pkg).collect();
329-
let to_visit = dumb_deps.clone();
335+
let to_visit: Vec<PackageId> = deps.iter().map(|dep| dep.pkg_id).collect();
330336
let node = MetadataResolveNode {
331-
id: normalize_id(pkg_id),
332-
dependencies: dumb_deps,
337+
id: normalize_id(pkg_id).to_spec(),
338+
dependencies: to_visit.iter().map(|id| id.to_spec()).collect(),
333339
deps,
334340
features,
335341
};

src/doc/man/cargo-metadata.md

+16-11
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ considersed as incompatible:
3434
* **Adding new values for enum-like fields** — Same as adding new fields. It
3535
keeps metadata evolving without stagnation.
3636
* **Changing opaque representations** — The inner representations of some
37-
fields are implementation details. For example, fields related to "Package ID"
38-
or "Source ID" are treated as opaque identifiers to differentiate packages or
37+
fields are implementation details. For example, fields related to
38+
"Source ID" are treated as opaque identifiers to differentiate packages or
3939
sources. Consumers shouldn't rely on those representations unless specified.
4040

4141
### JSON format
@@ -53,10 +53,10 @@ The JSON output has the following format:
5353
"name": "my-package",
5454
/* The version of the package. */
5555
"version": "0.1.0",
56-
/* The Package ID, an opaque and unique identifier for referring to the
57-
package. See "Compatibility" above for the stability guarantee.
56+
/* The Package ID for referring to the
57+
package within the document and as the `--package` argument to many commands
5858
*/
59-
"id": "my-package 0.1.0 (path+file:///path/to/my-package)",
59+
"id": "file:///path/to/my-package#0.1.0",
6060
/* The license value from the manifest, or null. */
6161
"license": "MIT/Apache-2.0",
6262
/* The license-file value from the manifest, or null. */
@@ -242,13 +242,13 @@ The JSON output has the following format:
242242
Each entry is the Package ID for the package.
243243
*/
244244
"workspace_members": [
245-
"my-package 0.1.0 (path+file:///path/to/my-package)",
245+
"file:///path/to/my-package#0.1.0",
246246
],
247247
/* Array of default members of the workspace.
248248
Each entry is the Package ID for the package.
249249
*/
250250
"workspace_default_members": [
251-
"my-package 0.1.0 (path+file:///path/to/my-package)",
251+
"file:///path/to/my-package#0.1.0",
252252
],
253253
// The resolved dependency graph for the entire workspace. The enabled
254254
// features are based on the enabled features for the "current" package.
@@ -266,10 +266,10 @@ The JSON output has the following format:
266266
"nodes": [
267267
{
268268
/* The Package ID of this node. */
269-
"id": "my-package 0.1.0 (path+file:///path/to/my-package)",
269+
"id": "file:///path/to/my-package#0.1.0",
270270
/* The dependencies of this package, an array of Package IDs. */
271271
"dependencies": [
272-
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)"
272+
"https://github.com/rust-lang/crates.io-index#[email protected]"
273273
],
274274
/* The dependencies of this package. This is an alternative to
275275
"dependencies" which contains additional information. In
@@ -283,7 +283,7 @@ The JSON output has the following format:
283283
*/
284284
"name": "bitflags",
285285
/* The Package ID of the dependency. */
286-
"pkg": "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
286+
"pkg": "https://github.com/rust-lang/crates.io-index#[email protected]"
287287
/* Array of dependency kinds. Added in Cargo 1.40. */
288288
"dep_kinds": [
289289
{
@@ -309,7 +309,7 @@ The JSON output has the following format:
309309
This is null if this is a virtual workspace. Otherwise it is
310310
the Package ID of the root package.
311311
*/
312-
"root": "my-package 0.1.0 (path+file:///path/to/my-package)"
312+
"root": "file:///path/to/my-package#0.1.0",
313313
},
314314
/* The absolute path to the build directory where Cargo places its output. */
315315
"target_directory": "/path/to/my-package/target",
@@ -331,6 +331,11 @@ The JSON output has the following format:
331331
}
332332
````
333333

334+
Notes:
335+
- For `"id"` field syntax, see [Package ID Specifications] in the reference.
336+
337+
[Package ID Specifications]: ../reference/pkgid-spec.html
338+
334339
## OPTIONS
335340

336341
### Output Options

src/doc/man/generated_txt/cargo-metadata.txt

+18-12
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ OUTPUT FORMAT
3232

3333
o Changing opaque representations — The inner representations of some
3434
fields are implementation details. For example, fields related to
35-
Package ID” or “Source ID” are treated as opaque identifiers
36-
to differentiate packages or sources. Consumers shouldn’t rely on
37-
those representations unless specified.
35+
“Source ID” are treated as opaque identifiers to differentiate
36+
packages or sources. Consumers shouldn’t rely on those
37+
representations unless specified.
3838

3939
JSON format
4040
The JSON output has the following format:
@@ -49,10 +49,10 @@ OUTPUT FORMAT
4949
"name": "my-package",
5050
/* The version of the package. */
5151
"version": "0.1.0",
52-
/* The Package ID, an opaque and unique identifier for referring to the
53-
package. See "Compatibility" above for the stability guarantee.
52+
/* The Package ID for referring to the
53+
package within the document and as the `--package` argument to many commands
5454
*/
55-
"id": "my-package 0.1.0 (path+file:///path/to/my-package)",
55+
"id": "file:///path/to/my-package#0.1.0",
5656
/* The license value from the manifest, or null. */
5757
"license": "MIT/Apache-2.0",
5858
/* The license-file value from the manifest, or null. */
@@ -238,13 +238,13 @@ OUTPUT FORMAT
238238
Each entry is the Package ID for the package.
239239
*/
240240
"workspace_members": [
241-
"my-package 0.1.0 (path+file:///path/to/my-package)",
241+
"file:///path/to/my-package#0.1.0",
242242
],
243243
/* Array of default members of the workspace.
244244
Each entry is the Package ID for the package.
245245
*/
246246
"workspace_default_members": [
247-
"my-package 0.1.0 (path+file:///path/to/my-package)",
247+
"file:///path/to/my-package#0.1.0",
248248
],
249249
// The resolved dependency graph for the entire workspace. The enabled
250250
// features are based on the enabled features for the "current" package.
@@ -262,10 +262,10 @@ OUTPUT FORMAT
262262
"nodes": [
263263
{
264264
/* The Package ID of this node. */
265-
"id": "my-package 0.1.0 (path+file:///path/to/my-package)",
265+
"id": "file:///path/to/my-package#0.1.0",
266266
/* The dependencies of this package, an array of Package IDs. */
267267
"dependencies": [
268-
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)"
268+
"https://github.com/rust-lang/crates.io-index#[email protected]"
269269
],
270270
/* The dependencies of this package. This is an alternative to
271271
"dependencies" which contains additional information. In
@@ -279,7 +279,7 @@ OUTPUT FORMAT
279279
*/
280280
"name": "bitflags",
281281
/* The Package ID of the dependency. */
282-
"pkg": "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
282+
"pkg": "https://github.com/rust-lang/crates.io-index#[email protected]"
283283
/* Array of dependency kinds. Added in Cargo 1.40. */
284284
"dep_kinds": [
285285
{
@@ -305,7 +305,7 @@ OUTPUT FORMAT
305305
This is null if this is a virtual workspace. Otherwise it is
306306
the Package ID of the root package.
307307
*/
308-
"root": "my-package 0.1.0 (path+file:///path/to/my-package)"
308+
"root": "file:///path/to/my-package#0.1.0",
309309
},
310310
/* The absolute path to the build directory where Cargo places its output. */
311311
"target_directory": "/path/to/my-package/target",
@@ -326,6 +326,12 @@ OUTPUT FORMAT
326326
}
327327
}
328328

329+
Notes:
330+
331+
o For "id" field syntax, see Package ID Specifications
332+
<https://doc.rust-lang.org/cargo/reference/pkgid-spec.html> in the
333+
reference.
334+
329335
OPTIONS
330336
Output Options
331337
--no-deps

src/doc/src/commands/cargo-metadata.md

+16-11
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ considersed as incompatible:
3434
* **Adding new values for enum-like fields** — Same as adding new fields. It
3535
keeps metadata evolving without stagnation.
3636
* **Changing opaque representations** — The inner representations of some
37-
fields are implementation details. For example, fields related to "Package ID"
38-
or "Source ID" are treated as opaque identifiers to differentiate packages or
37+
fields are implementation details. For example, fields related to
38+
"Source ID" are treated as opaque identifiers to differentiate packages or
3939
sources. Consumers shouldn't rely on those representations unless specified.
4040

4141
### JSON format
@@ -53,10 +53,10 @@ The JSON output has the following format:
5353
"name": "my-package",
5454
/* The version of the package. */
5555
"version": "0.1.0",
56-
/* The Package ID, an opaque and unique identifier for referring to the
57-
package. See "Compatibility" above for the stability guarantee.
56+
/* The Package ID for referring to the
57+
package within the document and as the `--package` argument to many commands
5858
*/
59-
"id": "my-package 0.1.0 (path+file:///path/to/my-package)",
59+
"id": "file:///path/to/my-package#0.1.0",
6060
/* The license value from the manifest, or null. */
6161
"license": "MIT/Apache-2.0",
6262
/* The license-file value from the manifest, or null. */
@@ -242,13 +242,13 @@ The JSON output has the following format:
242242
Each entry is the Package ID for the package.
243243
*/
244244
"workspace_members": [
245-
"my-package 0.1.0 (path+file:///path/to/my-package)",
245+
"file:///path/to/my-package#0.1.0",
246246
],
247247
/* Array of default members of the workspace.
248248
Each entry is the Package ID for the package.
249249
*/
250250
"workspace_default_members": [
251-
"my-package 0.1.0 (path+file:///path/to/my-package)",
251+
"file:///path/to/my-package#0.1.0",
252252
],
253253
// The resolved dependency graph for the entire workspace. The enabled
254254
// features are based on the enabled features for the "current" package.
@@ -266,10 +266,10 @@ The JSON output has the following format:
266266
"nodes": [
267267
{
268268
/* The Package ID of this node. */
269-
"id": "my-package 0.1.0 (path+file:///path/to/my-package)",
269+
"id": "file:///path/to/my-package#0.1.0",
270270
/* The dependencies of this package, an array of Package IDs. */
271271
"dependencies": [
272-
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)"
272+
"https://github.com/rust-lang/crates.io-index#[email protected]"
273273
],
274274
/* The dependencies of this package. This is an alternative to
275275
"dependencies" which contains additional information. In
@@ -283,7 +283,7 @@ The JSON output has the following format:
283283
*/
284284
"name": "bitflags",
285285
/* The Package ID of the dependency. */
286-
"pkg": "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
286+
"pkg": "https://github.com/rust-lang/crates.io-index#[email protected]"
287287
/* Array of dependency kinds. Added in Cargo 1.40. */
288288
"dep_kinds": [
289289
{
@@ -309,7 +309,7 @@ The JSON output has the following format:
309309
This is null if this is a virtual workspace. Otherwise it is
310310
the Package ID of the root package.
311311
*/
312-
"root": "my-package 0.1.0 (path+file:///path/to/my-package)"
312+
"root": "file:///path/to/my-package#0.1.0",
313313
},
314314
/* The absolute path to the build directory where Cargo places its output. */
315315
"target_directory": "/path/to/my-package/target",
@@ -331,6 +331,11 @@ The JSON output has the following format:
331331
}
332332
````
333333

334+
Notes:
335+
- For `"id"` field syntax, see [Package ID Specifications] in the reference.
336+
337+
[Package ID Specifications]: ../reference/pkgid-spec.html
338+
334339
## OPTIONS
335340

336341
### Output Options

0 commit comments

Comments
 (0)