diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b0c1cdd..09875e29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com ## [Unreleased] +- Add `--no-private` flag to exclude `publish = false` crates. + + This flag is more powerful than [`--ignore-private` flag](https://github.com/taiki-e/cargo-hack#--ignore-private), which also prevents private crate from affecting dependency resolution. + +- Restore `Cargo.lock` after run to match behavior with [cargo-minimal-versions](https://github.com/taiki-e/cargo-minimal-versions) and [cargo-no-dev-deps](https://github.com/taiki-e/cargo-no-dev-deps), when `--no-dev-deps`, `--remove-dev-deps`, or `--no-private` is used. + ## [0.5.29] - 2023-08-06 - Documentation improvements. diff --git a/src/cli.rs b/src/cli.rs index 24175655..fc09d9f9 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -37,6 +37,8 @@ pub(crate) struct Args { pub(crate) no_dev_deps: bool, /// --remove-dev-deps pub(crate) remove_dev_deps: bool, + /// --no-private + pub(crate) no_private: bool, /// --ignore-private pub(crate) ignore_private: bool, /// --ignore-unknown-features @@ -136,6 +138,7 @@ impl Args { let mut remove_dev_deps = false; let mut each_feature = false; let mut feature_powerset = false; + let mut no_private = false; let mut ignore_private = false; let mut ignore_unknown_features = false; let mut clean_per_run = false; @@ -274,6 +277,7 @@ impl Args { Long("remove-dev-deps") => parse_flag!(remove_dev_deps), Long("each-feature") => parse_flag!(each_feature), Long("feature-powerset") => parse_flag!(feature_powerset), + Long("no-private") => parse_flag!(no_private), Long("ignore-private") => parse_flag!(ignore_private), Long("exclude-no-default-features") => parse_flag!(exclude_no_default_features), Long("exclude-all-features") => parse_flag!(exclude_all_features), @@ -553,6 +557,7 @@ impl Args { feature_powerset, no_dev_deps, remove_dev_deps, + no_private, ignore_private, ignore_unknown_features, optional_deps, diff --git a/src/main.rs b/src/main.rs index 0d431fda..480a9004 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,98 +48,87 @@ fn main() { fn try_main() -> Result<()> { let cx = &Context::new()?; - exec_on_workspace(cx) -} - -fn exec_on_workspace(cx: &Context) -> Result<()> { - let restore_handles = if cx.no_dev_deps || cx.remove_dev_deps { - let mut restore_handles = Vec::with_capacity(cx.metadata.workspace_members.len()); - for id in &cx.metadata.workspace_members { - let manifest_path = &cx.packages(id).manifest_path; - let manifest = cx.manifests(id); - let doc = manifest.remove_dev_deps(); - restore_handles.push(cx.restore.register(&manifest.raw, manifest_path)); - if term::verbose() { - info!("removing dev-dependencies from {}", manifest_path.display()); - } - fs::write(manifest_path, doc)?; + manifest::with(cx, || { + if cx.subcommand.is_none() { + return Ok(()); } - restore_handles - } else { - vec![] - }; - if cx.subcommand.is_none() { - // Restore original Cargo.toml and Cargo.lock. - drop(restore_handles); - return Ok(()); - } - - let mut progress = Progress::default(); - let packages = determine_package_list(cx, &mut progress)?; - let mut keep_going = KeepGoing::default(); - if let Some(range) = &cx.version_range { - let total = progress.total; - progress.total = 0; - for (cargo_version, _) in range { - if cx.target.is_empty() || *cargo_version >= 64 { - progress.total += total; - } else { - progress.total += total * cx.target.len(); - } - } - let line = cmd!("cargo"); - { - // First, generate the lockfile using the oldest cargo specified. - // https://github.com/taiki-e/cargo-hack/issues/105 - let toolchain = &range[0].1; - rustup::install_toolchain(toolchain, &cx.target, true)?; - let mut line = line.clone(); - line.leading_arg(toolchain); - line.arg("generate-lockfile"); - if let Some(pid) = cx.current_package() { - let package = cx.packages(pid); - if !cx.no_manifest_path { - line.arg("--manifest-path"); - line.arg( - package - .manifest_path - .strip_prefix(&cx.current_dir) - .unwrap_or(&package.manifest_path), - ); + let mut progress = Progress::default(); + let packages = determine_package_list(cx, &mut progress)?; + let mut keep_going = KeepGoing::default(); + if let Some(range) = &cx.version_range { + let total = progress.total; + progress.total = 0; + for (cargo_version, _) in range { + if cx.target.is_empty() || *cargo_version >= 64 { + progress.total += total; + } else { + progress.total += total * cx.target.len(); } } - line.run_with_output()?; - } - - range.iter().enumerate().try_for_each(|(i, (cargo_version, toolchain))| { - if i != 0 { + let line = cmd!("cargo"); + { + // First, generate the lockfile using the oldest cargo specified. + // https://github.com/taiki-e/cargo-hack/issues/105 + let toolchain = &range[0].1; rustup::install_toolchain(toolchain, &cx.target, true)?; + let mut line = line.clone(); + line.leading_arg(toolchain); + line.arg("generate-lockfile"); + if let Some(pid) = cx.current_package() { + let package = cx.packages(pid); + if !cx.no_manifest_path { + line.arg("--manifest-path"); + line.arg( + package + .manifest_path + .strip_prefix(&cx.current_dir) + .unwrap_or(&package.manifest_path), + ); + } + } + line.run_with_output()?; } - if cx.clean_per_version { - cargo_clean(cx, None)?; - } - - let mut line = line.clone(); - line.leading_arg(toolchain); - line.apply_context(cx); - exec_on_packages(cx, &packages, line, &mut progress, &mut keep_going, *cargo_version) - })?; - } else { - let mut line = cx.cargo(); - line.apply_context(cx); - exec_on_packages(cx, &packages, line, &mut progress, &mut keep_going, cx.cargo_version)?; - } - if keep_going.count > 0 { - eprintln!(); - error!("{keep_going}"); - } + range.iter().enumerate().try_for_each(|(i, (cargo_version, toolchain))| { + if i != 0 { + rustup::install_toolchain(toolchain, &cx.target, true)?; + } - // Restore original Cargo.toml and Cargo.lock. - drop(restore_handles); + if cx.clean_per_version { + cargo_clean(cx, None)?; + } - Ok(()) + let mut line = line.clone(); + line.leading_arg(toolchain); + line.apply_context(cx); + exec_on_packages( + cx, + &packages, + line, + &mut progress, + &mut keep_going, + *cargo_version, + ) + })?; + } else { + let mut line = cx.cargo(); + line.apply_context(cx); + exec_on_packages( + cx, + &packages, + line, + &mut progress, + &mut keep_going, + cx.cargo_version, + )?; + } + if keep_going.count > 0 { + eprintln!(); + error!("{keep_going}"); + } + Ok(()) + }) } #[derive(Default)] diff --git a/src/manifest.rs b/src/manifest.rs index aa98c866..ea08066c 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -1,8 +1,16 @@ -use std::{collections::BTreeMap, path::Path}; +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, +}; -use anyhow::{format_err, Context as _, Result}; +use anyhow::{bail, format_err, Context as _, Result}; -use crate::{fs, metadata::Metadata}; +use crate::{ + context::Context, + fs, + metadata::{self, Metadata}, + term, +}; type ParseResult = Result; @@ -29,12 +37,6 @@ impl Manifest { })?; Ok(Self { raw, doc, package, features }) } - - pub(crate) fn remove_dev_deps(&self) -> String { - let mut doc = self.doc.clone(); - remove_dev_deps(&mut doc); - doc.to_string() - } } pub(crate) struct Package { @@ -100,6 +102,85 @@ impl Features { } } +pub(crate) fn with(cx: &Context, f: impl FnOnce() -> Result<()>) -> Result<()> { + // TODO: provide option to keep updated Cargo.lock + let restore_lockfile = true; + let no_dev_deps = cx.no_dev_deps | cx.remove_dev_deps; + let no_private = cx.no_private; + let restore_handles = if no_dev_deps || no_private { + let mut restore_handles = Vec::with_capacity(cx.metadata.workspace_members.len()); + let workspace_root = &cx.metadata.workspace_root; + let root_manifest = &workspace_root.join("Cargo.toml"); + let mut has_root_crate = false; + let mut root_id = None; + let mut private_crates = vec![]; + for id in &cx.metadata.workspace_members { + let package = cx.packages(id); + let manifest_path = &package.manifest_path; + let is_root = manifest_path == root_manifest; + if is_root { + root_id = Some(id); + } + has_root_crate |= is_root; + let is_private = cx.is_private(id); + if is_private && no_private { + if is_root { + bail!( + "--no-private is not supported yet with workspace with private root crate" + ); + } + private_crates.push(manifest_path); + } else if is_root && no_private { + // + } else if no_dev_deps { + let manifest = cx.manifests(id); + let mut doc = manifest.doc.clone(); + if term::verbose() { + info!("removing dev-dependencies from {}", manifest_path.display()); + } + remove_dev_deps(&mut doc); + restore_handles.push(cx.restore.register(&manifest.raw, manifest_path)); + fs::write(manifest_path, doc.to_string())?; + } + } + if no_private && (no_dev_deps && has_root_crate || !private_crates.is_empty()) { + let manifest_path = root_manifest; + let manifest = cx.manifests(root_id.unwrap()); + let mut doc = manifest.doc.clone(); + if no_dev_deps && has_root_crate { + if term::verbose() { + info!("removing dev-dependencies from {}", manifest_path.display()); + } + remove_dev_deps(&mut doc); + } + if !private_crates.is_empty() { + if term::verbose() { + info!("removing private crates from {}", manifest_path.display()); + } + remove_private_crates(&mut doc, &cx.metadata, &private_crates)?; + } + restore_handles.push(cx.restore.register(&manifest.raw, manifest_path)); + fs::write(manifest_path, doc.to_string())?; + } + if restore_lockfile { + let lockfile = &cx.metadata.workspace_root.join("Cargo.lock"); + if lockfile.exists() { + restore_handles.push(cx.restore.register(fs::read_to_string(lockfile)?, lockfile)); + } + } + restore_handles + } else { + vec![] + }; + + f()?; + + // Restore original Cargo.toml and Cargo.lock. + drop(restore_handles); + + Ok(()) +} + fn remove_dev_deps(doc: &mut toml_edit::Document) { const KEY: &str = "dev-dependencies"; let table = doc.as_table_mut(); @@ -113,6 +194,33 @@ fn remove_dev_deps(doc: &mut toml_edit::Document) { } } +fn remove_private_crates( + doc: &mut toml_edit::Document, + metadata: &metadata::Metadata, + private_crates: &[&PathBuf], +) -> Result<()> { + let table = doc.as_table_mut(); + if let Some(workspace) = table.get_mut("workspace").and_then(toml_edit::Item::as_table_like_mut) + { + if let Some(members) = workspace.get_mut("members").and_then(toml_edit::Item::as_array_mut) + { + let mut i = 0; + while i < members.len() { + if let Some(member) = members.get(i).and_then(toml_edit::Value::as_str) { + let manifest_path = + metadata.workspace_root.join(member).join("Cargo.toml").canonicalize()?; + if private_crates.iter().any(|p| **p == manifest_path) { + members.remove(i); + continue; + } + } + i += 1; + } + } + } + Ok(()) +} + #[cfg(test)] mod tests { use super::remove_dev_deps; diff --git a/src/process.rs b/src/process.rs index 1f9f5274..4332c08d 100644 --- a/src/process.rs +++ b/src/process.rs @@ -14,7 +14,7 @@ use crate::{term, Context, PackageId}; macro_rules! cmd { ($program:expr $(, $arg:expr)* $(,)?) => {{ - let mut _cmd = crate::process::ProcessBuilder::new($program); + let mut _cmd = $crate::process::ProcessBuilder::new($program); $( _cmd.arg($arg); )*