2 //! Structured access to the output of `cargo metadata` and `cargo --message-format=json`.
3 //! Usually used from within a `cargo-*` executable
5 //! See the [cargo book](https://doc.rust-lang.org/cargo/index.html) for
6 //! details on cargo itself.
11 //! # extern crate cargo_metadata;
12 //! # use std::path::Path;
13 //! let mut args = std::env::args().skip_while(|val| !val.starts_with("--manifest-path"));
15 //! let mut cmd = cargo_metadata::MetadataCommand::new();
16 //! let manifest_path = match args.next() {
17 //! Some(ref p) if p == "--manifest-path" => {
18 //! cmd.manifest_path(args.next().unwrap());
21 //! cmd.manifest_path(p.trim_start_matches("--manifest-path="));
26 //! let _metadata = cmd.exec().unwrap();
29 //! Pass features flags
32 //! # // This should be kept in sync with the equivalent example in the readme.
33 //! # extern crate cargo_metadata;
34 //! # use std::path::Path;
36 //! use cargo_metadata::{MetadataCommand, CargoOpt};
38 //! let _metadata = MetadataCommand::new()
39 //! .manifest_path("./Cargo.toml")
40 //! .features(CargoOpt::AllFeatures)
46 //! Parse message-format output:
49 //! # extern crate cargo_metadata;
50 //! use std::process::{Stdio, Command};
51 //! use cargo_metadata::Message;
53 //! let mut command = Command::new("cargo")
54 //! .args(&["build", "--message-format=json"])
55 //! .stdout(Stdio::piped())
59 //! let reader = std::io::BufReader::new(command.stdout.take().unwrap());
60 //! for message in cargo_metadata::Message::parse_stream(reader) {
61 //! match message.unwrap() {
62 //! Message::CompilerMessage(msg) => {
63 //! println!("{:?}", msg);
65 //! Message::CompilerArtifact(artifact) => {
66 //! println!("{:?}", artifact);
68 //! Message::BuildScriptExecuted(script) => {
69 //! println!("{:?}", script);
71 //! Message::BuildFinished(finished) => {
72 //! println!("{:?}", finished);
74 //! _ => () // Unknown message
78 //! let output = command.wait().expect("Couldn't get cargo's exit status");
81 use std
::collections
::HashMap
;
84 use std
::path
::PathBuf
;
85 use std
::process
::Command
;
86 use std
::str::from_utf8
;
88 pub use semver
::Version
;
90 pub use dependency
::{Dependency, DependencyKind}
;
91 use diagnostic
::Diagnostic
;
92 pub use errors
::{Error, Result}
;
94 pub use messages
::parse_messages
;
96 Artifact
, ArtifactProfile
, BuildFinished
, BuildScript
, CompilerMessage
, Message
, MessageIter
,
98 use serde
::{Deserialize, Serialize}
;
105 /// An "opaque" identifier for a package.
106 /// It is possible to inspect the `repr` field, if the need arises, but its
107 /// precise format is an implementation detail and is subject to change.
109 /// `Metadata` can be indexed by `PackageId`.
110 #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
111 #[serde(transparent)]
112 pub struct PackageId
{
113 /// The underlying string representation of id.
117 impl std
::fmt
::Display
for PackageId
{
118 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
119 fmt
::Display
::fmt(&self.repr
, f
)
123 #[derive(Clone, Serialize, Deserialize, Debug)]
124 /// Starting point for metadata returned by `cargo metadata`
125 pub struct Metadata
{
126 /// A list of all crates referenced by this crate (and the crate itself)
127 pub packages
: Vec
<Package
>,
128 /// A list of all workspace members
129 pub workspace_members
: Vec
<PackageId
>,
130 /// Dependencies graph
131 pub resolve
: Option
<Resolve
>,
133 pub workspace_root
: PathBuf
,
135 pub target_directory
: PathBuf
,
139 __do_not_match_exhaustively
: (),
142 impl<'a
> std
::ops
::Index
<&'a PackageId
> for Metadata
{
143 type Output
= Package
;
145 fn index(&self, idx
: &'a PackageId
) -> &Package
{
148 .find(|p
| p
.id
== *idx
)
149 .unwrap_or_else(|| panic
!("no package with this id: {:?}", idx
))
153 #[derive(Clone, Serialize, Deserialize, Debug)]
154 /// A dependency graph
156 /// Nodes in a dependencies graph
157 pub nodes
: Vec
<Node
>,
159 /// The crate for which the metadata was read.
160 pub root
: Option
<PackageId
>,
163 __do_not_match_exhaustively
: (),
166 #[derive(Clone, Serialize, Deserialize, Debug)]
167 /// A node in a dependencies graph
169 /// An opaque identifier for a package
171 /// Dependencies in a structured format.
173 /// `deps` handles renamed dependencies whereas `dependencies` does not.
175 pub deps
: Vec
<NodeDep
>,
177 /// List of opaque identifiers for this node's dependencies.
178 /// It doesn't support renamed dependencies. See `deps`.
179 pub dependencies
: Vec
<PackageId
>,
181 /// Features enabled on the crate
183 pub features
: Vec
<String
>,
186 __do_not_match_exhaustively
: (),
189 #[derive(Clone, Serialize, Deserialize, Debug)]
190 /// A dependency in a node
192 /// The name of the dependency's library target.
193 /// If the crate was renamed, it is the new name.
195 /// Package ID (opaque unique identifier)
197 /// The kinds of dependencies.
199 /// This field was added in Rust 1.41.
201 pub dep_kinds
: Vec
<DepKindInfo
>,
204 __do_not_match_exhaustively
: (),
207 #[derive(Clone, Serialize, Deserialize, Debug)]
208 /// Information about a dependency kind.
209 pub struct DepKindInfo
{
210 /// The kind of dependency.
211 #[serde(deserialize_with = "dependency::parse_dependency_kind")]
212 pub kind
: DependencyKind
,
213 /// The target platform for the dependency.
215 /// This is `None` if it is not a target dependency.
217 /// Use the [`Display`] trait to access the contents.
219 /// By default all platform dependencies are included in the resolve
220 /// graph. Use Cargo's `--filter-platform` flag if you only want to
221 /// include dependencies for a specific platform.
223 /// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
224 pub target
: Option
<dependency
::Platform
>,
227 __do_not_match_exhaustively
: (),
230 #[derive(Clone, Serialize, Deserialize, Debug)]
233 /// Name as given in the `Cargo.toml`
235 /// Version given in the `Cargo.toml`
236 pub version
: Version
,
237 /// Authors given in the `Cargo.toml`
239 pub authors
: Vec
<String
>,
240 /// An opaque identifier for a package
242 /// The source of the package, e.g.
243 /// crates.io or `None` for local projects.
244 pub source
: Option
<Source
>,
245 /// Description as given in the `Cargo.toml`
246 pub description
: Option
<String
>,
247 /// List of dependencies of this particular package
248 pub dependencies
: Vec
<Dependency
>,
249 /// License as given in the `Cargo.toml`
250 pub license
: Option
<String
>,
251 /// If the package is using a nonstandard license, this key may be specified instead of
252 /// `license`, and must point to a file relative to the manifest.
253 pub license_file
: Option
<PathBuf
>,
254 /// Targets provided by the crate (lib, bin, example, test, ...)
255 pub targets
: Vec
<Target
>,
256 /// Features provided by the crate, mapped to the features required by that feature.
257 pub features
: HashMap
<String
, Vec
<String
>>,
258 /// Path containing the `Cargo.toml`
259 pub manifest_path
: PathBuf
,
260 /// Categories as given in the `Cargo.toml`
262 pub categories
: Vec
<String
>,
263 /// Keywords as given in the `Cargo.toml`
265 pub keywords
: Vec
<String
>,
266 /// Readme as given in the `Cargo.toml`
267 pub readme
: Option
<PathBuf
>,
268 /// Repository as given in the `Cargo.toml`
269 // can't use `url::Url` because that requires a more recent stable compiler
270 pub repository
: Option
<String
>,
271 /// Default Rust edition for the package
273 /// Beware that individual targets may specify their own edition in
274 /// [`Target::edition`](struct.Target.html#structfield.edition).
275 #[serde(default = "edition_default")]
277 /// Contents of the free form package.metadata section
279 /// This contents can be serialized to a struct using serde:
282 /// use serde::Deserialize;
283 /// use serde_json::json;
285 /// #[derive(Debug, Deserialize)]
286 /// struct SomePackageMetadata {
291 /// let value = json!({
292 /// "some_value": 42,
295 /// let package_metadata: SomePackageMetadata = serde_json::from_value(value).unwrap();
296 /// assert_eq!(package_metadata.some_value, 42);
301 pub metadata
: serde_json
::Value
,
302 /// The name of a native library the package is linking to.
303 pub links
: Option
<String
>,
304 /// List of registries to which this package may be published.
306 /// Publishing is unrestricted if `None`, and forbidden if the `Vec` is empty.
308 /// This is always `None` if running with a version of Cargo older than 1.39.
309 pub publish
: Option
<Vec
<String
>>,
312 __do_not_match_exhaustively
: (),
316 /// Full path to the license file if one is present in the manifest
317 pub fn license_file(&self) -> Option
<PathBuf
> {
318 self.license_file
.as_ref().map(|file
| {
321 .unwrap_or(&self.manifest_path
)
326 /// Full path to the readme file if one is present in the manifest
327 pub fn readme(&self) -> Option
<PathBuf
> {
330 .map(|file
| self.manifest_path
.join(file
))
334 /// The source of a package such as crates.io.
335 #[derive(Clone, Serialize, Deserialize, Debug)]
336 #[serde(transparent)]
338 /// The underlying string representation of a source.
343 /// Returns true if the source is crates.io.
344 pub fn is_crates_io(&self) -> bool
{
345 self.repr
== "registry+https://github.com/rust-lang/crates.io-index"
349 impl std
::fmt
::Display
for Source
{
350 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
351 fmt
::Display
::fmt(&self.repr
, f
)
355 #[derive(Clone, Serialize, Deserialize, Debug)]
356 /// A single target (lib, bin, example, ...) provided by a crate
358 /// Name as given in the `Cargo.toml` or generated from the file name
360 /// Kind of target ("bin", "example", "test", "bench", "lib")
361 pub kind
: Vec
<String
>,
362 /// Almost the same as `kind`, except when an example is a library instead of an executable.
363 /// In that case `crate_types` contains things like `rlib` and `dylib` while `kind` is `example`
365 pub crate_types
: Vec
<String
>,
368 #[serde(rename = "required-features")]
369 /// This target is built only if these features are enabled.
370 /// It doesn't apply to `lib` targets.
371 pub required_features
: Vec
<String
>,
372 /// Path to the main source file of the target
373 pub src_path
: PathBuf
,
374 /// Rust edition for this target
375 #[serde(default = "edition_default")]
377 /// Whether or not this target has doc tests enabled, and the target is
378 /// compatible with doc testing.
380 /// This is always `true` if running with a version of Cargo older than 1.37.
381 #[serde(default = "default_true")]
385 __do_not_match_exhaustively
: (),
388 fn default_true() -> bool
{
392 fn edition_default() -> String
{
396 /// Cargo features flags
397 #[derive(Debug, Clone)]
399 /// Run cargo with `--features-all`
401 /// Run cargo with `--no-default-features`
403 /// Run cargo with `--features <FEATURES>`
404 SomeFeatures(Vec
<String
>),
407 /// A builder for configurating `cargo metadata` invocation.
408 #[derive(Debug, Clone, Default)]
409 pub struct MetadataCommand
{
410 cargo_path
: Option
<PathBuf
>,
411 manifest_path
: Option
<PathBuf
>,
412 current_dir
: Option
<PathBuf
>,
414 /// Collections of `CargoOpt::SomeFeatures(..)`
415 features
: Vec
<String
>,
416 /// Latched `CargoOpt::AllFeatures`
418 /// Latched `CargoOpt::NoDefaultFeatures`
419 no_default_features
: bool
,
420 other_options
: Vec
<String
>,
423 impl MetadataCommand
{
424 /// Creates a default `cargo metadata` command, which will look for
425 /// `Cargo.toml` in the ancestors of the current directory.
426 pub fn new() -> MetadataCommand
{
427 MetadataCommand
::default()
429 /// Path to `cargo` executable. If not set, this will use the
430 /// the `$CARGO` environment variable, and if that is not set, will
431 /// simply be `cargo`.
432 pub fn cargo_path(&mut self, path
: impl Into
<PathBuf
>) -> &mut MetadataCommand
{
433 self.cargo_path
= Some(path
.into());
436 /// Path to `Cargo.toml`
437 pub fn manifest_path(&mut self, path
: impl Into
<PathBuf
>) -> &mut MetadataCommand
{
438 self.manifest_path
= Some(path
.into());
441 /// Current directory of the `cargo metadata` process.
442 pub fn current_dir(&mut self, path
: impl Into
<PathBuf
>) -> &mut MetadataCommand
{
443 self.current_dir
= Some(path
.into());
446 /// Output information only about the root package and don't fetch dependencies.
447 pub fn no_deps(&mut self) -> &mut MetadataCommand
{
451 /// Which features to include.
453 /// Call this multiple times to specify advanced feature configurations:
456 /// # use cargo_metadata::{CargoOpt, MetadataCommand};
457 /// MetadataCommand::new()
458 /// .features(CargoOpt::NoDefaultFeatures)
459 /// .features(CargoOpt::SomeFeatures(vec!["feat1".into(), "feat2".into()]))
460 /// .features(CargoOpt::SomeFeatures(vec!["feat3".into()]))
467 /// `cargo metadata` rejects multiple `--no-default-features` flags. Similarly, the `features()`
468 /// method panics when specifiying multiple `CargoOpt::NoDefaultFeatures`:
471 /// # use cargo_metadata::{CargoOpt, MetadataCommand};
472 /// MetadataCommand::new()
473 /// .features(CargoOpt::NoDefaultFeatures)
474 /// .features(CargoOpt::NoDefaultFeatures) // <-- panic!
479 /// The method also panics for multiple `CargoOpt::AllFeatures` arguments:
482 /// # use cargo_metadata::{CargoOpt, MetadataCommand};
483 /// MetadataCommand::new()
484 /// .features(CargoOpt::AllFeatures)
485 /// .features(CargoOpt::AllFeatures) // <-- panic!
489 pub fn features(&mut self, features
: CargoOpt
) -> &mut MetadataCommand
{
491 CargoOpt
::SomeFeatures(features
) => self.features
.extend(features
),
492 CargoOpt
::NoDefaultFeatures
=> {
494 !self.no_default_features
,
495 "Do not supply CargoOpt::NoDefaultFeatures more than once!"
497 self.no_default_features
= true;
499 CargoOpt
::AllFeatures
=> {
502 "Do not supply CargoOpt::AllFeatures more than once!"
504 self.all_features
= true;
509 /// Arbitrary command line flags to pass to `cargo`. These will be added
510 /// to the end of the command line invocation.
511 pub fn other_options(&mut self, options
: impl Into
<Vec
<String
>>) -> &mut MetadataCommand
{
512 self.other_options
= options
.into();
516 /// Builds a command for `cargo metadata`. This is the first
517 /// part of the work of `exec`.
518 pub fn cargo_command(&self) -> Result
<Command
> {
522 .or_else(|| env
::var("CARGO").map(PathBuf
::from
).ok())
523 .unwrap_or_else(|| PathBuf
::from("cargo"));
524 let mut cmd
= Command
::new(cargo
);
525 cmd
.args(&["metadata", "--format-version", "1"]);
528 cmd
.arg("--no-deps");
531 if let Some(path
) = self.current_dir
.as_ref() {
532 cmd
.current_dir(path
);
535 if !self.features
.is_empty() {
536 cmd
.arg("--features").arg(self.features
.join(","));
538 if self.all_features
{
539 cmd
.arg("--all-features");
541 if self.no_default_features
{
542 cmd
.arg("--no-default-features");
545 if let Some(manifest_path
) = &self.manifest_path
{
546 cmd
.arg("--manifest-path").arg(manifest_path
.as_os_str());
548 cmd
.args(&self.other_options
);
553 /// Parses `cargo metadata` output. `data` must have been
554 /// produced by a command built with `cargo_command`.
555 pub fn parse
<T
: AsRef
<str>>(data
: T
) -> Result
<Metadata
> {
556 let meta
= serde_json
::from_str(data
.as_ref())?
;
560 /// Runs configured `cargo metadata` and returns parsed `Metadata`.
561 pub fn exec(&self) -> Result
<Metadata
> {
562 let mut cmd
= self.cargo_command()?
;
563 let output
= cmd
.output()?
;
564 if !output
.status
.success() {
565 return Err(Error
::CargoMetadata
{
566 stderr
: String
::from_utf8(output
.stderr
)?
,
569 let stdout
= from_utf8(&output
.stdout
)?
571 .find(|line
| line
.starts_with('
{'
))
572 .ok_or_else(|| Error
::NoJson
)?
;