Skip to content

Commit a9465fe

Browse files
committed
Auto merge of #12015 - tedinski:fix-11999-recursive-deps, r=weihanglo
Build by PackageIdSpec, not name, to avoid ambiguity ### What does this PR try to resolve? Fixes #11999 I previously added this code to ensure `cargo install` will build the intended package, but it turns out to cause ambiguity in the case where a package depends on prior versions of itself. This ambiguity can be resolved by identifying the package to build by its pkg-spec, not name. ### How should we test and review this PR? A test case is included. I have additionally manually tested that, with this patch, the issue in #11999 is fixed and now installs correctly.
2 parents ac84010 + 7fb35c9 commit a9465fe

File tree

2 files changed

+137
-9
lines changed

2 files changed

+137
-9
lines changed

src/cargo/ops/cargo_install.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use std::sync::Arc;
44
use std::{env, fs};
55

66
use crate::core::compiler::{CompileKind, DefaultExecutor, Executor, UnitOutput};
7-
use crate::core::{Dependency, Edition, Package, PackageId, Source, SourceId, Target, Workspace};
7+
use crate::core::{
8+
Dependency, Edition, Package, PackageId, PackageIdSpec, Source, SourceId, Target, Workspace,
9+
};
810
use crate::ops::{common_for_install_and_uninstall::*, FilterRule};
911
use crate::ops::{CompileFilter, Packages};
1012
use crate::sources::{GitSource, PathSource, SourceConfigMap};
@@ -168,14 +170,8 @@ impl<'cfg, 'a> InstallablePackage<'cfg, 'a> {
168170
}
169171
};
170172

