Add unit-graph JSON output.

This commit is contained in:
Eric Huss 2020-03-08 15:12:18 -07:00
parent 94093c270b
commit aa80a984c0
24 changed files with 531 additions and 37 deletions

View File

@ -44,6 +44,7 @@ pub fn cli() -> App {
"no-fail-fast",
"Run all benchmarks regardless of failure",
))
.arg_unit_graph()
.after_help(
"\
The benchmark filtering argument BENCHNAME and all the arguments following the

View File

@ -41,6 +41,7 @@ pub fn cli() -> App {
.arg_manifest_path()
.arg_message_format()
.arg_build_plan()
.arg_unit_graph()
.after_help(
"\
All packages in the workspace are built if the `--workspace` flag is supplied. The

View File

@ -33,6 +33,7 @@ pub fn cli() -> App {
.arg_target_dir()
.arg_manifest_path()
.arg_message_format()
.arg_unit_graph()
.after_help(
"\
If the `--package` argument is given, then SPEC is a package ID specification

View File

@ -30,6 +30,7 @@ pub fn cli() -> App {
.arg_target_dir()
.arg_manifest_path()
.arg_message_format()
.arg_unit_graph()
.after_help(
"\
By default the documentation for the local package and all dependencies is

View File

@ -24,6 +24,7 @@ pub fn cli() -> App {
.arg_target_dir()
.arg_manifest_path()
.arg_message_format()
.arg_unit_graph()
.after_help(
"\
If neither `--bin` nor `--example` are given, then if the package only has one

View File

@ -29,6 +29,7 @@ pub fn cli() -> App {
.arg_target_dir()
.arg_manifest_path()
.arg_message_format()
.arg_unit_graph()
.after_help(
"\
The specified target for the current package (or package specified by SPEC if

View File

@ -33,6 +33,7 @@ pub fn cli() -> App {
.arg_target_dir()
.arg_manifest_path()
.arg_message_format()
.arg_unit_graph()
.after_help(
"\
The specified target for the current package (or package specified by SPEC if

View File

@ -54,6 +54,7 @@ pub fn cli() -> App {
.arg_target_dir()
.arg_manifest_path()
.arg_message_format()
.arg_unit_graph()
.after_help(
"\
The test filtering argument TESTNAME and all the arguments following the

View File

@ -22,6 +22,8 @@ pub struct BuildConfig {
pub force_rebuild: bool,
/// Output a build plan to stdout instead of actually compiling.
pub build_plan: bool,
/// Output the unit graph to stdout instead of actually compiling.
pub unit_graph: bool,
/// An optional override of the rustc process for primary units
pub primary_unit_rustc: Option<ProcessBuilder>,
pub rustfix_diagnostic_server: RefCell<Option<RustfixDiagnosticServer>>,
@ -79,6 +81,7 @@ impl BuildConfig {
message_format: MessageFormat::Human,
force_rebuild: false,
build_plan: false,
unit_graph: false,
primary_unit_rustc: None,
rustfix_diagnostic_server: RefCell::new(None),
})

View File

@ -9,7 +9,7 @@ use std::path::Path;
/// compilations, where cross compilations happen at the request of `--target`
/// and host compilations happen for things like build scripts and procedural
/// macros.
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord, Serialize)]
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord)]
pub enum CompileKind {
/// Attached to a unit that is compiled for the "host" system or otherwise
/// is compiled without a `--target` flag. This is used for procedural
@ -41,6 +41,18 @@ impl CompileKind {
}
}
impl serde::ser::Serialize for CompileKind {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
match self {
CompileKind::Host => None::<&str>.serialize(s),
CompileKind::Target(t) => Some(t.name).serialize(s),
}
}
}
/// Abstraction for the representation of a compilation target that Cargo has.
///
/// Compilation targets are one of two things right now:

View File

@ -16,7 +16,7 @@ use super::custom_build::{self, BuildDeps, BuildScriptOutputs, BuildScripts};
use super::fingerprint::Fingerprint;
use super::job_queue::JobQueue;
use super::layout::Layout;
use super::unit_dependencies::{UnitDep, UnitGraph};
use super::unit_graph::{UnitDep, UnitGraph};
use super::{BuildContext, Compilation, CompileKind, CompileMode, Executor, FileFlavor};
mod compilation_files;

View File

@ -202,7 +202,7 @@ use serde::de;
use serde::ser;
use serde::{Deserialize, Serialize};
use crate::core::compiler::unit_dependencies::UnitDep;
use crate::core::compiler::unit_graph::UnitDep;
use crate::core::{InternedString, Package};
use crate::util;
use crate::util::errors::{CargoResult, CargoResultExt};

View File

@ -1,4 +1,4 @@
use super::unit_dependencies::UnitGraph;
use super::unit_graph::UnitGraph;
use crate::core::{PackageId, Resolve};
use crate::util::errors::CargoResult;
use std::collections::{HashMap, HashSet};

View File

@ -15,6 +15,7 @@ pub mod standard_lib;
mod timings;
mod unit;
pub mod unit_dependencies;
pub mod unit_graph;
use std::env;
use std::ffi::{OsStr, OsString};
@ -38,7 +39,7 @@ pub use self::job::Freshness;
use self::job::{Job, Work};
use self::job_queue::{JobQueue, JobState};
use self::output_depinfo::output_depinfo;
use self::unit_dependencies::UnitDep;
use self::unit_graph::UnitDep;
pub use crate::core::compiler::unit::{Unit, UnitInterner};
use crate::core::manifest::TargetSourcePath;
use crate::core::profiles::{Lto, PanicStrategy, Profile};

View File

@ -15,6 +15,7 @@
//! (for example, with and without tests), so we actually build a dependency
//! graph of `Unit`s, which capture these properties.
use crate::core::compiler::unit_graph::{UnitDep, UnitGraph};
use crate::core::compiler::Unit;
use crate::core::compiler::{BuildContext, CompileKind, CompileMode};
use crate::core::dependency::DepKind;
@ -27,25 +28,6 @@ use crate::CargoResult;
use log::trace;
use std::collections::{HashMap, HashSet};
/// The dependency graph of Units.
pub type UnitGraph<'a> = HashMap<Unit<'a>, Vec<UnitDep<'a>>>;
/// A unit dependency.
#[derive(Debug, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct UnitDep<'a> {
/// The dependency unit.
pub unit: Unit<'a>,
/// The purpose of this dependency (a dependency for a test, or a build
/// script, etc.).
pub unit_for: UnitFor,
/// The name the parent uses to refer to this dependency.
pub extern_crate_name: InternedString,
/// Whether or not this is a public dependency.
pub public: bool,
/// If `true`, the dependency should not be added to Rust's prelude.
pub noprelude: bool,
}
/// Collection of stuff used while creating the `UnitGraph`.
struct State<'a, 'cfg> {
bcx: &'a BuildContext<'a, 'cfg>,

View File

@ -0,0 +1,121 @@
use crate::core::compiler::Unit;
use crate::core::compiler::{CompileKind, CompileMode};
use crate::core::profiles::{Profile, UnitFor};
use crate::core::{nightly_features_allowed, InternedString, PackageId, Target};
use crate::util::CargoResult;
use std::collections::HashMap;
use std::io::Write;
/// The dependency graph of Units.
pub type UnitGraph<'a> = HashMap<Unit<'a>, Vec<UnitDep<'a>>>;
/// A unit dependency.
#[derive(Debug, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct UnitDep<'a> {
/// The dependency unit.
pub unit: Unit<'a>,
/// The purpose of this dependency (a dependency for a test, or a build
/// script, etc.).
pub unit_for: UnitFor,
/// The name the parent uses to refer to this dependency.
pub extern_crate_name: InternedString,
/// Whether or not this is a public dependency.
pub public: bool,
/// If `true`, the dependency should not be added to Rust's prelude.
pub noprelude: bool,
}
const VERSION: u32 = 1;
#[derive(serde::Serialize)]
struct SerializedUnitGraph<'a> {
version: u32,
units: Vec<SerializedUnit<'a>>,
roots: Vec<usize>,
}
#[derive(serde::Serialize)]
struct SerializedUnit<'a> {
pkg_id: PackageId,
target: &'a Target,
profile: &'a Profile,
platform: CompileKind,
mode: CompileMode,
features: &'a Vec<InternedString>,
#[serde(skip_serializing_if = "std::ops::Not::not")] // hide for unstable build-std
is_std: bool,
dependencies: Vec<SerializedUnitDep>,
}
#[derive(serde::Serialize)]
struct SerializedUnitDep {
index: usize,
extern_crate_name: InternedString,
// This is only set on nightly since it is unstable.
#[serde(skip_serializing_if = "Option::is_none")]
public: Option<bool>,
// This is only set on nightly since it is unstable.
#[serde(skip_serializing_if = "Option::is_none")]
noprelude: Option<bool>,
// Intentionally not including `unit_for` because it is a low-level
// internal detail that is mostly used for building the graph.
}
pub fn emit_serialized_unit_graph(
root_units: &[Unit<'_>],
unit_graph: &UnitGraph<'_>,
) -> CargoResult<()> {
let is_nightly = nightly_features_allowed();
let mut units: Vec<(&Unit<'_>, &Vec<UnitDep<'_>>)> = unit_graph.iter().collect();
units.sort_unstable();
// Create a map for quick lookup for dependencies.
let indices: HashMap<&Unit<'_>, usize> = units
.iter()
.enumerate()
.map(|(i, val)| (val.0, i))
.collect();
let roots = root_units.iter().map(|root| indices[root]).collect();
let ser_units = units
.iter()
.map(|(unit, unit_deps)| {
let dependencies = unit_deps
.iter()
.map(|unit_dep| {
// https://github.com/rust-lang/rust/issues/64260 when stabilized.
let (public, noprelude) = if is_nightly {
(Some(unit_dep.public), Some(unit_dep.noprelude))
} else {
(None, None)
};
SerializedUnitDep {
index: indices[&unit_dep.unit],
extern_crate_name: unit_dep.extern_crate_name,
public,
noprelude,
}
})
.collect();
SerializedUnit {
pkg_id: unit.pkg.package_id(),
target: unit.target,
profile: &unit.profile,
platform: unit.kind,
mode: unit.mode,
features: &unit.features,
is_std: unit.is_std,
dependencies,
}
})
.collect();
let s = SerializedUnitGraph {
version: VERSION,
units: ser_units,
roots,
};
let stdout = std::io::stdout();
let mut lock = stdout.lock();
serde_json::to_writer(&mut lock, &s)?;
write!(lock, "\n")?;
Ok(())
}

View File

@ -374,6 +374,7 @@ impl Profiles {
/// times).
pub fn get_profile_run_custom_build(&self, for_unit_profile: &Profile) -> Profile {
let mut result = Profile::default();
result.name = for_unit_profile.name;
result.root = for_unit_profile.root;
result.debuginfo = for_unit_profile.debuginfo;
result.opt_level = for_unit_profile.opt_level;
@ -578,10 +579,11 @@ pub enum ProfileRoot {
/// Profile settings used to determine which compiler flags to use for a
/// target.
#[derive(Clone, Copy, Eq, PartialOrd, Ord)]
#[derive(Clone, Copy, Eq, PartialOrd, Ord, serde::Serialize)]
pub struct Profile {
pub name: InternedString,
pub opt_level: InternedString,
#[serde(skip)] // named profiles are unstable
pub root: ProfileRoot,
pub lto: Lto,
// `None` means use rustc default.
@ -743,8 +745,21 @@ pub enum Lto {
Named(InternedString),
}
impl serde::ser::Serialize for Lto {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
match self {
Lto::Bool(b) => b.to_string().serialize(s),
Lto::Named(n) => n.serialize(s),
}
}
}
/// The `panic` setting.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord, serde::Serialize)]
#[serde(rename_all = "lowercase")]
pub enum PanicStrategy {
Unwind,
Abort,

View File

@ -30,6 +30,7 @@ use std::sync::Arc;
use crate::core::compiler::standard_lib;
use crate::core::compiler::unit_dependencies::build_unit_dependencies;
use crate::core::compiler::unit_graph;
use crate::core::compiler::{BuildConfig, BuildContext, Compilation, Context};
use crate::core::compiler::{CompileKind, CompileMode, RustcTargetData, Unit};
use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner};
@ -490,6 +491,11 @@ pub fn compile_ws<'a>(
&std_roots,
)?;
if bcx.build_config.unit_graph {
unit_graph::emit_serialized_unit_graph(&units, &unit_dependencies)?;
return Ok(Compilation::new(&bcx, build_config.requested_kind)?);
}
let ret = {
let _p = profile::start("compiling");
let cx = Context::new(config, &bcx, unit_dependencies, build_config.requested_kind)?;

View File

@ -150,6 +150,10 @@ pub trait AppExt: Sized {
))
}
fn arg_unit_graph(self) -> Self {
self._arg(opt("unit-graph", "Output build graph in JSON (unstable)").hidden(true))
}
fn arg_new_opts(self) -> Self {
self._arg(
opt(
@ -438,11 +442,17 @@ pub trait ArgMatchesExt {
build_config.message_format = message_format.unwrap_or(MessageFormat::Human);
build_config.requested_profile = self.get_profile_name(config, "dev", profile_checking)?;
build_config.build_plan = self._is_present("build-plan");
build_config.unit_graph = self._is_present("unit-graph");
if build_config.build_plan {
config
.cli_unstable()
.fail_if_stable_opt("--build-plan", 5579)?;
};
if build_config.unit_graph {
config
.cli_unstable()
.fail_if_stable_opt("--unit-graph", 0)?;
}
let opts = CompileOptions {
config,

View File

@ -552,3 +552,127 @@ The available options are:
The `-Z crate-versions` flag will make `cargo doc` include appropriate crate versions for the current crate and all of its dependencies (unless `--no-deps` was provided) in the compiled documentation.
You can find an example screenshot for the cargo itself in the tracking issue.
### unit-graph
* Tracking Issue: TODO
The `--unit-graph` flag can be passed to any build command (`build`, `check`,
`run`, `test`, `bench`, `doc`, etc.) to emit a JSON object to stdout which
represents Cargo's internal unit graph. Nothing is actually built, and the
command returns immediately after printing. Each "unit" corresponds to an
execution of the compiler. These objects also include which unit each unit
depends on.
```
cargo +nightly build --unit-graph -Z unstable-options
```
The following is a description of the JSON structure:
```javascript
{
/* Version of the JSON output structure. If any backwards incompatible
changes are made, this value will be increased.
*/
"version": 1,
/* Array of all build units. */
"units": [
{
/* An opaque string which indicates the package.
Information about the package can be obtained from `cargo metadata`.
*/
"pkg_id": "my-package 0.1.0 (path+file:///path/to/my-package)",
/* The Cargo target. See the `cargo metadata` documentation for more
information about these fields.
https://doc.rust-lang.org/cargo/commands/cargo-metadata.html
*/
"target": {
"kind": ["lib"],
"crate_types": ["lib"],
"name": "my-package",
"src_path": "/path/to/my-package/src/lib.rs",
"edition": "2018",
"doctest": true
},
/* The profile settings for this unit.
These values may not match the profile defined in the manifest.
Units can use modified profile settings. For example, the "panic"
setting can be overridden for tests to force it to "unwind".
*/
"profile": {
/* The profile name these settings are derived from. */
"name": "dev",
/* The optimization level as a string. */
"opt_level": "0",
/* The LTO setting as a string. */
"lto": "false",
/* The codegen units as an integer.
`null` if it should use the compiler's default.
*/
"codegen_units": null,
/* The debug information level as an integer.
`null` if it should use the compiler's default (0).
*/
"debuginfo": 2,
/* Whether or not debug-assertions are enabled. */
"debug_assertions": true,
/* Whether or not overflow-checks are enabled. */
"overflow_checks": true,
/* Whether or not rpath is enabled. */
"rpath": false,
/* Whether or not incremental is enabled. */
"incremental": true,
/* The panic strategy, "unwind" or "abort". */
"panic": "unwind"
},
/* Which platform this target is being built for.
A value of `null` indicates it is for the host.
Otherwise it is a string of the target triple (such as
"x86_64-unknown-linux-gnu").
*/
"platform": null,
/* The "mode" for this unit. Valid values:
* "test" — Build using `rustc` as a test.
* "build" — Build using `rustc`.
* "check" — Build using `rustc` in "check" mode.
* "doc" — Build using `rustdoc`.
* "doctest" — Test using `rustdoc`.
* "run-custom-build" — Represents the execution of a build script.
*/
"mode": "build",
/* Array of features enabled on this unit as strings. */
"features": ["somefeat"],
/* Whether or not this is a standard-library unit,
part of the unstable build-std feature.
If not set, treat as `false`.
*/
"is_std": false,
/* Array of dependencies of this unit. */
"dependencies": [
{
/* Index in the "units" array for the dependency. */
"index": 1,
/* The name that this dependency will be referred as. */
"extern_crate_name": "unicode_xid",
/* Whether or not this dependency is "public",
part of the unstable public-dependency feature.
If not set, the public-dependency feature is not enabled.
*/
"public": false,
/* Whether or not this dependency is injected into the prelude,
currently used by the build-std feature.
If not set, treat as `false`.
*/
"noprelude": false
}
]
},
// ...
],
/* Array of indices in the "units" array that are the "roots" of the
dependency graph.
*/
"roots": [0],
}
```

View File

@ -24,7 +24,7 @@ fn cargo_build_plan_simple() {
"cwd": "[..]/cit/[..]/foo",
"deps": [],
"env": "{...}",
"kind": "Host",
"kind": null,
"links": "{...}",
"outputs": "{...}",
"package_name": "foo",
@ -84,7 +84,7 @@ fn cargo_build_plan_single_dep() {
"cwd": "[..]/cit/[..]/foo",
"deps": [],
"env": "{...}",
"kind": "Host",
"kind": null,
"links": "{...}",
"outputs": [
"[..]/foo/target/debug/deps/libbar-[..].rlib",
@ -101,7 +101,7 @@ fn cargo_build_plan_single_dep() {
"cwd": "[..]/cit/[..]/foo",
"deps": [0],
"env": "{...}",
"kind": "Host",
"kind": null,
"links": "{...}",
"outputs": [
"[..]/foo/target/debug/deps/libfoo-[..].rlib",
@ -152,7 +152,7 @@ fn cargo_build_plan_build_script() {
"cwd": "[..]/cit/[..]/foo",
"deps": [],
"env": "{...}",
"kind": "Host",
"kind": null,
"links": "{...}",
"outputs": [
"[..]/foo/target/debug/build/[..]/build_script_build-[..]"
@ -168,7 +168,7 @@ fn cargo_build_plan_build_script() {
"cwd": "[..]/cit/[..]/foo",
"deps": [0],
"env": "{...}",
"kind": "Host",
"kind": null,
"links": "{...}",
"outputs": [],
"package_name": "foo",
@ -182,7 +182,7 @@ fn cargo_build_plan_build_script() {
"cwd": "[..]/cit/[..]/foo",
"deps": [1],
"env": "{...}",
"kind": "Host",
"kind": null,
"links": "{...}",
"outputs": "{...}",
"package_name": "foo",

View File

@ -103,6 +103,7 @@ mod standard_lib;
mod test;
mod timings;
mod tool_paths;
mod unit_graph;
mod update;
mod vendor;
mod verify_project;

View File

@ -461,7 +461,7 @@ fn metabuild_build_plan() {
"package_version": "0.5.0",
"target_kind": ["lib"],
"compile_mode": "build",
"kind": "Host",
"kind": null,
"deps": [],
"outputs": [
"[..]/target/debug/deps/libmb-[..].rlib",
@ -478,7 +478,7 @@ fn metabuild_build_plan() {
"package_version": "0.0.1",
"target_kind": ["lib"],
"compile_mode": "build",
"kind": "Host",
"kind": null,
"deps": [],
"outputs": [
"[..]/target/debug/deps/libmb_other-[..].rlib",
@ -495,7 +495,7 @@ fn metabuild_build_plan() {
"package_version": "0.0.1",
"target_kind": ["custom-build"],
"compile_mode": "build",
"kind": "Host",
"kind": null,
"deps": [0, 1],
"outputs": ["[..]/target/debug/build/foo-[..]/metabuild_foo-[..][EXE]"],
"links": "{...}",
@ -509,7 +509,7 @@ fn metabuild_build_plan() {
"package_version": "0.0.1",
"target_kind": ["custom-build"],
"compile_mode": "run-custom-build",
"kind": "Host",
"kind": null,
"deps": [2],
"outputs": [],
"links": {},
@ -523,7 +523,7 @@ fn metabuild_build_plan() {
"package_version": "0.0.1",
"target_kind": ["lib"],
"compile_mode": "build",
"kind": "Host",
"kind": null,
"deps": [3],
"outputs": [
"[..]/foo/target/debug/deps/libfoo-[..].rlib",

View File

@ -0,0 +1,211 @@
//! Tests for --unit-graph option.
use cargo_test_support::project;
use cargo_test_support::registry::Package;
#[cargo_test]
fn gated() {
let p = project().file("src/lib.rs", "").build();
p.cargo("build --unit-graph")
.with_status(101)
.with_stderr(
"\
[ERROR] the `--unit-graph` flag is unstable[..]
See [..]
See [..]
",
)
.run();
}
#[cargo_test]
fn simple() {
Package::new("a", "1.0.0")
.dep("b", "1.0")
.feature("feata", &["b/featb"])
.publish();
Package::new("b", "1.0.0")
.dep("c", "1.0")
.feature("featb", &["c/featc"])
.publish();
Package::new("c", "1.0.0").feature("featc", &[]).publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
a = "1.0"
"#,
)
.file("src/lib.rs", "")
.build();
p.cargo("build --features a/feata --unit-graph -Zunstable-options")
.masquerade_as_nightly_cargo()
.with_json(
r#"{
"version": 1,
"units": [
{
"pkg_id": "a 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"target": {
"kind": [
"lib"
],
"crate_types": [
"lib"
],
"name": "a",
"src_path": "[..]/a-1.0.0/src/lib.rs",
"edition": "2015",
"doctest": true
},
"profile": {
"name": "dev",
"opt_level": "0",
"lto": "false",
"codegen_units": null,
"debuginfo": 2,
"debug_assertions": true,
"overflow_checks": true,
"rpath": false,
"incremental": false,
"panic": "unwind"
},
"platform": null,
"mode": "build",
"features": [
"feata"
],
"dependencies": [
{
"index": 1,
"extern_crate_name": "b",
"public": false,
"noprelude": false
}
]
},
{
"pkg_id": "b 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"target": {
"kind": [
"lib"
],
"crate_types": [
"lib"
],
"name": "b",
"src_path": "[..]/b-1.0.0/src/lib.rs",
"edition": "2015",
"doctest": true
},
"profile": {
"name": "dev",
"opt_level": "0",
"lto": "false",
"codegen_units": null,
"debuginfo": 2,
"debug_assertions": true,
"overflow_checks": true,
"rpath": false,
"incremental": false,
"panic": "unwind"
},
"platform": null,
"mode": "build",
"features": [
"featb"
],
"dependencies": [
{
"index": 2,
"extern_crate_name": "c",
"public": false,
"noprelude": false
}
]
},
{
"pkg_id": "c 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"target": {
"kind": [
"lib"
],
"crate_types": [
"lib"
],
"name": "c",
"src_path": "[..]/c-1.0.0/src/lib.rs",
"edition": "2015",
"doctest": true
},
"profile": {
"name": "dev",
"opt_level": "0",
"lto": "false",
"codegen_units": null,
"debuginfo": 2,
"debug_assertions": true,
"overflow_checks": true,
"rpath": false,
"incremental": false,
"panic": "unwind"
},
"platform": null,
"mode": "build",
"features": [
"featc"
],
"dependencies": []
},
{
"pkg_id": "foo 0.1.0 (path+file://[..]/foo)",
"target": {
"kind": [
"lib"
],
"crate_types": [
"lib"
],
"name": "foo",
"src_path": "[..]/foo/src/lib.rs",
"edition": "2015",
"doctest": true
},
"profile": {
"name": "dev",
"opt_level": "0",
"lto": "false",
"codegen_units": null,
"debuginfo": 2,
"debug_assertions": true,
"overflow_checks": true,
"rpath": false,
"incremental": false,
"panic": "unwind"
},
"platform": null,
"mode": "build",
"features": [],
"dependencies": [
{
"index": 0,
"extern_crate_name": "a",
"public": false,
"noprelude": false
}
]
}
],
"roots": [3]
}
"#,
)
.run();
}