Skip to content

Commit 1fad770

Browse files
committed
Auto merge of #14556 - Ifropc:lockfile-path-install, r=weihanglo
feat: lockfile path implies --locked on cargo install Follow-up of #14421 Resolving one of the items > cargo install should make --lockfile-path imply --locked Simply mirrored behavior as if `--locked` was provided (on creating the workspace)
2 parents b396f2c + bf37cf7 commit 1fad770

File tree

6 files changed

+181
-41
lines changed

6 files changed

+181
-41
lines changed

src/bin/cargo/commands/install.rs

+9
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ pub fn cli() -> Command {
9898
.arg_target_triple("Build for the target triple")
9999
.arg_target_dir()
100100
.arg_timings()
101+
.arg_lockfile_path()
101102
.after_help(color_print::cstr!(
102103
"Run `<cyan,bold>cargo help install</>` for more detailed information.\n"
103104
))
@@ -204,6 +205,13 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
204205
if args.dry_run() {
205206
gctx.cli_unstable().fail_if_stable_opt("--dry-run", 11123)?;
206207
}
208+
209+
let requested_lockfile_path = args.lockfile_path(gctx)?;
210+
// 14421: lockfile path should imply --locked on running `install`
211+
if requested_lockfile_path.is_some() {
212+
gctx.set_locked(true);
213+
}
214+
207215
if args.flag("list") {
208216
ops::install_list(root, gctx)?;
209217
} else {
@@ -217,6 +225,7 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
217225
args.flag("force"),
218226
args.flag("no-track"),
219227
args.dry_run(),
228+
requested_lockfile_path.as_deref(),
220229
)?;
221230
}
222231
Ok(())

src/cargo/core/workspace.rs

+4
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,10 @@ impl<'gctx> Workspace<'gctx> {
662662
self.requested_lockfile_path = path;
663663
}
664664