171-
// When we build this package, we want to build the *specified* package only,
172-
// and avoid building e.g. workspace default-members instead. Do so by constructing
173-
// specialized compile options specific to the identified package.
174-
// See test `path_install_workspace_root_despite_default_members`.
175-
let mut opts = original_opts.clone();
176-
opts.spec = Packages::Packages(vec![pkg.name().to_string()]);
177-
178-
let (ws, rustc, target) = make_ws_rustc_target(config, &opts, &source_id, pkg.clone())?;
173+
let (ws, rustc, target) =
174+
make_ws_rustc_target(config, &original_opts, &source_id, pkg.clone())?;
179175
// If we're installing in --locked mode and there's no `Cargo.lock` published
180176
// ie. the bin was published before https://github.com/rust-lang/cargo/pull/7026
181177
if config.locked() && !ws.root().join("Cargo.lock").exists() {
@@ -192,6 +188,17 @@ impl<'cfg, 'a> InstallablePackage<'cfg, 'a> {
192188
ws.current()?.clone()
193189
};
194190

191+
// When we build this package, we want to build the *specified* package only,
192+
// and avoid building e.g. workspace default-members instead. Do so by constructing
193+
// specialized compile options specific to the identified package.
194+
// See test `path_install_workspace_root_despite_default_members`.
195+
let mut opts = original_opts.clone();
196+
// For cargo install tracking, we retain the source git url in `pkg`, but for the build spec
197+
// we need to unconditionally use `ws.current()` to correctly address the path where we
198+
// locally cloned that repo.
199+
let pkgidspec = PackageIdSpec::from_package_id(ws.current()?.package_id());
200+
opts.spec = Packages::Packages(vec![pkgidspec.to_string()]);
201+
195202
if from_cwd {
196203
if pkg.manifest().edition() == Edition::Edition2015 {
197204
config.shell().warn(

tests/testsuite/install.rs

+121
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,46 @@ fn path_install_workspace_root_despite_default_members() {
14151415
.run();
14161416
}
14171417

1418+
#[cargo_test]
1419+
fn git_install_workspace_root_despite_default_members() {
1420+
let p = git::repo(&paths::root().join("foo"))
1421+
.file(
1422+
"Cargo.toml",
1423+
r#"
1424+
[package]
1425+
name = "ws-root"
1426+
version = "0.1.0"
1427+
authors = []
1428+
1429+
[workspace]
1430+
members = ["ws-member"]
1431+
default-members = ["ws-member"]
1432+
"#,
1433+
)
1434+
.file("src/main.rs", "fn main() {}")
1435+
.file(
1436+
"ws-member/Cargo.toml",
1437+
r#"
1438+
[package]
1439+
name = "ws-member"
1440+
version = "0.1.0"
1441+
authors = []
1442+
"#,
1443+
)
1444+
.file("ws-member/src/main.rs", "fn main() {}")
1445+
.build();
1446+
1447+
cargo_process("install --git")
1448+
.arg(p.url().to_string())
1449+
.arg("ws-root")
1450+
.with_stderr_contains(
1451+
"[INSTALLED] package `ws-root v0.1.0 ([..])` (executable `ws-root[EXE]`)",
1452+
)
1453+
// Particularly avoid "Installed package `ws-root v0.1.0 ([..]])` (executable `ws-member`)":
1454+
.with_stderr_does_not_contain("ws-member")
1455+
.run();
1456+
}
1457+
14181458
#[cargo_test]
14191459
fn dev_dependencies_no_check() {
14201460
Package::new("foo", "1.0.0").publish();
@@ -2287,3 +2327,84 @@ fn sparse_install() {
22872327
"#,
22882328
);
22892329
}
2330+
2331+
#[cargo_test]
2332+
fn self_referential() {
2333+
// Some packages build-dep on prior versions of themselves.
2334+
Package::new("foo", "0.0.1")
2335+
.file("src/lib.rs", "fn hello() {}")
2336+
.file("src/main.rs", "fn main() {}")
2337+
.file("build.rs", "fn main() {}")
2338+
.publish();
2339+
Package::new("foo", "0.0.2")
2340+
.file("src/lib.rs", "fn hello() {}")
2341+
.file("src/main.rs", "fn main() {}")
2342+
.file("build.rs", "fn main() {}")
2343+
.build_dep("foo", "0.0.1")
2344+
.publish();
2345+
2346+
cargo_process("install foo")
2347+
.with_stderr(
2348+
"\
2349+
[UPDATING] `[..]` index
2350+
[DOWNLOADING] crates ...
2351+
[DOWNLOADED] foo v0.0.2 (registry [..])
2352+
[INSTALLING] foo v0.0.2
2353+
[DOWNLOADING] crates ...
2354+
[DOWNLOADED] foo v0.0.1 (registry [..])
2355+
[COMPILING] foo v0.0.1
2356+
[COMPILING] foo v0.0.2
2357+
[FINISHED] release [optimized] target(s) in [..]
2358+
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
2359+
[INSTALLED] package `foo v0.0.2` (executable `foo[EXE]`)
2360+
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
2361+
",
2362+
)
2363+
.run();
2364+
assert_has_installed_exe(cargo_home(), "foo");
2365+
}
2366+
2367+
#[cargo_test]
2368+
fn ambiguous_registry_vs_local_package() {
2369+
// Correctly install 'foo' from a local package, even if that package also
2370+
// depends on a registry dependency named 'foo'.
2371+
Package::new("foo", "0.0.1")
2372+
.file("src/lib.rs", "fn hello() {}")
2373+
.publish();
2374+
2375+
let p = project()
2376+
.file("src/main.rs", "fn main() {}")
2377+
.file(
2378+
"Cargo.toml",
2379+
r#"
2380+
[package]
2381+
name = "foo"
2382+
version = "0.1.0"
2383+
authors = []
2384+
edition = "2021"
2385+
2386+
[dependencies]
2387+
foo = "0.0.1"
2388+
"#,
2389+
)
2390+
.build();
2391+
2392+
cargo_process("install --path")
2393+
.arg(p.root())
2394+
.with_stderr(
2395+
"\
2396+
[INSTALLING] foo v0.1.0 ([..])
2397+
[UPDATING] `[..]` index
2398+
[DOWNLOADING] crates ...
2399+
[DOWNLOADED] foo v0.0.1 (registry [..])
2400+
[COMPILING] foo v0.0.1
2401+
[COMPILING] foo v0.1.0 ([..])
2402+
[FINISHED] release [optimized] target(s) in [..]
2403+
[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
2404+
[INSTALLED] package `foo v0.1.0 ([..])` (executable `foo[EXE]`)
2405+
[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
2406+
",
2407+
)
2408+
.run();
2409+
assert_has_installed_exe(cargo_home(), "foo");
2410+
}

0 commit comments

Comments
 (0)