Skip to content

Commit ea4a156

Browse files
committed
Allow binary-only dependencies in metadata
A number of projects, such as https://github.com/google/cargo-raze consume the output of `cargo metadata` when generating input to other build systems, such as Bazel and Buck. Typically, these projects use a `Cargo.toml` file as the input to drive their work, and it can be useful to express to these systems that a binary is required as part of the build. If such a dependency happens to have a lib target, sufficient dependency information happens to be exposed via the resolve field to recreate the dependency structure adequately. However, for binary-only targets, that entire branch of the dependency tree is cut off, making it hard to recreate this information. This PR adds an option to `cargo metadata` to allow rendering these subtrees, describing these deps as of kind "binary". This isn't really a concept in cargo-proper, but may become one via rust-lang/rfcs#3168 and/or rust-lang/rfcs#3028 - this output format is forwards-compatible with these proposals. In an RFC 3028 world, binary deps would appear as `dep_kind` "binary", and `cdylib` or `staticlib` deps would appear as new `DepKind` variants. We'd probably add a new value of the flag, `declared`, and set that to be the default. We may also want to deprecate the `include-if-no-library-dep` value, and make these ignored binary deps a hard error (replacing the existing warning log). In an RFC 3168 world, there's an interesting metadata question to have - there are (at least) three reasonable options in terms of handling metadata: 1. Require a direct dependency on any package whose binary deps are being used from the crate which uses them - this gives a nicely restricted search space, and allows for nicely curated metadata output. 2. Allow any transitive package dependencies to be used as binary deps - this may significantly expand the output produced, but would model the implementation of the feature. 3. Require explicitly tagging binary deps as being used as binary deps - this looks a lot like RFC 3028, and would tend in that direction.
1 parent c8b38af commit ea4a156

File tree

11 files changed

+284
-14
lines changed

11 files changed

+284
-14
lines changed

benches/benchsuite/benches/resolve.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use cargo::core::compiler::{CompileKind, RustcTargetData};
22
use cargo::core::resolver::features::{CliFeatures, FeatureOpts, FeatureResolver, ForceAllTargets};
33
use cargo::core::resolver::{HasDevUnits, ResolveBehavior};
44
use cargo::core::{PackageIdSpec, Workspace};
5-
use cargo::ops::WorkspaceResolve;
5+
use cargo::ops::{BinaryOnlyDepsBehavior, WorkspaceResolve};
66
use cargo::Config;
77
use criterion::{criterion_group, criterion_main, Criterion};
88
use std::fs;
@@ -198,6 +198,7 @@ fn do_resolve<'cfg>(config: &'cfg Config, ws_root: &Path) -> ResolveInfo<'cfg> {
198198
&specs,
199199
has_dev_units,
200200
force_all_targets,
201+
BinaryOnlyDepsBehavior::Ignore,
201202
)
202203
.unwrap();
203204
ResolveInfo {
@@ -272,6 +273,7 @@ fn resolve_ws(c: &mut Criterion) {
272273
specs,
273274
*has_dev_units,
274275
*force_all_targets,
276+
BinaryOnlyDepsBehavior::Ignore,
275277
)
276278
.unwrap();
277279
})

src/bin/cargo/commands/metadata.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::command_prelude::*;
2-
use cargo::ops::{self, OutputMetadataOptions};
2+
use anyhow::anyhow;
3+
use cargo::ops::{self, BinaryDepsMode, OutputMetadataOptions};
34

45
pub fn cli() -> App {
56
subcommand("metadata")
@@ -26,6 +27,11 @@ pub fn cli() -> App {
2627
.value_name("VERSION")
2728
.possible_value("1"),
2829
)
30+
.arg(
31+
opt("binary-deps", "How to treat binary dependencies")
32+
.possible_values(&["include-if-no-library-dep", "ignore"])
33+
.default_value("ignore"),
34+
)
2935
.after_help("Run `cargo help metadata` for more detailed information.\n")
3036
}
3137

@@ -43,11 +49,30 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
4349
Some(version) => version.parse().unwrap(),
4450
};
4551

