mirror of
https://github.com/rust-lang/cargo
synced 2024-10-04 06:49:15 +00:00
Auto merge of #13979 - tweag:issue-12425, r=epage
Add `cargo update --breaking` Related to #12425. There are two kinds of manifest mutations here. In `upgrade_manifests` we have to mutate `cargo::core::manifest::Manifest` so that `resolve_ws` does what it needs to do, and outputs a correct lock file. Then, in `write_manifest_upgrades` we mutate `cargo::util::toml_mut::manifest::Manifest`, because that is how we can preserve the existing formatting in the manifest files on disk. Some of the code here is copied from `cargo-edit`. # Comparison with `cargo upgrade` Running on the Cargo source itself gives the following: ``` ❯ cargo upgrade --dry-run --incompatible allow --compatible ignore Updating 'https://github.com/rust-lang/crates.io-index' index Checking benchsuite's dependencies Checking capture's dependencies Checking cargo's dependencies name old req compatible latest new req note ==== ======= ========== ====== ======= ==== anstream 0.6.13 0.6.14 0.6.14 0.6.13 compatible anstyle 1.0.6 1.0.7 1.0.7 1.0.6 compatible anyhow 1.0.82 1.0.86 1.0.86 1.0.82 compatible itertools 0.12.1 0.12.1 0.13.0 0.13.0 libc 0.2.154 0.2.155 0.2.155 0.2.154 compatible opener 0.7.0 0.7.1 0.7.1 0.7.0 compatible openssl 0.10.57 0.10.64 0.10.64 0.10.57 compatible openssl-sys =0.9.92 0.9.92 0.9.102 =0.9.92 pinned pulldown-cmark 0.10.3 0.10.3 0.11.0 0.11.0 security-framework 2.10.0 2.11.0 2.11.0 2.10.0 compatible semver 1.0.22 1.0.23 1.0.23 1.0.22 compatible serde 1.0.199 1.0.203 1.0.203 1.0.199 compatible serde-untagged 0.1.5 0.1.6 0.1.6 0.1.5 compatible serde_json 1.0.116 1.0.117 1.0.117 1.0.116 compatible tar 0.4.40 0.4.41 0.4.41 0.4.40 compatible thiserror 1.0.59 1.0.61 1.0.61 1.0.59 compatible toml 0.8.12 0.8.14 0.8.14 0.8.12 compatible toml_edit 0.22.12 0.22.14 0.22.14 0.22.12 compatible unicode-width 0.1.12 0.1.13 0.1.13 0.1.12 compatible Checking cargo-credential's dependencies Checking cargo-credential-1password's dependencies Checking cargo-credential-libsecret's dependencies Checking cargo-credential-macos-keychain's dependencies Checking cargo-credential-wincred's dependencies Checking cargo-platform's dependencies Checking cargo-test-macro's dependencies Checking cargo-test-support's dependencies Checking cargo-util's dependencies Checking cargo-util-schemas's dependencies Checking crates-io's dependencies Checking home's dependencies Checking mdman's dependencies Checking resolver-tests's dependencies Checking rustfix's dependencies Checking semver-check's dependencies Checking xtask-build-man's dependencies Checking xtask-bump-check's dependencies Checking xtask-stale-label's dependencies note: Re-run with `--pinned` to upgrade pinned version requirements note: Re-run with `--verbose` to show all dependencies local: cargo unchanged: annotate-snippets, base64, bytesize, cargo-credential, cargo-credential-libsecret, cargo-credential-macos-keychain, cargo-credential-wincred, cargo-platform, cargo-test-macro, cargo-test-support, cargo-util, cargo-util-schemas, cargo_metadata, clap, color-print, core-foundation, crates-io, criterion, curl, curl-sys, filetime, flate2, git2, git2-curl, gix, glob, handlebars, hex, hmac, home, http-auth, humantime, ignore, im-rc, indexmap, jobserver, lazycell, libgit2-sys, libloading, memchr, miow, os_info, pasetors, pathdiff, percent-encoding, pkg-config, proptest, rand, regex, rusqlite, rustfix, same-file, serde-value, serde_ignored, sha1, sha2, shell-escape, similar, snapbox, supports-hyperlinks, supports-unicode, tempfile, time, tracing, tracing-chrome, tracing-subscriber, unicase, unicode-xid, url, varisat, walkdir, windows-sys warning: aborting upgrade due to dry run ❯ target/debug/cargo update --breaking --dry-run -Zunstable-options Updating crates.io index Upgrading itertools ^0.12.1 -> ^0.13.0 Upgrading pulldown-cmark ^0.10.3 -> ^0.11.0 Updating crates.io index Locking 3 packages to latest compatible versions Updating itertools v0.12.1 -> v0.13.0 Updating pulldown-cmark v0.10.3 -> v0.11.0 Updating pulldown-cmark-escape v0.10.0 -> v0.11.0 warning: not updating any files due to dry run ``` In both cases we see an upgrade of `itertools` to `^0.13.0` and `pulldown-cmark` to `^0.11.0`. The diff to the manifest (when it isn't a dry run) is as follows: ``` --- a/Cargo.toml +++ b/Cargo.toml `@@` -57,7 +57,7 `@@` humantime = "2.1.0" ignore = "0.4.22" im-rc = "15.1.0" indexmap = "2.2.6" -itertools = "0.12.1" +itertools = "0.13.0" jobserver = "0.1.31" lazycell = "1.3.0" libc = "0.2.154" `@@` -74,7 +74,7 `@@` pathdiff = "0.2.1" percent-encoding = "2.3.1" pkg-config = "0.3.30" proptest = "1.4.0" -pulldown-cmark = { version = "0.10.3", default-features = false, features = ["html"] } +pulldown-cmark = { version = "0.11.0", default-features = false, features = ["html"] } rand = "0.8.5" regex = "1.10.4" rusqlite = { version = "0.31.0", features = ["bundled"] } ``` # TODO - [x] In the case of `--incompatible`, we also need to let `update_lockfile` use `upgrades` in order to only update the incompatible dependencies. - [x] Testing all the different cases of package sources, version requirements, pinned versions, renamed dependencies, inherited workspace dependencies, multiple versions of the same dependency, selecting a subset `--package`, etc. - [x] Passing tests. - [x] Implement suggestions from reviews. - [x] The preservation of formatting in manifest files should be improved. - [x] Compare with `cargo upgrade`.
This commit is contained in:
commit
fb123c668b
|
@ -33,6 +33,7 @@ fn do_resolve<'gctx>(gctx: &'gctx GlobalContext, ws_root: &Path) -> ResolveInfo<
|
|||
let force_all_targets = ForceAllTargets::No;
|
||||
// Do an initial run to download anything necessary so that it does
|
||||
// not confuse criterion's warmup.
|
||||
let dry_run = false;
|
||||
let ws_resolve = cargo::ops::resolve_ws_with_opts(
|
||||
&ws,
|
||||
&mut target_data,
|
||||
|
@ -41,6 +42,7 @@ fn do_resolve<'gctx>(gctx: &'gctx GlobalContext, ws_root: &Path) -> ResolveInfo<
|
|||
&specs,
|
||||
has_dev_units,
|
||||
force_all_targets,
|
||||
dry_run,
|
||||
)
|
||||
.unwrap();
|
||||
ResolveInfo {
|
||||
|
@ -71,6 +73,7 @@ fn resolve_ws(c: &mut Criterion) {
|
|||
// iterator once, and we don't want to call `do_resolve` in every
|
||||
// "step", since that would just be some useless work.
|
||||
let mut lazy_info = None;
|
||||
let dry_run = false;
|
||||
group.bench_function(&ws_name, |b| {
|
||||
let ResolveInfo {
|
||||
ws,
|
||||
|
@ -91,6 +94,7 @@ fn resolve_ws(c: &mut Criterion) {
|
|||
specs,
|
||||
*has_dev_units,
|
||||
*force_all_targets,
|
||||
dry_run,
|
||||
)
|
||||
.unwrap();
|
||||
})
|
||||
|
|
|
@ -176,6 +176,7 @@ static E2E_LITERAL_REDACTIONS: &[(&str, &str)] = &[
|
|||
("[DIRTY]", " Dirty"),
|
||||
("[LOCKING]", " Locking"),
|
||||
("[UPDATING]", " Updating"),
|
||||
("[UPGRADING]", " Upgrading"),
|
||||
("[ADDING]", " Adding"),
|
||||
("[REMOVING]", " Removing"),
|
||||
("[REMOVED]", " Removed"),
|
||||
|
|
|
@ -214,11 +214,9 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
|
|||
};
|
||||
add(&ws, &options)?;
|
||||
|
||||
if !dry_run {
|
||||
// Reload the workspace since we've changed dependencies
|
||||
let ws = args.workspace(gctx)?;
|
||||
resolve_ws(&ws)?;
|
||||
}
|
||||
resolve_ws(&ws, dry_run)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
|
|||
ws.gctx()
|
||||
.shell()
|
||||
.set_verbosity(cargo::core::Verbosity::Quiet);
|
||||
let resolve = resolve_ws(&ws);
|
||||
let resolve = resolve_ws(&ws, dry_run);
|
||||
ws.gctx().shell().set_verbosity(verbosity);
|
||||
resolve?.1
|
||||
};
|
||||
|
@ -129,7 +129,7 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
|
|||
// Attempt to gc unused patches and re-resolve if anything is removed
|
||||
if gc_unused_patches(&workspace, &resolve)? {
|
||||
let ws = args.workspace(gctx)?;
|
||||
resolve_ws(&ws)?;
|
||||
resolve_ws(&ws, dry_run)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -35,6 +35,13 @@ pub fn cli() -> Command {
|
|||
.value_name("PRECISE")
|
||||
.requires("package-group"),
|
||||
)
|
||||
.arg(
|
||||
flag(
|
||||
"breaking",
|
||||
"Upgrade [SPEC] to latest breaking versions, unless pinned (unstable)",
|
||||
)
|
||||
.short('b'),
|
||||
)
|
||||
.arg_silent_suggestion()
|
||||
.arg(
|
||||
flag("workspace", "Only update the workspace packages")
|
||||
|
@ -59,7 +66,8 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
|
|||
gctx.cli_unstable().msrv_policy,
|
||||
)?;
|
||||
}
|
||||
let ws = args.workspace(gctx)?;
|
||||
|
||||
let mut ws = args.workspace(gctx)?;
|
||||
|
||||
if args.is_present_with_zero_values("package") {
|
||||
print_available_packages(&ws)?;
|
||||
|
@ -89,6 +97,24 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
|
|||
workspace: args.flag("workspace"),
|
||||
gctx,
|
||||
};
|
||||
|
||||
if args.flag("breaking") {
|
||||
gctx.cli_unstable()
|
||||
.fail_if_stable_opt("--breaking", 12425)?;
|
||||
|
||||
let upgrades = ops::upgrade_manifests(&mut ws, &update_opts.to_update)?;
|
||||
ops::resolve_ws(&ws, update_opts.dry_run)?;
|
||||
ops::write_manifest_upgrades(&ws, &upgrades, update_opts.dry_run)?;
|
||||
|
||||
if update_opts.dry_run {
|
||||
update_opts
|
||||
.gctx
|
||||
.shell()
|
||||
.warn("aborting update due to dry run")?;
|
||||
}
|
||||
} else {
|
||||
ops::update_lockfile(&ws, &update_opts)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -149,6 +149,7 @@ pub fn resolve_std<'gctx>(
|
|||
let cli_features = CliFeatures::from_command_line(
|
||||
&features, /*all_features*/ false, /*uses_default_features*/ false,
|
||||
)?;
|
||||
let dry_run = false;
|
||||
let resolve = ops::resolve_ws_with_opts(
|
||||
&std_ws,
|
||||
target_data,
|
||||
|
@ -157,6 +158,7 @@ pub fn resolve_std<'gctx>(
|
|||
&specs,
|
||||
HasDevUnits::No,
|
||||
crate::core::resolver::features::ForceAllTargets::No,
|
||||
dry_run,
|
||||
)?;
|
||||
Ok((
|
||||
resolve.pkg_set,
|
||||
|
|
|
@ -103,15 +103,25 @@ impl Summary {
|
|||
Rc::make_mut(&mut self.inner).checksum = Some(cksum);
|
||||
}
|
||||
|
||||
pub fn map_dependencies<F>(mut self, f: F) -> Summary
|
||||
pub fn map_dependencies<F>(self, mut f: F) -> Summary
|
||||
where
|
||||
F: FnMut(Dependency) -> Dependency,
|
||||
{
|
||||
self.try_map_dependencies(|dep| Ok(f(dep))).unwrap()
|
||||
}
|
||||
|
||||
pub fn try_map_dependencies<F>(mut self, f: F) -> CargoResult<Summary>
|
||||
where
|
||||
F: FnMut(Dependency) -> CargoResult<Dependency>,
|
||||
{
|
||||
{
|
||||
let slot = &mut Rc::make_mut(&mut self.inner).dependencies;
|
||||
*slot = mem::take(slot).into_iter().map(f).collect();
|
||||
*slot = mem::take(slot)
|
||||
.into_iter()
|
||||
.map(f)
|
||||
.collect::<CargoResult<_>>()?;
|
||||
}
|
||||
self
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Summary {
|
||||
|
|
|
@ -77,7 +77,14 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
|
|||
if opts.spec.is_empty() {
|
||||
clean_ctx.remove_paths(&[target_dir.into_path_unlocked()])?;
|
||||
} else {
|
||||
clean_specs(&mut clean_ctx, &ws, &profiles, &opts.targets, &opts.spec)?;
|
||||
clean_specs(
|
||||
&mut clean_ctx,
|
||||
&ws,
|
||||
&profiles,
|
||||
&opts.targets,
|
||||
&opts.spec,
|
||||
opts.dry_run,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,11 +98,12 @@ fn clean_specs(
|
|||
profiles: &Profiles,
|
||||
targets: &[String],
|
||||
spec: &[String],
|
||||
dry_run: bool,
|
||||
) -> CargoResult<()> {
|
||||
// Clean specific packages.
|
||||
let requested_kinds = CompileKind::from_requested_targets(clean_ctx.gctx, targets)?;
|
||||
let target_data = RustcTargetData::new(ws, &requested_kinds)?;
|
||||
let (pkg_set, resolve) = ops::resolve_ws(ws)?;
|
||||
let (pkg_set, resolve) = ops::resolve_ws(ws, dry_run)?;
|
||||
let prof_dir_name = profiles.get_dir_name();
|
||||
let host_layout = Layout::new(ws, None, &prof_dir_name)?;
|
||||
// Convert requested kinds to a Vec of layouts.
|
||||
|
|
|
@ -264,6 +264,7 @@ pub fn create_bcx<'a, 'gctx>(
|
|||
HasDevUnits::No
|
||||
}
|
||||
};
|
||||
let dry_run = false;
|
||||
let resolve = ops::resolve_ws_with_opts(
|
||||
ws,
|
||||
&mut target_data,
|
||||
|
@ -272,6 +273,7 @@ pub fn create_bcx<'a, 'gctx>(
|
|||
&specs,
|
||||
has_dev_units,
|
||||
crate::core::resolver::features::ForceAllTargets::No,
|
||||
dry_run,
|
||||
)?;
|
||||
let WorkspaceResolve {
|
||||
mut pkg_set,
|
||||
|
|
|
@ -19,7 +19,8 @@ pub fn fetch<'a>(
|
|||
options: &FetchOptions<'a>,
|
||||
) -> CargoResult<(Resolve, PackageSet<'a>)> {
|
||||
ws.emit_warnings()?;
|
||||
let (mut packages, resolve) = ops::resolve_ws(ws)?;
|
||||
let dry_run = false;
|
||||
let (mut packages, resolve) = ops::resolve_ws(ws, dry_run)?;
|
||||
|
||||
let jobs = Some(JobsConfig::Integer(1));
|
||||
let keep_going = false;
|
||||
|
|
|
@ -561,7 +561,8 @@ impl<'gctx> InstallablePackage<'gctx> {
|
|||
// It would be best if `source` could be passed in here to avoid a
|
||||
// duplicate "Updating", but since `source` is taken by value, then it
|
||||
// wouldn't be available for `compile_ws`.
|
||||
let (pkg_set, resolve) = ops::resolve_ws(&self.ws)?;
|
||||
let dry_run = false;
|
||||
let (pkg_set, resolve) = ops::resolve_ws(&self.ws, dry_run)?;
|
||||
ops::check_yanked(
|
||||
self.ws.gctx(),
|
||||
&pkg_set,
|
||||
|
|
|
@ -142,6 +142,7 @@ fn build_resolve_graph(
|
|||
|
||||
// Note that even with --filter-platform we end up downloading host dependencies as well,
|
||||
// as that is the behavior of download_accessible.
|
||||
let dry_run = false;
|
||||
let ws_resolve = ops::resolve_ws_with_opts(
|
||||
ws,
|
||||
&mut target_data,
|
||||
|
@ -150,6 +151,7 @@ fn build_resolve_graph(
|
|||
&specs,
|
||||
HasDevUnits::Yes,
|
||||
force_all,
|
||||
dry_run,
|
||||
)?;
|
||||
|
||||
let package_map: BTreeMap<PackageId, Package> = ws_resolve
|
||||
|
|
|
@ -190,7 +190,8 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Option
|
|||
|
||||
if ws.root().join("Cargo.lock").exists() {
|
||||
// Make sure the Cargo.lock is up-to-date and valid.
|
||||
let _ = ops::resolve_ws(ws)?;
|
||||
let dry_run = false;
|
||||
let _ = ops::resolve_ws(ws, dry_run)?;
|
||||
// If Cargo.lock does not exist, it will be generated by `build_lock`
|
||||
// below, and will be validated during the verification step.
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::core::dependency::Dependency;
|
||||
use crate::core::registry::PackageRegistry;
|
||||
use crate::core::resolver::features::{CliFeatures, HasDevUnits};
|
||||
use crate::core::shell::Verbosity;
|
||||
|
@ -8,11 +9,18 @@ use crate::ops;
|
|||
use crate::sources::source::QueryKind;
|
||||
use crate::util::cache_lock::CacheLockMode;
|
||||
use crate::util::context::GlobalContext;
|
||||
use crate::util::style;
|
||||
use crate::util::CargoResult;
|
||||
use crate::util::toml_mut::dependency::{MaybeWorkspace, Source};
|
||||
use crate::util::toml_mut::manifest::LocalManifest;
|
||||
use crate::util::toml_mut::upgrade::upgrade_requirement;
|
||||
use crate::util::{style, OptVersionReq};
|
||||
use crate::util::{CargoResult, VersionExt};
|
||||
use itertools::Itertools;
|
||||
use semver::{Op, Version, VersionReq};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use tracing::debug;
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
pub type UpgradeMap = HashMap<(String, SourceId), Version>;
|
||||
|
||||
pub struct UpdateOptions<'a> {
|
||||
pub gctx: &'a GlobalContext,
|
||||
|
@ -206,6 +214,251 @@ pub fn print_lockfile_changes(
|
|||
print_lockfile_generation(ws, resolve, registry)
|
||||
}
|
||||
}
|
||||
pub fn upgrade_manifests(
|
||||
ws: &mut Workspace<'_>,
|
||||
to_update: &Vec<String>,
|
||||
) -> CargoResult<UpgradeMap> {
|
||||
let gctx = ws.gctx();
|
||||
let mut upgrades = HashMap::new();
|
||||
let mut upgrade_messages = HashSet::new();
|
||||
|
||||
// Updates often require a lot of modifications to the registry, so ensure
|
||||
// that we're synchronized against other Cargos.
|
||||
let _lock = gctx.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
|
||||
|
||||
let mut registry = PackageRegistry::new(gctx)?;
|
||||
registry.lock_patches();
|
||||
|
||||
for member in ws.members_mut().sorted() {
|
||||
debug!("upgrading manifest for `{}`", member.name());
|
||||
|
||||
*member.manifest_mut().summary_mut() = member
|
||||
.manifest()
|
||||
.summary()
|
||||
.clone()
|
||||
.try_map_dependencies(|d| {
|
||||
upgrade_dependency(
|
||||
&gctx,
|
||||
to_update,
|
||||
&mut registry,
|
||||
&mut upgrades,
|
||||
&mut upgrade_messages,
|
||||
d,
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(upgrades)
|
||||
}
|
||||
|
||||
fn upgrade_dependency(
|
||||
gctx: &GlobalContext,
|
||||
to_update: &Vec<String>,
|
||||
registry: &mut PackageRegistry<'_>,
|
||||
upgrades: &mut UpgradeMap,
|
||||
upgrade_messages: &mut HashSet<String>,
|
||||
dependency: Dependency,
|
||||
) -> CargoResult<Dependency> {
|
||||
let name = dependency.package_name();
|
||||
let renamed_to = dependency.name_in_toml();
|
||||
|
||||
if name != renamed_to {
|
||||
trace!(
|
||||
"skipping dependency renamed from `{}` to `{}`",
|
||||
name,
|
||||
renamed_to
|
||||
);
|
||||
return Ok(dependency);
|
||||
}
|
||||
|
||||
if !to_update.is_empty() && !to_update.contains(&name.to_string()) {
|
||||
trace!("skipping dependency `{}` not selected for upgrading", name);
|
||||
return Ok(dependency);
|
||||
}
|
||||
|
||||
if !dependency.source_id().is_registry() {
|
||||
trace!("skipping non-registry dependency: {}", name);
|
||||
return Ok(dependency);
|
||||
}
|
||||
|
||||
let version_req = dependency.version_req();
|
||||
|
||||
let OptVersionReq::Req(current) = version_req else {
|
||||
trace!(
|
||||
"skipping dependency `{}` without a simple version requirement: {}",
|
||||
name,
|
||||
version_req
|
||||
);
|
||||
return Ok(dependency);
|
||||
};
|
||||
|
||||
let [comparator] = ¤t.comparators[..] else {
|
||||
trace!(
|
||||
"skipping dependency `{}` with multiple version comparators: {:?}",
|
||||
name,
|
||||
¤t.comparators
|
||||
);
|
||||
return Ok(dependency);
|
||||
};
|
||||
|
||||
if comparator.op != Op::Caret {
|
||||
trace!("skipping non-caret dependency `{}`: {}", name, comparator);
|
||||
return Ok(dependency);
|
||||
}
|
||||
|
||||
let query =
|
||||
crate::core::dependency::Dependency::parse(name, None, dependency.source_id().clone())?;
|
||||
|
||||
let possibilities = {
|
||||
loop {
|
||||
match registry.query_vec(&query, QueryKind::Exact) {
|
||||
std::task::Poll::Ready(res) => {
|
||||
break res?;
|
||||
}
|
||||
std::task::Poll::Pending => registry.block_until_ready()?,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let latest = if !possibilities.is_empty() {
|
||||
possibilities
|
||||
.iter()
|
||||
.map(|s| s.as_summary())
|
||||
.map(|s| s.version())
|
||||
.filter(|v| !v.is_prerelease())
|
||||
.max()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let Some(latest) = latest else {
|
||||
trace!(
|
||||
"skipping dependency `{}` without any published versions",
|
||||
name
|
||||
);
|
||||
return Ok(dependency);
|
||||
};
|
||||
|
||||
if current.matches(&latest) {
|
||||
trace!(
|
||||
"skipping dependency `{}` without a breaking update available",
|
||||
name
|
||||
);
|
||||
return Ok(dependency);
|
||||
}
|
||||
|
||||
let Some(new_req_string) = upgrade_requirement(¤t.to_string(), latest)? else {
|
||||
trace!(
|
||||
"skipping dependency `{}` because the version requirement didn't change",
|
||||
name
|
||||
);
|
||||
return Ok(dependency);
|
||||
};
|
||||
|
||||
let upgrade_message = format!("{} {} -> {}", name, current, new_req_string);
|
||||
trace!(upgrade_message);
|
||||
|
||||
if upgrade_messages.insert(upgrade_message.clone()) {
|
||||
gctx.shell()
|
||||
.status_with_color("Upgrading", &upgrade_message, &style::GOOD)?;
|
||||
}
|
||||
|
||||
upgrades.insert((name.to_string(), dependency.source_id()), latest.clone());
|
||||
|
||||
let req = OptVersionReq::Req(VersionReq::parse(&latest.to_string())?);
|
||||
let mut dep = dependency.clone();
|
||||
dep.set_version_req(req);
|
||||
Ok(dep)
|
||||
}
|
||||
|
||||
/// Update manifests with upgraded versions, and write to disk. Based on cargo-edit.
|
||||
/// Returns true if any file has changed.
|
||||
pub fn write_manifest_upgrades(
|
||||
ws: &Workspace<'_>,
|
||||
upgrades: &UpgradeMap,
|
||||
dry_run: bool,
|
||||
) -> CargoResult<bool> {
|
||||
if upgrades.is_empty() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let mut any_file_has_changed = false;
|
||||
|
||||
let manifest_paths = std::iter::once(ws.root_manifest())
|
||||
.chain(ws.members().map(|member| member.manifest_path()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for manifest_path in manifest_paths {
|
||||
trace!(
|
||||
"updating TOML manifest at `{:?}` with upgraded dependencies",
|
||||
manifest_path
|
||||
);
|
||||
|
||||
let crate_root = manifest_path
|
||||
.parent()
|
||||
.expect("manifest path is absolute")
|
||||
.to_owned();
|
||||
|
||||
let mut local_manifest = LocalManifest::try_new(&manifest_path)?;
|
||||
let mut manifest_has_changed = false;
|
||||
|
||||
for dep_table in local_manifest.get_dependency_tables_mut() {
|
||||
for (mut dep_key, dep_item) in dep_table.iter_mut() {
|
||||
let dep_key_str = dep_key.get();
|
||||
let dependency = crate::util::toml_mut::dependency::Dependency::from_toml(
|
||||
&manifest_path,
|
||||
dep_key_str,
|
||||
dep_item,
|
||||
)?;
|
||||
|
||||
let Some(current) = dependency.version() else {
|
||||
trace!("skipping dependency without a version: {}", dependency.name);
|
||||
continue;
|
||||
};
|
||||
|
||||
let (MaybeWorkspace::Other(source_id), Some(Source::Registry(source))) =
|
||||
(dependency.source_id(ws.gctx())?, dependency.source())
|
||||
else {
|
||||
trace!("skipping non-registry dependency: {}", dependency.name);
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(latest) = upgrades.get(&(dependency.name.to_owned(), source_id)) else {
|
||||
trace!(
|
||||
"skipping dependency without an upgrade: {}",
|
||||
dependency.name
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(new_req_string) = upgrade_requirement(current, latest)? else {
|
||||
trace!(
|
||||
"skipping dependency `{}` because the version requirement didn't change",
|
||||
dependency.name
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut dep = dependency.clone();
|
||||
let mut source = source.clone();
|
||||
source.version = new_req_string;
|
||||
dep.source = Some(Source::Registry(source));
|
||||
|
||||
trace!("upgrading dependency {}", dependency.name);
|
||||
dep.update_toml(&crate_root, &mut dep_key, dep_item);
|
||||
manifest_has_changed = true;
|
||||
any_file_has_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if manifest_has_changed && !dry_run {
|
||||
debug!("writing upgraded manifest to {}", manifest_path.display());
|
||||
local_manifest.write()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(any_file_has_changed)
|
||||
}
|
||||
|
||||
fn print_lockfile_generation(
|
||||
ws: &Workspace<'_>,
|
|
@ -506,6 +506,7 @@ fn check_resolver_change<'gctx>(
|
|||
assert_eq!(ws.resolve_behavior(), ResolveBehavior::V1);
|
||||
let specs = opts.compile_opts.spec.to_package_id_specs(ws)?;
|
||||
let mut resolve_differences = |has_dev_units| -> CargoResult<(WorkspaceResolve<'_>, DiffMap)> {
|
||||
let dry_run = false;
|
||||
let ws_resolve = ops::resolve_ws_with_opts(
|
||||
ws,
|
||||
target_data,
|
||||
|
@ -514,6 +515,7 @@ fn check_resolver_change<'gctx>(
|
|||
&specs,
|
||||
has_dev_units,
|
||||
crate::core::resolver::features::ForceAllTargets::No,
|
||||
dry_run,
|
||||
)?;
|
||||
|
||||
let feature_opts = FeatureOpts::new_behavior(ResolveBehavior::V2, has_dev_units);
|
||||
|
|
|
@ -7,10 +7,6 @@ pub use self::cargo_compile::{
|
|||
pub use self::cargo_compile::{CompileFilter, FilterRule, LibRule, Packages};
|
||||
pub use self::cargo_doc::{doc, DocOptions, OutputFormat};
|
||||
pub use self::cargo_fetch::{fetch, FetchOptions};
|
||||
pub use self::cargo_generate_lockfile::generate_lockfile;
|
||||
pub use self::cargo_generate_lockfile::print_lockfile_changes;
|
||||
pub use self::cargo_generate_lockfile::update_lockfile;
|
||||
pub use self::cargo_generate_lockfile::UpdateOptions;
|
||||
pub use self::cargo_install::{install, install_list};
|
||||
pub use self::cargo_new::{init, new, NewOptions, NewProjectKind, VersionControl};
|
||||
pub use self::cargo_output_metadata::{output_metadata, ExportInfo, OutputMetadataOptions};
|
||||
|
@ -20,6 +16,12 @@ pub use self::cargo_read_manifest::{read_package, read_packages};
|
|||
pub use self::cargo_run::run;
|
||||
pub use self::cargo_test::{run_benches, run_tests, TestOptions};
|
||||
pub use self::cargo_uninstall::uninstall;
|
||||
pub use self::cargo_update::generate_lockfile;
|
||||
pub use self::cargo_update::print_lockfile_changes;
|
||||
pub use self::cargo_update::update_lockfile;
|
||||
pub use self::cargo_update::upgrade_manifests;
|
||||
pub use self::cargo_update::write_manifest_upgrades;
|
||||
pub use self::cargo_update::UpdateOptions;
|
||||
pub use self::fix::{fix, fix_exec_rustc, fix_get_proxy_lock_addr, FixOptions};
|
||||
pub use self::lockfile::{load_pkg_lockfile, resolve_to_string, write_pkg_lockfile};
|
||||
pub use self::registry::modify_owners;
|
||||
|
@ -44,7 +46,6 @@ pub(crate) mod cargo_compile;
|
|||
pub mod cargo_config;
|
||||
mod cargo_doc;
|
||||
mod cargo_fetch;
|
||||
mod cargo_generate_lockfile;
|
||||
mod cargo_install;
|
||||
mod cargo_new;
|
||||
mod cargo_output_metadata;
|
||||
|
@ -55,6 +56,7 @@ pub mod cargo_remove;
|
|||
mod cargo_run;
|
||||
mod cargo_test;
|
||||
mod cargo_uninstall;
|
||||
mod cargo_update;
|
||||
mod common_for_install_and_uninstall;
|
||||
mod fix;
|
||||
pub(crate) mod lockfile;
|
||||
|
|
|
@ -115,9 +115,9 @@ version. This may also occur with an optional dependency that is not enabled.";
|
|||
///
|
||||
/// This is a simple interface used by commands like `clean`, `fetch`, and
|
||||
/// `package`, which don't specify any options or features.
|
||||
pub fn resolve_ws<'a>(ws: &Workspace<'a>) -> CargoResult<(PackageSet<'a>, Resolve)> {
|
||||
pub fn resolve_ws<'a>(ws: &Workspace<'a>, dry_run: bool) -> CargoResult<(PackageSet<'a>, Resolve)> {
|
||||
let mut registry = PackageRegistry::new(ws.gctx())?;
|
||||
let resolve = resolve_with_registry(ws, &mut registry)?;
|
||||
let resolve = resolve_with_registry(ws, &mut registry, dry_run)?;
|
||||
let packages = get_resolved_packages(&resolve, registry)?;
|
||||
Ok((packages, resolve))
|
||||
}
|
||||
|
@ -140,6 +140,7 @@ pub fn resolve_ws_with_opts<'gctx>(
|
|||
specs: &[PackageIdSpec],
|
||||
has_dev_units: HasDevUnits,
|
||||
force_all_targets: ForceAllTargets,
|
||||
dry_run: bool,
|
||||
) -> CargoResult<WorkspaceResolve<'gctx>> {
|
||||
let mut registry = PackageRegistry::new(ws.gctx())?;
|
||||
let (resolve, resolved_with_overrides) = if ws.ignore_lock() {
|
||||
|
@ -160,7 +161,7 @@ pub fn resolve_ws_with_opts<'gctx>(
|
|||
} else if ws.require_optional_deps() {
|
||||
// First, resolve the root_package's *listed* dependencies, as well as
|
||||
// downloading and updating all remotes and such.
|
||||
let resolve = resolve_with_registry(ws, &mut registry)?;
|
||||
let resolve = resolve_with_registry(ws, &mut registry, dry_run)?;
|
||||
// No need to add patches again, `resolve_with_registry` has done it.
|
||||
let add_patches = false;
|
||||
|
||||
|
@ -269,6 +270,7 @@ pub fn resolve_ws_with_opts<'gctx>(
|
|||
fn resolve_with_registry<'gctx>(
|
||||
ws: &Workspace<'gctx>,
|
||||
registry: &mut PackageRegistry<'gctx>,
|
||||
dry_run: bool,
|
||||
) -> CargoResult<Resolve> {
|
||||
let prev = ops::load_pkg_lockfile(ws)?;
|
||||
let mut resolve = resolve_with_previous(
|
||||
|
@ -283,7 +285,11 @@ fn resolve_with_registry<'gctx>(
|
|||
)?;
|
||||
|
||||
let print = if !ws.is_ephemeral() && ws.require_optional_deps() {
|
||||
if !dry_run {
|
||||
ops::write_pkg_lockfile(ws, &mut resolve)?
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
// This mostly represents
|
||||
// - `cargo install --locked` and the only change is the package is no longer local but
|
||||
|
|
|
@ -131,6 +131,7 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<()
|
|||
} else {
|
||||
ForceAllTargets::No
|
||||
};
|
||||
let dry_run = false;
|
||||
let ws_resolve = ops::resolve_ws_with_opts(
|
||||
ws,
|
||||
&mut target_data,
|
||||
|
@ -139,6 +140,7 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<()
|
|||
&specs,
|
||||
has_dev,
|
||||
force_all,
|
||||
dry_run,
|
||||
)?;
|
||||
|
||||
let package_map: HashMap<PackageId, &Package> = ws_resolve
|
||||
|
|
|
@ -80,6 +80,7 @@ fn sync(
|
|||
workspaces: &[&Workspace<'_>],
|
||||
opts: &VendorOptions<'_>,
|
||||
) -> CargoResult<VendorConfig> {
|
||||
let dry_run = false;
|
||||
let canonical_destination = try_canonicalize(opts.destination);
|
||||
let canonical_destination = canonical_destination.as_deref().unwrap_or(opts.destination);
|
||||
let dest_dir_already_exists = canonical_destination.exists();
|
||||
|
@ -112,7 +113,7 @@ fn sync(
|
|||
// crate to work with.
|
||||
for ws in workspaces {
|
||||
let (packages, resolve) =
|
||||
ops::resolve_ws(ws).with_context(|| "failed to load pkg lockfile")?;
|
||||
ops::resolve_ws(ws, dry_run).with_context(|| "failed to load pkg lockfile")?;
|
||||
|
||||
packages
|
||||
.get_many(resolve.iter())
|
||||
|
@ -144,7 +145,7 @@ fn sync(
|
|||
// tables about them.
|
||||
for ws in workspaces {
|
||||
let (packages, resolve) =
|
||||
ops::resolve_ws(ws).with_context(|| "failed to load pkg lockfile")?;
|
||||
ops::resolve_ws(ws, dry_run).with_context(|| "failed to load pkg lockfile")?;
|
||||
|
||||
packages
|
||||
.get_many(resolve.iter())
|
||||
|
|
|
@ -276,23 +276,6 @@ impl LocalManifest {
|
|||
|
||||
/// Write changes back to the file.
|
||||
pub fn write(&self) -> CargoResult<()> {
|
||||
if !self.manifest.data.contains_key("package")
|
||||
&& !self.manifest.data.contains_key("project")
|
||||
{
|
||||
if self.manifest.data.contains_key("workspace") {
|
||||
anyhow::bail!(
|
||||
"found virtual manifest at {}, but this command requires running against an \
|
||||
actual package in this workspace.",
|
||||
self.path.display()
|
||||
);
|
||||
} else {
|
||||
anyhow::bail!(
|
||||
"missing expected `package` or `project` fields in {}",
|
||||
self.path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let s = self.manifest.data.to_string();
|
||||
let new_contents_bytes = s.as_bytes();
|
||||
|
||||
|
@ -397,6 +380,55 @@ impl LocalManifest {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Allow mutating depedencies, wherever they live.
|
||||
/// Copied from cargo-edit.
|
||||
pub fn get_dependency_tables_mut(
|
||||
&mut self,
|
||||
) -> impl Iterator<Item = &mut dyn toml_edit::TableLike> + '_ {
|
||||
let root = self.data.as_table_mut();
|
||||
root.iter_mut().flat_map(|(k, v)| {
|
||||
if DepTable::KINDS
|
||||
.iter()
|
||||
.any(|dt| dt.kind.kind_table() == k.get())
|
||||
{
|
||||
v.as_table_like_mut().into_iter().collect::<Vec<_>>()
|
||||
} else if k == "workspace" {
|
||||
v.as_table_like_mut()
|
||||
.unwrap()
|
||||
.iter_mut()
|
||||
.filter_map(|(k, v)| {
|
||||
if k.get() == "dependencies" {
|
||||
v.as_table_like_mut()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else if k == "target" {
|
||||
v.as_table_like_mut()
|
||||
.unwrap()
|
||||
.iter_mut()
|
||||
.flat_map(|(_, v)| {
|
||||
v.as_table_like_mut().into_iter().flat_map(|v| {
|
||||
v.iter_mut().filter_map(|(k, v)| {
|
||||
if DepTable::KINDS
|
||||
.iter()
|
||||
.any(|dt| dt.kind.kind_table() == k.get())
|
||||
{
|
||||
v.as_table_like_mut()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Remove references to `dep_key` if its no longer present.
|
||||
pub fn gc_dep(&mut self, dep_key: &str) {
|
||||
let explicit_dep_activation = self.is_explicit_dep_activation(dep_key);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
pub mod dependency;
|
||||
pub mod manifest;
|
||||
pub mod upgrade;
|
||||
|
||||
// Based on Iterator::is_sorted from nightly std; remove in favor of that when stabilized.
|
||||
pub fn is_sorted(mut it: impl Iterator<Item = impl PartialOrd>) -> bool {
|
||||
|
|
219
src/cargo/util/toml_mut/upgrade.rs
Normal file
219
src/cargo/util/toml_mut/upgrade.rs
Normal file
|
@ -0,0 +1,219 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use crate::CargoResult;
|
||||
|
||||
/// Upgrade an existing requirement to a new version.
|
||||
/// Copied from cargo-edit.
|
||||
pub(crate) fn upgrade_requirement(
|
||||
req: &str,
|
||||
version: &semver::Version,
|
||||
) -> CargoResult<Option<String>> {
|
||||
let req_text = req.to_string();
|
||||
let raw_req = semver::VersionReq::parse(&req_text)
|
||||
.expect("semver to generate valid version requirements");
|
||||
if raw_req.comparators.is_empty() {
|
||||
// Empty matches everything, no-change.
|
||||
Ok(None)
|
||||
} else {
|
||||
let comparators: CargoResult<Vec<_>> = raw_req
|
||||
.comparators
|
||||
.into_iter()
|
||||
.map(|p| set_comparator(p, version))
|
||||
.collect();
|
||||
let comparators = comparators?;
|
||||
let new_req = semver::VersionReq { comparators };
|
||||
let mut new_req_text = new_req.to_string();
|
||||
if new_req_text.starts_with('^') && !req.starts_with('^') {
|
||||
new_req_text.remove(0);
|
||||
}
|
||||
// Validate contract
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
assert!(
|
||||
new_req.matches(version),
|
||||
"New req {} is invalid, because {} does not match {}",
|
||||
new_req_text,
|
||||
new_req,
|
||||
version
|
||||
)
|
||||
}
|
||||
if new_req_text == req_text {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(new_req_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_comparator(
|
||||
mut pred: semver::Comparator,
|
||||
version: &semver::Version,
|
||||
) -> CargoResult<semver::Comparator> {
|
||||
match pred.op {
|
||||
semver::Op::Wildcard => {
|
||||
pred.major = version.major;
|
||||
if pred.minor.is_some() {
|
||||
pred.minor = Some(version.minor);
|
||||
}
|
||||
if pred.patch.is_some() {
|
||||
pred.patch = Some(version.patch);
|
||||
}
|
||||
Ok(pred)
|
||||
}
|
||||
semver::Op::Exact => Ok(assign_partial_req(version, pred)),
|
||||
semver::Op::Greater | semver::Op::GreaterEq | semver::Op::Less | semver::Op::LessEq => {
|
||||
let user_pred = pred.to_string();
|
||||
Err(unsupported_version_req(user_pred))
|
||||
}
|
||||
semver::Op::Tilde => Ok(assign_partial_req(version, pred)),
|
||||
semver::Op::Caret => Ok(assign_partial_req(version, pred)),
|
||||
_ => {
|
||||
let user_pred = pred.to_string();
|
||||
Err(unsupported_version_req(user_pred))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assign_partial_req(
|
||||
version: &semver::Version,
|
||||
mut pred: semver::Comparator,
|
||||
) -> semver::Comparator {
|
||||
pred.major = version.major;
|
||||
if pred.minor.is_some() {
|
||||
pred.minor = Some(version.minor);
|
||||
}
|
||||
if pred.patch.is_some() {
|
||||
pred.patch = Some(version.patch);
|
||||
}
|
||||
pred.pre = version.pre.clone();
|
||||
pred
|
||||
}
|
||||
|
||||
fn unsupported_version_req(req: impl Display) -> anyhow::Error {
|
||||
anyhow::format_err!("Support for modifying {} is currently unsupported", req)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
mod upgrade_requirement {
|
||||
use super::*;
|
||||
|
||||
#[track_caller]
|
||||
fn assert_req_bump<'a, O: Into<Option<&'a str>>>(version: &str, req: &str, expected: O) {
|
||||
let version = semver::Version::parse(version).unwrap();
|
||||
let actual = upgrade_requirement(req, &version).unwrap();
|
||||
let expected = expected.into();
|
||||
assert_eq!(actual.as_deref(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wildcard_major() {
|
||||
assert_req_bump("1.0.0", "*", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wildcard_minor() {
|
||||
assert_req_bump("1.0.0", "1.*", None);
|
||||
assert_req_bump("1.1.0", "1.*", None);
|
||||
assert_req_bump("2.0.0", "1.*", "2.*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wildcard_patch() {
|
||||
assert_req_bump("1.0.0", "1.0.*", None);
|
||||
assert_req_bump("1.1.0", "1.0.*", "1.1.*");
|
||||
assert_req_bump("1.1.1", "1.0.*", "1.1.*");
|
||||
assert_req_bump("2.0.0", "1.0.*", "2.0.*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn caret_major() {
|
||||
assert_req_bump("1.0.0", "1", None);
|
||||
assert_req_bump("1.0.0", "^1", None);
|
||||
|
||||
assert_req_bump("1.1.0", "1", None);
|
||||
assert_req_bump("1.1.0", "^1", None);
|
||||
|
||||
assert_req_bump("2.0.0", "1", "2");
|
||||
assert_req_bump("2.0.0", "^1", "^2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn caret_minor() {
|
||||
assert_req_bump("1.0.0", "1.0", None);
|
||||
assert_req_bump("1.0.0", "^1.0", None);
|
||||
|
||||
assert_req_bump("1.1.0", "1.0", "1.1");
|
||||
assert_req_bump("1.1.0", "^1.0", "^1.1");
|
||||
|
||||
assert_req_bump("1.1.1", "1.0", "1.1");
|
||||
assert_req_bump("1.1.1", "^1.0", "^1.1");
|
||||
|
||||
assert_req_bump("2.0.0", "1.0", "2.0");
|
||||
assert_req_bump("2.0.0", "^1.0", "^2.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn caret_patch() {
|
||||
assert_req_bump("1.0.0", "1.0.0", None);
|
||||
assert_req_bump("1.0.0", "^1.0.0", None);
|
||||
|
||||
assert_req_bump("1.1.0", "1.0.0", "1.1.0");
|
||||
assert_req_bump("1.1.0", "^1.0.0", "^1.1.0");
|
||||
|
||||
assert_req_bump("1.1.1", "1.0.0", "1.1.1");
|
||||
assert_req_bump("1.1.1", "^1.0.0", "^1.1.1");
|
||||
|
||||
assert_req_bump("2.0.0", "1.0.0", "2.0.0");
|
||||
assert_req_bump("2.0.0", "^1.0.0", "^2.0.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tilde_major() {
|
||||
assert_req_bump("1.0.0", "~1", None);
|
||||
assert_req_bump("1.1.0", "~1", None);
|
||||
assert_req_bump("2.0.0", "~1", "~2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tilde_minor() {
|
||||
assert_req_bump("1.0.0", "~1.0", None);
|
||||
assert_req_bump("1.1.0", "~1.0", "~1.1");
|
||||
assert_req_bump("1.1.1", "~1.0", "~1.1");
|
||||
assert_req_bump("2.0.0", "~1.0", "~2.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tilde_patch() {
|
||||
assert_req_bump("1.0.0", "~1.0.0", None);
|
||||
assert_req_bump("1.1.0", "~1.0.0", "~1.1.0");
|
||||
assert_req_bump("1.1.1", "~1.0.0", "~1.1.1");
|
||||
assert_req_bump("2.0.0", "~1.0.0", "~2.0.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal_major() {
|
||||
assert_req_bump("1.0.0", "=1", None);
|
||||
assert_req_bump("1.1.0", "=1", None);
|
||||
assert_req_bump("2.0.0", "=1", "=2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal_minor() {
|
||||
assert_req_bump("1.0.0", "=1.0", None);
|
||||
assert_req_bump("1.1.0", "=1.0", "=1.1");
|
||||
assert_req_bump("1.1.1", "=1.0", "=1.1");
|
||||
assert_req_bump("2.0.0", "=1.0", "=2.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal_patch() {
|
||||
assert_req_bump("1.0.0", "=1.0.0", None);
|
||||
assert_req_bump("1.1.0", "=1.0.0", "=1.1.0");
|
||||
assert_req_bump("1.1.1", "=1.0.0", "=1.1.1");
|
||||
assert_req_bump("2.0.0", "=1.0.0", "=2.0.0");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -73,6 +73,7 @@ For the latest nightly, see the [nightly version] of this page.
|
|||
* [public-dependency](#public-dependency) --- Allows dependencies to be classified as either public or private.
|
||||
* [msrv-policy](#msrv-policy) --- MSRV-aware resolver and version selection
|
||||
* [precise-pre-release](#precise-pre-release) --- Allows pre-release versions to be selected with `update --precise`
|
||||
* [update-breaking](#update-breaking) --- Allows upgrading to breaking versions with `update --breaking`
|
||||
* Output behavior
|
||||
* [artifact-dir](#artifact-dir) --- Adds a directory where artifacts are copied to.
|
||||
* [Different binary name](#different-binary-name) --- Assign a name to the built binary that is separate from the crate name.
|
||||
|
@ -378,6 +379,25 @@ It's possible to update `my-dependency` to a pre-release with `update -Zunstable
|
|||
This is because `0.1.2-pre.0` is considered compatible with `0.1.1`.
|
||||
It would not be possible to upgrade to `0.2.0-pre.0` from `0.1.1` in the same way.
|
||||
|
||||
## update-breaking
|
||||
|
||||
* Tracking Issue: [#12425](https://github.com/rust-lang/cargo/issues/12425)
|
||||
|
||||
This feature allows upgrading dependencies to breaking versions with
|
||||
`update --breaking`.
|
||||
|
||||
This is essentially migrating `cargo upgrade` from `cargo-edit` into Cargo itself,
|
||||
and involves making changes to the `Cargo.toml` manifests, not just the lock file.
|
||||
|
||||
When doing a breaking update, Cargo will keep all non-breaking dependencies
|
||||
unchanged. It will also not change any dependencies that use a different version
|
||||
operator than the default caret. Also, it will not upgrade any renamed package
|
||||
dependencies. Example:
|
||||
|
||||
```sh
|
||||
cargo +nightly update --breaking -Z unstable-options
|
||||
```
|
||||
|
||||
## build-std
|
||||
* Tracking Repository: <https://github.com/rust-lang/wg-cargo-std-aware>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<svg width="852px" height="524px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="852px" height="542px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { background: #000000 }
|
||||
|
@ -35,45 +35,47 @@
|
|||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-cyan bold">--precise</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan"><PRECISE></tspan><tspan> Update [SPEC] to exactly PRECISE</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-cyan bold">-v</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--verbose</tspan><tspan class="fg-cyan">...</tspan><tspan> Use verbose output (-vv very verbose/build.rs output)</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-cyan bold">-b</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--breaking</tspan><tspan> Upgrade [SPEC] to latest breaking versions, unless pinned (unstable)</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan> </tspan><tspan class="fg-cyan bold">-q</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--quiet</tspan><tspan> Do not print cargo log messages</tspan>
|
||||
<tspan x="10px" y="190px"><tspan> </tspan><tspan class="fg-cyan bold">-v</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--verbose</tspan><tspan class="fg-cyan">...</tspan><tspan> Use verbose output (-vv very verbose/build.rs output)</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan> </tspan><tspan class="fg-cyan bold">--color</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan"><WHEN></tspan><tspan> Coloring: auto, always, never</tspan>
|
||||
<tspan x="10px" y="208px"><tspan> </tspan><tspan class="fg-cyan bold">-q</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--quiet</tspan><tspan> Do not print cargo log messages</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-cyan bold">--config</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan"><KEY=VALUE></tspan><tspan> Override a configuration value</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-cyan bold">--color</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan"><WHEN></tspan><tspan> Coloring: auto, always, never</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="244px"><tspan> </tspan><tspan class="fg-cyan bold">-Z</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan"><FLAG></tspan><tspan> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details</tspan>
|
||||
<tspan x="10px" y="244px"><tspan> </tspan><tspan class="fg-cyan bold">--config</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan"><KEY=VALUE></tspan><tspan> Override a configuration value</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="262px"><tspan> </tspan><tspan class="fg-cyan bold">-h</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--help</tspan><tspan> Print help</tspan>
|
||||
<tspan x="10px" y="262px"><tspan> </tspan><tspan class="fg-cyan bold">-Z</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan"><FLAG></tspan><tspan> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="280px">
|
||||
<tspan x="10px" y="280px"><tspan> </tspan><tspan class="fg-cyan bold">-h</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--help</tspan><tspan> Print help</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="298px"><tspan class="fg-green bold">Package Selection:</tspan>
|
||||
<tspan x="10px" y="298px">
|
||||
</tspan>
|
||||
<tspan x="10px" y="316px"><tspan> </tspan><tspan class="fg-cyan bold">-w</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--workspace</tspan><tspan> Only update the workspace packages</tspan>
|
||||
<tspan x="10px" y="316px"><tspan class="fg-green bold">Package Selection:</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="334px"><tspan> </tspan><tspan class="fg-cyan">[SPEC]...</tspan><tspan> Package to update</tspan>
|
||||
<tspan x="10px" y="334px"><tspan> </tspan><tspan class="fg-cyan bold">-w</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--workspace</tspan><tspan> Only update the workspace packages</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="352px">
|
||||
<tspan x="10px" y="352px"><tspan> </tspan><tspan class="fg-cyan">[SPEC]...</tspan><tspan> Package to update</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="370px"><tspan class="fg-green bold">Manifest Options:</tspan>
|
||||
<tspan x="10px" y="370px">
|
||||
</tspan>
|
||||
<tspan x="10px" y="388px"><tspan> </tspan><tspan class="fg-cyan bold">--manifest-path</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan"><PATH></tspan><tspan> Path to Cargo.toml</tspan>
|
||||
<tspan x="10px" y="388px"><tspan class="fg-green bold">Manifest Options:</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="406px"><tspan> </tspan><tspan class="fg-cyan bold">--ignore-rust-version</tspan><tspan> Ignore `rust-version` specification in packages (unstable)</tspan>
|
||||
<tspan x="10px" y="406px"><tspan> </tspan><tspan class="fg-cyan bold">--manifest-path</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan"><PATH></tspan><tspan> Path to Cargo.toml</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="424px"><tspan> </tspan><tspan class="fg-cyan bold">--locked</tspan><tspan> Assert that `Cargo.lock` will remain unchanged</tspan>
|
||||
<tspan x="10px" y="424px"><tspan> </tspan><tspan class="fg-cyan bold">--ignore-rust-version</tspan><tspan> Ignore `rust-version` specification in packages (unstable)</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="442px"><tspan> </tspan><tspan class="fg-cyan bold">--offline</tspan><tspan> Run without accessing the network</tspan>
|
||||
<tspan x="10px" y="442px"><tspan> </tspan><tspan class="fg-cyan bold">--locked</tspan><tspan> Assert that `Cargo.lock` will remain unchanged</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="460px"><tspan> </tspan><tspan class="fg-cyan bold">--frozen</tspan><tspan> Equivalent to specifying both --locked and --offline</tspan>
|
||||
<tspan x="10px" y="460px"><tspan> </tspan><tspan class="fg-cyan bold">--offline</tspan><tspan> Run without accessing the network</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="478px">
|
||||
<tspan x="10px" y="478px"><tspan> </tspan><tspan class="fg-cyan bold">--frozen</tspan><tspan> Equivalent to specifying both --locked and --offline</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="496px"><tspan>Run `</tspan><tspan class="fg-cyan bold">cargo help update</tspan><tspan class="bold">` for more detailed information.</tspan>
|
||||
<tspan x="10px" y="496px">
|
||||
</tspan>
|
||||
<tspan x="10px" y="514px">
|
||||
<tspan x="10px" y="514px"><tspan>Run `</tspan><tspan class="fg-cyan bold">cargo help update</tspan><tspan class="bold">` for more detailed information.</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="532px">
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.1 KiB |
|
@ -1,7 +1,9 @@
|
|||
//! Tests for the `cargo update` command.
|
||||
|
||||
use cargo_test_support::registry::Package;
|
||||
use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project};
|
||||
use cargo_test_support::compare::assert_e2e;
|
||||
use cargo_test_support::registry::{self};
|
||||
use cargo_test_support::registry::{Dependency, Package};
|
||||
use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project, str};
|
||||
|
||||
#[cargo_test]
|
||||
fn minor_update_two_places() {
|
||||
|
@ -1637,3 +1639,458 @@ fn update_with_missing_feature() {
|
|||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn update_breaking_unstable() {
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.0.1"
|
||||
edition = "2015"
|
||||
authors = []
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("update --breaking")
|
||||
.masquerade_as_nightly_cargo(&["update-breaking"])
|
||||
.with_status(101)
|
||||
.with_stderr(
|
||||
"\
|
||||
[ERROR] the `--breaking` flag is unstable, pass `-Z unstable-options` to enable it
|
||||
See https://github.com/rust-lang/cargo/issues/12425 for more information about the `--breaking` flag.
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn update_breaking_dry_run() {
|
||||
Package::new("incompatible", "1.0.0").publish();
|
||||
Package::new("ws", "1.0.0").publish();
|
||||
|
||||
let root_manifest = r#"
|
||||
# Check if formatting is preserved. Nothing here should change, due to dry-run.
|
||||
|
||||
[workspace]
|
||||
members = ["foo"]
|
||||
|
||||
[workspace.dependencies]
|
||||
ws = "1.0" # Preserve formatting
|
||||
"#;
|
||||
|
||||
let crate_manifest = r#"
|
||||
# Check if formatting is preserved. Nothing here should change, due to dry-run.
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.0.1"
|
||||
edition = "2015"
|
||||
authors = []
|
||||
|
||||
[dependencies]
|
||||
incompatible = "1.0" # Preserve formatting
|
||||
ws.workspace = true # Preserve formatting
|
||||
"#;
|
||||
|
||||
let p = project()
|
||||
.file("Cargo.toml", root_manifest)
|
||||
.file("foo/Cargo.toml", crate_manifest)
|
||||
.file("foo/src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("generate-lockfile").run();
|
||||
let lock_file = p.read_file("Cargo.lock");
|
||||
|
||||
Package::new("incompatible", "1.0.1").publish();
|
||||
Package::new("ws", "1.0.1").publish();
|
||||
|
||||
Package::new("incompatible", "2.0.0").publish();
|
||||
Package::new("ws", "2.0.0").publish();
|
||||
|
||||
p.cargo("update -Zunstable-options --dry-run --breaking")
|
||||
.masquerade_as_nightly_cargo(&["update-breaking"])
|
||||
.with_stderr(
|
||||
"\
|
||||
[UPDATING] `dummy-registry` index
|
||||
[UPGRADING] incompatible ^1.0 -> ^2.0
|
||||
[UPGRADING] ws ^1.0 -> ^2.0
|
||||
[LOCKING] 2 packages to latest compatible versions
|
||||
[UPDATING] incompatible v1.0.0 -> v2.0.0
|
||||
[UPDATING] ws v1.0.0 -> v2.0.0
|
||||
[WARNING] aborting update due to dry run
|
||||
",
|
||||
)
|
||||
.run();
|
||||
|
||||
let root_manifest_after = p.read_file("Cargo.toml");
|
||||
assert_e2e().eq(&root_manifest_after, root_manifest);
|
||||
|
||||
let crate_manifest_after = p.read_file("foo/Cargo.toml");
|
||||
assert_e2e().eq(&crate_manifest_after, crate_manifest);
|
||||
|
||||
let lock_file_after = p.read_file("Cargo.lock");
|
||||
assert_e2e().eq(&lock_file_after, lock_file);
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn update_breaking() {
|
||||
registry::alt_init();
|
||||
Package::new("compatible", "1.0.0").publish();
|
||||
Package::new("incompatible", "1.0.0").publish();
|
||||
Package::new("pinned", "1.0.0").publish();
|
||||
Package::new("less-than", "1.0.0").publish();
|
||||
Package::new("renamed-from", "1.0.0").publish();
|
||||
Package::new("pre-release", "1.0.0").publish();
|
||||
Package::new("yanked", "1.0.0").publish();
|
||||
Package::new("ws", "1.0.0").publish();
|
||||
Package::new("shared", "1.0.0").publish();
|
||||
Package::new("multiple-versions", "1.0.0").publish();
|
||||
Package::new("multiple-versions", "2.0.0").publish();
|
||||
Package::new("alternative-1", "1.0.0")
|
||||
.alternative(true)
|
||||
.publish();
|
||||
Package::new("alternative-2", "1.0.0")
|
||||
.alternative(true)
|
||||
.publish();
|
||||
Package::new("bar", "1.0.0").alternative(true).publish();
|
||||
Package::new("multiple-registries", "1.0.0").publish();
|
||||
Package::new("multiple-registries", "2.0.0")
|
||||
.alternative(true)
|
||||
.publish();
|
||||
Package::new("multiple-source-types", "1.0.0").publish();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
# Check if formatting is preserved
|
||||
|
||||
[workspace]
|
||||
members = ["foo", "bar"]
|
||||
|
||||
[workspace.dependencies]
|
||||
ws = "1.0" # This line gets partially rewritten
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"foo/Cargo.toml",
|
||||
r#"
|
||||
# Check if formatting is preserved
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.0.1"
|
||||
edition = "2015"
|
||||
authors = []
|
||||
|
||||
[dependencies]
|
||||
compatible = "1.0" # Comment
|
||||
incompatible = "1.0" # Comment
|
||||
pinned = "=1.0" # Comment
|
||||
less-than = "<99.0" # Comment
|
||||
renamed-to = { package = "renamed-from", version = "1.0" } # Comment
|
||||
pre-release = "1.0" # Comment
|
||||
yanked = "1.0" # Comment
|
||||
ws.workspace = true # Comment
|
||||
shared = "1.0" # Comment
|
||||
multiple-versions = "1.0" # Comment
|
||||
alternative-1 = { registry = "alternative", version = "1.0" } # Comment
|
||||
multiple-registries = "1.0" # Comment
|
||||
bar = { path = "../bar", registry = "alternative", version = "1.0.0" } # Comment
|
||||
multiple-source-types = { path = "../multiple-source-types", version = "1.0.0" } # Comment
|
||||
|
||||
[dependencies.alternative-2] # Comment
|
||||
version = "1.0" # Comment
|
||||
registry = "alternative" # Comment
|
||||
"#,
|
||||
)
|
||||
.file("foo/src/lib.rs", "")
|
||||
.file(
|
||||
"bar/Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "bar"
|
||||
version = "1.0.0"
|
||||
edition = "2015"
|
||||
authors = []
|
||||
|
||||
[dependencies]
|
||||
shared = "1.0"
|
||||
multiple-versions = "2.0"
|
||||
multiple-registries = { registry = "alternative", version = "2.0" } # Comment
|
||||
multiple-source-types = "1.0" # Comment
|
||||
"#,
|
||||
)
|
||||
.file("bar/src/lib.rs", "")
|
||||
.file(
|
||||
"multiple-source-types/Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "multiple-source-types"
|
||||
version = "1.0.0"
|
||||
edition = "2015"
|
||||
authors = []
|
||||
"#,
|
||||
)
|
||||
.file("multiple-source-types/src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("generate-lockfile").run();
|
||||
|
||||
Package::new("compatible", "1.0.1").publish();
|
||||
Package::new("incompatible", "1.0.1").publish();
|
||||
Package::new("pinned", "1.0.1").publish();
|
||||
Package::new("less-than", "1.0.1").publish();
|
||||
Package::new("renamed-from", "1.0.1").publish();
|
||||
Package::new("ws", "1.0.1").publish();
|
||||
Package::new("multiple-versions", "1.0.1").publish();
|
||||
Package::new("multiple-versions", "2.0.1").publish();
|
||||
Package::new("alternative-1", "1.0.1")
|
||||
.alternative(true)
|
||||
.publish();
|
||||
Package::new("alternative-2", "1.0.1")
|
||||
.alternative(true)
|
||||
.publish();
|
||||
|
||||
Package::new("incompatible", "2.0.0").publish();
|
||||
Package::new("pinned", "2.0.0").publish();
|
||||
Package::new("less-than", "2.0.0").publish();
|
||||
Package::new("renamed-from", "2.0.0").publish();
|
||||
Package::new("pre-release", "2.0.0-alpha").publish();
|
||||
Package::new("yanked", "2.0.0").yanked(true).publish();
|
||||
Package::new("ws", "2.0.0").publish();
|
||||
Package::new("shared", "2.0.0").publish();
|
||||
Package::new("multiple-versions", "3.0.0").publish();
|
||||
Package::new("alternative-1", "2.0.0")
|
||||
.alternative(true)
|
||||
.publish();
|
||||
Package::new("alternative-2", "2.0.0")
|
||||
.alternative(true)
|
||||
.publish();
|
||||
Package::new("bar", "2.0.0").alternative(true).publish();
|
||||
Package::new("multiple-registries", "2.0.0").publish();
|
||||
Package::new("multiple-registries", "3.0.0")
|
||||
.alternative(true)
|
||||
.publish();
|
||||
Package::new("multiple-source-types", "2.0.0").publish();
|
||||
|
||||
p.cargo("update -Zunstable-options --breaking")
|
||||
.masquerade_as_nightly_cargo(&["update-breaking"])
|
||||
.with_stderr(
|
||||
"\
|
||||
[UPDATING] `alternative` index
|
||||
[UPGRADING] multiple-registries ^2.0 -> ^3.0
|
||||
[UPDATING] `dummy-registry` index
|
||||
[UPGRADING] multiple-source-types ^1.0 -> ^2.0
|
||||
[UPGRADING] multiple-versions ^2.0 -> ^3.0
|
||||
[UPGRADING] shared ^1.0 -> ^2.0
|
||||
[UPGRADING] alternative-1 ^1.0 -> ^2.0
|
||||
[UPGRADING] alternative-2 ^1.0 -> ^2.0
|
||||
[UPGRADING] incompatible ^1.0 -> ^2.0
|
||||
[UPGRADING] multiple-registries ^1.0 -> ^2.0
|
||||
[UPGRADING] multiple-versions ^1.0 -> ^3.0
|
||||
[UPGRADING] ws ^1.0 -> ^2.0
|
||||
[LOCKING] 9 packages to latest compatible versions
|
||||
[UPDATING] alternative-1 v1.0.0 (registry `alternative`) -> v2.0.0
|
||||
[UPDATING] alternative-2 v1.0.0 (registry `alternative`) -> v2.0.0
|
||||
[UPDATING] incompatible v1.0.0 -> v2.0.0
|
||||
[UPDATING] multiple-registries v2.0.0 (registry `alternative`) -> v3.0.0
|
||||
[UPDATING] multiple-registries v1.0.0 -> v2.0.0
|
||||
[UPDATING] multiple-source-types v1.0.0 -> v2.0.0
|
||||
[ADDING] multiple-versions v3.0.0
|
||||
[UPDATING] shared v1.0.0 -> v2.0.0
|
||||
[UPDATING] ws v1.0.0 -> v2.0.0
|
||||
",
|
||||
)
|
||||
.run();
|
||||
|
||||
let root_manifest = p.read_file("Cargo.toml");
|
||||
assert_e2e().eq(
|
||||
&root_manifest,
|
||||
str![[r#"
|
||||
|
||||
# Check if formatting is preserved
|
||||
|
||||
[workspace]
|
||||
members = ["foo", "bar"]
|
||||
|
||||
[workspace.dependencies]
|
||||
ws = "2.0" # This line gets partially rewritten
|
||||
|
||||
"#]],
|
||||
);
|
||||
|
||||
let foo_manifest = p.read_file("foo/Cargo.toml");
|
||||
|
||||
assert_e2e().eq(
|
||||
&foo_manifest,
|
||||
str![[r#"
|
||||
|
||||
# Check if formatting is preserved
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.0.1"
|
||||
edition = "2015"
|
||||
authors = []
|
||||
|
||||
[dependencies]
|
||||
compatible = "1.0" # Comment
|
||||
incompatible = "2.0" # Comment
|
||||
pinned = "=1.0" # Comment
|
||||
less-than = "<99.0" # Comment
|
||||
renamed-to = { package = "renamed-from", version = "1.0" } # Comment
|
||||
pre-release = "1.0" # Comment
|
||||
yanked = "1.0" # Comment
|
||||
ws.workspace = true # Comment
|
||||
shared = "2.0" # Comment
|
||||
multiple-versions = "3.0" # Comment
|
||||
alternative-1 = { registry = "alternative", version = "2.0" } # Comment
|
||||
multiple-registries = "2.0" # Comment
|
||||
bar = { path = "../bar", registry = "alternative", version = "1.0.0" } # Comment
|
||||
multiple-source-types = { path = "../multiple-source-types", version = "1.0.0" } # Comment
|
||||
|
||||
[dependencies.alternative-2] # Comment
|
||||
version = "2.0" # Comment
|
||||
registry = "alternative" # Comment
|
||||
"#]],
|
||||
);
|
||||
|
||||
let bar_manifest = p.read_file("bar/Cargo.toml");
|
||||
|
||||
assert_e2e().eq(
|
||||
&bar_manifest,
|
||||
str![[r#"
|
||||
|
||||
[package]
|
||||
name = "bar"
|
||||
version = "1.0.0"
|
||||
edition = "2015"
|
||||
authors = []
|
||||
|
||||
[dependencies]
|
||||
shared = "2.0"
|
||||
multiple-versions = "3.0"
|
||||
multiple-registries = { registry = "alternative", version = "3.0" } # Comment
|
||||
multiple-source-types = "2.0" # Comment
|
||||
"#]],
|
||||
);
|
||||
|
||||
p.cargo("update")
|
||||
.with_stderr(
|
||||
"\
|
||||
[UPDATING] `alternative` index
|
||||
[UPDATING] `dummy-registry` index
|
||||
[LOCKING] 4 packages to latest compatible versions
|
||||
[UPDATING] compatible v1.0.0 -> v1.0.1
|
||||
[UPDATING] less-than v1.0.0 -> v2.0.0
|
||||
[UPDATING] pinned v1.0.0 -> v1.0.1 (latest: v2.0.0)
|
||||
[UPDATING] renamed-from v1.0.0 -> v1.0.1 (latest: v2.0.0)
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn update_breaking_specific_packages() {
|
||||
Package::new("just-foo", "1.0.0")
|
||||
.add_dep(Dependency::new("transitive-compatible", "1.0.0").build())
|
||||
.add_dep(Dependency::new("transitive-incompatible", "1.0.0").build())
|
||||
.publish();
|
||||
Package::new("just-bar", "1.0.0").publish();
|
||||
Package::new("shared", "1.0.0").publish();
|
||||
Package::new("ws", "1.0.0").publish();
|
||||
Package::new("transitive-compatible", "1.0.0").publish();
|
||||
Package::new("transitive-incompatible", "1.0.0").publish();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[workspace]
|
||||
members = ["foo", "bar"]
|
||||
|
||||
[workspace.dependencies]
|
||||
ws = "1.0"
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"foo/Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.0.1"
|
||||
edition = "2015"
|
||||
authors = []
|
||||
|
||||
[dependencies]
|
||||
just-foo = "1.0"
|
||||
shared = "1.0"
|
||||
ws.workspace = true
|
||||
"#,
|
||||
)
|
||||
.file("foo/src/lib.rs", "")
|
||||
.file(
|
||||
"bar/Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "bar"
|
||||
version = "0.0.1"
|
||||
edition = "2015"
|
||||
authors = []
|
||||
|
||||
[dependencies]
|
||||
just-bar = "1.0"
|
||||
shared = "1.0"
|
||||
ws.workspace = true
|
||||
"#,
|
||||
)
|
||||
.file("bar/src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("generate-lockfile").run();
|
||||
|
||||
Package::new("just-foo", "1.0.1")
|
||||
.add_dep(Dependency::new("transitive-compatible", "1.0.0").build())
|
||||
.add_dep(Dependency::new("transitive-incompatible", "1.0.0").build())
|
||||
.publish();
|
||||
Package::new("just-bar", "1.0.1").publish();
|
||||
Package::new("shared", "1.0.1").publish();
|
||||
Package::new("ws", "1.0.1").publish();
|
||||
Package::new("transitive-compatible", "1.0.1").publish();
|
||||
Package::new("transitive-incompatible", "1.0.1").publish();
|
||||
|
||||
Package::new("just-foo", "2.0.0")
|
||||
// Upgrading just-foo implies accepting an update of transitive-compatible.
|
||||
.add_dep(Dependency::new("transitive-compatible", "1.0.1").build())
|
||||
// Upgrading just-foo implies accepting a major update of transitive-incompatible.
|
||||
.add_dep(Dependency::new("transitive-incompatible", "2.0.0").build())
|
||||
.publish();
|
||||
Package::new("just-bar", "2.0.0").publish();
|
||||
Package::new("shared", "2.0.0").publish();
|
||||
Package::new("ws", "2.0.0").publish();
|
||||
Package::new("transitive-incompatible", "2.0.0").publish();
|
||||
|
||||
p.cargo("update -Zunstable-options --breaking just-foo shared ws")
|
||||
.masquerade_as_nightly_cargo(&["update-breaking"])
|
||||
.with_stderr(
|
||||
"\
|
||||
[UPDATING] `[..]` index
|
||||
[UPGRADING] shared ^1.0 -> ^2.0
|
||||
[UPGRADING] ws ^1.0 -> ^2.0
|
||||
[UPGRADING] just-foo ^1.0 -> ^2.0
|
||||
[LOCKING] 5 packages to latest compatible versions
|
||||
[UPDATING] just-foo v1.0.0 -> v2.0.0
|
||||
[UPDATING] shared v1.0.0 -> v2.0.0
|
||||
[UPDATING] transitive-compatible v1.0.0 -> v1.0.1
|
||||
[UPDATING] transitive-incompatible v1.0.0 -> v2.0.0
|
||||
[UPDATING] ws v1.0.0 -> v2.0.0
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -265,7 +265,7 @@ trigger_files = [
|
|||
]
|
||||
|
||||
[autolabel."Command-generate-lockfile"]
|
||||
trigger_files = ["src/bin/cargo/commands/generate_lockfile.rs", "src/cargo/ops/cargo_generate_lockfile.rs"]
|
||||
trigger_files = ["src/bin/cargo/commands/generate_lockfile.rs"]
|
||||
|
||||
[autolabel."Command-git-checkout"]
|
||||
trigger_files = ["src/bin/cargo/commands/git_checkout.rs"]
|
||||
|
@ -334,7 +334,7 @@ trigger_files = ["src/bin/cargo/commands/tree.rs", "src/cargo/ops/tree/"]
|
|||
trigger_files = ["src/bin/cargo/commands/uninstall.rs", "src/cargo/ops/cargo_uninstall.rs"]
|
||||
|
||||
[autolabel."Command-update"]
|
||||
trigger_files = ["src/bin/cargo/commands/update.rs"]
|
||||
trigger_files = ["src/bin/cargo/commands/update.rs", "src/cargo/ops/cargo_update.rs"]
|
||||
|
||||
[autolabel."Command-vendor"]
|
||||
trigger_files = ["src/bin/cargo/commands/vendor.rs", "src/cargo/ops/vendor.rs"]
|
||||
|
|
Loading…
Reference in a new issue