]> git.proxmox.com Git - rustc.git/blame - vendor/cargo_metadata/src/lib.rs
New upstream version 1.47.0~beta.2+dfsg1
[rustc.git] / vendor / cargo_metadata / src / lib.rs
CommitLineData
ba9703b0
XL
1#![deny(missing_docs)]
2//! Structured access to the output of `cargo metadata` and `cargo --message-format=json`.
3//! Usually used from within a `cargo-*` executable
4//!
5//! See the [cargo book](https://doc.rust-lang.org/cargo/index.html) for
6//! details on cargo itself.
7//!
8//! ## Examples
9//!
ba9703b0 10//! ```rust
ba9703b0
XL
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"));
14//!
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());
19//! }
20//! Some(p) => {
21//! cmd.manifest_path(p.trim_start_matches("--manifest-path="));
22//! }
23//! None => {}
24//! };
25//!
26//! let _metadata = cmd.exec().unwrap();
27//! ```
28//!
ba9703b0
XL
29//! Pass features flags
30//!
31//! ```rust
32//! # // This should be kept in sync with the equivalent example in the readme.
33//! # extern crate cargo_metadata;
34//! # use std::path::Path;
35//! # fn main() {
36//! use cargo_metadata::{MetadataCommand, CargoOpt};
37//!
38//! let _metadata = MetadataCommand::new()
39//! .manifest_path("./Cargo.toml")
40//! .features(CargoOpt::AllFeatures)
41//! .exec()
42//! .unwrap();
43//! # }
44//! ```
45//!
46//! Parse message-format output:
47//!
48//! ```
49//! # extern crate cargo_metadata;
50//! use std::process::{Stdio, Command};
51//! use cargo_metadata::Message;
52//!
53//! let mut command = Command::new("cargo")
54//! .args(&["build", "--message-format=json"])
55//! .stdout(Stdio::piped())
56//! .spawn()
57//! .unwrap();
58//!
f035d41b
XL
59//! let reader = std::io::BufReader::new(command.stdout.take().unwrap());
60//! for message in cargo_metadata::Message::parse_stream(reader) {
ba9703b0
XL
61//! match message.unwrap() {
62//! Message::CompilerMessage(msg) => {
63//! println!("{:?}", msg);
64//! },
65//! Message::CompilerArtifact(artifact) => {
66//! println!("{:?}", artifact);
67//! },
68//! Message::BuildScriptExecuted(script) => {
69//! println!("{:?}", script);
70//! },
f035d41b
XL
71//! Message::BuildFinished(finished) => {
72//! println!("{:?}", finished);
73//! },
ba9703b0
XL
74//! _ => () // Unknown message
75//! }
76//! }
77//!
78//! let output = command.wait().expect("Couldn't get cargo's exit status");
79//! ```
80
ba9703b0
XL
81use std::collections::HashMap;
82use std::env;
83use std::fmt;
f035d41b 84use std::path::PathBuf;
ba9703b0
XL
85use std::process::Command;
86use std::str::from_utf8;
87
3dfed10e 88pub use semver::Version;
ba9703b0
XL
89
90pub use dependency::{Dependency, DependencyKind};
91use diagnostic::Diagnostic;
92pub use errors::{Error, Result};
f035d41b
XL
93#[allow(deprecated)]
94pub use messages::parse_messages;
3dfed10e
XL
95pub use messages::{
96 Artifact, ArtifactProfile, BuildFinished, BuildScript, CompilerMessage, Message, MessageIter,
97};
98use serde::{Deserialize, Serialize};
ba9703b0
XL
99
100mod dependency;
101pub mod diagnostic;
102mod errors;
103mod messages;
104
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.
108///
109/// `Metadata` can be indexed by `PackageId`.
110#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
111#[serde(transparent)]
112pub struct PackageId {
113 /// The underlying string representation of id.
114 pub repr: String,
115}
116
117impl std::fmt::Display for PackageId {
118 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119 fmt::Display::fmt(&self.repr, f)
120 }
121}
122
123#[derive(Clone, Serialize, Deserialize, Debug)]
124/// Starting point for metadata returned by `cargo metadata`
125pub 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>,
132 /// Workspace root
133 pub workspace_root: PathBuf,
134 /// Build directory
135 pub target_directory: PathBuf,
136 version: usize,
137 #[doc(hidden)]
138 #[serde(skip)]
139 __do_not_match_exhaustively: (),
140}
141
142impl<'a> std::ops::Index<&'a PackageId> for Metadata {
143 type Output = Package;
144
145 fn index(&self, idx: &'a PackageId) -> &Package {
146 self.packages
147 .iter()
148 .find(|p| p.id == *idx)
149 .unwrap_or_else(|| panic!("no package with this id: {:?}", idx))
150 }
151}
152
153#[derive(Clone, Serialize, Deserialize, Debug)]
154/// A dependency graph
155pub struct Resolve {
156 /// Nodes in a dependencies graph
157 pub nodes: Vec<Node>,
158
159 /// The crate for which the metadata was read.
160 pub root: Option<PackageId>,
161 #[doc(hidden)]
162 #[serde(skip)]
163 __do_not_match_exhaustively: (),
164}
165
166#[derive(Clone, Serialize, Deserialize, Debug)]
167/// A node in a dependencies graph
168pub struct Node {
169 /// An opaque identifier for a package
170 pub id: PackageId,
171 /// Dependencies in a structured format.
172 ///
173 /// `deps` handles renamed dependencies whereas `dependencies` does not.
174 #[serde(default)]
175 pub deps: Vec<NodeDep>,
176
177 /// List of opaque identifiers for this node's dependencies.
178 /// It doesn't support renamed dependencies. See `deps`.
179 pub dependencies: Vec<PackageId>,
180
181 /// Features enabled on the crate
182 #[serde(default)]
183 pub features: Vec<String>,
184 #[doc(hidden)]
185 #[serde(skip)]
186 __do_not_match_exhaustively: (),
187}
188
189#[derive(Clone, Serialize, Deserialize, Debug)]
190/// A dependency in a node
191pub struct NodeDep {
192 /// The name of the dependency's library target.
193 /// If the crate was renamed, it is the new name.
194 pub name: String,
195 /// Package ID (opaque unique identifier)
196 pub pkg: PackageId,
197 /// The kinds of dependencies.
198 ///
199 /// This field was added in Rust 1.41.
200 #[serde(default)]
201 pub dep_kinds: Vec<DepKindInfo>,
202 #[doc(hidden)]
203 #[serde(skip)]
204 __do_not_match_exhaustively: (),
205}
206
207#[derive(Clone, Serialize, Deserialize, Debug)]
208/// Information about a dependency kind.
209pub 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.
214 ///
215 /// This is `None` if it is not a target dependency.
216 ///
217 /// Use the [`Display`] trait to access the contents.
218 ///
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.
222 ///
223 /// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
224 pub target: Option<dependency::Platform>,
225 #[doc(hidden)]
226 #[serde(skip)]
227 __do_not_match_exhaustively: (),
228}
229
230#[derive(Clone, Serialize, Deserialize, Debug)]
231/// A crate
232pub struct Package {
233 /// Name as given in the `Cargo.toml`
234 pub name: String,
235 /// Version given in the `Cargo.toml`
236 pub version: Version,
237 /// Authors given in the `Cargo.toml`
238 #[serde(default)]
239 pub authors: Vec<String>,
240 /// An opaque identifier for a package
241 pub id: PackageId,
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`
261 #[serde(default)]
262 pub categories: Vec<String>,
263 /// Keywords as given in the `Cargo.toml`
264 #[serde(default)]
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
272 ///
273 /// Beware that individual targets may specify their own edition in
274 /// [`Target::edition`](struct.Target.html#structfield.edition).
275 #[serde(default = "edition_default")]
276 pub edition: String,
277 /// Contents of the free form package.metadata section
278 ///
279 /// This contents can be serialized to a struct using serde:
280 ///
281 /// ```rust
3dfed10e
XL
282 /// use serde::Deserialize;
283 /// use serde_json::json;
ba9703b0
XL
284 ///
285 /// #[derive(Debug, Deserialize)]
286 /// struct SomePackageMetadata {
287 /// some_value: i32,
288 /// }
289 ///
290 /// fn main() {
291 /// let value = json!({
292 /// "some_value": 42,
293 /// });
294 ///
295 /// let package_metadata: SomePackageMetadata = serde_json::from_value(value).unwrap();
296 /// assert_eq!(package_metadata.some_value, 42);
297 /// }
298 ///
299 /// ```
300 #[serde(default)]
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.
305 ///
306 /// Publishing is unrestricted if `None`, and forbidden if the `Vec` is empty.
307 ///
308 /// This is always `None` if running with a version of Cargo older than 1.39.
309 pub publish: Option<Vec<String>>,
310 #[doc(hidden)]
311 #[serde(skip)]
312 __do_not_match_exhaustively: (),
313}
314
315impl Package {
316 /// Full path to the license file if one is present in the manifest
317 pub fn license_file(&self) -> Option<PathBuf> {
3dfed10e
XL
318 self.license_file.as_ref().map(|file| {
319 self.manifest_path
320 .parent()
321 .unwrap_or(&self.manifest_path)
322 .join(file)
323 })
ba9703b0
XL
324 }
325
326 /// Full path to the readme file if one is present in the manifest
327 pub fn readme(&self) -> Option<PathBuf> {
328 self.readme
329 .as_ref()
330 .map(|file| self.manifest_path.join(file))
331 }
332}
333
334/// The source of a package such as crates.io.
335#[derive(Clone, Serialize, Deserialize, Debug)]
336#[serde(transparent)]
f035d41b
XL
337pub struct Source {
338 /// The underlying string representation of a source.
339 pub repr: String,
340}
ba9703b0
XL
341
342impl Source {
343 /// Returns true if the source is crates.io.
344 pub fn is_crates_io(&self) -> bool {
f035d41b 345 self.repr == "registry+https://github.com/rust-lang/crates.io-index"
ba9703b0
XL
346 }
347}
348
349impl std::fmt::Display for Source {
350 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f035d41b 351 fmt::Display::fmt(&self.repr, f)
ba9703b0
XL
352 }
353}
354
355#[derive(Clone, Serialize, Deserialize, Debug)]
356/// A single target (lib, bin, example, ...) provided by a crate
357pub struct Target {
358 /// Name as given in the `Cargo.toml` or generated from the file name
359 pub name: String,
360 /// Kind of target ("bin", "example", "test", "bench", "lib")
361 pub kind: Vec<String>,
f035d41b 362 /// Almost the same as `kind`, except when an example is a library instead of an executable.
ba9703b0
XL
363 /// In that case `crate_types` contains things like `rlib` and `dylib` while `kind` is `example`
364 #[serde(default)]
365 pub crate_types: Vec<String>,
366
367 #[serde(default)]
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")]
376 pub edition: String,
377 /// Whether or not this target has doc tests enabled, and the target is
378 /// compatible with doc testing.
379 ///
380 /// This is always `true` if running with a version of Cargo older than 1.37.
381 #[serde(default = "default_true")]
382 pub doctest: bool,
383 #[doc(hidden)]
384 #[serde(skip)]
385 __do_not_match_exhaustively: (),
386}
387
388fn default_true() -> bool {
389 true
390}
391
392fn edition_default() -> String {
393 "2015".to_string()
394}
395
396/// Cargo features flags
397#[derive(Debug, Clone)]
398pub enum CargoOpt {
399 /// Run cargo with `--features-all`
400 AllFeatures,
401 /// Run cargo with `--no-default-features`
402 NoDefaultFeatures,
403 /// Run cargo with `--features <FEATURES>`
404 SomeFeatures(Vec<String>),
405}
406
407/// A builder for configurating `cargo metadata` invocation.
408#[derive(Debug, Clone, Default)]
409pub struct MetadataCommand {
410 cargo_path: Option<PathBuf>,
411 manifest_path: Option<PathBuf>,
412 current_dir: Option<PathBuf>,
413 no_deps: bool,
3dfed10e
XL
414 /// Collections of `CargoOpt::SomeFeatures(..)`
415 features: Vec<String>,
416 /// Latched `CargoOpt::AllFeatures`
417 all_features: bool,
418 /// Latched `CargoOpt::NoDefaultFeatures`
419 no_default_features: bool,
ba9703b0
XL
420 other_options: Vec<String>,
421}
422
423impl 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()
428 }
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`.
f035d41b
XL
432 pub fn cargo_path(&mut self, path: impl Into<PathBuf>) -> &mut MetadataCommand {
433 self.cargo_path = Some(path.into());
ba9703b0
XL
434 self
435 }
436 /// Path to `Cargo.toml`
f035d41b
XL
437 pub fn manifest_path(&mut self, path: impl Into<PathBuf>) -> &mut MetadataCommand {
438 self.manifest_path = Some(path.into());
ba9703b0
XL
439 self
440 }
441 /// Current directory of the `cargo metadata` process.
f035d41b
XL
442 pub fn current_dir(&mut self, path: impl Into<PathBuf>) -> &mut MetadataCommand {
443 self.current_dir = Some(path.into());
ba9703b0
XL
444 self
445 }
446 /// Output information only about the root package and don't fetch dependencies.
447 pub fn no_deps(&mut self) -> &mut MetadataCommand {
448 self.no_deps = true;
449 self
450 }
451 /// Which features to include.
3dfed10e
XL
452 ///
453 /// Call this multiple times to specify advanced feature configurations:
454 ///
455 /// ```no_run
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()]))
461 /// // ...
462 /// # ;
463 /// ```
464 ///
465 /// # Panics
466 ///
467 /// `cargo metadata` rejects multiple `--no-default-features` flags. Similarly, the `features()`
468 /// method panics when specifiying multiple `CargoOpt::NoDefaultFeatures`:
469 ///
470 /// ```should_panic
471 /// # use cargo_metadata::{CargoOpt, MetadataCommand};
472 /// MetadataCommand::new()
473 /// .features(CargoOpt::NoDefaultFeatures)
474 /// .features(CargoOpt::NoDefaultFeatures) // <-- panic!
475 /// // ...
476 /// # ;
477 /// ```
478 ///
479 /// The method also panics for multiple `CargoOpt::AllFeatures` arguments:
480 ///
481 /// ```should_panic
482 /// # use cargo_metadata::{CargoOpt, MetadataCommand};
483 /// MetadataCommand::new()
484 /// .features(CargoOpt::AllFeatures)
485 /// .features(CargoOpt::AllFeatures) // <-- panic!
486 /// // ...
487 /// # ;
488 /// ```
ba9703b0 489 pub fn features(&mut self, features: CargoOpt) -> &mut MetadataCommand {
3dfed10e
XL
490 match features {
491 CargoOpt::SomeFeatures(features) => self.features.extend(features),
492 CargoOpt::NoDefaultFeatures => {
493 assert!(
494 !self.no_default_features,
495 "Do not supply CargoOpt::NoDefaultFeatures more than once!"
496 );
497 self.no_default_features = true;
498 }
499 CargoOpt::AllFeatures => {
500 assert!(
501 !self.all_features,
502 "Do not supply CargoOpt::AllFeatures more than once!"
503 );
504 self.all_features = true;
505 }
506 }
ba9703b0
XL
507 self
508 }
509 /// Arbitrary command line flags to pass to `cargo`. These will be added
510 /// to the end of the command line invocation.
f035d41b
XL
511 pub fn other_options(&mut self, options: impl Into<Vec<String>>) -> &mut MetadataCommand {
512 self.other_options = options.into();
ba9703b0
XL
513 self
514 }
f035d41b
XL
515
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> {
ba9703b0
XL
519 let cargo = self
520 .cargo_path
521 .clone()
3dfed10e 522 .or_else(|| env::var("CARGO").map(PathBuf::from).ok())
ba9703b0
XL
523 .unwrap_or_else(|| PathBuf::from("cargo"));
524 let mut cmd = Command::new(cargo);
525 cmd.args(&["metadata", "--format-version", "1"]);
526
527 if self.no_deps {
528 cmd.arg("--no-deps");
529 }
530
531 if let Some(path) = self.current_dir.as_ref() {
532 cmd.current_dir(path);
533 }
534
3dfed10e
XL
535 if !self.features.is_empty() {
536 cmd.arg("--features").arg(self.features.join(","));
537 }
538 if self.all_features {
539 cmd.arg("--all-features");
540 }
541 if self.no_default_features {
542 cmd.arg("--no-default-features");
ba9703b0
XL
543 }
544
545 if let Some(manifest_path) = &self.manifest_path {
546 cmd.arg("--manifest-path").arg(manifest_path.as_os_str());
547 }
548 cmd.args(&self.other_options);
f035d41b
XL
549
550 Ok(cmd)
551 }
552
553 /// Parses `cargo metadata` output. `data` must have been
554 /// produced by a command built with `cargo_command`.
3dfed10e 555 pub fn parse<T: AsRef<str>>(data: T) -> Result<Metadata> {
f035d41b
XL
556 let meta = serde_json::from_str(data.as_ref())?;
557 Ok(meta)
558 }
559
560 /// Runs configured `cargo metadata` and returns parsed `Metadata`.
561 pub fn exec(&self) -> Result<Metadata> {
562 let mut cmd = self.cargo_command()?;
ba9703b0
XL
563 let output = cmd.output()?;
564 if !output.status.success() {
565 return Err(Error::CargoMetadata {
566 stderr: String::from_utf8(output.stderr)?,
567 });
568 }
569 let stdout = from_utf8(&output.stdout)?
570 .lines()
571 .find(|line| line.starts_with('{'))
572 .ok_or_else(|| Error::NoJson)?;
f035d41b 573 Self::parse(stdout)
ba9703b0
XL
574 }
575}