#![deny(missing_docs)] //! Structured access to the output of `cargo metadata` and `cargo --message-format=json`. //! Usually used from within a `cargo-*` executable //! //! See the [cargo book](https://doc.rust-lang.org/cargo/index.html) for //! details on cargo itself. //! //! ## Examples //! //! ```rust //! # extern crate cargo_metadata; //! # use std::path::Path; //! let mut args = std::env::args().skip_while(|val| !val.starts_with("--manifest-path")); //! //! let mut cmd = cargo_metadata::MetadataCommand::new(); //! let manifest_path = match args.next() { //! Some(ref p) if p == "--manifest-path" => { //! cmd.manifest_path(args.next().unwrap()); //! } //! Some(p) => { //! cmd.manifest_path(p.trim_start_matches("--manifest-path=")); //! } //! None => {} //! }; //! //! let _metadata = cmd.exec().unwrap(); //! ``` //! //! Pass features flags //! //! ```rust //! # // This should be kept in sync with the equivalent example in the readme. //! # extern crate cargo_metadata; //! # use std::path::Path; //! # fn main() { //! use cargo_metadata::{MetadataCommand, CargoOpt}; //! //! let _metadata = MetadataCommand::new() //! .manifest_path("./Cargo.toml") //! .features(CargoOpt::AllFeatures) //! .exec() //! .unwrap(); //! # } //! ``` //! //! Parse message-format output: //! //! ``` //! # extern crate cargo_metadata; //! use std::process::{Stdio, Command}; //! use cargo_metadata::Message; //! //! let mut command = Command::new("cargo") //! .args(&["build", "--message-format=json-render-diagnostics"]) //! .stdout(Stdio::piped()) //! .spawn() //! .unwrap(); //! //! let reader = std::io::BufReader::new(command.stdout.take().unwrap()); //! for message in cargo_metadata::Message::parse_stream(reader) { //! match message.unwrap() { //! Message::CompilerMessage(msg) => { //! println!("{:?}", msg); //! }, //! Message::CompilerArtifact(artifact) => { //! println!("{:?}", artifact); //! }, //! Message::BuildScriptExecuted(script) => { //! println!("{:?}", script); //! }, //! Message::BuildFinished(finished) => { //! println!("{:?}", finished); //! }, //! _ => () // Unknown message //! } //! } //! //! let output = command.wait().expect("Couldn't get cargo's exit status"); //! ```
use camino::Utf8PathBuf; #[cfg(feature = "builder")] use derive_builder::Builder; use std::collections::HashMap; use std::env; use std::ffi::OsString; use std::fmt; use std::hash::Hash; use std::path::PathBuf; use std::process::{Command, Stdio}; use std::str::from_utf8;
pubuse camino; pubuse semver; use semver::{Version, VersionReq};
mod dependency; pubmod diagnostic; mod errors; mod messages;
/// An "opaque" identifier for a package. /// It is possible to inspect the `repr` field, if the need arises, but its /// precise format is an implementation detail and is subject to change. /// /// `Metadata` can be indexed by `PackageId`. #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[serde(transparent)] pubstruct PackageId { /// The underlying string representation of id. pub repr: String,
}
#[derive(Clone, Serialize, Deserialize, Debug)] #[cfg_attr(feature = "builder", derive(Builder))] #[non_exhaustive] #[cfg_attr(feature = "builder", builder(pattern = "owned", setter(into)))] /// Starting point for metadata returned by `cargo metadata` pubstruct Metadata { /// A list of all crates referenced by this crate (and the crate itself) pub packages: Vec<Package>, /// A list of all workspace members pub workspace_members: Vec<PackageId>, /// Dependencies graph pub resolve: Option<Resolve>, /// Workspace root pub workspace_root: Utf8PathBuf, /// Build directory pub target_directory: Utf8PathBuf, /// The workspace-level metadata object. Null if non-existent. #[serde(rename = "metadata", default, skip_serializing_if = "is_null")] pub workspace_metadata: serde_json::Value, /// The metadata format version
version: usize,
}
impl Metadata { /// Get the workspace's root package of this metadata instance. pubfn root_package(&self) -> Option<&Package> { match &self.resolve {
Some(resolve) => { // if dependencies are resolved, use Cargo's answer let root = resolve.root.as_ref()?; self.packages.iter().find(|pkg| &pkg.id == root)
}
None => { // if dependencies aren't resolved, check for a root package manually let root_manifest_path = self.workspace_root.join("Cargo.toml"); self.packages
.iter()
.find(|pkg| pkg.manifest_path == root_manifest_path)
}
}
}
/// Get the workspace packages. pubfn workspace_packages(&self) -> Vec<&Package> { self.packages
.iter()
.filter(|&p| self.workspace_members.contains(&p.id))
.collect()
}
}
impl<'a> std::ops::Index<&'a PackageId> for Metadata { type Output = Package;
/// The crate for which the metadata was read. pub root: Option<PackageId>,
}
#[derive(Clone, Serialize, Deserialize, Debug)] #[cfg_attr(feature = "builder", derive(Builder))] #[non_exhaustive] #[cfg_attr(feature = "builder", builder(pattern = "owned", setter(into)))] /// A node in a dependencies graph pubstruct Node { /// An opaque identifier for a package pub id: PackageId, /// Dependencies in a structured format. /// /// `deps` handles renamed dependencies whereas `dependencies` does not. #[serde(default)] pub deps: Vec<NodeDep>,
/// List of opaque identifiers for this node's dependencies. /// It doesn't support renamed dependencies. See `deps`. pub dependencies: Vec<PackageId>,
/// Features enabled on the crate #[serde(default)] pub features: Vec<String>,
}
#[derive(Clone, Serialize, Deserialize, Debug)] #[cfg_attr(feature = "builder", derive(Builder))] #[non_exhaustive] #[cfg_attr(feature = "builder", builder(pattern = "owned", setter(into)))] /// A dependency in a node pubstruct NodeDep { /// The name of the dependency's library target. /// If the crate was renamed, it is the new name. pub name: String, /// Package ID (opaque unique identifier) pub pkg: PackageId, /// The kinds of dependencies. /// /// This field was added in Rust 1.41. #[serde(default)] pub dep_kinds: Vec<DepKindInfo>,
}
#[derive(Clone, Serialize, Deserialize, Debug)] #[cfg_attr(feature = "builder", derive(Builder))] #[non_exhaustive] #[cfg_attr(feature = "builder", builder(pattern = "owned", setter(into)))] /// Information about a dependency kind. pubstruct DepKindInfo { /// The kind of dependency. #[serde(deserialize_with = "dependency::parse_dependency_kind")] pub kind: DependencyKind, /// The target platform for the dependency. /// /// This is `None` if it is not a target dependency. /// /// Use the [`Display`] trait to access the contents. /// /// By default all platform dependencies are included in the resolve /// graph. Use Cargo's `--filter-platform` flag if you only want to /// include dependencies for a specific platform. /// /// [`Display`]: std::fmt::Display pub target: Option<dependency::Platform>,
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] #[cfg_attr(feature = "builder", derive(Builder))] #[non_exhaustive] #[cfg_attr(feature = "builder", builder(pattern = "owned", setter(into)))] /// One or more crates described by a single `Cargo.toml` /// /// Each [`target`][Package::targets] of a `Package` will be built as a crate. /// For more information, see <https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html>. pubstruct Package { /// Name as given in the `Cargo.toml` pub name: String, /// Version given in the `Cargo.toml` pub version: Version, /// Authors given in the `Cargo.toml` #[serde(default)] pub authors: Vec<String>, /// An opaque identifier for a package pub id: PackageId, /// The source of the package, e.g. /// crates.io or `None` for local projects. pub source: Option<Source>, /// Description as given in the `Cargo.toml` pub description: Option<String>, /// List of dependencies of this particular package pub dependencies: Vec<Dependency>, /// License as given in the `Cargo.toml` pub license: Option<String>, /// If the package is using a nonstandard license, this key may be specified instead of /// `license`, and must point to a file relative to the manifest. pub license_file: Option<Utf8PathBuf>, /// Targets provided by the crate (lib, bin, example, test, ...) pub targets: Vec<Target>, /// Features provided by the crate, mapped to the features required by that feature. pub features: HashMap<String, Vec<String>>, /// Path containing the `Cargo.toml` pub manifest_path: Utf8PathBuf, /// Categories as given in the `Cargo.toml` #[serde(default)] pub categories: Vec<String>, /// Keywords as given in the `Cargo.toml` #[serde(default)] pub keywords: Vec<String>, /// Readme as given in the `Cargo.toml` pub readme: Option<Utf8PathBuf>, /// Repository as given in the `Cargo.toml` // can't use `url::Url` because that requires a more recent stable compiler pub repository: Option<String>, /// Homepage as given in the `Cargo.toml` /// /// On versions of cargo before 1.49, this will always be [`None`]. pub homepage: Option<String>, /// Documentation URL as given in the `Cargo.toml` /// /// On versions of cargo before 1.49, this will always be [`None`]. pub documentation: Option<String>, /// Default Rust edition for the package /// /// Beware that individual targets may specify their own edition in /// [`Target::edition`]. #[serde(default)] pub edition: Edition, /// Contents of the free form package.metadata section /// /// This contents can be serialized to a struct using serde: /// /// ```rust /// use serde::Deserialize; /// use serde_json::json; /// /// #[derive(Debug, Deserialize)] /// struct SomePackageMetadata { /// some_value: i32, /// } /// /// fn main() { /// let value = json!({ /// "some_value": 42, /// }); /// /// let package_metadata: SomePackageMetadata = serde_json::from_value(value).unwrap(); /// assert_eq!(package_metadata.some_value, 42); /// } /// /// ``` #[serde(default, skip_serializing_if = "is_null")] pub metadata: serde_json::Value, /// The name of a native library the package is linking to. pub links: Option<String>, /// List of registries to which this package may be published. /// /// Publishing is unrestricted if `None`, and forbidden if the `Vec` is empty. /// /// This is always `None` if running with a version of Cargo older than 1.39. pub publish: Option<Vec<String>>, /// The default binary to run by `cargo run`. /// /// This is always `None` if running with a version of Cargo older than 1.55. pub default_run: Option<String>, /// The minimum supported Rust version of this package. /// /// This is always `None` if running with a version of Cargo older than 1.58. pub rust_version: Option<VersionReq>,
}
impl Package { /// Full path to the license file if one is present in the manifest pubfn license_file(&self) -> Option<Utf8PathBuf> { self.license_file.as_ref().map(|file| { self.manifest_path
.parent()
.unwrap_or(&self.manifest_path)
.join(file)
})
}
/// Full path to the readme file if one is present in the manifest pubfn readme(&self) -> Option<Utf8PathBuf> { self.readme.as_ref().map(|file| { self.manifest_path
.parent()
.unwrap_or(&self.manifest_path)
.join(file)
})
}
}
/// The source of a package such as crates.io. #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] #[serde(transparent)] pubstruct Source { /// The underlying string representation of a source. pub repr: String,
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "builder", derive(Builder))] #[cfg_attr(feature = "builder", builder(pattern = "owned", setter(into)))] #[non_exhaustive] /// A single target (lib, bin, example, ...) provided by a crate pubstruct Target { /// Name as given in the `Cargo.toml` or generated from the file name pub name: String, /// Kind of target ("bin", "example", "test", "bench", "lib", "custom-build") pub kind: Vec<String>, /// Almost the same as `kind`, except when an example is a library instead of an executable. /// In that case `crate_types` contains things like `rlib` and `dylib` while `kind` is `example` #[serde(default)] #[cfg_attr(feature = "builder", builder(default))] pub crate_types: Vec<String>,
#[serde(default)] #[cfg_attr(feature = "builder", builder(default))] #[serde(rename = "required-features")] /// This target is built only if these features are enabled. /// It doesn't apply to `lib` targets. pub required_features: Vec<String>, /// Path to the main source file of the target pub src_path: Utf8PathBuf, /// Rust edition for this target #[serde(default)] #[cfg_attr(feature = "builder", builder(default))] pub edition: Edition, /// Whether or not this target has doc tests enabled, and the target is /// compatible with doc testing. /// /// This is always `true` if running with a version of Cargo older than 1.37. #[serde(default = "default_true")] #[cfg_attr(feature = "builder", builder(default = "true"))] pub doctest: bool, /// Whether or not this target is tested by default by `cargo test`. /// /// This is always `true` if running with a version of Cargo older than 1.47. #[serde(default = "default_true")] #[cfg_attr(feature = "builder", builder(default = "true"))] pub test: bool, /// Whether or not this target is documented by `cargo doc`. /// /// This is always `true` if running with a version of Cargo older than 1.50. #[serde(default = "default_true")] #[cfg_attr(feature = "builder", builder(default = "true"))] pub doc: bool,
}
/// Cargo features flags #[derive(Debug, Clone)] pubenum CargoOpt { /// Run cargo with `--features-all`
AllFeatures, /// Run cargo with `--no-default-features`
NoDefaultFeatures, /// Run cargo with `--features <FEATURES>`
SomeFeatures(Vec<String>),
}
/// A builder for configurating `cargo metadata` invocation. #[derive(Debug, Clone, Default)] pubstruct MetadataCommand { /// Path to `cargo` executable. If not set, this will use the /// the `$CARGO` environment variable, and if that is not set, will /// simply be `cargo`.
cargo_path: Option<PathBuf>, /// Path to `Cargo.toml`
manifest_path: Option<PathBuf>, /// Current directory of the `cargo metadata` process.
current_dir: Option<PathBuf>, /// Output information only about workspace members and don't fetch dependencies.
no_deps: bool, /// Collections of `CargoOpt::SomeFeatures(..)`
features: Vec<String>, /// Latched `CargoOpt::AllFeatures`
all_features: bool, /// Latched `CargoOpt::NoDefaultFeatures`
no_default_features: bool, /// Arbitrary command line flags to pass to `cargo`. These will be added /// to the end of the command line invocation.
other_options: Vec<String>, /// Arbitrary environment variables to set when running `cargo`. These will be merged into /// the calling environment, overriding any which clash.
env: HashMap<OsString, OsString>, /// Show stderr
verbose: bool,
}
impl MetadataCommand { /// Creates a default `cargo metadata` command, which will look for /// `Cargo.toml` in the ancestors of the current directory. pubfn new() -> MetadataCommand {
MetadataCommand::default()
} /// Path to `cargo` executable. If not set, this will use the /// the `$CARGO` environment variable, and if that is not set, will /// simply be `cargo`. pubfn cargo_path(&mutself, path: impl Into<PathBuf>) -> &'color:red'>mut MetadataCommand { self.cargo_path = Some(path.into()); self
} /// Path to `Cargo.toml` pubfn manifest_path(&mutself, path: impl Into<PathBuf>) -> &le='color:red'>mut MetadataCommand { self.manifest_path = Some(path.into()); self
} /// Current directory of the `cargo metadata` process. pubfn current_dir(&mutself, path: impl Into<PathBuf>) -> &='color:red'>mut MetadataCommand { self.current_dir = Some(path.into()); self
} /// Output information only about workspace members and don't fetch dependencies. pubfn no_deps(&mutself) -> &mut MetadataCommand { self.no_deps = true; self
} /// Which features to include. /// /// Call this multiple times to specify advanced feature configurations: /// /// ```no_run /// # use cargo_metadata::{CargoOpt, MetadataCommand}; /// MetadataCommand::new() /// .features(CargoOpt::NoDefaultFeatures) /// .features(CargoOpt::SomeFeatures(vec!["feat1".into(), "feat2".into()])) /// .features(CargoOpt::SomeFeatures(vec!["feat3".into()])) /// // ... /// # ; /// ``` /// /// # Panics /// /// `cargo metadata` rejects multiple `--no-default-features` flags. Similarly, the `features()` /// method panics when specifying multiple `CargoOpt::NoDefaultFeatures`: /// /// ```should_panic /// # use cargo_metadata::{CargoOpt, MetadataCommand}; /// MetadataCommand::new() /// .features(CargoOpt::NoDefaultFeatures) /// .features(CargoOpt::NoDefaultFeatures) // <-- panic! /// // ... /// # ; /// ``` /// /// The method also panics for multiple `CargoOpt::AllFeatures` arguments: /// /// ```should_panic /// # use cargo_metadata::{CargoOpt, MetadataCommand}; /// MetadataCommand::new() /// .features(CargoOpt::AllFeatures) /// .features(CargoOpt::AllFeatures) // <-- panic! /// // ... /// # ; /// ``` pubfn features(&mutself, features: CargoOpt) -> &mut MetadataCommand { match features {
CargoOpt::SomeFeatures(features) => self.features.extend(features),
CargoOpt::NoDefaultFeatures => {
assert!(
!self.no_default_features, "Do not supply CargoOpt::NoDefaultFeatures more than once!"
); self.no_default_features = true;
}
CargoOpt::AllFeatures => {
assert!(
!self.all_features, "Do not supply CargoOpt::AllFeatures more than once!"
); self.all_features = true;
}
} self
} /// Arbitrary command line flags to pass to `cargo`. These will be added /// to the end of the command line invocation. pubfn other_options(&mutself, options: impl Into<Vec<String>>) -> &n style='color:red'>mut MetadataCommand { self.other_options = options.into(); self
}
/// Arbitrary environment variables to set when running `cargo`. These will be merged into /// the calling environment, overriding any which clash. /// /// Some examples of when you may want to use this: /// 1. Setting cargo config values without needing a .cargo/config.toml file, e.g. to set /// `CARGO_NET_GIT_FETCH_WITH_CLI=true` /// 2. To specify a custom path to RUSTC if your rust toolchain components aren't laid out in /// the way cargo expects by default. /// /// ```no_run /// # use cargo_metadata::{CargoOpt, MetadataCommand}; /// MetadataCommand::new() /// .env("CARGO_NET_GIT_FETCH_WITH_CLI", "true") /// .env("RUSTC", "/path/to/rustc") /// // ... /// # ; /// ``` pubfn env<K: Into<OsString>, V: Into<OsString>>(
&mutself,
key: K,
val: V,
) -> &mut MetadataCommand { self.env.insert(key.into(), val.into()); self
}
/// Set whether to show stderr pubfn verbose(&mutself, verbose: bool) -> &>mut MetadataCommand { self.verbose = verbose; self
}
/// Builds a command for `cargo metadata`. This is the first /// part of the work of `exec`. pubfn cargo_command(&self) -> Command { let cargo = self
.cargo_path
.clone()
.or_else(|| env::var("CARGO").map(PathBuf::from).ok())
.unwrap_or_else(|| PathBuf::from("cargo")); letmut cmd = Command::new(cargo);
cmd.args(&["metadata", "--format-version", "1"]);
/// Parses `cargo metadata` output. `data` must have been /// produced by a command built with `cargo_command`. pubfn parse<T: AsRef<str>>(data: T) -> Result<Metadata> { let meta = serde_json::from_str(data.as_ref())?;
Ok(meta)
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.