Skip to content

Commit

Permalink
Auto merge of rust-lang#5461 - matklad:meta-rename, r=alexcrichton
Browse files Browse the repository at this point in the history
Support crate renames in `cargo metadata`

This adds information about (currently unstable) crate renames to metadata. Unfortunately, we already expose dependencies as a list of package ids (which are strings), so we can't easily add a `rename` field easily. For this reason, I've added a parallel `deps` key, which basically deprecates `dependencies` field.

So, the new format looks like this

```JavaScript
{
    "dependencies": [
        "bar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
        "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)"
    ],
    "deps": [
        {
            "extern_crate_name": "baz", // this one is actually renamed
            "id": "bar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)"
        },
        {
            "extern_crate_name": "bar",
            "id": "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)"
        }
    ],
    "features": [],
    "id": "foo 0.5.0 (path+file://[..])"
},
```

Questions:

* Is there a better name for `extern_crate_name`? This name is precise, but it's longer than I like, and might become opaque in meaning if we remove `extern crate` from the language :)
* Should we feature gate this (i.e, only produce `deps` if we have a feature in `Cargo.toml`)? I think the answer is yes, but that'll require threading `Features` to `Workspace`...

r? @alexcrichton
  • Loading branch information
bors committed May 5, 2018
2 parents 757112c + a312c55 commit d0d3cb5
Show file tree
Hide file tree
Showing 5 changed files with 316 additions and 39 deletions.
4 changes: 2 additions & 2 deletions src/cargo/core/compiler/context/unit_dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ fn compute_deps<'a, 'b, 'cfg>(
true
})
}).filter_map(|(id, _)| match bcx.get_package(id) {
Ok(pkg) => pkg.targets().iter().find(|t| t.is_lib()).map(|t| {
Ok(pkg) => pkg.lib_target().map(|t| {
let mode = check_or_build_mode(&unit.mode, t);
let unit = new_unit(bcx, pkg, t, profile_for, unit.kind.for_target(t), mode);
Ok((unit, profile_for))
Expand Down Expand Up @@ -222,7 +222,7 @@ fn compute_deps_doc<'a, 'cfg>(
let mut ret = Vec::new();
for dep in deps {
let dep = dep?;
let lib = match dep.targets().iter().find(|t| t.is_lib()) {
let lib = match dep.lib_target() {
Some(lib) => lib,
None => continue,
};
Expand Down
4 changes: 4 additions & 0 deletions src/cargo/core/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ impl Package {
pub fn targets(&self) -> &[Target] {
self.manifest.targets()
}
/// Get the library target for the package
pub fn lib_target(&self) -> Option<&Target> {
self.targets().iter().find(|t| t.is_lib())
}
/// Get the current package version
pub fn version(&self) -> &Version {
self.package_id().version()
Expand Down
9 changes: 6 additions & 3 deletions src/cargo/ops/cargo_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,11 @@ fn generate_targets<'a>(
proposals.extend(default_units);
if build_config.mode == CompileMode::Test {
// Include the lib as it will be required for doctests.
if let Some(t) = pkg.targets().iter().find(|t| t.is_lib() && t.doctested()) {
proposals.push((new_unit(pkg, t, CompileMode::Build), false));
match pkg.lib_target() {
Some(t) if t.doctested() => {
proposals.push((new_unit(pkg, t, CompileMode::Build), false));
}
_ => {}
}
}
}
Expand All @@ -560,7 +563,7 @@ fn generate_targets<'a>(
ref benches,
} => {
if lib {
if let Some(target) = pkg.targets().iter().find(|t| t.is_lib()) {
if let Some(target) = pkg.lib_target() {
proposals.push((new_unit(pkg, target, build_config.mode), false));
} else if !all_targets {
bail!("no library targets found")
Expand Down
113 changes: 81 additions & 32 deletions src/cargo/ops/cargo_output_metadata.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use serde::ser::{self, Serialize};

use core::resolver::Resolve;
use core::{Package, PackageId, Workspace};
use core::{Package, PackageId, PackageSet, Workspace};
use core::dependency;
use ops::{self, Packages};
use util::CargoResult;

Expand Down Expand Up @@ -56,18 +55,19 @@ fn metadata_full(ws: &Workspace, opt: &OutputMetadataOptions) -> CargoResult<Exp
)?;
let (packages, resolve) = deps;

let resolve = MetadataResolve::new(
&packages,
&resolve,
ws.current_opt().map(|pkg| pkg.package_id().clone()),
);
let packages = packages
.package_ids()
.map(|i| packages.get(i).map(|p| p.clone()))
.collect::<CargoResult<Vec<_>>>()?;

Ok(ExportInfo {
packages,
workspace_members: ws.members().map(|pkg| pkg.package_id().clone()).collect(),
resolve: Some(MetadataResolve {
resolve,
root: ws.current_opt().map(|pkg| pkg.package_id().clone()),
}),
resolve: Some(resolve),
target_directory: ws.target_dir().display().to_string(),
version: VERSION,
workspace_root: ws.root().display().to_string(),
Expand All @@ -76,42 +76,91 @@ fn metadata_full(ws: &Workspace, opt: &OutputMetadataOptions) -> CargoResult<Exp

#[derive(Serialize)]
pub struct ExportInfo {
/// All packages for this project, with dependencies.
packages: Vec<Package>,
/// Packages which are direct members of the current project.
workspace_members: Vec<PackageId>,
/// A graph of the dependencies between packages.
resolve: Option<MetadataResolve>,
/// The directory where intermediate build artifacts will be stored.
target_directory: String,
/// Version of this JSON format
version: u32,
/// Path to the directory with the project.
workspace_root: String,
}

/// Newtype wrapper to provide a custom `Serialize` implementation.
/// The one from lockfile does not fit because it uses a non-standard
/// format for `PackageId`s
// The serialization format is different from lockfile, because
// here we use different format for `PackageId`s, and give more
// information about dependencies.
#[derive(Serialize)]
struct MetadataResolve {
#[serde(rename = "nodes", serialize_with = "serialize_resolve")]
resolve: Resolve,
/// Dependencies for each package from `ExportInfo::package`.
nodes: Vec<Node>,
/// Deprecated, use `ExportInfo::workspace_members`.
root: Option<PackageId>,
}

fn serialize_resolve<S>(resolve: &Resolve, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
#[derive(Serialize)]
struct Node<'a> {
id: &'a PackageId,
dependencies: Vec<&'a PackageId>,
features: Vec<&'a str>,
}
/// Describes dependencies of a single package.
#[derive(Serialize)]
struct Node {
/// The id of the package.
id: PackageId,
/// Deprecated, use `deps` field.
dependencies: Vec<PackageId>,
/// Dependencies of this package.
deps: Vec<Dependency>,
/// Features, enabled for this package.
features: Vec<String>,
}

resolve
.iter()
.map(|id| Node {
id,
dependencies: resolve.deps(id).map(|p| p.0).collect(),
features: resolve.features_sorted(id),
})
.collect::<Vec<_>>()
.serialize(s)
/// Describes a single dependency.
#[derive(Serialize)]
struct Dependency {
/// The id of the dependency.
id: PackageId,
/// The name used for `extern crate` declaration of this dependency.
name: String,
/// Is this normal, dev or build dependency
kind: dependency::Kind,
}

impl MetadataResolve {
pub fn new(
packages: &PackageSet,
resolve: &Resolve,
root: Option<PackageId>,
) -> MetadataResolve {
let nodes = resolve
.iter()
.map(|pkg| {
Node {
id: pkg.clone(),
dependencies: resolve.deps(pkg).map(|(dep, _)| dep.clone()).collect(),
deps: resolve
.deps(pkg)
.flat_map(|(id, deps)| {
let dep_name = packages.get(id).unwrap()
.lib_target().unwrap()
.crate_name();
deps.iter().map(|dep| {
Dependency {
id: id.clone(),
name: dep.rename().unwrap_or(&dep_name)
.to_owned(),
kind: dep.kind(),
}
}).collect::<Vec<_>>().into_iter()
})
.collect(),
features: resolve
.features_sorted(pkg)
.into_iter()
.map(|s| s.to_string())
.collect(),
}
})
.collect();
MetadataResolve { nodes, root }
}
}
Loading

0 comments on commit d0d3cb5

Please sign in to comment.