52+
let binary_deps = {
53+
match args
54+
.value_of("binary-deps")
55+
.unwrap()
56+
.to_ascii_lowercase()
57+
.as_str()
58+
{
59+
"include-if-no-library-dep" => BinaryDepsMode::IncludeIfNoLibraryDep,
60+
"ignore" => BinaryDepsMode::Ignore,
61+
s => {
62+
return Err(CliError::new(
63+
anyhow!("invalid binary-deps specifier: `{}`", s),
64+
1,
65+
))
66+
}
67+
}
68+
};
69+
4670
let options = OutputMetadataOptions {
4771
cli_features: args.cli_features()?,
4872
no_deps: args.is_present("no-deps"),
4973
filter_platforms: args._values_of("filter-platform"),
5074
version,
75+
binary_deps,
5176
};
5277

5378
let result = ops::output_metadata(&ws, &options)?;

src/cargo/core/compiler/standard_lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ pub fn resolve_std<'cfg>(
118118
&specs,
119119
HasDevUnits::No,
120120
crate::core::resolver::features::ForceAllTargets::No,
121+
ops::BinaryOnlyDepsBehavior::Warn,
121122
)?;
122123
Ok((
123124
resolve.pkg_set,

src/cargo/core/package.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ impl<'cfg> PackageSet<'cfg> {
569569
requested_kinds: &[CompileKind],
570570
target_data: &RustcTargetData<'_>,
571571
force_all_targets: ForceAllTargets,
572+
treat_binary_only_as_lib: bool,
572573
) -> BTreeMap<PackageId, Vec<&Package>> {
573574
root_ids
574575
.iter()
@@ -583,7 +584,11 @@ impl<'cfg> PackageSet<'cfg> {
583584
)
584585
.filter_map(|package_id| {
585586
if let Ok(dep_pkg) = self.get_one(package_id) {
586-
if !dep_pkg.targets().iter().any(|t| t.is_lib()) {
587+
if !dep_pkg
588+
.targets()
589+
.iter()
590+
.any(|t| t.is_lib() || (treat_binary_only_as_lib && t.is_bin()))
591+
{
587592
Some(dep_pkg)
588593
} else {
589594
None

src/cargo/ops/cargo_compile.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ pub fn create_bcx<'a, 'cfg>(
377377
&specs,
378378
has_dev_units,
379379
crate::core::resolver::features::ForceAllTargets::No,
380+
ops::BinaryOnlyDepsBehavior::Warn,
380381
)?;
381382
let WorkspaceResolve {
382383
mut pkg_set,

src/cargo/ops/cargo_output_metadata.rs

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::ops::{self, Packages};
77
use crate::util::interning::InternedString;
88
use crate::util::CargoResult;
99
use cargo_platform::Platform;
10-
use serde::Serialize;
10+
use serde::{Serialize, Serializer};
1111
use std::collections::BTreeMap;
1212
use std::path::PathBuf;
1313

@@ -18,6 +18,7 @@ pub struct OutputMetadataOptions {
1818
pub no_deps: bool,
1919
pub version: u32,
2020
pub filter_platforms: Vec<String>,
21+
pub binary_deps: BinaryDepsMode,
2122
}
2223

2324
/// Loads the manifest, resolves the dependencies of the package to the concrete
@@ -88,19 +89,52 @@ struct Dep {
8889

8990
#[derive(Serialize, PartialEq, Eq, PartialOrd, Ord)]
9091
struct DepKindInfo {
91-
kind: DepKind,
92+
kind: DepKindIncludingBinaryDeps,
9293
target: Option<Platform>,
9394
}
9495

9596
impl From<&Dependency> for DepKindInfo {
9697
fn from(dep: &Dependency) -> DepKindInfo {
9798
DepKindInfo {
98-
kind: dep.kind(),
99+
kind: dep.kind().into(),
99100
target: dep.platform().cloned(),
100101
}
101102
}
102103
}
103104

105+
#[derive(PartialEq, Eq, PartialOrd, Ord)]
106+
enum DepKindIncludingBinaryDeps {
107+
Normal,
108+
Development,
109+
Build,
110+
Binary,
111+
}
112+
113+
impl From<DepKind> for DepKindIncludingBinaryDeps {
114+
fn from(dep_kind: DepKind) -> Self {
115+
match dep_kind {
116+
DepKind::Normal => DepKindIncludingBinaryDeps::Normal,
117+
DepKind::Development => DepKindIncludingBinaryDeps::Development,
118+
DepKind::Build => DepKindIncludingBinaryDeps::Build,
119+
}
120+
}
121+
}
122+
123+
impl Serialize for DepKindIncludingBinaryDeps {
124+
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
125+
where
126+
S: Serializer,
127+
{
128+
match *self {
129+
DepKindIncludingBinaryDeps::Normal => None,
130+
DepKindIncludingBinaryDeps::Development => Some("dev"),
131+
DepKindIncludingBinaryDeps::Build => Some("build"),
132+
DepKindIncludingBinaryDeps::Binary => Some("binary"),
133+
}
134+
.serialize(s)
135+
}
136+
}
137+
104138
/// Builds the resolve graph as it will be displayed to the user.
105139
fn build_resolve_graph(
106140
ws: &Workspace<'_>,
@@ -119,6 +153,11 @@ fn build_resolve_graph(
119153
crate::core::resolver::features::ForceAllTargets::No
120154
};
121155

156+
let binary_only_deps_behavior = match metadata_opts.binary_deps {
157+
BinaryDepsMode::Ignore => ops::BinaryOnlyDepsBehavior::Warn,
158+
BinaryDepsMode::IncludeIfNoLibraryDep => ops::BinaryOnlyDepsBehavior::Ignore,
159+
};
160+
122161
// Note that even with --filter-platform we end up downloading host dependencies as well,
123162
// as that is the behavior of download_accessible.
124163
let ws_resolve = ops::resolve_ws_with_opts(
@@ -129,6 +168,7 @@ fn build_resolve_graph(
129168
&specs,
130169
HasDevUnits::Yes,
131170
force_all,
171+
binary_only_deps_behavior,
132172
)?;
133173

134174
let package_map: BTreeMap<PackageId, Package> = ws_resolve
@@ -149,6 +189,7 @@ fn build_resolve_graph(
149189
&package_map,
150190
&target_data,
151191
&requested_kinds,
192+
metadata_opts.binary_deps,
152193
);
153194
}
154195
// Get a Vec of Packages.
@@ -166,13 +207,20 @@ fn build_resolve_graph(
166207
Ok((actual_packages, mr))
167208
}
168209

210+
#[derive(Clone, Copy)]
211+
pub enum BinaryDepsMode {
212+
IncludeIfNoLibraryDep,
213+
Ignore,
214+
}
215+
169216
fn build_resolve_graph_r(
170217
node_map: &mut BTreeMap<PackageId, MetadataResolveNode>,
171218
pkg_id: PackageId,
172219
resolve: &Resolve,
173220
package_map: &BTreeMap<PackageId, Package>,
174221
target_data: &RustcTargetData<'_>,
175222
requested_kinds: &[CompileKind],
223+
binary_deps: BinaryDepsMode,
176224
) {
177225
if node_map.contains_key(&pkg_id) {
178226
return;
@@ -206,14 +254,42 @@ fn build_resolve_graph_r(
206254
})
207255
}
208256
})
209-
.filter_map(|(dep_id, deps)| {
210-
let mut dep_kinds: Vec<_> = deps.iter().map(DepKindInfo::from).collect();
211-
dep_kinds.sort();
257+
.flat_map(|(dep_id, deps)| {
212258
package_map
213259
.get(&dep_id)
214-
.and_then(|pkg| pkg.targets().iter().find(|t| t.is_lib()))
215-
.and_then(|lib_target| resolve.extern_crate_name(pkg_id, dep_id, lib_target).ok())
216-
.map(|name| Dep {
260+
.into_iter()
261+
.flat_map(move |pkg| {
262+
if let Some(lib_target) = pkg.targets().iter().find(|t| t.is_lib()) {
263+
let mut dep_kinds: Vec<_> = deps.iter().map(DepKindInfo::from).collect();
264+
dep_kinds.sort();
265+
vec![(lib_target, dep_kinds)]
266+
} else {
267+
match binary_deps {
268+
BinaryDepsMode::IncludeIfNoLibraryDep => pkg
269+
.targets()
270+
.iter()
271+
.filter(|t| t.is_bin())
272+
.map(|t| {
273+
(
274+
t,
275+
vec![DepKindInfo {
276+
kind: DepKindIncludingBinaryDeps::Binary,
277+
target: None,
278+
}],
279+
)
280+
})
281+
.collect(),
282+
BinaryDepsMode::Ignore => vec![],
283+
}
284+
}
285+
})
286+
.filter_map(move |(lib_target, dep_kinds)| {
287+
resolve
288+
.extern_crate_name(pkg_id, dep_id, lib_target)
289+
.ok()
290+
.map(|crate_name| (crate_name, dep_kinds))
291+
})
292+
.map(move |(name, dep_kinds)| Dep {
217293
name,
218294
pkg: normalize_id(dep_id),
219295
dep_kinds,
@@ -237,6 +313,7 @@ fn build_resolve_graph_r(
237313
package_map,
238314
target_data,
239315
requested_kinds,
316+
binary_deps,
240317
);
241318
}
242319
}

src/cargo/ops/fix.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ fn check_resolver_change(ws: &Workspace<'_>, opts: &FixOptions) -> CargoResult<(
252252
&specs,
253253
has_dev_units,
254254
crate::core::resolver::features::ForceAllTargets::No,
255+
ops::BinaryOnlyDepsBehavior::Warn,
255256
)?;
256257

257258
let feature_opts = FeatureOpts::new_behavior(ResolveBehavior::V2, has_dev_units);

src/cargo/ops/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ pub use self::cargo_generate_lockfile::update_lockfile;
1212
pub use self::cargo_generate_lockfile::UpdateOptions;
1313
pub use self::cargo_install::{install, install_list};
1414
pub use self::cargo_new::{init, new, NewOptions, VersionControl};
15-
pub use self::cargo_output_metadata::{output_metadata, ExportInfo, OutputMetadataOptions};
15+
pub use self::cargo_output_metadata::{
16+
output_metadata, BinaryDepsMode, ExportInfo, OutputMetadataOptions,
17+
};
1618
pub use self::cargo_package::{package, package_one, PackageOpts};
1719
pub use self::cargo_pkgid::pkgid;
1820
pub use self::cargo_read_manifest::{read_package, read_packages};
@@ -28,7 +30,7 @@ pub use self::registry::{needs_custom_http_transport, registry_login, registry_l
2830
pub use self::registry::{publish, registry_configuration, RegistryConfig};
2931
pub use self::resolve::{
3032
add_overrides, get_resolved_packages, resolve_with_previous, resolve_ws, resolve_ws_with_opts,
31-
WorkspaceResolve,
33+
BinaryOnlyDepsBehavior, WorkspaceResolve,
3234
};
3335
pub use self::vendor::{vendor, VendorOptions};
3436

src/cargo/ops/resolve.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ pub fn resolve_ws<'a>(ws: &Workspace<'a>) -> CargoResult<(PackageSet<'a>, Resolv
6969
Ok((packages, resolve))
7070
}
7171

72+
#[derive(Eq, PartialEq)]
73+
pub enum BinaryOnlyDepsBehavior {
74+
Warn,
75+
Ignore,
76+
}
77+
7278
/// Resolves dependencies for some packages of the workspace,
7379
/// taking into account `paths` overrides and activated features.
7480
///
@@ -87,6 +93,7 @@ pub fn resolve_ws_with_opts<'cfg>(
8793
specs: &[PackageIdSpec],
8894
has_dev_units: HasDevUnits,
8995
force_all_targets: ForceAllTargets,
96+
binary_only_deps_behaviour: BinaryOnlyDepsBehavior,
9097
) -> CargoResult<WorkspaceResolve<'cfg>> {
9198
let mut registry = PackageRegistry::new(ws.config())?;
9299
let mut add_patches = true;
@@ -178,6 +185,7 @@ pub fn resolve_ws_with_opts<'cfg>(
178185
requested_targets,
179186
target_data,
180187
force_all_targets,
188+
binary_only_deps_behaviour == BinaryOnlyDepsBehavior::Ignore,
181189
);
182190
for (pkg_id, dep_pkgs) in no_lib_pkgs {
183191
for dep_pkg in dep_pkgs {

src/cargo/ops/tree/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<()
158158
&specs,
159159
has_dev,
160160
force_all,
161+
ops::BinaryOnlyDepsBehavior::Warn,
161162
)?;
162163

163164
let package_map: HashMap<PackageId, &Package> = ws_resolve

0 commit comments

Comments
 (0)