diff --git a/src/cargo/core/compiler/unit_dependencies.rs b/src/cargo/core/compiler/unit_dependencies.rs index 2d0ea7c3c62..3f4f7f40ce5 100644 --- a/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/cargo/core/compiler/unit_dependencies.rs @@ -310,6 +310,16 @@ fn compute_deps( let dep_unit_for = unit_for.with_dependency(unit, dep_lib, unit_for.root_compile_kind()); let manifest_deps = deps.iter().map(|d| (*d).clone()).collect::>(); + let lib_dep_name = if deps.iter().any(|dep| dep.artifact().is_some()) { + state.resolve().lib_dependency_name( + unit.pkg.package_id(), + dep_pkg_id, + deps.iter().copied(), + dep_lib, + )? + } else { + None + }; let start = ret.len(); if state.gctx.cli_unstable().dual_proc_macros @@ -322,6 +332,7 @@ fn compute_deps( dep_pkg, dep_lib, Some(manifest_deps.clone()), + lib_dep_name, dep_unit_for, unit.kind, mode, @@ -334,6 +345,7 @@ fn compute_deps( dep_pkg, dep_lib, Some(manifest_deps), + lib_dep_name, dep_unit_for, CompileKind::Host, mode, @@ -347,6 +359,7 @@ fn compute_deps( dep_pkg, dep_lib, Some(manifest_deps), + lib_dep_name, dep_unit_for, unit.kind.for_target(dep_lib), mode, @@ -426,6 +439,7 @@ fn compute_deps( &unit.pkg, t, None, // artificial + None, UnitFor::new_normal(unit_for.root_compile_kind()), unit.kind.for_target(t), CompileMode::Build, @@ -452,6 +466,7 @@ fn calc_artifact_deps<'a>( let mut has_artifact_lib = false; let mut maybe_non_artifact_lib = false; let artifact_pkg = state.get(dep_id); + let all_artifact = deps.iter().all(|d| d.artifact().is_some()); for dep in deps { let Some(artifact) = dep.artifact() else { maybe_non_artifact_lib = true; @@ -478,6 +493,7 @@ fn calc_artifact_deps<'a>( .unwrap_or(unit.kind), artifact_pkg, dep, + all_artifact.then_some(*dep), )?); } } @@ -522,6 +538,7 @@ fn compute_deps_custom_build( &unit.pkg, &unit.target, None, // artificial + None, script_unit_for, // Build scripts always compiled for the host. CompileKind::Host, @@ -546,6 +563,7 @@ fn compute_deps_custom_build( let root_unit_compile_target = unit_for.root_compile_kind(); let unit_for = UnitFor::new_host(/*host_features*/ true, root_unit_compile_target); for (dep_pkg_id, deps) in state.deps(unit, script_unit_for) { + let all_artifact = deps.iter().all(|d| d.artifact().is_some()); for dep in deps { if dep.kind() != DepKind::Build || dep.artifact().is_none() { continue; @@ -565,6 +583,7 @@ fn compute_deps_custom_build( resolved_artifact_compile_kind.unwrap_or(CompileKind::Host), artifact_pkg, dep, + all_artifact.then_some(dep), )?); } } @@ -589,6 +608,7 @@ fn artifact_targets_to_unit_deps( compile_kind: CompileKind, artifact_pkg: &Package, dep: &Dependency, + naming_dep: Option<&Dependency>, ) -> CargoResult> { let ret = match_artifacts_kind_with_targets(dep, artifact_pkg.targets(), parent.pkg.name().as_str())? @@ -608,14 +628,18 @@ fn artifact_targets_to_unit_deps( _ => false, }) .map(|target_kind| { + let mut target = target.clone(); + target.set_kind(TargetKind::Lib(vec![target_kind.clone()])); + let name_override = naming_dep.map(|dep| { + Resolve::dep_extern_crate_name_and_dep_name(dep, &target) + }); new_unit_dep( state, parent, artifact_pkg, - target - .clone() - .set_kind(TargetKind::Lib(vec![target_kind.clone()])), + &target, None, // TBD + name_override, parent_unit_for, compile_kind, CompileMode::Build, @@ -623,17 +647,22 @@ fn artifact_targets_to_unit_deps( ) }), ) as Box>, - _ => Box::new(std::iter::once(new_unit_dep( - state, - parent, - artifact_pkg, - target, - None, // TBD - parent_unit_for, - compile_kind, - CompileMode::Build, - dep.artifact(), - ))), + _ => { + let name_override = naming_dep + .map(|dep| Resolve::dep_extern_crate_name_and_dep_name(dep, target)); + Box::new(std::iter::once(new_unit_dep( + state, + parent, + artifact_pkg, + target, + None, // TBD + name_override, + parent_unit_for, + compile_kind, + CompileMode::Build, + dep.artifact(), + ))) + } } }) .collect::, _>>()?; @@ -659,12 +688,23 @@ 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_dep_name = if deps.iter().any(|dep| dep.artifact().is_some()) { + state.resolve().lib_dependency_name( + unit.pkg.package_id(), + id, + deps.iter().copied(), + dep_lib, + )? + } else { + None + }; let lib_unit_dep = new_unit_dep( state, unit, dep_pkg, dep_lib, None, // not checking unused deps + lib_dep_name, dep_unit_for, unit.kind.for_target(dep_lib), mode, @@ -679,6 +719,7 @@ fn compute_deps_doc( dep_pkg, dep_lib, None, // not checking unused deps + lib_dep_name, dep_unit_for, unit.kind.for_target(dep_lib), unit.mode, @@ -713,6 +754,7 @@ fn compute_deps_doc( &unit.pkg, lib, None, // not checking unused deps + None, dep_unit_for, unit.kind.for_target(lib), unit.mode, @@ -733,6 +775,7 @@ fn compute_deps_doc( &scrape_unit.pkg, &scrape_unit.target, None, // not checking unused deps + None, scrape_unit_for, scrape_unit.kind, scrape_unit.mode, @@ -762,6 +805,7 @@ fn maybe_lib( &unit.pkg, t, None, + None, dep_unit_for, unit.kind.for_target(t), mode, @@ -824,6 +868,7 @@ fn dep_build_script( &unit.pkg, t, None, // artificial + None, script_unit_for, unit.kind, CompileMode::RunCustomBuild, @@ -861,6 +906,7 @@ fn new_unit_dep( pkg: &Package, target: &Target, manifest_deps: Option>, + name_override: Option<(InternedString, Option)>, unit_for: UnitFor, kind: CompileKind, mode: CompileMode, @@ -880,6 +926,7 @@ fn new_unit_dep( pkg, target, manifest_deps, + name_override, unit_for, kind, mode, @@ -894,17 +941,21 @@ fn new_unit_dep_with_profile( pkg: &Package, target: &Target, manifest_deps: Option>, + name_override: Option<(InternedString, Option)>, 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 (extern_crate_name, dep_name) = match name_override { + Some(name) => name, + None => state.resolve().extern_crate_name_and_dep_name( + parent.pkg.package_id(), + pkg.package_id(), + target, + )?, + }; let public = state .resolve() .is_public_dep(parent.pkg.package_id(), pkg.package_id()); diff --git a/src/cargo/core/resolver/resolve.rs b/src/cargo/core/resolver/resolve.rs index 3e3b8a5e98e..c865fb770f4 100644 --- a/src/cargo/core/resolver/resolve.rs +++ b/src/cargo/core/resolver/resolve.rs @@ -419,12 +419,19 @@ 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) - }); + Self::extern_crate_name_and_dep_name_from_deps(from, to, deps.iter(), to_target) + } + + fn extern_crate_name_and_dep_name_from_deps<'a>( + from: PackageId, + to: PackageId, + deps: impl IntoIterator, + to_target: &Target, + ) -> CargoResult<(InternedString, Option)> { + let target_crate_name = || (to_target.crate_name().into(), None); + let mut name_pairs = deps + .into_iter() + .map(|d| Self::dep_extern_crate_name_and_dep_name(d, to_target)); let (extern_crate_name, dep_name) = name_pairs.next().unwrap_or_else(target_crate_name); for (n, _) in name_pairs { anyhow::ensure!( @@ -434,7 +441,58 @@ unable to verify that `{0}` is the same as when the lockfile was generated to, ); } - Ok((extern_crate_name.into(), dep_name)) + Ok((extern_crate_name, dep_name)) + } + + pub fn dep_extern_crate_name_and_dep_name( + dep: &Dependency, + to_target: &Target, + ) -> (InternedString, Option) { + dep.explicit_name_in_toml() + .map(|name| (name.as_str().replace('-', "_").into(), Some(name))) + .unwrap_or_else(|| (to_target.crate_name().into(), None)) + } + + pub fn lib_dependency_name<'a>( + &self, + from: PackageId, + to: PackageId, + deps: impl IntoIterator, + to_target: &Target, + ) -> CargoResult)>> { + let deps = deps.into_iter().collect::>(); + if !deps.iter().all(|dep| dep.artifact().is_some()) { + return Self::extern_crate_name_and_dep_name_from_deps( + from, + to, + deps.into_iter(), + to_target, + ) + .map(Some); + } + + let mut names = deps + .into_iter() + .filter(|dep| dep.artifact().is_some_and(|artifact| artifact.is_lib())) + .map(|dep| Self::dep_extern_crate_name_and_dep_name(dep, to_target)) + .collect::>(); + + if names.is_empty() { + return Ok(None); + } + + names.sort(); + names.dedup(); + let (extern_crate_name, dep_name) = names[0]; + for (name, _) in &names[1..] { + anyhow::ensure!( + *name == extern_crate_name, + "the crate `{}` depends on crate `{}` multiple times with different names", + from, + to, + ); + } + Ok(Some((extern_crate_name, 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 929a4892e97..cca9b158b72 100644 --- a/src/cargo/ops/cargo_output_metadata.rs +++ b/src/cargo/ops/cargo_output_metadata.rs @@ -246,14 +246,11 @@ fn build_resolve_graph_r( let targets = package_map[&dep_id].targets(); - // Try to get the extern name for lib, or crate name for bins. - let extern_name = |target| { - resolve - .extern_crate_name_and_dep_name(pkg_id, dep_id, target) - .map(|(ext_crate_name, _)| ext_crate_name) - }; - let lib_target = targets.iter().find(|t| t.is_lib()); + let lib_dep_name = lib_target + .map(|target| resolve.lib_dependency_name(pkg_id, dep_id, deps.iter(), target)) + .transpose()? + .flatten(); for dep in deps.iter() { if let Some(target) = lib_target { @@ -269,7 +266,7 @@ fn build_resolve_graph_r( // if the user is not using -Zbindeps. // Remove this condition ` after -Zbindeps gets stabilized. let extern_name = if dep.artifact().is_some() { - Some(extern_name(target)?) + Some(Resolve::dep_extern_crate_name_and_dep_name(dep, target).0) } else { None }; @@ -308,7 +305,9 @@ fn build_resolve_graph_r( dep_kinds.push(DepKindInfo { kind: dep.kind(), target: dep.platform().cloned(), - extern_name: extern_name(target).ok(), + extern_name: Some( + Resolve::dep_extern_crate_name_and_dep_name(dep, target).0, + ), artifact: Some(kind.crate_type()), compile_target, bin_name: target.is_bin().then(|| target.name().to_string()), @@ -320,15 +319,24 @@ fn build_resolve_graph_r( let pkg_id = normalize_id(dep_id); - let dep = match (lib_target, dep_kinds.len()) { - (Some(target), _) => Dep { - name: extern_name(target)?, + let dep = match (lib_target, dep_kinds.len(), lib_dep_name) { + (Some(_), _, Some((name, _))) => Dep { + name, + pkg: pkg_id.to_spec(), + pkg_id, + dep_kinds, + }, + (Some(_), 1.., None) => Dep { + name: "".into(), pkg: pkg_id.to_spec(), pkg_id, dep_kinds, }, + // All active artifact deps emit at least one dep_kind or error while + // matching targets, but keep this arm explicit for exhaustiveness. + (Some(_), 0, None) => continue, // No lib target exists but contains artifact deps. - (None, 1..) => Dep { + (None, 1.., _) => Dep { name: "".into(), pkg: pkg_id.to_spec(), pkg_id, @@ -336,7 +344,7 @@ fn build_resolve_graph_r( }, // No lib or artifact dep exists. // Usually this mean parent depending on non-lib bin crate. - (None, _) => continue, + (None, _, _) => continue, }; dep_metadatas.push(dep) diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index 06eaf2943f2..e4c4e76b9fb 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -1024,6 +1024,15 @@ Artifact-dependencies adds the following keys to a dependency declaration in `Ca zoo = { version = "1.0", artifact = ["bin:cat", "bin:dog"]} ``` + To build artifacts from the same package for multiple target platforms, + declare each alias separately: + + ```toml + [build-dependencies] + firmware-x86 = { package = "firmware", version = "1.0", artifact = "bin", target = "x86_64-unknown-none" } + firmware-arm = { package = "firmware", version = "1.0", artifact = "bin", target = "thumbv7em-none-eabihf" } + ``` + - `lib` --- This is a Boolean value which indicates whether or not to also build the dependency's library as a normal Rust `lib` dependency. This field can only be specified when `artifact` is specified. @@ -1038,6 +1047,10 @@ Artifact-dependencies adds the following keys to a dependency declaration in `Ca bar = { version = "1.0", artifact = "bin", lib = true } ``` + If multiple aliases to the same package set `lib = true`, Cargo must be + able to refer to the package's library with a single Rust extern crate name. + Aliases that would give that library different names are rejected. + - `target` --- The platform target to build the dependency for. This field can only be specified when `artifact` is specified. diff --git a/tests/testsuite/artifact_dep.rs b/tests/testsuite/artifact_dep.rs index 4842127bcc5..354b82f691c 100644 --- a/tests/testsuite/artifact_dep.rs +++ b/tests/testsuite/artifact_dep.rs @@ -229,6 +229,55 @@ fn disallow_artifact_and_no_artifact_dep_to_same_package_within_the_same_dep_cat .run(); } +#[cargo_test] +fn disallow_artifact_lib_and_no_artifact_dep_to_same_package_within_the_same_dep_category() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + edition = "2015" + authors = [] + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "bin", lib = true } + bar_stable = { path = "bar/", package = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + edition = "2015" + + [lib] + name = "bar" + + [[bin]] + name = "bar" + "#, + ) + .file("bar/src/lib.rs", "") + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_status(101) + .with_stderr_data(str![[r#" +[LOCKING] 1 package to latest compatible version +[ERROR] the crate `foo v0.0.0 ([ROOT]/foo)` depends on crate `bar v0.5.0 ([ROOT]/foo/bar)` multiple times with different names + +"#]]) + .run(); +} + #[cargo_test] fn features_are_unified_among_lib_and_bin_dep_of_same_target() { let p = project() @@ -1133,9 +1182,8 @@ fn build_script_deps_adopt_specified_target_unconditionally() { .run(); } -/// 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_allow_multiple_targets_under_different_names() { if cross_compile_disabled() { return; } @@ -1154,16 +1202,9 @@ fn build_script_deps_adopt_do_not_allow_multiple_targets_under_different_name_an authors = [] resolver = "2" - [build-dependencies.bar] - path = "bar/" - artifact = "bin" - target = "{}" - - [build-dependencies.bar-native] - package = "bar" - path = "bar/" - artifact = "bin" - target = "{}" + [build-dependencies] + bar = {{ path = "bar/", artifact = "bin", target = "{}" }} + bar-native = {{ package = "bar", path = "bar/", artifact = "bin", target = "{}" }} "#, alternate, native @@ -1183,6 +1224,273 @@ fn build_script_deps_adopt_do_not_allow_multiple_targets_under_different_name_an .build(); p.cargo("check -v -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bar --edition=2015 bar/src/main.rs [..]--target [ALT_TARGET] [..]", + ) + .run(); +} + +#[cargo_test] +fn non_build_deps_allow_multiple_targets_under_different_names() { + if cross_compile_disabled() { + return; + } + + let alternate = cross_compile::alternate(); + let native = cross_compile::native(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.0" + edition = "2015" + authors = [] + resolver = "2" + + [dependencies] + bar = {{ path = "bar/", artifact = "bin", target = "{alternate}" }} + bar-native = {{ package = "bar", path = "bar/", artifact = "bin", target = "{native}" }} + "# + ), + ) + .file( + "src/lib.rs", + r#" + pub fn f() { + let _alternate = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar")); + let _native = include_bytes!(env!("CARGO_BIN_FILE_BAR_NATIVE_bar")); + } + "#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -v -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_contains( + "[RUNNING] `rustc --crate-name bar --edition=2015 bar/src/main.rs [..]--target [ALT_TARGET] [..]", + ) + .run(); +} + +#[cargo_test] +fn build_script_deps_allow_same_target_under_different_names() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + edition = "2015" + authors = [] + resolver = "2" + + [build-dependencies] + bar = { path = "bar/", artifact = "bin" } + bar-alt = { package = "bar", path = "bar/", artifact = "bin" } + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + fn main() { + let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into(); + assert!(bar.is_file()); + let bar_alt: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_ALT_bar").expect("CARGO_BIN_FILE_BAR_ALT_bar").into(); + assert!(bar_alt.is_file()); + assert_eq!(bar_alt, bar, "same-target aliases should share the artifact path"); + } + "#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_data( + str![[r#" +[LOCKING] 1 package to latest compatible version +[COMPILING] bar v0.5.0 ([ROOT]/foo/bar) +[COMPILING] foo v0.0.0 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]] + .unordered(), + ) + .run(); +} + +#[cargo_test] +fn artifact_output_only_dep_can_coexist_with_one_artifact_lib_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + edition = "2015" + authors = [] + resolver = "2" + + [build-dependencies.bar-lib] + package = "bar" + path = "bar/" + artifact = "bin" + lib = true + + [build-dependencies.bar-bin] + package = "bar" + path = "bar/" + artifact = "bin" + "#, + ) + .file("src/lib.rs", "") + .file( + "build.rs", + r#" + extern crate bar_lib; + + fn main() { + bar_lib::f(); + let bar_lib_bin: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_LIB_bar").expect("CARGO_BIN_FILE_BAR_LIB_bar").into(); + assert!(bar_lib_bin.is_file()); + let bar_bin: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_BIN_bar").expect("CARGO_BIN_FILE_BAR_BIN_bar").into(); + assert!(bar_bin.is_file()); + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + edition = "2015" + + [lib] + name = "bar" + + [[bin]] + name = "bar" + "#, + ) + .file("bar/src/lib.rs", "pub fn f() {}") + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .run(); +} + +#[cargo_test] +fn multiple_artifact_lib_deps_to_same_package_still_error() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + edition = "2015" + authors = [] + resolver = "2" + + [dependencies.bar-a] + package = "bar" + path = "bar/" + artifact = "bin" + lib = true + + [dependencies.bar-b] + package = "bar" + path = "bar/" + artifact = "bin" + lib = true + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + edition = "2015" + + [lib] + name = "bar" + + [[bin]] + name = "bar" + "#, + ) + .file("bar/src/lib.rs", "") + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_status(101) + .with_stderr_data(str![[r#" +[LOCKING] 1 package to latest compatible version +[ERROR] the crate `foo v0.0.0 ([ROOT]/foo)` depends on crate `bar v0.5.0 ([ROOT]/foo/bar)` multiple times with different names + +"#]]) + .run(); +} + +#[cargo_test] +fn multiple_artifact_lib_deps_with_different_artifact_targets_still_error() { + if cross_compile_disabled() { + return; + } + + let alternate = cross_compile::alternate(); + let native = cross_compile::native(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.0" + edition = "2015" + authors = [] + resolver = "2" + + [dependencies.bar] + path = "bar/" + artifact = "bin" + lib = true + target = "{alternate}" + + [dependencies.bar-native] + package = "bar" + path = "bar/" + artifact = "bin" + lib = true + target = "{native}" + "# + ), + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("bar/src/lib.rs", "pub fn doit() {}") + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_status(101) .with_stderr_data(str![[r#" @@ -1193,6 +1501,50 @@ fn build_script_deps_adopt_do_not_allow_multiple_targets_under_different_name_an .run(); } +#[cargo_test] +fn multiple_artifact_aliases_no_op_rebuild() { + if cross_compile_disabled() { + return; + } + + let alternate = cross_compile::alternate(); + let native = cross_compile::native(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.0" + edition = "2015" + authors = [] + resolver = "2" + + [build-dependencies] + bar = {{ path = "bar/", artifact = "bin", target = "{alternate}" }} + bar-native = {{ package = "bar", path = "bar/", artifact = "bin", target = "{native}" }} + "# + ), + ) + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .run(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_data(str![[r#" +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} + #[cargo_test] fn non_build_script_deps_adopt_specified_target_unconditionally() { if cross_compile_disabled() { diff --git a/tests/testsuite/metadata.rs b/tests/testsuite/metadata.rs index dd6f6c93188..83d530a6861 100644 --- a/tests/testsuite/metadata.rs +++ b/tests/testsuite/metadata.rs @@ -2129,6 +2129,181 @@ fn cargo_metadata_with_invalid_artifact_deps() { .run(); } +#[cargo_test] +fn cargo_metadata_with_one_artifact_lib_alias_to_same_package() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + + [dependencies.bar-lib] + package = "bar" + path = "bar" + artifact = "bin" + lib = true + + [dependencies.bar-bin] + package = "bar" + path = "bar" + artifact = "bin" + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + + [lib] + name = "bar" + + [[bin]] + name = "bar" + "#, + ) + .file("bar/src/lib.rs", "") + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("metadata -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stdout_contains(r#"[..]"name":"bar_lib","pkg":"path+[ROOTURL]/foo/bar#0.5.0"[..]"#) + .with_stdout_contains(r#"[..]"rename":"bar-lib"[..]"#) + .with_stdout_contains(r#"[..]"rename":"bar-bin"[..]"#) + .with_stdout_contains(r#"[..]"extern_name":"bar_lib"[..]"#) + .with_stdout_contains(r#"[..]"extern_name":"bar_bin"[..]"#) + .run(); +} + +#[cargo_test] +fn cargo_metadata_with_multiple_bin_only_artifact_aliases_to_lib_package() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + + [dependencies.bar-a] + package = "bar" + path = "bar" + artifact = "bin" + + [dependencies.bar-b] + package = "bar" + path = "bar" + artifact = "bin" + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + + [lib] + name = "bar" + + [[bin]] + name = "bar" + "#, + ) + .file("bar/src/lib.rs", "") + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("metadata -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stdout_contains(r#"[..]"rename":"bar-a"[..]"#) + .with_stdout_contains(r#"[..]"rename":"bar-b"[..]"#) + .with_stdout_contains(r#"[..]"extern_name":"bar_a"[..]"#) + .with_stdout_contains(r#"[..]"extern_name":"bar_b"[..]"#) + .with_stdout_contains(r#"[..]"dependencies":["path+[ROOTURL]/foo/bar#0.5.0"][..]"#) + .with_stdout_contains(r#"[..]"deps":[{"name":"","pkg":"path+[ROOTURL]/foo/bar#0.5.0"[..]"#) + .run(); +} + +#[cargo_test] +fn cargo_metadata_rejects_mixed_artifact_and_no_artifact_dep_to_same_package() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + + [dependencies] + bar = { path = "bar", artifact = "bin" } + bar_stable = { path = "bar", package = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.5.0" + + [lib] + name = "bar" + + [[bin]] + name = "bar" + "#, + ) + .file("bar/src/lib.rs", "") + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("metadata -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_status(101) + .with_stderr_data(str![[r#" +[WARNING] please specify `--format-version` flag explicitly to avoid compatibility problems +[LOCKING] 1 package to latest compatible version +[ERROR] the crate `foo v0.5.0 ([ROOT]/foo)` depends on crate `bar v0.5.0 ([ROOT]/foo/bar)` multiple times with different names + +"#]]) + .run(); +} + +#[cargo_test] +fn cargo_metadata_allows_mixed_artifact_and_no_artifact_dep_to_bin_only_package() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + + [dependencies] + bar = { path = "bar", artifact = "bin" } + bar_stable = { path = "bar", package = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + p.cargo("metadata -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stdout_contains(r#"[..]"rename":"bar_stable"[..]"#) + .with_stdout_contains(r#"[..]"deps":[{"name":"","pkg":"path+[ROOTURL]/foo/bar#0.5.0"[..]"#) + .run(); +} + #[cargo_test] fn cargo_metadata_with_invalid_duplicate_renamed_deps() { let p = project() diff --git a/tests/testsuite/unit_graph.rs b/tests/testsuite/unit_graph.rs index cead13bc6e8..e2f5c22de50 100644 --- a/tests/testsuite/unit_graph.rs +++ b/tests/testsuite/unit_graph.rs @@ -1,9 +1,9 @@ //! Tests for --unit-graph option. use crate::prelude::*; -use cargo_test_support::project; use cargo_test_support::registry::Package; use cargo_test_support::str; +use cargo_test_support::{basic_bin_manifest, project}; #[cargo_test] fn gated() { @@ -237,3 +237,61 @@ fn simple() { ) .run(); } + +#[cargo_test] +fn artifact_alias_edges() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2015" + authors = [] + + [dependencies] + bar = { path = "bar/", artifact = "bin" } + bar-alt = { package = "bar", path = "bar/", artifact = "bin" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", "fn main() {}") + .build(); + + let graph = p + .cargo("build --unit-graph -Zunstable-options -Z bindeps") + .masquerade_as_nightly_cargo(&["unit-graph", "bindeps"]) + .run_json(); + + let units = graph["units"].as_array().unwrap(); + let artifact_index = units + .iter() + .position(|unit| unit["target"]["name"] == "bar") + .unwrap(); + let root_unit = units + .iter() + .find(|unit| unit["target"]["name"] == "foo") + .unwrap(); + let mut aliases = root_unit["dependencies"] + .as_array() + .unwrap() + .iter() + .map(|dep| { + ( + dep["extern_crate_name"].as_str().unwrap().to_owned(), + dep["index"].as_u64().unwrap(), + ) + }) + .collect::>(); + aliases.sort(); + + assert_eq!( + aliases, + vec![ + ("bar".to_owned(), artifact_index as u64), + ("bar_alt".to_owned(), artifact_index as u64), + ] + ); +}