665+
pub fn requested_lockfile_path(&self) -> Option<&Path> {
666+
self.requested_lockfile_path.as_deref()
667+
}
668+
665669
/// Get the lowest-common denominator `package.rust-version` within the workspace, if specified
666670
/// anywhere
667671
pub fn lowest_rust_version(&self) -> Option<&RustVersion> {

src/cargo/ops/cargo_install.rs

+39-12
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ struct InstallablePackage<'gctx> {
4646
vers: Option<VersionReq>,
4747
force: bool,
4848
no_track: bool,
49-
5049
pkg: Package,
5150
ws: Workspace<'gctx>,
5251
rustc: Rustc,
@@ -68,6 +67,7 @@ impl<'gctx> InstallablePackage<'gctx> {
6867
no_track: bool,
6968
needs_update_if_source_is_index: bool,
7069
current_rust_version: Option<&PartialVersion>,
70+
lockfile_path: Option<&Path>,
7171
) -> CargoResult<Option<Self>> {
7272
if let Some(name) = krate {
7373
if name == "." {
@@ -155,6 +155,7 @@ impl<'gctx> InstallablePackage<'gctx> {
155155
&root,
156156
&dst,
157157
force,
158+
lockfile_path,
158159
) {
159160
let msg = format!(
160161
"package `{}` is already installed, use --force to override",
@@ -179,15 +180,32 @@ impl<'gctx> InstallablePackage<'gctx> {
179180
}
180181
};
181182

182-
let (ws, rustc, target) =
183-
make_ws_rustc_target(gctx, &original_opts, &source_id, pkg.clone())?;
184-
// If we're installing in --locked mode and there's no `Cargo.lock` published
185-
// ie. the bin was published before https://github.com/rust-lang/cargo/pull/7026
186-
if gctx.locked() && !ws.root().join("Cargo.lock").exists() {
187-
gctx.shell().warn(format!(
188-
"no Cargo.lock file published in {}",
189-
pkg.to_string()
190-
))?;
183+
let (ws, rustc, target) = make_ws_rustc_target(
184+
gctx,
185+
&original_opts,
186+
&source_id,
187+
pkg.clone(),
188+
lockfile_path.clone(),
189+
)?;
190+
191+
if gctx.locked() {
192+
// When --lockfile-path is set, check that passed lock file exists
193+
// (unlike the usual flag behavior, lockfile won't be created as we imply --locked)
194+
if let Some(requested_lockfile_path) = ws.requested_lockfile_path() {
195+
if !requested_lockfile_path.is_file() {
196+
bail!(
197+
"no Cargo.lock file found in the requested path {}",
198+
requested_lockfile_path.display()
199+
);
200+
}
201+
// If we're installing in --locked mode and there's no `Cargo.lock` published
202+
// ie. the bin was published before https://github.com/rust-lang/cargo/pull/7026
203+
} else if !ws.root().join("Cargo.lock").exists() {
204+
gctx.shell().warn(format!(
205+
"no Cargo.lock file published in {}",
206+
pkg.to_string()
207+
))?;
208+
}
191209
}
192210
let pkg = if source_id.is_git() {
193211
// Don't use ws.current() in order to keep the package source as a git source so that
@@ -246,7 +264,6 @@ impl<'gctx> InstallablePackage<'gctx> {
246264
vers: vers.cloned(),
247265
force,
248266
no_track,
249-
250267
pkg,
251268
ws,
252269
rustc,
@@ -636,6 +653,7 @@ pub fn install(
636653
force: bool,
637654
no_track: bool,
638655
dry_run: bool,
656+
lockfile_path: Option<&Path>,
639657
) -> CargoResult<()> {
640658
let root = resolve_root(root, gctx)?;
641659
let dst = root.join("bin").into_path_unlocked();
@@ -667,6 +685,7 @@ pub fn install(
667685
no_track,
668686
true,
669687
current_rust_version.as_ref(),
688+
lockfile_path,
670689
)?;
671690
let mut installed_anything = true;
672691
if let Some(installable_pkg) = installable_pkg {
@@ -698,6 +717,7 @@ pub fn install(
698717
no_track,
699718
!did_update,
700719
current_rust_version.as_ref(),
720+
lockfile_path,
701721
) {
702722
Ok(Some(installable_pkg)) => {
703723
did_update = true;
@@ -804,6 +824,7 @@ fn installed_exact_package<T>(
804824
root: &Filesystem,
805825
dst: &Path,
806826
force: bool,
827+
lockfile_path: Option<&Path>,
807828
) -> CargoResult<Option<Package>>
808829
where
809830
T: Source,
@@ -819,7 +840,7 @@ where
819840
// best-effort check to see if we can avoid hitting the network.
820841
if let Ok(pkg) = select_dep_pkg(source, dep, gctx, false, None) {
821842
let (_ws, rustc, target) =
822-
make_ws_rustc_target(gctx, opts, &source.source_id(), pkg.clone())?;
843+
make_ws_rustc_target(gctx, opts, &source.source_id(), pkg.clone(), lockfile_path)?;
823844
if let Ok(true) = is_installed(&pkg, gctx, opts, &rustc, &target, root, dst, force) {
824845
return Ok(Some(pkg));
825846
}
@@ -832,6 +853,7 @@ fn make_ws_rustc_target<'gctx>(
832853
opts: &ops::CompileOptions,
833854
source_id: &SourceId,
834855
pkg: Package,
856+
lockfile_path: Option<&Path>,
835857
) -> CargoResult<(Workspace<'gctx>, Rustc, String)> {
836858
let mut ws = if source_id.is_git() || source_id.is_path() {
837859
Workspace::new(pkg.manifest_path(), gctx)?
@@ -841,6 +863,11 @@ fn make_ws_rustc_target<'gctx>(
841863
ws
842864
};
843865
ws.set_ignore_lock(gctx.lock_update_allowed());
866+
ws.set_requested_lockfile_path(lockfile_path.map(|p| p.to_path_buf()));
867+
// if --lockfile-path is set, imply --locked
868+
if ws.requested_lockfile_path().is_some() {
869+
ws.set_ignore_lock(false);
870+
}
844871
ws.set_require_optional_deps(false);
845872

846873
let rustc = gctx.load_global_rustc(Some(&ws))?;

src/cargo/util/context/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,10 @@ impl GlobalContext {
11421142
self.locked
11431143
}
11441144

1145+
pub fn set_locked(&mut self, locked: bool) {
1146+
self.locked = locked;
1147+
}
1148+
11451149
pub fn lock_update_allowed(&self) -> bool {
11461150
!self.frozen && !self.locked
11471151
}

tests/testsuite/cargo_install/help/stdout.term.svg

+29-27
Loading

tests/testsuite/lockfile_path.rs

+96-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ use std::fs;
55
use snapbox::str;
66

77
use cargo_test_support::compare::assert_e2e;
8+
use cargo_test_support::install::assert_has_installed_exe;
89
use cargo_test_support::registry::{Package, RegistryBuilder};
910
use cargo_test_support::{
10-
basic_bin_manifest, cargo_test, project, symlink_supported, ProjectBuilder,
11+
basic_bin_manifest, cargo_process, cargo_test, paths, project, symlink_supported,
12+
ProjectBuilder,
1113
};
12-
1314
///////////////////////////////
1415
//// Unstable feature tests start
1516
///////////////////////////////
@@ -400,6 +401,99 @@ bar = "0.1.0"
400401
assert_e2e().eq(contents, lockfile_original);
401402
}
402403

404+
#[cargo_test]
405+
fn install_respects_lock_file_path() {
406+
// `cargo install` will imply --locked when lockfile path is provided
407+
Package::new("bar", "0.1.0").publish();
408+
Package::new("bar", "0.1.1")
409+
.file("src/lib.rs", "not rust")
410+
.publish();
411+
// Publish with lockfile containing bad version of `bar` (0.1.1)
412+
Package::new("foo", "0.1.0")
413+
.dep("bar", "0.1")
414+
.file("src/lib.rs", "")
415+
.file(
416+
"src/main.rs",
417+
"extern crate foo; extern crate bar; fn main() {}",
418+
)
419+
.file(
420+
"Cargo.lock",
421+
r#"
422+
[[package]]
423+
name = "bar"
424+
version = "0.1.1"
425+
source = "registry+https://github.com/rust-lang/crates.io-index"
426+
427+
[[package]]
428+
name = "foo"
429+
version = "0.1.0"
430+
dependencies = [
431+
"bar 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
432+
]
433+
"#,
434+
)
435+
.publish();
436+
437+
cargo_process("install foo --locked")
438+
.with_stderr_data(str![[r#"
439+
...
440+
[..]not rust[..]
441+
...
442+
"#]])
443+
.with_status(101)
444+
.run();
445+
446+
// Create lockfile with the good `bar` version (0.1.0) and use it for install
447+
project()
448+
.file(
449+
"Cargo.lock",
450+
r#"
451+
[[package]]
452+
name = "bar"
453+
version = "0.1.0"
454+
source = "registry+https://github.com/rust-lang/crates.io-index"
455+
456+
[[package]]
457+
name = "foo"
458+
version = "0.1.0"
459+
dependencies = [
460+
"bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
461+
]
462+
"#,
463+
)
464+
.build();
465+
cargo_process("install foo -Zunstable-options --lockfile-path foo/Cargo.lock")
466+
.masquerade_as_nightly_cargo(&["lockfile-path"])
467+
.run();
468+
469+
assert!(paths::root().join("foo/Cargo.lock").is_file());
470+
assert_has_installed_exe(paths::cargo_home(), "foo");
471+
}
472+
473+
#[cargo_test]
474+
fn install_lock_file_path_must_present() {
475+
// `cargo install` will imply --locked when lockfile path is provided
476+
Package::new("bar", "0.1.0").publish();
477+
Package::new("foo", "0.1.0")
478+
.dep("bar", "0.1")
479+
.file("src/lib.rs", "")
480+
.file(
481+
"src/main.rs",
482+
"extern crate foo; extern crate bar; fn main() {}",
483+
)
484+
.publish();
485+
486+
cargo_process("install foo -Zunstable-options --lockfile-path lockfile_dir/Cargo.lock")
487+
.masquerade_as_nightly_cargo(&["lockfile-path"])
488+
.with_stderr_data(str![[r#"
489+
...
490+
[ERROR] no Cargo.lock file found in the requested path [ROOT]/lockfile_dir/Cargo.lock
491+
...
492+
"#]])
493+
.with_status(101)
494+
.run();
495+
}
496+
403497
#[cargo_test]
404498
fn run_embed() {
405499
let lockfile_path = "mylockfile/Cargo.lock";

0 commit comments

Comments
 (0)