feat: add lockfile-path flag for all requested commands

- All commands that support `manifest-path` (except `locate-project`, `verify-project` and `read-manifest`)
This commit is contained in:
Ifropc 2024-07-19 08:05:48 -07:00
parent dc1519d0d7
commit 1abaa88012
No known key found for this signature in database
GPG Key ID: D2704D4776B957A8
23 changed files with 294 additions and 163 deletions

View File

@ -87,6 +87,7 @@ Example uses:
- Depend on crates with the same name from different registries"),
])
.arg_manifest_path_without_unsupported_path_tip()
.arg_lockfile_path()
.arg_package("Package to modify")
.arg_ignore_rust_version()
.arg_dry_run("Don't actually write the manifest")

View File

@ -50,6 +50,7 @@ pub fn cli() -> Command {
.arg_unit_graph()
.arg_timings()
.arg_manifest_path()
.arg_lockfile_path()
.arg_ignore_rust_version()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help bench</>` for more detailed information.\n"

View File

@ -39,6 +39,7 @@ pub fn cli() -> Command {
.arg_unit_graph()
.arg_timings()
.arg_manifest_path()
.arg_lockfile_path()
.arg_ignore_rust_version()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help build</>` for more detailed information.\n"

View File

@ -36,6 +36,7 @@ pub fn cli() -> Command {
.arg_unit_graph()
.arg_timings()
.arg_manifest_path()
.arg_lockfile_path()
.arg_ignore_rust_version()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help check</>` for more detailed information.\n"

View File

@ -19,6 +19,7 @@ pub fn cli() -> Command {
.arg_target_triple("Target triple to clean output for")
.arg_target_dir()
.arg_manifest_path()
.arg_lockfile_path()
.arg_dry_run("Display what would be deleted without deleting anything")
.args_conflicts_with_subcommands(true)
.subcommand(

View File

@ -39,6 +39,7 @@ pub fn cli() -> Command {
.arg_unit_graph()
.arg_timings()
.arg_manifest_path()
.arg_lockfile_path()
.arg_ignore_rust_version()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help doc</>` for more detailed information.\n"

View File

@ -9,6 +9,7 @@ pub fn cli() -> Command {
.arg_silent_suggestion()
.arg_target_triple("Fetch dependencies for the target triple")
.arg_manifest_path()
.arg_lockfile_path()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help fetch</>` for more detailed information.\n"
))

View File

@ -1,6 +1,5 @@
use crate::command_prelude::*;
use cargo::core::Workspace;
use cargo::ops;
pub fn cli() -> Command {
@ -54,6 +53,7 @@ pub fn cli() -> Command {
.arg_target_dir()
.arg_timings()
.arg_manifest_path()
.arg_lockfile_path()
.arg_ignore_rust_version()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help fix</>` for more detailed information.\n"
@ -71,8 +71,7 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
// Unlike other commands default `cargo fix` to all targets to fix as much
// code as we can.
let root_manifest = args.root_manifest(gctx)?;
let mut ws = Workspace::new(&root_manifest, gctx)?;
ws.set_resolve_honors_rust_version(args.honor_rust_version());
let ws = args.workspace(gctx)?;
let mut opts = args.compile_options(gctx, mode, Some(&ws), ProfileChecking::LegacyTestOnly)?;
if !opts.filter.is_specific() {

View File

@ -7,6 +7,7 @@ pub fn cli() -> Command {
.about("Generate the lockfile for a package")
.arg_silent_suggestion()
.arg_manifest_path()
.arg_lockfile_path()
.arg_ignore_rust_version_with_help(
"Ignore `rust-version` specification in packages (unstable)",
)

View File

@ -37,6 +37,7 @@ pub fn cli() -> Command {
.arg_target_dir()
.arg_parallel()
.arg_manifest_path()
.arg_lockfile_path()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help package</>` for more detailed information.\n"
))

View File

@ -10,6 +10,7 @@ pub fn cli() -> Command {
.arg_silent_suggestion()
.arg_package("Argument to get the package ID specifier for")
.arg_manifest_path()
.arg_lockfile_path()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help pkgid</>` for more detailed information.\n"
))

View File

@ -24,6 +24,7 @@ pub fn cli() -> Command {
.arg_target_triple("Build for the target triple")
.arg_target_dir()
.arg_manifest_path()
.arg_lockfile_path()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help publish</>` for more detailed information.\n"
))

View File

@ -51,6 +51,7 @@ pub fn cli() -> clap::Command {
])
.arg_package("Package to remove from")
.arg_manifest_path()
.arg_lockfile_path()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help remove</>` for more detailed information.\n"
))

View File

@ -38,6 +38,7 @@ pub fn cli() -> Command {
.arg_target_triple("Build for the target triple")
.arg_target_dir()
.arg_manifest_path()
.arg_lockfile_path()
.arg_ignore_rust_version()
.arg_unit_graph()
.arg_timings()

View File

@ -52,6 +52,7 @@ pub fn cli() -> Command {
.arg_unit_graph()
.arg_timings()
.arg_manifest_path()
.arg_lockfile_path()
.arg_ignore_rust_version()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help rustc</>` for more detailed information.\n"

View File

@ -45,6 +45,7 @@ pub fn cli() -> Command {
.arg_unit_graph()
.arg_timings()
.arg_manifest_path()
.arg_lockfile_path()
.arg_ignore_rust_version()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help rustdoc</>` for more detailed information.\n"

View File

@ -60,6 +60,7 @@ pub fn cli() -> Command {
.arg_unit_graph()
.arg_timings()
.arg_manifest_path()
.arg_lockfile_path()
.arg_ignore_rust_version()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help test</>` for more detailed information.\n\

View File

@ -95,6 +95,7 @@ pub fn cli() -> Command {
Pass `all` to include all targets.",
)
.arg_manifest_path()
.arg_lockfile_path()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help tree</>` for more detailed information.\n"
))

View File

@ -49,6 +49,7 @@ pub fn cli() -> Command {
.help_heading(heading::PACKAGE_SELECTION),
)
.arg_manifest_path()
.arg_lockfile_path()
.arg_ignore_rust_version_with_help(
"Ignore `rust-version` specification in packages (unstable)",
)

View File

@ -37,6 +37,7 @@ pub fn cli() -> Command {
.arg(unsupported("only-git-deps"))
.arg(unsupported("disallow-duplicates"))
.arg_manifest_path()
.arg_lockfile_path()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help vendor</>` for more detailed information.\n"
))

View File

@ -248,7 +248,12 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Vec<Fi
}
let pkgs = ws.members_with_features(specs, &opts.cli_features)?;
if ws.root().join("Cargo.lock").exists() {
let default_lockfile = ws.root().join("Cargo.lock");
if ws
.requested_lockfile_path()
.unwrap_or_else(|| &default_lockfile)
.exists()
{
// Make sure the Cargo.lock is up-to-date and valid.
let dry_run = false;
let _ = ops::resolve_ws(ws, dry_run)?;

View File

@ -121,6 +121,9 @@ pub fn fix(
}
let mut ws = Workspace::new(&root_manifest, gctx)?;
ws.set_resolve_honors_rust_version(Some(original_ws.resolve_honors_rust_version()));
if let Some(p) = original_ws.requested_lockfile_path() {
ws.set_requested_lockfile_path(Some(p.clone()))
}
// Spin up our lock server, which our subprocesses will use to synchronize fixes.
let lock_server = LockServer::new()?;

View File

@ -1,131 +1,17 @@
//! Tests for `lockfile-path` flag
use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::{
basic_bin_manifest, cargo_test, project, symlink_supported, Project, ProjectBuilder,
};
use snapbox::str;
use std::fs;
fn basic_project() -> ProjectBuilder {
return project()
.file("Cargo.toml", &basic_bin_manifest("test_foo"))
.file("src/main.rs", "fn main() {}");
}
use snapbox::str;
fn run_basic_command(p: &Project, command: &str, lockfile_path_argument: &str) {
p.cargo(command)
.masquerade_as_nightly_cargo(&["unstable-options"])
.arg("-Zunstable-options")
.arg("--lockfile-path")
.arg(lockfile_path_argument)
.run();
}
use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::registry::RegistryBuilder;
use cargo_test_support::{
basic_bin_manifest, basic_lib_manifest, cargo_test, paths, project, symlink_supported, Execs,
Project, ProjectBuilder,
};
#[track_caller]
fn assert_lockfile_created(command: &str) {
let lockfile_path_argument = "mylockfile/Cargo.lock";
let p = basic_project().build();
for _ in 1..=2 {
run_basic_command(&p, command, lockfile_path_argument);
assert!(p.root().join(lockfile_path_argument).is_file());
assert!(!p.root().join("Cargo.lock").is_file());
}
p.root()
.join(lockfile_path_argument)
.parent()
.unwrap()
.rm_rf();
run_basic_command(&p, command, lockfile_path_argument);
assert!(p.root().join(lockfile_path_argument).is_file());
assert!(!p.root().join("Cargo.lock").is_file());
}
fn assert_embed(command: &str) {
let lockfile_path_argument = "mylockfile/Cargo.lock";
let embed = r#"#!/usr/bin/env cargo
//! ```cargo
//! [dependencies]
//! clap = { version = "4.2", features = ["derive"] }
//! ```
use clap::Parser;
#[derive(Parser, Debug)]
#[clap(version)]
struct Args {
#[clap(short, long, help = "Path to config")]
config: Option<std::path::PathBuf>,
}
fn main() {
let args = Args::parse();
println!("{:?}", args);
}"#;
let p = project()
.file("src/main.rs", &embed)
.build();
p.cargo(command)
.masquerade_as_nightly_cargo(&["unstable-options"])
.arg("-Zunstable-options")
.arg("--lockfile-path")
.arg(lockfile_path_argument)
.arg("--manifest-path")
.arg("src/main.rs")
.arg("-Zscript")
.run();
assert!(p.root().join(lockfile_path_argument).is_file());
assert!(!p.root().join("Cargo.lock").is_file());
}
fn assert_lockfile_override(command: &str) {
let lockfile_path_argument = "mylockfile/Cargo.lock";
let p = basic_project()
.file("Cargo.lock", "This is an invalid lock file!")
.build();
run_basic_command(&p, command, lockfile_path_argument);
assert!(p.root().join(lockfile_path_argument).is_file());
}
fn assert_symlink_in_path(command: &str) {
if !symlink_supported() {
return;
}
let dst = "dst";
let src = "somedir/link";
let lockfile_path_argument = format!("{src}/Cargo.lock");
let p = basic_project().symlink_dir(dst, src).build();
fs::create_dir(p.root().join("dst"))
.unwrap_or_else(|e| panic!("could not create directory {}", e));
assert!(p.root().join(src).is_dir());
run_basic_command(&p, command, lockfile_path_argument.as_str());
assert!(p.root().join(format!("{src}/Cargo.lock")).is_file());
assert!(p.root().join(lockfile_path_argument).is_file());
assert!(p.root().join(dst).join("Cargo.lock").is_file());
}
fn assert_symlink_lockfile(command: &str) {
if !symlink_supported() {
return;
}
let lockfile_path_argument = "dst/Cargo.lock";
let src = "somedir/link";
let lock_body = r#"# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
const VALID_LOCKFILE: &str = r#"# Test lockfile
version = 3
[[package]]
@ -133,19 +19,154 @@ name = "test_foo"
version = "0.5.0"
"#;
let p = basic_project()
const LIB_TOML: &str = r#"
[package]
name = "test_bar"
version = "0.1.0"
edition = "2021"
"#;
fn make_basic_project() -> ProjectBuilder {
return project()
.file("Cargo.toml", &basic_bin_manifest("test_foo"))
.file("src/main.rs", "fn main() {}");
}
fn make_basic_command(execs: &mut Execs, lockfile_path_argument: String) -> &mut Execs {
return execs
.masquerade_as_nightly_cargo(&["unstable-options"])
.arg("-Zunstable-options")
.arg("--lockfile-path")
.arg(lockfile_path_argument);
}
fn lockfile_must_exist(command: &str) -> bool {
return command == "pkgid" || command == "publish";
}
fn assert_lockfile_created(
command: &str,
make_execs: impl Fn(&mut Execs, String) -> &mut Execs,
make_project: impl FnOnce() -> ProjectBuilder,
) {
if lockfile_must_exist(command) {
return;
}
let lockfile_path_argument = "mylockfile/Cargo.lock";
let p = make_project().build();
let registry = RegistryBuilder::new().http_api().http_index().build();
make_execs(&mut p.cargo(command), lockfile_path_argument.to_string())
.replace_crates_io(registry.index_url())
.run();
assert!(!p.root().join("Cargo.lock").is_file());
assert!(p.root().join(lockfile_path_argument).is_file());
}
fn assert_lockfile_read(
command: &str,
make_execs: impl Fn(&mut Execs, String) -> &mut Execs,
make_project: impl FnOnce() -> ProjectBuilder,
) {
let lockfile_path_argument = "mylockfile/Cargo.lock";
let p = make_project()
.file("mylockfile/Cargo.lock", VALID_LOCKFILE)
.build();
let registry = RegistryBuilder::new().http_api().http_index().build();
make_execs(&mut p.cargo(command), lockfile_path_argument.to_string())
.replace_crates_io(registry.index_url())
.run();
assert!(!p.root().join("Cargo.lock").is_file());
assert!(p.root().join(lockfile_path_argument).is_file());
}
fn assert_lockfile_override(
command: &str,
make_execs: impl Fn(&mut Execs, String) -> &mut Execs,
make_project: impl FnOnce() -> ProjectBuilder,
) {
if lockfile_must_exist(command) {
return;
}
let lockfile_path_argument = "mylockfile/Cargo.lock";
let p = make_project()
.file("Cargo.lock", "This is an invalid lock file!")
.build();
let registry = RegistryBuilder::new().http_api().http_index().build();
make_execs(&mut p.cargo(command), lockfile_path_argument.to_string())
.replace_crates_io(registry.index_url())
.run();
assert!(p.root().join(lockfile_path_argument).is_file());
}
fn assert_symlink_in_path(
command: &str,
make_execs: impl Fn(&mut Execs, String) -> &mut Execs,
make_project: impl FnOnce() -> ProjectBuilder,
) {
if !symlink_supported() || lockfile_must_exist(command) {
return;
}
let dst = "dst";
let src = "somedir/link";
let lockfile_path_argument = format!("{src}/Cargo.lock");
let p = make_project().symlink_dir(dst, src).build();
let registry = RegistryBuilder::new().http_api().http_index().build();
fs::create_dir(p.root().join("dst"))
.unwrap_or_else(|e| panic!("could not create directory {}", e));
assert!(p.root().join(src).is_dir());
make_execs(&mut p.cargo(command), lockfile_path_argument.to_string())
.replace_crates_io(registry.index_url())
.run();
assert!(p.root().join(format!("{src}/Cargo.lock")).is_file());
assert!(p.root().join(lockfile_path_argument).is_file());
assert!(p.root().join(dst).join("Cargo.lock").is_file());
}
fn assert_symlink_lockfile(
command: &str,
make_execs: impl Fn(&mut Execs, String) -> &mut Execs,
make_project: impl FnOnce() -> ProjectBuilder,
) {
if !symlink_supported() {
return;
}
let lockfile_path_argument = "dst/Cargo.lock";
let src = "somedir/link";
let lock_body = VALID_LOCKFILE;
let p = make_project()
.file(lockfile_path_argument, lock_body)
.symlink(lockfile_path_argument, src)
.build();
let registry = RegistryBuilder::new().http_api().http_index().build();
assert!(p.root().join(src).is_file());
run_basic_command(&p, command, lockfile_path_argument);
make_execs(&mut p.cargo(command), lockfile_path_argument.to_string())
.replace_crates_io(registry.index_url())
.run();
assert!(!p.root().join("Cargo.lock").is_file());
}
fn assert_broken_symlink(command: &str) {
fn assert_broken_symlink(
command: &str,
make_execs: impl Fn(&mut Execs, String) -> &mut Execs,
make_project: impl FnOnce() -> ProjectBuilder,
) {
if !symlink_supported() {
return;
}
@ -154,14 +175,11 @@ fn assert_broken_symlink(command: &str) {
let src = "somedir/link";
let lockfile_path_argument = format!("{src}/Cargo.lock");
let p = basic_project().symlink_dir(invalid_dst, src).build();
let p = make_project().symlink_dir(invalid_dst, src).build();
assert!(!p.root().join(src).is_dir());
let registry = RegistryBuilder::new().http_api().http_index().build();
p.cargo(command)
.masquerade_as_nightly_cargo(&["unstable-options"])
.arg("-Zunstable-options")
.arg("--lockfile-path")
.arg(lockfile_path_argument)
make_execs(&mut p.cargo(command), lockfile_path_argument.to_string())
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] Failed to create lockfile-path parent directory somedir/link
@ -170,10 +188,15 @@ Caused by:
File exists (os error 17)
"#]])
.replace_crates_io(registry.index_url())
.run();
}
fn assert_loop_symlink(command: &str) {
fn assert_loop_symlink(
command: &str,
make_execs: impl Fn(&mut Execs, String) -> &mut Execs,
make_project: impl FnOnce() -> ProjectBuilder,
) {
if !symlink_supported() {
return;
}
@ -182,17 +205,14 @@ fn assert_loop_symlink(command: &str) {
let src = "somedir/link";
let lockfile_path_argument = format!("{src}/Cargo.lock");
let p = basic_project()
let p = make_project()
.symlink_dir(loop_link, src)
.symlink_dir(src, loop_link)
.build();
assert!(!p.root().join(src).is_dir());
let registry = RegistryBuilder::new().http_api().http_index().build();
p.cargo(command)
.masquerade_as_nightly_cargo(&["unstable-options"])
.arg("-Zunstable-options")
.arg("--lockfile-path")
.arg(lockfile_path_argument)
make_execs(&mut p.cargo(command), lockfile_path_argument.to_string())
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] Failed to fetch lock file's parent path metadata somedir/link
@ -201,40 +221,125 @@ Caused by:
Too many levels of symbolic links (os error 40)
"#]])
.replace_crates_io(registry.index_url())
.run();
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn metadata_lockfile_created() {
assert_lockfile_created("metadata");
/////////////////////
//// Generic tests
/////////////////////
macro_rules! tests {
($name: ident, $cmd_name:expr, $make_command:expr, $setup_test:expr) => {
#[cfg(test)]
mod $name {
use super::*;
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn test_lockfile_created() {
assert_lockfile_created($cmd_name, $make_command, $setup_test);
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn test_lockfile_read() {
assert_lockfile_read($cmd_name, $make_command, $setup_test);
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn test_lockfile_override() {
assert_lockfile_override($cmd_name, $make_command, $setup_test);
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn test_symlink_in_path() {
assert_symlink_in_path($cmd_name, $make_command, $setup_test);
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn test_symlink_lockfile() {
assert_symlink_lockfile($cmd_name, $make_command, $setup_test);
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn test_broken_symlink() {
assert_broken_symlink($cmd_name, $make_command, $setup_test);
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn test_loop_symlink() {
assert_loop_symlink($cmd_name, $make_command, $setup_test);
}
}
};
($name: ident, $cmd_name:expr) => {
tests!($name, $cmd_name, make_basic_command, make_basic_project);
};
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn metadata_embed() {
assert_embed("metadata");
fn make_add_command(execs: &mut Execs, lockfile_path_argument: String) -> &mut Execs {
return make_basic_command(execs, lockfile_path_argument)
.arg("--path")
.arg("../bar");
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn metadata_lockfile_override() {
assert_lockfile_override("metadata");
fn make_add_project() -> ProjectBuilder {
return make_basic_project()
.file("../bar/Cargo.toml", LIB_TOML)
.file("../bar/src/main.rs", "fn main() {}");
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn metadata_symlink_in_path() {
assert_symlink_in_path("metadata");
fn make_clean_command(execs: &mut Execs, lockfile_path_argument: String) -> &mut Execs {
return make_basic_command(execs, lockfile_path_argument)
.arg("--package")
.arg("test_foo");
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn metadata_symlink_lockfile() {
assert_symlink_lockfile("metadata");
fn make_fix_command(execs: &mut Execs, lockfile_path_argument: String) -> &mut Execs {
return make_basic_command(execs, lockfile_path_argument)
.arg("--package")
.arg("test_foo")
.arg("--allow-no-vcs");
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn metadata_broken_symlink() {
assert_broken_symlink("metadata");
fn make_remove_project() -> ProjectBuilder {
let mut manifest = basic_bin_manifest("test_foo");
manifest.push_str(
r#"#
[dependencies]
test_bar = { version = "0.1.0", path = "../bar" }
"#,
);
return project()
.file("Cargo.toml", &manifest)
.file("src/main.rs", "fn main() {}")
.file("../bar/Cargo.toml", LIB_TOML)
.file("../bar/src/main.rs", "fn main() {}");
}
#[cargo_test(nightly, reason = "--lockfile-path is unstable")]
fn metadata_loop_symlink() {
assert_loop_symlink("metadata");
fn make_remove_command(execs: &mut Execs, lockfile_path_argument: String) -> &mut Execs {
return make_basic_command(execs, lockfile_path_argument).arg("test_bar");
}
tests!(add, "add", make_add_command, make_add_project);
tests!(bench, "bench");
tests!(build, "build");
tests!(check, "check");
tests!(clean, "clean", make_clean_command, make_basic_project);
tests!(doc, "doc");
tests!(fetch, "fetch");
tests!(fix, "fix", make_fix_command, make_basic_project);
tests!(generate_lockfile, "generate-lockfile");
tests!(metadata, "metadata");
// tests!(package, "package"); // TODO: check why lockfile is not generated
tests!(pkgid, "pkgid");
tests!(publish, "publish");
tests!(remove, "remove", make_remove_command, make_remove_project);
tests!(run, "run");
tests!(rustc, "rustc");
tests!(rustdoc, "rustdoc");
tests!(test, "test");
tests!(tree, "tree");
tests!(update, "update");
tests!(vendor, "vendor");