From 9ae8796b16d0f34de07110a8a64aa4a88fc7708e Mon Sep 17 00:00:00 2001 From: Ethan Brierley Date: Fri, 6 Sep 2024 08:22:23 +0100 Subject: [PATCH] Include public/private dependency status in `cargo metadata` fixes #14502 --- .gitignore | 2 + src/bin/cargo/commands/read_manifest.rs | 3 +- src/cargo/core/dependency.rs | 68 ++--- src/cargo/core/mod.rs | 2 +- src/cargo/core/package.rs | 15 +- src/cargo/ops/cargo_output_metadata.rs | 7 +- tests/testsuite/metadata.rs | 320 ++++++++++++++++++++++++ 7 files changed, 378 insertions(+), 39 deletions(-) diff --git a/.gitignore b/.gitignore index e10c0128f116..de0acb1d7968 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ __pycache__ .vscode/ *.iml *.swp +.direnv +.envrc diff --git a/src/bin/cargo/commands/read_manifest.rs b/src/bin/cargo/commands/read_manifest.rs index b86bbf795bc7..c19647e9c56f 100644 --- a/src/bin/cargo/commands/read_manifest.rs +++ b/src/bin/cargo/commands/read_manifest.rs @@ -15,6 +15,7 @@ Deprecated, use `cargo metadata --no-deps` instead.\ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; - gctx.shell().print_json(&ws.current()?.serialized())?; + gctx.shell() + .print_json(&ws.current()?.serialized(gctx.cli_unstable()))?; Ok(()) } diff --git a/src/cargo/core/dependency.rs b/src/cargo/core/dependency.rs index 12c484119835..665d2e42d671 100644 --- a/src/cargo/core/dependency.rs +++ b/src/cargo/core/dependency.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use tracing::trace; use crate::core::compiler::{CompileKind, CompileTarget}; -use crate::core::{PackageId, SourceId, Summary}; +use crate::core::{CliUnstable, PackageId, SourceId, Summary}; use crate::util::errors::CargoResult; use crate::util::interning::InternedString; use crate::util::OptVersionReq; @@ -52,50 +52,32 @@ struct Inner { } #[derive(Serialize)] -struct SerializedDependency<'a> { - name: &'a str, +pub struct SerializedDependency { + name: InternedString, source: SourceId, req: String, kind: DepKind, - rename: Option<&'a str>, + rename: Option, optional: bool, uses_default_features: bool, - features: &'a [InternedString], + features: Vec, #[serde(skip_serializing_if = "Option::is_none")] - artifact: Option<&'a Artifact>, - target: Option<&'a Platform>, + artifact: Option, + target: Option, /// The registry URL this dependency is from. /// If None, then it comes from the default registry (crates.io). - registry: Option<&'a str>, + registry: Option, /// The file system path for a local path dependency. #[serde(skip_serializing_if = "Option::is_none")] path: Option, -} -impl ser::Serialize for Dependency { - fn serialize(&self, s: S) -> Result - where - S: ser::Serializer, - { - let registry_id = self.registry_id(); - SerializedDependency { - name: &*self.package_name(), - source: self.source_id(), - req: self.version_req().to_string(), - kind: self.kind(), - optional: self.is_optional(), - uses_default_features: self.uses_default_features(), - features: self.features(), - target: self.platform(), - rename: self.explicit_name_in_toml().map(|s| s.as_str()), - registry: registry_id.as_ref().map(|sid| sid.url().as_str()), - path: self.source_id().local_path(), - artifact: self.artifact(), - } - .serialize(s) - } + /// `public` flag is unset if `-Zpublic-dependency` is not enabled + /// + /// Once that feature is stabilized, `public` will not need to be `Option` + #[serde(skip_serializing_if = "Option::is_none")] + public: Option, } #[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug, Copy)] @@ -130,6 +112,30 @@ impl ser::Serialize for DepKind { } impl Dependency { + pub fn serialized(&self, unstable_flags: &CliUnstable) -> SerializedDependency { + SerializedDependency { + name: self.package_name(), + source: self.source_id(), + req: self.version_req().to_string(), + kind: self.kind(), + optional: self.is_optional(), + uses_default_features: self.uses_default_features(), + features: self.features().to_vec(), + target: self.inner.platform.clone(), + rename: self.explicit_name_in_toml(), + registry: self + .registry_id() + .as_ref() + .map(|sid| sid.url().as_str().to_owned()), + path: self.source_id().local_path(), + artifact: self.inner.artifact.clone(), + + public: unstable_flags + .public_dependency + .then_some(self.inner.public), + } + } + /// Attempt to create a `Dependency` from an entry in the manifest. pub fn parse( name: impl Into, diff --git a/src/cargo/core/mod.rs b/src/cargo/core/mod.rs index bdc9b2f4fe6f..e857ca35dad6 100644 --- a/src/cargo/core/mod.rs +++ b/src/cargo/core/mod.rs @@ -1,4 +1,4 @@ -pub use self::dependency::Dependency; +pub use self::dependency::{Dependency, SerializedDependency}; pub use self::features::{CliUnstable, Edition, Feature, Features}; pub use self::manifest::{EitherManifest, VirtualManifest}; pub use self::manifest::{Manifest, Target, TargetKind}; diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index 7dd412154f7f..fd44ddf2a651 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -22,7 +22,10 @@ use crate::core::compiler::{CompileKind, RustcTargetData}; use crate::core::dependency::DepKind; use crate::core::resolver::features::ForceAllTargets; use crate::core::resolver::{HasDevUnits, Resolve}; -use crate::core::{Dependency, Manifest, PackageId, PackageIdSpec, SourceId, Target}; +use crate::core::{ + CliUnstable, Dependency, Manifest, PackageId, PackageIdSpec, SerializedDependency, SourceId, + Target, +}; use crate::core::{Summary, Workspace}; use crate::sources::source::{MaybePackage, SourceMap}; use crate::util::cache_lock::{CacheLock, CacheLockMode}; @@ -73,7 +76,7 @@ pub struct SerializedPackage { license_file: Option, description: Option, source: SourceId, - dependencies: Vec, + dependencies: Vec, targets: Vec, features: BTreeMap>, manifest_path: PathBuf, @@ -188,7 +191,7 @@ impl Package { self.targets().iter().any(|t| t.is_example() || t.is_bin()) } - pub fn serialized(&self) -> SerializedPackage { + pub fn serialized(&self, unstable_flags: &CliUnstable) -> SerializedPackage { let summary = self.manifest().summary(); let package_id = summary.package_id(); let manmeta = self.manifest().metadata(); @@ -224,7 +227,11 @@ impl Package { license_file: manmeta.license_file.clone(), description: manmeta.description.clone(), source: summary.source_id(), - dependencies: summary.dependencies().to_vec(), + dependencies: summary + .dependencies() + .iter() + .map(|dep| dep.clone().serialized(unstable_flags)) + .collect(), targets, features, manifest_path: self.manifest_path().to_path_buf(), diff --git a/src/cargo/ops/cargo_output_metadata.rs b/src/cargo/ops/cargo_output_metadata.rs index 1aadc05d77a3..0d633301705a 100644 --- a/src/cargo/ops/cargo_output_metadata.rs +++ b/src/cargo/ops/cargo_output_metadata.rs @@ -33,7 +33,10 @@ pub fn output_metadata(ws: &Workspace<'_>, opt: &OutputMetadataOptions) -> Cargo ); } let (packages, resolve) = if opt.no_deps { - let packages = ws.members().map(|pkg| pkg.serialized()).collect(); + let packages = ws + .members() + .map(|pkg| pkg.serialized(ws.gctx().cli_unstable())) + .collect(); (packages, None) } else { let (packages, resolve) = build_resolve_graph(ws, opt)?; @@ -178,7 +181,7 @@ fn build_resolve_graph( let actual_packages = package_map .into_iter() .filter_map(|(pkg_id, pkg)| node_map.get(&pkg_id).map(|_| pkg)) - .map(|pkg| pkg.serialized()) + .map(|pkg| pkg.serialized(ws.gctx().cli_unstable())) .collect(); let mr = MetadataResolve { diff --git a/tests/testsuite/metadata.rs b/tests/testsuite/metadata.rs index e76796c0514c..06b1d5a30418 100644 --- a/tests/testsuite/metadata.rs +++ b/tests/testsuite/metadata.rs @@ -627,6 +627,326 @@ fn cargo_metadata_with_deps_and_version() { .run(); } +#[cargo_test] +fn cargo_metadata_public_private_dependencies() { + let p = project() + .file("src/foo.rs", "") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = [] + license = "MIT" + description = "foo" + + [[bin]] + name = "foo" + + [dependencies] + bar = { version = "*", public = false } + foobar = { version = "*", public = true } + baz = "*" + "#, + ) + .build(); + Package::new("bar", "0.0.1").publish(); + Package::new("foobar", "0.0.2").publish(); + Package::new("baz", "0.0.3").publish(); + + p.cargo("metadata -q --format-version 1 -Zpublic-dependency") + .masquerade_as_nightly_cargo(&["public-dependency"]) + .with_stdout_data( + str![[r#" +{ + "metadata": null, + "packages": [ + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.0.1/Cargo.toml", + "metadata": null, + "name": "bar", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "bar", + "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/bar-0.0.1/src/lib.rs", + "test": true + } + ], + "version": "0.0.1" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "registry+https://github.com/rust-lang/crates.io-index#baz@0.0.3", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/baz-0.0.3/Cargo.toml", + "metadata": null, + "name": "baz", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "baz", + "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/baz-0.0.3/src/lib.rs", + "test": true + } + ], + "version": "0.0.3" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [ + { + "features": [], + "kind": null, + "name": "bar", + "optional": false, + "public": false, + "registry": null, + "rename": null, + "req": "*", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": null, + "name": "baz", + "optional": false, + "public": false, + "registry": null, + "rename": null, + "req": "*", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": null, + "name": "foobar", + "optional": false, + "public": true, + "registry": null, + "rename": null, + "req": "*", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "target": null, + "uses_default_features": true + } + ], + "description": "foo", + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "path+[ROOTURL]/foo#0.5.0", + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "manifest_path": "[ROOT]/foo/Cargo.toml", + "metadata": null, + "name": "foo", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "foo", + "src_path": "[ROOT]/foo/src/foo.rs", + "test": true + } + ], + "version": "0.5.0" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.2", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/foobar-0.0.2/Cargo.toml", + "metadata": null, + "name": "foobar", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": "registry+https://github.com/rust-lang/crates.io-index", + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "foobar", + "src_path": "[ROOT]/home/.cargo/registry/src/-[HASH]/foobar-0.0.2/src/lib.rs", + "test": true + } + ], + "version": "0.0.2" + } + ], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [], + "id": "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1" + }, + { + "dependencies": [], + "deps": [], + "features": [], + "id": "registry+https://github.com/rust-lang/crates.io-index#baz@0.0.3" + }, + { + "dependencies": [ + "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1", + "registry+https://github.com/rust-lang/crates.io-index#baz@0.0.3", + "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.2" + ], + "deps": [ + { + "dep_kinds": [ + { + "kind": null, + "target": null + } + ], + "name": "bar", + "pkg": "registry+https://github.com/rust-lang/crates.io-index#bar@0.0.1" + }, + { + "dep_kinds": [ + { + "kind": null, + "target": null + } + ], + "name": "baz", + "pkg": "registry+https://github.com/rust-lang/crates.io-index#baz@0.0.3" + }, + { + "dep_kinds": [ + { + "kind": null, + "target": null + } + ], + "name": "foobar", + "pkg": "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.2" + } + ], + "features": [], + "id": "path+[ROOTURL]/foo#0.5.0" + }, + { + "dependencies": [], + "deps": [], + "features": [], + "id": "registry+https://github.com/rust-lang/crates.io-index#foobar@0.0.2" + } + ], + "root": "path+[ROOTURL]/foo#0.5.0" + }, + "target_directory": "[ROOT]/foo/target", + "version": 1, + "workspace_default_members": [ + "path+[ROOTURL]/foo#0.5.0" + ], + "workspace_members": [ + "path+[ROOTURL]/foo#0.5.0" + ], + "workspace_root": "[ROOT]/foo" +} +"#]] + .is_json(), + ) + .run(); +} + #[cargo_test] fn example() { let p = project()