Skip to content

Commit dc1519d

Browse files
committed
feat: add new lockfile-path for metadata
- Also add necessary tests for the new flag
1 parent d3e84f0 commit dc1519d

File tree

6 files changed

+349
-10
lines changed

6 files changed

+349
-10
lines changed

src/bin/cargo/commands/metadata.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
use crate::command_prelude::*;
21
use cargo::ops::{self, OutputMetadataOptions};
32

3+
use crate::command_prelude::*;
4+
45
pub fn cli() -> Command {
56
subcommand("metadata")
67
.about(
@@ -26,6 +27,7 @@ pub fn cli() -> Command {
2627
.arg_silent_suggestion()
2728
.arg_features()
2829
.arg_manifest_path()
30+
.arg_lockfile_path()
2931
.after_help(color_print::cstr!(
3032
"Run `<cyan,bold>cargo help metadata</>` for more detailed information.\n"
3133
))

src/cargo/core/workspace.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ pub struct Workspace<'gctx> {
103103
// file. This is set for `cargo install` without `--locked`.
104104
ignore_lock: bool,
105105

106+
// Requested path of the lockfile (i.e. passed as the cli flag)
107+
requested_lockfile_path: Option<PathBuf>,
108+
106109
/// The resolver behavior specified with the `resolver` field.
107110
resolve_behavior: ResolveBehavior,
108111
resolve_honors_rust_version: bool,
@@ -237,6 +240,7 @@ impl<'gctx> Workspace<'gctx> {
237240
require_optional_deps: true,
238241
loaded_packages: RefCell::new(HashMap::new()),
239242
ignore_lock: false,
243+
requested_lockfile_path: None,
240244
resolve_behavior: ResolveBehavior::V1,
241245
resolve_honors_rust_version: false,
242246
custom_metadata: None,
@@ -647,6 +651,14 @@ impl<'gctx> Workspace<'gctx> {
647651
self
648652
}
649653

654+
pub fn requested_lockfile_path(&self) -> Option<&PathBuf> {
655+
self.requested_lockfile_path.as_ref()
656+
}
657+
658+
pub fn set_requested_lockfile_path(&mut self, path: Option<PathBuf>) {
659+
self.requested_lockfile_path = path;
660+
}
661+
650662
/// Get the lowest-common denominator `package.rust-version` within the workspace, if specified
651663
/// anywhere
652664
pub fn rust_version(&self) -> Option<&RustVersion> {

src/cargo/ops/lockfile.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
use std::io::prelude::*;
2-
31
use crate::core::{resolver, Resolve, ResolveVersion, Workspace};
42
use crate::util::errors::CargoResult;
53
use crate::util::Filesystem;
4+
use std::io::prelude::*;
65

76
use anyhow::Context as _;
87

8+
pub const LOCKFILE_NAME: &str = "Cargo.lock";
9+
910
#[tracing::instrument(skip_all)]
1011
pub fn load_pkg_lockfile(ws: &Workspace<'_>) -> CargoResult<Option<Resolve>> {
1112
let lock_root = lock_root(ws);
12-
if !lock_root.as_path_unlocked().join("Cargo.lock").exists() {
13+
if !lock_root.as_path_unlocked().join(LOCKFILE_NAME).exists() {
1314
return Ok(None);
1415
}
1516

16-
let mut f = lock_root.open_ro_shared("Cargo.lock", ws.gctx(), "Cargo.lock file")?;
17+
let mut f = lock_root.open_ro_shared(LOCKFILE_NAME, ws.gctx(), "Cargo.lock file")?;
1718

1819
let mut s = String::new();
1920
f.read_to_string(&mut s)
@@ -58,7 +59,7 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoRes
5859
"the lock file {} needs to be updated but {} was passed to prevent this\n\
5960
If you want to try to generate the lock file without accessing the network, \
6061
remove the {} flag and use --offline instead.",
61-
lock_root.as_path_unlocked().join("Cargo.lock").display(),
62+
lock_root.as_path_unlocked().join(LOCKFILE_NAME).display(),
6263
flag,
6364
flag
6465
);
@@ -84,7 +85,7 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoRes
8485

8586
// Ok, if that didn't work just write it out
8687
lock_root
87-
.open_rw_exclusive_create("Cargo.lock", ws.gctx(), "Cargo.lock file")
88+
.open_rw_exclusive_create(LOCKFILE_NAME, ws.gctx(), "Cargo.lock file")
8889
.and_then(|mut f| {
8990
f.file().set_len(0)?;
9091
f.write_all(out.as_bytes())?;
@@ -93,7 +94,7 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoRes
9394
.with_context(|| {
9495
format!(
9596
"failed to write {}",
96-
lock_root.as_path_unlocked().join("Cargo.lock").display()
97+
lock_root.as_path_unlocked().join(LOCKFILE_NAME).display()
9798
)
9899
})?;
99100
Ok(true)
@@ -105,7 +106,7 @@ fn resolve_to_string_orig(
105106
) -> (Option<String>, String, Filesystem) {
106107
// Load the original lock file if it exists.
107108
let lock_root = lock_root(ws);
108-
let orig = lock_root.open_ro_shared("Cargo.lock", ws.gctx(), "Cargo.lock file");
109+
let orig = lock_root.open_ro_shared(LOCKFILE_NAME, ws.gctx(), "Cargo.lock file");
109110
let orig = orig.and_then(|mut f| {
110111
let mut s = String::new();
111112
f.read_to_string(&mut s)?;
@@ -247,6 +248,14 @@ fn emit_package(dep: &toml::Table, out: &mut String) {
247248
}
248249

249250
fn lock_root(ws: &Workspace<'_>) -> Filesystem {
251+
if let Some(requested) = ws.requested_lockfile_path() {
252+
return Filesystem::new(
253+
requested
254+
.parent()
255+
.unwrap_or_else(|| unreachable!("Lockfile path can't be root"))
256+
.to_owned(),
257+
);
258+
}
250259
if ws.root_maybe().is_embedded() {
251260
ws.target_dir()
252261
} else {

src/cargo/util/command_prelude.rs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::core::compiler::{BuildConfig, MessageFormat, TimingOutput};
22
use crate::core::resolver::CliFeatures;
33
use crate::core::{Edition, Workspace};
4+
use crate::ops::lockfile::LOCKFILE_NAME;
45
use crate::ops::registry::RegistryOrIndex;
56
use crate::ops::{CompileFilter, CompileOptions, NewOptions, Packages, VersionControl};
67
use crate::util::important_paths::find_root_manifest_for_wd;
@@ -12,13 +13,14 @@ use crate::util::{
1213
print_available_packages, print_available_tests,
1314
};
1415
use crate::CargoResult;
15-
use anyhow::bail;
16+
use anyhow::{bail, Context};
1617
use cargo_util::paths;
1718
use cargo_util_schemas::manifest::ProfileName;
1819
use cargo_util_schemas::manifest::RegistryName;
1920
use cargo_util_schemas::manifest::StringOrVec;
2021
use clap::builder::UnknownArgumentValueParser;
2122
use std::ffi::{OsStr, OsString};
23+
use std::fs;
2224
use std::path::Path;
2325
use std::path::PathBuf;
2426

@@ -295,6 +297,15 @@ pub trait CommandExt: Sized {
295297
)
296298
}
297299

300+
fn arg_lockfile_path(self) -> Self {
301+
self._arg(
302+
opt("lockfile-path", "Path to the Cargo.lock file (unstable)")
303+
.value_name("FILE")
304+
// TODO: seemed to be the best option, but maybe should be something else?
305+
.help_heading(heading::MANIFEST_OPTIONS),
306+
)
307+
}
308+
298309
fn arg_message_format(self) -> Self {
299310
self._arg(multi_opt("message-format", "FMT", "Error format"))
300311
}
@@ -517,14 +528,20 @@ pub trait ArgMatchesExt {
517528
root_manifest(self._value_of("manifest-path").map(Path::new), gctx)
518529
}
519530

531+
fn lockfile_path(&self, gctx: &GlobalContext) -> CargoResult<Option<PathBuf>> {
532+
lockfile_path(self._value_of("lockfile-path").map(Path::new), gctx)
533+
}
534+
520535
#[tracing::instrument(skip_all)]
521536
fn workspace<'a>(&self, gctx: &'a GlobalContext) -> CargoResult<Workspace<'a>> {
522537
let root = self.root_manifest(gctx)?;
538+
let lockfile_path = self.lockfile_path(gctx)?;
523539
let mut ws = Workspace::new(&root, gctx)?;
524540
ws.set_resolve_honors_rust_version(self.honor_rust_version());
525541
if gctx.cli_unstable().avoid_dev_deps {
526542
ws.set_require_optional_deps(false);
527543
}
544+
ws.set_requested_lockfile_path(lockfile_path);
528545
Ok(ws)
529546
}
530547

@@ -986,6 +1003,64 @@ pub fn root_manifest(manifest_path: Option<&Path>, gctx: &GlobalContext) -> Carg
9861003
}
9871004
}
9881005

1006+
pub fn lockfile_path(
1007+
lockfile_path: Option<&Path>,
1008+
gctx: &GlobalContext,
1009+
) -> CargoResult<Option<PathBuf>> {
1010+
let path;
1011+
1012+
if let Some(lockfile_path) = lockfile_path {
1013+
if !gctx.cli_unstable().unstable_options {
1014+
bail!("`--lockfile-path` option requires `-Zunstable-options`")
1015+
}
1016+
1017+
if lockfile_path.is_absolute() {
1018+
path = lockfile_path.to_path_buf();
1019+
} else {
1020+
path = gctx.cwd().join(lockfile_path);
1021+
}
1022+
1023+
if !path.ends_with(LOCKFILE_NAME) && !crate::util::toml::is_embedded(&path) {
1024+
bail!(
1025+
"the lockfile-path must be a path to a {} file",
1026+
LOCKFILE_NAME
1027+
)
1028+
}
1029+
if path.is_dir() {
1030+
bail!(
1031+
"lockfile path `{}` is a directory but expected a file",
1032+
lockfile_path.display()
1033+
)
1034+
}
1035+
if !path.exists() {
1036+
// Root case should already be covered above
1037+
let parent_path = lockfile_path
1038+
.parent()
1039+
.unwrap_or_else(|| unreachable!("Lockfile path can't be root"));
1040+
1041+
let exists = parent_path.try_exists().with_context(|| {
1042+
format!(
1043+
"Failed to fetch lock file's parent path metadata {}",
1044+
parent_path.display()
1045+
)
1046+
})?;
1047+
1048+
if !exists {
1049+
fs::create_dir_all(parent_path).with_context(|| {
1050+
format!(
1051+
"Failed to create lockfile-path parent directory {}",
1052+
parent_path.display()
1053+
)
1054+
})?
1055+
}
1056+
}
1057+
1058+
return Ok(Some(path));
1059+
}
1060+
1061+
Ok(None)
1062+
}
1063+
9891064
#[track_caller]
9901065
pub fn ignore_unknown<T: Default>(r: Result<T, clap::parser::MatchesError>) -> T {
9911066
match r {

0 commit comments

Comments
 (0)