diff --git a/src/cargo/core/compiler/context/compilation_files.rs b/src/cargo/core/compiler/context/compilation_files.rs index 80a41cdaeff..af4be12dd40 100644 --- a/src/cargo/core/compiler/context/compilation_files.rs +++ b/src/cargo/core/compiler/context/compilation_files.rs @@ -89,6 +89,10 @@ struct MetaInfo { /// /// If this is `true`, the `meta_hash` is used for the filename. use_extra_filename: bool, + + /// This flag is set by collision detection and forces the use of a more unique hash + /// instead of the stable one. + output_conflicts_without_meta_hash: bool, } /// Collection of information about the files emitted by the compiler, and the @@ -220,7 +224,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { fn pkg_dir(&self, unit: &Unit) -> String { let name = unit.pkg.package_id().name(); let meta = &self.metas[unit]; - if meta.use_extra_filename { + if meta.use_extra_filename || meta.output_conflicts_without_meta_hash { format!("{}-{}", name, meta.meta_hash) } else { format!("{}-{}", name, self.target_short_hash(unit)) @@ -370,6 +374,12 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { .map(Arc::clone) } + pub(super) fn set_non_colliding_outputs(&mut self, unit: &Unit, outputs: Arc>) { + let cell = LazyCell::new(); + cell.fill(outputs).unwrap(); + self.outputs.insert(unit.clone(), cell); + } + /// Returns the path where the output for the given unit and FileType /// should be uplifted to. /// @@ -421,7 +431,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { Some(uplift_path) } - fn calc_outputs( + pub(super) fn calc_outputs( &self, unit: &Unit, bcx: &BuildContext<'a, 'cfg>, @@ -625,6 +635,7 @@ fn compute_metadata( MetaInfo { meta_hash: Metadata(hasher.finish()), use_extra_filename: should_use_metadata(bcx, unit), + output_conflicts_without_meta_hash: false, } } diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index 7efe77ef91f..f7e0523518b 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -128,7 +128,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { self.prepare_units()?; self.prepare()?; custom_build::build_map(&mut self)?; - self.check_collisions()?; + self.check_collisions_and_avoid_some()?; self.compute_metadata_for_doc_units(); // We need to make sure that if there were any previous docs @@ -421,7 +421,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { } } - fn check_collisions(&self) -> CargoResult<()> { + fn check_collisions_and_avoid_some(&mut self) -> CargoResult<()> { let mut output_collisions = HashMap::new(); let describe_collision = |unit: &Unit, other_unit: &Unit, path: &PathBuf| -> String { format!( @@ -528,6 +528,8 @@ impl<'a, 'cfg> Context<'a, 'cfg> { doc_collision_error(unit, prev)?; } } + let mut alt_outputs = None; + let mut collisions_prevented = 0; for output in self.outputs(unit)?.iter() { if let Some(other_unit) = output_collisions.insert(output.path.clone(), unit) { if unit.mode.is_doc() { @@ -535,10 +537,27 @@ impl<'a, 'cfg> Context<'a, 'cfg> { // and https://github.com/rust-lang/rust/issues/61378 report_collision(unit, other_unit, &output.path, rustdoc_suggestion)?; } else { - report_collision(unit, other_unit, &output.path, suggestion)?; + let files = self.files.as_mut().unwrap(); + if !files.use_extra_filename(unit) + && !alt_outputs + .get_or_insert_with(|| { + files + .calc_outputs(unit, &self.bcx) + .expect("infallible by now") + }) + .iter() + .any(|out| out.path == output.path) + { + collisions_prevented += 1; + continue; + } else { + report_collision(unit, other_unit, &output.path, suggestion)?; + } } } if let Some(hardlink) = output.hardlink.as_ref() { + // We don't check for collision prevention on unix as typically these only happen on MSVC platforms + // which doesn't use hardlinks. If this changes, one would do another collision here. if let Some(other_unit) = output_collisions.insert(hardlink.clone(), unit) { report_collision(unit, other_unit, hardlink, suggestion)?; } @@ -556,6 +575,15 @@ impl<'a, 'cfg> Context<'a, 'cfg> { } } } + + if collisions_prevented != 0 { + if let Some(alt_outputs) = alt_outputs { + self.files + .as_mut() + .unwrap() + .set_non_colliding_outputs(unit, alt_outputs); + } + } } Ok(()) } diff --git a/src/cargo/core/compiler/unit_dependencies.rs b/src/cargo/core/compiler/unit_dependencies.rs index a69c2501fbc..adaf1a98cd1 100644 --- a/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/cargo/core/compiler/unit_dependencies.rs @@ -262,6 +262,9 @@ fn compute_deps( return compute_deps_doc(unit, state, unit_for); } + let dep_filter = &|unit: &Unit, dep: &Dependency| { + non_custom_and_non_transitive_deps(unit, dep) && dep.maybe_lib() + }; let mut ret = Vec::new(); let mut dev_deps = Vec::new(); for (dep_pkg_id, deps) in state.deps(unit, unit_for) { @@ -279,7 +282,7 @@ fn compute_deps( && dep_lib.proc_macro() && !unit.kind.is_host() { - let unit_dep = new_unit_dep( + let unit_deps = new_unit_deps( state, unit, dep_pkg, @@ -288,9 +291,10 @@ fn compute_deps( unit.kind, mode, IS_NO_ARTIFACT_DEP, + dep_filter, )?; - ret.push(unit_dep); - let unit_dep = new_unit_dep( + ret.extend(unit_deps); + let unit_deps = new_unit_deps( state, unit, dep_pkg, @@ -299,10 +303,11 @@ fn compute_deps( CompileKind::Host, mode, IS_NO_ARTIFACT_DEP, + dep_filter, )?; - ret.push(unit_dep); + ret.extend(unit_deps); } else { - let unit_dep = new_unit_dep( + let unit_deps = new_unit_deps( state, unit, dep_pkg, @@ -311,8 +316,9 @@ fn compute_deps( unit.kind.for_target(dep_lib), mode, IS_NO_ARTIFACT_DEP, + dep_filter, )?; - ret.push(unit_dep); + ret.extend(unit_deps); } // If the unit added was a dev-dependency unit, then record that in the @@ -342,7 +348,8 @@ fn compute_deps( if unit.target.is_lib() && unit.mode != CompileMode::Doctest { return Ok(ret); } - ret.extend(maybe_lib(unit, state, unit_for)?); + let dep_filter = &|_: &Unit, _: &Dependency| true; + ret.extend(maybe_lib(unit, state, unit_for, dep_filter)?); // If any integration tests/benches are being run, make sure that // binaries are built as well. @@ -351,40 +358,42 @@ fn compute_deps( && (unit.target.is_test() || unit.target.is_bench()) { let id = unit.pkg.package_id(); - ret.extend( - unit.pkg - .targets() - .iter() - .filter(|t| { - // Skip binaries with required features that have not been selected. - match t.required_features() { - Some(rf) if t.is_bin() => { - let features = resolve_all_features( - state.resolve(), - state.features(), - state.package_set, - id, - ); - rf.iter().all(|f| features.contains(f)) - } - None if t.is_bin() => true, - _ => false, + for unit_deps_for_target in unit + .pkg + .targets() + .iter() + .filter(|t| { + // Skip binaries with required features that have not been selected. + match t.required_features() { + Some(rf) if t.is_bin() => { + let features = resolve_all_features( + state.resolve(), + state.features(), + state.package_set, + id, + ); + rf.iter().all(|f| features.contains(f)) } - }) - .map(|t| { - new_unit_dep( - state, - unit, - &unit.pkg, - t, - UnitFor::new_normal(unit_for.root_compile_kind()), - unit.kind.for_target(t), - CompileMode::Build, - IS_NO_ARTIFACT_DEP, - ) - }) - .collect::>>()?, - ); + None if t.is_bin() => true, + _ => false, + } + }) + .map(|t| { + new_unit_deps( + state, + unit, + &unit.pkg, + t, + UnitFor::new_normal(unit_for.root_compile_kind()), + unit.kind.for_target(t), + CompileMode::Build, + IS_NO_ARTIFACT_DEP, + dep_filter, + ) + }) + { + ret.extend(unit_deps_for_target?); + } } Ok(ret) @@ -474,7 +483,7 @@ fn compute_deps_custom_build( // We don't have a great way of handling (2) here right now so this is // deferred until after the graph of all unit dependencies has been // constructed. - let compile_script_unit = new_unit_dep( + let compile_script_unit = new_unit_deps( state, unit, &unit.pkg, @@ -484,9 +493,15 @@ fn compute_deps_custom_build( CompileKind::Host, CompileMode::Build, IS_NO_ARTIFACT_DEP, + &|_, _| true, )?; + assert_eq!( + compile_script_unit.len(), + 1, + "BUG: build scripts can only return a single name, these are not derived from dependency names." + ); - let mut result = vec![compile_script_unit]; + let mut result = compile_script_unit; // Include any artifact dependencies. // @@ -547,47 +562,51 @@ fn artifact_targets_to_unit_deps( artifact_pkg: &Package, dep: &Dependency, ) -> CargoResult> { - let ret = + let mut ret = Vec::new(); + for target in match_artifacts_kind_with_targets(dep, artifact_pkg.targets(), parent.pkg.name().as_str())? - .into_iter() - .flat_map(|target| { - // We split target libraries into individual units, even though rustc is able - // to produce multiple kinds in an single invocation for the sole reason that - // each artifact kind has its own output directory, something we can't easily - // teach rustc for now. - match target.kind() { - TargetKind::Lib(kinds) => Box::new( - kinds - .iter() - .filter(|tk| matches!(tk, CrateType::Cdylib | CrateType::Staticlib)) - .map(|target_kind| { - new_unit_dep( - state, - parent, - artifact_pkg, - target - .clone() - .set_kind(TargetKind::Lib(vec![target_kind.clone()])), - parent_unit_for, - compile_kind, - CompileMode::Build, - dep.artifact(), - ) - }), - ) as Box>, - _ => Box::new(std::iter::once(new_unit_dep( - state, - parent, - artifact_pkg, - target, - parent_unit_for, - compile_kind, - CompileMode::Build, - dep.artifact(), - ))), + { + // We split target libraries into individual units, even though rustc is able + // to produce multiple kinds in an single invocation for the sole reason that + // each artifact kind has its own output directory, something we can't easily + // teach rustc for now. + match target.kind() { + TargetKind::Lib(kinds) => { + for unit_deps_for_kind in kinds + .iter() + .filter(|tk| matches!(tk, CrateType::Cdylib | CrateType::Staticlib)) + .map(|target_kind| { + new_unit_deps( + state, + parent, + artifact_pkg, + target + .clone() + .set_kind(TargetKind::Lib(vec![target_kind.clone()])), + parent_unit_for, + compile_kind, + CompileMode::Build, + dep.artifact(), + &|_u, d| dep == d, + ) + }) + { + ret.extend(unit_deps_for_kind?); } - }) - .collect::, _>>()?; + } + _ => ret.extend(new_unit_deps( + state, + parent, + artifact_pkg, + target, + parent_unit_for, + compile_kind, + CompileMode::Build, + dep.artifact(), + &|_u, d| dep == d, + )?), + } + } Ok(ret) } @@ -640,6 +659,10 @@ fn compute_deps_doc( // built. If we're documenting *all* libraries, then we also depend on // the documentation of the library being built. let mut ret = Vec::new(); + + let dep_filter = &|unit: &Unit, dep: &Dependency| { + non_custom_and_non_transitive_deps(unit, dep) && dep.maybe_lib() + }; for (id, deps) in state.deps(unit, unit_for) { let dep_lib = match calc_artifact_deps(unit, unit_for, id, &deps, state, &mut ret)? { Some(lib) => lib, @@ -650,7 +673,7 @@ fn compute_deps_doc( // However, for plugins/proc macros, deps should be built like normal. let mode = check_or_build_mode(unit.mode, dep_lib); let dep_unit_for = unit_for.with_dependency(unit, dep_lib, unit_for.root_compile_kind()); - let lib_unit_dep = new_unit_dep( + let lib_unit_deps = new_unit_deps( state, unit, dep_pkg, @@ -659,12 +682,13 @@ fn compute_deps_doc( unit.kind.for_target(dep_lib), mode, IS_NO_ARTIFACT_DEP, + dep_filter, )?; - ret.push(lib_unit_dep); + ret.extend(lib_unit_deps); if dep_lib.documented() { if let CompileMode::Doc { deps: true } = unit.mode { // Document this lib as well. - let doc_unit_dep = new_unit_dep( + let doc_unit_deps = new_unit_deps( state, unit, dep_pkg, @@ -673,8 +697,9 @@ fn compute_deps_doc( unit.kind.for_target(dep_lib), unit.mode, IS_NO_ARTIFACT_DEP, + dep_filter, )?; - ret.push(doc_unit_dep); + ret.extend(doc_unit_deps); } } } @@ -685,7 +710,7 @@ fn compute_deps_doc( // If we document a binary/example, we need the library available. if unit.target.is_bin() || unit.target.is_example() { // build the lib - ret.extend(maybe_lib(unit, state, unit_for)?); + ret.extend(maybe_lib(unit, state, unit_for, dep_filter)?); // and also the lib docs for intra-doc links if let Some(lib) = unit .pkg @@ -694,7 +719,7 @@ fn compute_deps_doc( .find(|t| t.is_linkable() && t.documented()) { let dep_unit_for = unit_for.with_dependency(unit, lib, unit_for.root_compile_kind()); - let lib_doc_unit = new_unit_dep( + let lib_doc_units = new_unit_deps( state, unit, &unit.pkg, @@ -703,8 +728,9 @@ fn compute_deps_doc( unit.kind.for_target(lib), unit.mode, IS_NO_ARTIFACT_DEP, + dep_filter, )?; - ret.push(lib_doc_unit); + ret.extend(lib_doc_units); } } @@ -717,7 +743,7 @@ fn compute_deps_doc( unit_for.root_compile_kind(), ); deps_of(scrape_unit, state, unit_for)?; - ret.push(new_unit_dep( + ret.extend(new_unit_deps( state, scrape_unit, &scrape_unit.pkg, @@ -726,6 +752,7 @@ fn compute_deps_doc( scrape_unit.kind, scrape_unit.mode, IS_NO_ARTIFACT_DEP, + dep_filter, )?); } } @@ -737,7 +764,8 @@ fn maybe_lib( unit: &Unit, state: &mut State<'_, '_>, unit_for: UnitFor, -) -> CargoResult> { + dep_filter: &dyn Fn(&Unit, &Dependency) -> bool, +) -> CargoResult> { unit.pkg .targets() .iter() @@ -745,7 +773,7 @@ fn maybe_lib( .map(|t| { let mode = check_or_build_mode(unit.mode, t); let dep_unit_for = unit_for.with_dependency(unit, t, unit_for.root_compile_kind()); - new_unit_dep( + new_unit_deps( state, unit, &unit.pkg, @@ -754,9 +782,10 @@ fn maybe_lib( unit.kind.for_target(t), mode, IS_NO_ARTIFACT_DEP, + dep_filter, ) }) - .transpose() + .unwrap_or_else(|| Ok(Vec::new())) } /// If a build script is scheduled to be run for the package specified by @@ -770,7 +799,7 @@ fn dep_build_script( unit: &Unit, unit_for: UnitFor, state: &State<'_, '_>, -) -> CargoResult> { +) -> CargoResult> { unit.pkg .targets() .iter() @@ -805,7 +834,7 @@ fn dep_build_script( // once because it would break a large number of scripts (they // would think they have the wrong set of features enabled). let script_unit_for = unit_for.for_custom_build(); - new_unit_dep_with_profile( + new_unit_deps_with_profile( state, unit, &unit.pkg, @@ -815,9 +844,10 @@ fn dep_build_script( CompileMode::RunCustomBuild, profile, IS_NO_ARTIFACT_DEP, + &|_, _| true, ) }) - .transpose() + .unwrap_or_else(|| Ok(Vec::new())) } /// Choose the correct mode for dependencies. @@ -839,7 +869,10 @@ fn check_or_build_mode(mode: CompileMode, target: &Target) -> CompileMode { } /// Create a new Unit for a dependency from `parent` to `pkg` and `target`. -fn new_unit_dep( +/// There may be multiple (to the same crate) with different extern names. +/// Use `dep_filter` to assure only the desired dependencies for a crate will +/// cause a unit to be created, otherwise extern-crate names may leak. +fn new_unit_deps( state: &State<'_, '_>, parent: &Unit, pkg: &Package, @@ -848,7 +881,8 @@ fn new_unit_dep( kind: CompileKind, mode: CompileMode, artifact: Option<&Artifact>, -) -> CargoResult { + dep_filter: &dyn Fn(&Unit, &Dependency) -> bool, +) -> CargoResult> { let is_local = pkg.package_id().source_id().is_path() && !state.is_std; let profile = state.profiles.get_profile( pkg.package_id(), @@ -857,51 +891,61 @@ fn new_unit_dep( unit_for, kind, ); - new_unit_dep_with_profile( - state, parent, pkg, target, unit_for, kind, mode, profile, artifact, + new_unit_deps_with_profile( + state, parent, pkg, target, unit_for, kind, mode, profile, artifact, dep_filter, ) } -fn new_unit_dep_with_profile( +fn new_unit_deps_with_profile( state: &State<'_, '_>, parent: &Unit, - pkg: &Package, + target_pkg: &Package, target: &Target, unit_for: UnitFor, kind: CompileKind, mode: CompileMode, profile: Profile, artifact: Option<&Artifact>, -) -> CargoResult { - let (extern_crate_name, dep_name) = state.resolve().extern_crate_name_and_dep_name( - parent.pkg.package_id(), - pkg.package_id(), - target, - )?; - let public = state + dep_filter: &dyn Fn(&Unit, &Dependency) -> bool, +) -> CargoResult> { + let ret = state .resolve() - .is_public_dep(parent.pkg.package_id(), pkg.package_id()); - let features_for = unit_for.map_to_features_for(artifact); - let features = state.activated_features(pkg.package_id(), features_for); - let unit = state.interner.intern( - pkg, - target, - profile, - kind, - mode, - features, - state.is_std, - /*dep_hash*/ 0, - artifact.map_or(IsArtifact::No, |_| IsArtifact::Yes), - ); - Ok(UnitDep { - unit, - unit_for, - extern_crate_name, - dep_name, - public, - noprelude: false, - }) + .extern_crate_names_and_dep_names( + parent.pkg.package_id(), + target_pkg.package_id(), + target, + &|dep| dep_filter(parent, dep), + state.config.cli_unstable().multidep, + )? + .into_iter() + .fold(Vec::new(), |mut acc, (extern_crate_name, dep_name)| { + let public = state + .resolve() + .is_public_dep(parent.pkg.package_id(), target_pkg.package_id()); + let features_for = unit_for.map_to_features_for(artifact); + let features = state.activated_features(target_pkg.package_id(), features_for); + let unit = state.interner.intern( + target_pkg, + target, + profile.clone(), + kind, + mode, + features, + state.is_std, + /*dep_hash*/ 0, + artifact.map_or(IsArtifact::No, |_| IsArtifact::Yes), + ); + acc.push(UnitDep { + unit, + unit_for, + extern_crate_name, + dep_name, + public, + noprelude: false, + }); + acc + }); + Ok(ret) } /// Fill in missing dependencies for units of the `RunCustomBuild` @@ -1072,21 +1116,7 @@ impl<'a, 'cfg> State<'a, 'cfg> { let deps: Vec<_> = deps .iter() .filter(|dep| { - // If this target is a build command, then we only want build - // dependencies, otherwise we want everything *other than* build - // dependencies. - if unit.target.is_custom_build() != dep.is_build() { - return false; - } - - // If this dependency is **not** a transitive dependency, then it - // only applies to test/example targets. - if !dep.is_transitive() - && !unit.target.is_test() - && !unit.target.is_example() - && !unit.mode.is_doc_scrape() - && !unit.mode.is_any_test() - { + if !non_custom_and_non_transitive_deps(unit, dep) { return false; } @@ -1119,3 +1149,27 @@ impl<'a, 'cfg> State<'a, 'cfg> { .collect() } } + +fn non_custom_and_non_transitive_deps(unit: &Unit, dep: &Dependency) -> bool { + // If this target is a build command, then we only want build + // dependencies, otherwise we want everything *other than* build + // dependencies. + if unit.target.is_custom_build() != dep.is_build() { + return false; + } + + // If this dependency is **not** a transitive dependency, then it + // only applies to test/example targets. + if !dep.is_transitive() + && !unit.target.is_test() + && !unit.target.is_example() + && !unit.mode.is_doc_scrape() + && !unit.mode.is_any_test() + { + return false; + } + + // If we've gotten past all that, then this dependency is + // actually used! + true +} diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index cc8d0a3cdbe..cd5e8f5977f 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -633,6 +633,7 @@ unstable_cli_options!( avoid_dev_deps: bool = ("Avoid installing dev-dependencies if possible"), binary_dep_depinfo: bool = ("Track changes to dependency artifacts"), bindeps: bool = ("Allow Cargo packages to depend on bin, cdylib, and staticlib crates, and use the artifacts built by those crates"), + multidep: bool = ("Allow Cargo packages to depend on the same crate multiple times with different dependency names, to support artifact dependencies for multiple targets."), #[serde(deserialize_with = "deserialize_build_std")] build_std: Option> = ("Enable Cargo to compile the standard library itself as part of a crate graph compilation"), build_std_features: Option> = ("Configure features enabled for the standard library itself when building the standard library"), @@ -843,6 +844,7 @@ impl CliUnstable { "named-profiles" => stabilized_warn(k, "1.57", STABILIZED_NAMED_PROFILES), "binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?, "bindeps" => self.bindeps = parse_empty(k, v)?, + "multidep" => self.multidep = parse_empty(k, v)?, "build-std" => { self.build_std = Some(crate::core::compiler::standard_lib::parse_unstable_flag(v)) } diff --git a/src/cargo/core/resolver/resolve.rs b/src/cargo/core/resolver/resolve.rs index 90a33f0ea75..cbb0c26e878 100644 --- a/src/cargo/core/resolver/resolve.rs +++ b/src/cargo/core/resolver/resolve.rs @@ -169,10 +169,10 @@ this could be indicative of a few possible situations: id.source_id() ) - // If our checksum hasn't been calculated, then it could mean - // that future Cargo figured out how to checksum something or - // more realistically we were overridden with a source that does - // not have checksums. + // If our checksum hasn't been calculated, then it could mean + // that future Cargo figured out how to checksum something or + // more realistically we were overridden with a source that does + // not have checksums. } else if mine.is_none() { anyhow::bail!( "\ @@ -191,9 +191,9 @@ unable to verify that `{0}` is the same as when the lockfile was generated id.source_id() ) - // If the checksums aren't equal, and neither is None, then they - // must both be Some, in which case the checksum now differs. - // That's quite bad! + // If the checksums aren't equal, and neither is None, then they + // must both be Some, in which case the checksum now differs. + // That's quite bad! } else { anyhow::bail!( "\ @@ -295,12 +295,18 @@ unable to verify that `{0}` is the same as when the lockfile was generated &self.metadata } - pub fn extern_crate_name_and_dep_name( + /// Based on the dependencies between `from -> to.to_target`, return a list of one + /// or more unique extern_crate_name/dependency-name pairs. + /// Note that the `dependency-name` is used to derive the `extern_crate_name`. + /// If `to == from` or if there is no dependency, the targets crate name is used. + pub fn extern_crate_names_and_dep_names( &self, from: PackageId, to: PackageId, to_target: &Target, - ) -> CargoResult<(InternedString, Option)> { + dep_filter: &dyn Fn(&Dependency) -> bool, + multidep_support: bool, /*unstable cli feature toggle*/ + ) -> CargoResult)>> { let empty_set: HashSet = HashSet::new(); let deps = if from == to { &empty_set @@ -308,22 +314,43 @@ unable to verify that `{0}` is the same as when the lockfile was generated self.dependencies_listed(from, to) }; - let target_crate_name = || (to_target.crate_name(), None); - let mut name_pairs = deps.iter().map(|d| { - d.explicit_name_in_toml() - .map(|s| (s.as_str().replace("-", "_"), Some(s))) - .unwrap_or_else(target_crate_name) - }); - let (extern_crate_name, dep_name) = name_pairs.next().unwrap_or_else(target_crate_name); - for (n, _) in name_pairs { - anyhow::ensure!( - n == extern_crate_name, - "the crate `{}` depends on crate `{}` multiple times with different names", - from, - to, - ); + let target_crate_name = || (to_target.crate_name().into(), None); + let mut name_pairs = deps + .iter() + .filter(|dep| { + if multidep_support { + dep_filter(dep) + } else { + return true; + } + }) + .map(|d| { + d.explicit_name_in_toml() + .map(|s| (s.as_str().replace("-", "_").into(), Some(s))) + .unwrap_or_else(target_crate_name) + }) + .peekable(); + if multidep_support { + Ok(if name_pairs.peek().is_none() { + vec![target_crate_name()] + } else { + let mut ret = name_pairs.collect::>(); + ret.sort(); + ret.dedup(); + ret + }) + } else { + let (extern_crate_name, dep_name) = name_pairs.next().unwrap_or_else(target_crate_name); + for (n, _) in name_pairs { + anyhow::ensure!( + n == extern_crate_name, + "the crate `{}` depends on crate `{}` multiple times with different names", + from, + to, + ); + } + Ok(vec![(extern_crate_name, dep_name)]) } - Ok((extern_crate_name.into(), dep_name)) } fn dependencies_listed(&self, from: PackageId, to: PackageId) -> &HashSet { diff --git a/src/cargo/ops/cargo_output_metadata.rs b/src/cargo/ops/cargo_output_metadata.rs index d8a86eae778..87562eae201 100644 --- a/src/cargo/ops/cargo_output_metadata.rs +++ b/src/cargo/ops/cargo_output_metadata.rs @@ -213,9 +213,15 @@ fn build_resolve_graph_r( .and_then(|pkg| pkg.targets().iter().find(|t| t.is_lib())) .and_then(|lib_target| { resolve - .extern_crate_name_and_dep_name(pkg_id, dep_id, lib_target) - .map(|(ext_crate_name, _)| ext_crate_name) + .extern_crate_names_and_dep_names( + pkg_id, + dep_id, + lib_target, + &|_dep| true, + /* multidep-support */ false, + ) .ok() + .and_then(|v| v.get(0).map(|(ext_crate_name, _)| *ext_crate_name)) }) .map(|name| Dep { name, diff --git a/tests/testsuite/artifact_dep.rs b/tests/testsuite/artifact_dep.rs index 3b28ad0aa28..0b396f3b95e 100644 --- a/tests/testsuite/artifact_dep.rs +++ b/tests/testsuite/artifact_dep.rs @@ -1,6 +1,7 @@ //! Tests specific to artifact dependencies, designated using //! the new `dep = { artifact = "bin", … }` syntax in manifests. +use cargo_test_support::basic_lib_manifest; use cargo_test_support::compare::match_exact; use cargo_test_support::registry::Package; use cargo_test_support::{ @@ -1075,7 +1076,7 @@ fn build_script_deps_adopt_specified_target_unconditionally() { /// inverse RFC-3176 #[cargo_test] -fn build_script_deps_adopt_do_not_allow_multiple_targets_under_different_name_and_same_version() { +fn build_script_deps_do_not_allow_multiple_targets_under_different_name_and_same_version() { if cross_compile::disabled() { return; } @@ -1383,6 +1384,58 @@ fn profile_override_basic() { .run(); } +#[cargo_test] +fn profile_override_basic_multidep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [build-dependencies] + bar = { path = "bar", artifact = "bin" } + + [dependencies] + bar = { path = "bar", artifact = "bin" } + + [profile.dev.build-override] + opt-level = 1 + + [profile.dev] + opt-level = 3 + "#, + ) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("build -v -Z bindeps -Z multidep") + .masquerade_as_nightly_cargo() + .with_stderr_contains( + "[RUNNING] `rustc --crate-name build_script_build [..] -C opt-level=1 [..]`", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..] -C opt-level=3 [..]`", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..] -C opt-level=1 [..]`", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..] -C opt-level=1 [..]`", + ) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..] -C opt-level=3 [..]`", + ) + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..] -C opt-level=3 [..]`") + .run(); +} + #[cargo_test] fn dependencies_of_dependencies_work_in_artifacts() { Package::new("baz", "1.0.0") @@ -2154,6 +2207,337 @@ fn build_script_output_string(p: &Project, package_name: &str) -> String { std::fs::read_to_string(&paths[0]).unwrap() } +#[cargo_test] +fn lib_artifacts_do_not_leak_when_same_package_gets_renamed() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies.bar-alternate] + path = "bar/" + package = "bar" + artifact = "bin" + lib = false + + [dependencies.bar] + path = "bar/" + package = "bar" + artifact = "bin" + lib = true + "#, + ) + // Lib artifacts are always available in the required arch + .file( + "src/lib.rs", + "extern crate bar; extern crate bar_alternate;", + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .file("bar/src/lib.rs", "pub fn doit() {}") + .build(); + + p.cargo("check -v -Z bindeps -Z multidep") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr_contains("error[E0463]: can't find crate for `bar_alternate`") + .run(); +} + +// A variant on build_script_deps_do_not_allow_multiple_targets_under_different_name_and_same_version() +// which works only with multidep +#[cargo_test] +fn multiple_bin_artifacts_with_different_names_and_different_targets() { + if cross_compile::disabled() { + return; + } + + let target = cross_compile::alternate(); + let native = cross_compile::native(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [build-dependencies.bar-alternate] + path = "bar/" + package = "bar" + artifact = "bin" + features = ["f1"] + target = "{alternate}" + + [build-dependencies.bar] + path = "bar/" + package = "bar" + artifact = "bin" + features = ["f2"] + target = "{native}" + + [build-dependencies.bar-f1] + path = "bar/" + package = "bar" + artifact = "bin" + features = ["f1"] + target = "{native}" + + [dependencies.bar-alternate] + path = "bar/" + package = "bar" + artifact = "bin" + lib = true + target = "{alternate}" + + [dependencies.bar] + path = "bar/" + package = "bar" + artifact = "bin" + lib = true + target = "{native}" + "#, + alternate = target, + native = native + ), + ) + // Lib artifacts are always available in the required arch + .file("src/lib.rs", "extern crate bar; extern crate bar_alternate;") + .file("build.rs", &r#" + fn main() { + let file = std::env::var("CARGO_BIN_FILE_BAR").expect("BAR present"); + println!("running {}", file); + assert!(std::process::Command::new(file).status().unwrap().success()); + + let file = std::env::var("CARGO_BIN_FILE_BAR_F1_bar").expect("BAR f1 present"); + println!("running {}", file); + assert!(std::process::Command::new(file).status().unwrap().success()); + + if $CAN_RUN_ON_HOST { + let file = std::env::var("CARGO_BIN_FILE_BAR_ALTERNATE_bar").expect("BAR_ALTERNATE present"); + println!("running {}", file); + assert!(std::process::Command::new(file).status().unwrap().success()); + } + }"#.replace("$CAN_RUN_ON_HOST", &cross_compile::can_run_on_host().to_string())) + .file("bar/Cargo.toml", r#" + [package] + + name = "bar" + version = "0.5.0" + authors = [] + + [features] + f1 = [] + f2 = [] + "#) + .file("bar/src/main.rs", r#"fn main() { + #[cfg(feature = "f1")] + println!("f1"); + #[cfg(feature = "f2")] + println!("f2"); + }"#) + .file("bar/src/lib.rs", "pub fn doit() {}") + .build(); + + p.cargo("check -v -Z bindeps -Z multidep") + .masquerade_as_nightly_cargo() + .with_stderr_does_not_contain(format!( + "[RUNNING] `rustc --crate-name build_script_build build.rs [..]--target {} [..]", + target + )) + .with_stderr_contains("[RUNNING] `rustc --crate-name build_script_build build.rs [..]") + .with_stderr_contains(format!( + "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--target {} [..]", + target + )) + .with_stderr_contains(format!( + "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..]--target {} [..]", + target + )) + // native targets aren't specifying a target, and it's easiest to recognize them by their outdir which doesn't contain the target triple. + .with_stderr_contains("[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--out-dir [..]/debug/deps [..]") + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..]--out-dir [..]/debug/deps/artifact/[..]", + ) + .with_stderr_does_not_contain(format!( + "[RUNNING] `rustc --crate-name foo [..]--target {} [..]", + target + )) + .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]") + .run(); + + let build_script_output = build_script_output_string(&p, "foo"); + // unification happens across the same target, so 'native' should see both features. + // The non-native one should only see one though. + if cross_compile::can_run_on_host() { + match_exact( + &format!( + r#"running [..]/debug/deps/artifact/bar-[..] +f1 +f2 +running [..]/debug/deps/artifact/bar-[..] +f1 +f2 +running [..]/{triple}/debug/deps/artifact/bar-[..] +f1"#, // there should only be one feature active here to prove lack of unification + triple = target + ), + &build_script_output, + "build script output", + "", + None, + ) + } else { + match_exact( + r#"running [..]/debug/deps/artifact/bar-[..] +f1 +f2 +running [..]/debug/deps/artifact/bar-[..] +f1 +f2 +"#, + &build_script_output, + "build script output", + "", + None, + ) + } + .unwrap(); +} + +#[cargo_test] +fn deps_do_not_allow_same_resolved_versions_under_different_name_without_multidep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar-renamed = { package = "bar", path = "bar/" } + bar = { path = "bar/" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr("[ERROR] the crate `foo v0.0.0 ([CWD])` depends on crate `bar v0.5.0 ([CWD]/bar)` multiple times with different names") + .run(); +} + +#[cargo_test] +fn different_names_to_the_same_crate_in_different_dep_kinds_with_multidep_toggle() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/" } + + [dev-dependencies] + bar-again = { package = "bar", path = "bar/" } + "#, + ) + .file("src/lib.rs", "extern crate bar;") + .file("tests/test.rs", "extern crate bar; extern crate bar_again;") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("test -Z multidep") + .masquerade_as_nightly_cargo() + .run(); +} + +#[cargo_test] +fn different_dep_names_to_the_same_crate_in_different_categories_do_not_leak_with_multidep_toggle() +{ + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/" } + + [dev-dependencies] + bar-again = { package = "bar", path = "bar/" } + "#, + ) + .file("src/lib.rs", "extern crate bar_again;") + .file("tests/test.rs", "extern crate bar_again; extern crate bar;") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("test -Z multidep") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr_contains("[..] can't find crate for `bar_again`") + .with_stderr_contains(" --> src/lib.rs:1:1") + .run(); +} + +#[cargo_test] +fn deps_allow_renaming_the_same_resolved_version_with_multidep_toggle() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + resolver = "2" + + [dependencies] + bar-renamed = { package = "bar", path = "bar/" } + bar = { path = "bar/" } + "#, + ) + .file("src/lib.rs", "extern crate bar; extern crate bar_renamed;") + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("check -Z multidep") + .masquerade_as_nightly_cargo() + .with_stderr( + "\ +[CHECKING] bar [..] +[CHECKING] foo [..] +[FINISHED] dev [..]", + ) + .run(); +} + #[cargo_test] fn build_script_features_for_shared_dependency() { // When a build script is built and run, its features should match. Here: diff --git a/tests/testsuite/metadata.rs b/tests/testsuite/metadata.rs index 63f92d8555b..6d667b46d43 100644 --- a/tests/testsuite/metadata.rs +++ b/tests/testsuite/metadata.rs @@ -925,9 +925,8 @@ fn workspace_metadata() { } #[cargo_test] -fn workspace_metadata_with_dependencies_no_deps() { +fn workspace_metadata_no_deps() { let p = project() - // NOTE that 'artifact' isn't mentioned in the workspace here, yet it shows up as member. .file( "Cargo.toml", r#" @@ -935,29 +934,13 @@ fn workspace_metadata_with_dependencies_no_deps() { members = ["bar", "baz"] "#, ) - .file( - "bar/Cargo.toml", - r#" - [package] - - name = "bar" - version = "0.5.0" - authors = ["wycats@example.com"] - - [dependencies] - baz = { path = "../baz/" } - artifact = { path = "../artifact/", artifact = "bin" } - "#, - ) + .file("bar/Cargo.toml", &basic_lib_manifest("bar")) .file("bar/src/lib.rs", "") .file("baz/Cargo.toml", &basic_lib_manifest("baz")) .file("baz/src/lib.rs", "") - .file("artifact/Cargo.toml", &basic_bin_manifest("artifact")) - .file("artifact/src/main.rs", "fn main() {}") .build(); - p.cargo("metadata --no-deps -Z bindeps") - .masquerade_as_nightly_cargo() + p.cargo("metadata --no-deps") .with_json( r#" { @@ -978,42 +961,8 @@ fn workspace_metadata_with_dependencies_no_deps() { "id": "bar[..]", "keywords": [], "source": null, + "dependencies": [], "license": null, - "dependencies": [ - { - "features": [], - "kind": null, - "name": "artifact", - "optional": false, - "path": "[..]/foo/artifact", - "registry": null, - "rename": null, - "req": "*", - "source": null, - "target": null, - "uses_default_features": true, - "artifact": { - "kinds": [ - "bin" - ], - "lib": false, - "target": null - } - }, - { - "features": [], - "kind": null, - "name": "baz", - "optional": false, - "path": "[..]/foo/baz", - "registry": null, - "rename": null, - "req": "*", - "source": null, - "target": null, - "uses_default_features": true - } - ], "license_file": null, "links": null, "description": null, @@ -1035,49 +984,6 @@ fn workspace_metadata_with_dependencies_no_deps() { "metadata": null, "publish": null }, - { - "authors": [ - "wycats@example.com" - ], - "categories": [], - "default_run": null, - "dependencies": [], - "description": null, - "documentation": null, - "edition": "2015", - "features": {}, - "homepage": null, - "id": "artifact 0.5.0 (path+file:[..]/foo/artifact)", - "keywords": [], - "license": null, - "license_file": null, - "links": null, - "manifest_path": "[..]/foo/artifact/Cargo.toml", - "metadata": null, - "name": "artifact", - "publish": null, - "readme": null, - "repository": null, - "rust_version": null, - "source": null, - "targets": [ - { - "crate_types": [ - "bin" - ], - "doc": true, - "doctest": false, - "edition": "2015", - "kind": [ - "bin" - ], - "name": "artifact", - "src_path": "[..]/foo/artifact/src/main.rs", - "test": true - } - ], - "version": "0.5.0" - }, { "authors": [ "wycats@example.com" @@ -1118,11 +1024,7 @@ fn workspace_metadata_with_dependencies_no_deps() { "publish": null } ], - "workspace_members": [ - "bar 0.5.0 (path+file:[..]bar)", - "artifact 0.5.0 (path+file:[..]/foo/artifact)", - "baz 0.5.0 (path+file:[..]baz)" - ], + "workspace_members": ["bar 0.5.0 (path+file:[..]bar)", "baz 0.5.0 (path+file:[..]baz)"], "resolve": null, "target_directory": "[..]foo/target", "version": 1, @@ -3983,3 +3885,558 @@ fn workspace_metadata_with_dependencies_no_deps_artifact() { ) .run(); } + +// TODO: Consider using this test instead of the version without the 'multidep' suffix or merge them because they should be pretty much the same. +// TODO(ST): Fix the logic of how renamed deps are displayed. The 'name' is a problem is this that the name is supposed to be the renamed dep name, +// causes duplication +#[cargo_test] +fn workspace_metadata_with_dependencies_and_resolve_multidep() { + let alt_target = "wasm32-unknown-unknown"; + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["bar", "artifact", "non-artifact", "bin-only-artifact"] + "#, + ) + .file( + "bar/Cargo.toml", + &r#" + [package] + + name = "bar" + version = "0.5.0" + authors = [] + + [build-dependencies] + artifact = { path = "../artifact/", artifact = "bin", target = "target" } + bin-only-artifact = { path = "../bin-only-artifact/", artifact = "bin", target = "$ALT_TARGET" } + non-artifact = { path = "../non-artifact" } + + [dependencies] + artifact = { path = "../artifact/", artifact = ["cdylib", "staticlib", "bin:baz-name"], lib = true, target = "$ALT_TARGET" } + bin-only-artifact = { path = "../bin-only-artifact/", artifact = "bin:a-name" } + non-artifact = { path = "../non-artifact" } + non-artifact-renamed = { package = "non-artifact", path = "../non-artifact" } + + [dev-dependencies] + artifact = { path = "../artifact/" } + non-artifact = { path = "../non-artifact" } + bin-only-artifact = { path = "../bin-only-artifact/", artifact = "bin:b-name" } + "#.replace("$ALT_TARGET", alt_target), + ) + .file("bar/src/lib.rs", "") + .file("bar/build.rs", "fn main() {}") + .file( + "artifact/Cargo.toml", + r#" + [package] + name = "artifact" + version = "0.5.0" + authors = [] + + [lib] + crate-type = ["staticlib", "cdylib", "rlib"] + + [[bin]] + name = "bar-name" + + [[bin]] + name = "baz-name" + "#, + ) + .file("artifact/src/main.rs", "fn main() {}") + .file("artifact/src/lib.rs", "") + .file( + "bin-only-artifact/Cargo.toml", + r#" + [package] + name = "bin-only-artifact" + version = "0.5.0" + authors = [] + + [[bin]] + name = "a-name" + + [[bin]] + name = "b-name" + "#, + ) + .file("bin-only-artifact/src/main.rs", "fn main() {}") + .file("non-artifact/Cargo.toml", + r#" + [package] + + name = "non-artifact" + version = "0.5.0" + authors = [] + "#, + ) + .file("non-artifact/src/lib.rs", "") + .build(); + + p.cargo("metadata -Z bindeps -Z multidep") + .masquerade_as_nightly_cargo() + .with_json( + r#" + { + "metadata": null, + "packages": [ + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "artifact 0.5.0 (path+file://[..]/foo/artifact)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/artifact/Cargo.toml", + "metadata": null, + "name": "artifact", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "staticlib", + "cdylib", + "rlib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "staticlib", + "cdylib", + "rlib" + ], + "name": "artifact", + "src_path": "[..]/foo/artifact/src/lib.rs", + "test": true + }, + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "bar-name", + "src_path": "[..]/foo/artifact/src/main.rs", + "test": true + }, + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "baz-name", + "src_path": "[..]/foo/artifact/src/main.rs", + "test": true + } + ], + "version": "0.5.0" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [ + { + "artifact": { + "kinds": [ + "cdylib", + "staticlib", + "bin:baz-name" + ], + "lib": true, + "target": "wasm32-unknown-unknown" + }, + "features": [], + "kind": null, + "name": "artifact", + "optional": false, + "path": "[..]/foo/artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "artifact": { + "kinds": [ + "bin:a-name" + ], + "lib": false, + "target": null + }, + "features": [], + "kind": null, + "name": "bin-only-artifact", + "optional": false, + "path": "[..]/foo/bin-only-artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": null, + "name": "non-artifact", + "optional": false, + "path": "[..]/foo/non-artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": null, + "name": "non-artifact", + "optional": false, + "path": "[..]/foo/non-artifact", + "registry": null, + "rename": "non-artifact-renamed", + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": "dev", + "name": "artifact", + "optional": false, + "path": "[..]/foo/artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "artifact": { + "kinds": [ + "bin:b-name" + ], + "lib": false, + "target": null + }, + "features": [], + "kind": "dev", + "name": "bin-only-artifact", + "optional": false, + "path": "[..]/foo/bin-only-artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": "dev", + "name": "non-artifact", + "optional": false, + "path": "[..]/foo/non-artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "artifact": { + "kinds": [ + "bin" + ], + "lib": false, + "target": "target" + }, + "features": [], + "kind": "build", + "name": "artifact", + "optional": false, + "path": "[..]/foo/artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "artifact": { + "kinds": [ + "bin" + ], + "lib": false, + "target": "wasm32-unknown-unknown" + }, + "features": [], + "kind": "build", + "name": "bin-only-artifact", + "optional": false, + "path": "[..]/foo/bin-only-artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + }, + { + "features": [], + "kind": "build", + "name": "non-artifact", + "optional": false, + "path": "[..]/foo/non-artifact", + "registry": null, + "rename": null, + "req": "*", + "source": null, + "target": null, + "uses_default_features": true + } + ], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "bar 0.5.0 (path+file://[..]/foo/bar)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/bar/Cargo.toml", + "metadata": null, + "name": "bar", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "bar", + "src_path": "[..]/foo/bar/src/lib.rs", + "test": true + }, + { + "crate_types": [ + "bin" + ], + "doc": false, + "doctest": false, + "edition": "2015", + "kind": [ + "custom-build" + ], + "name": "build-script-build", + "src_path": "[..]/foo/bar/build.rs", + "test": false + } + ], + "version": "0.5.0" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "bin-only-artifact 0.5.0 (path+file://[..]/foo/bin-only-artifact)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/bin-only-artifact/Cargo.toml", + "metadata": null, + "name": "bin-only-artifact", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "a-name", + "src_path": "[..]/foo/bin-only-artifact/src/main.rs", + "test": true + }, + { + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "edition": "2015", + "kind": [ + "bin" + ], + "name": "b-name", + "src_path": "[..]/foo/bin-only-artifact/src/main.rs", + "test": true + } + ], + "version": "0.5.0" + }, + { + "authors": [], + "categories": [], + "default_run": null, + "dependencies": [], + "description": null, + "documentation": null, + "edition": "2015", + "features": {}, + "homepage": null, + "id": "non-artifact 0.5.0 (path+file://[..]/foo/non-artifact)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "manifest_path": "[..]/foo/non-artifact/Cargo.toml", + "metadata": null, + "name": "non-artifact", + "publish": null, + "readme": null, + "repository": null, + "rust_version": null, + "source": null, + "targets": [ + { + "crate_types": [ + "lib" + ], + "doc": true, + "doctest": true, + "edition": "2015", + "kind": [ + "lib" + ], + "name": "non-artifact", + "src_path": "[..]/foo/non-artifact/src/lib.rs", + "test": true + } + ], + "version": "0.5.0" + } + ], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [], + "id": "artifact 0.5.0 (path+file://[..]/foo/artifact)" + }, + { + "dependencies": [ + "artifact 0.5.0 (path+file://[..]/foo/artifact)" + ], + "deps": [ + { + "dep_kinds": [ + { + "kind": null, + "target": null + }, + { + "kind": "dev", + "target": null + }, + { + "kind": "build", + "target": null + } + ], + "name": "artifact", + "pkg": "artifact 0.5.0 (path+file://[..]/foo/artifact)" + } + ], + "features": [], + "id": "bar 0.5.0 (path+file://[..]/foo/bar)" + }, + { + "dependencies": [], + "deps": [], + "features": [], + "id": "bin-only-artifact 0.5.0 (path+file://[..]/foo/bin-only-artifact)" + }, + { + "dependencies": [], + "deps": [], + "features": [], + "id": "non-artifact 0.5.0 (path+file://[..]/foo/non-artifact)" + } + ], + "root": null + }, + "target_directory": "[..]/foo/target", + "version": 1, + "workspace_members": [ + "bar 0.5.0 (path+file://[..]/foo/bar)", + "artifact 0.5.0 (path+file://[..]/foo/artifact)", + "bin-only-artifact 0.5.0 (path+file://[..]/foo/bin-only-artifact)", + "non-artifact 0.5.0 (path+file://[..]/foo/non-artifact)" + ], + "workspace_root": "[..]/foo" + } + "#, + ) + .run(); +}