Skip to content

Commit 140a770

Browse files
author
Jon Gjengset
committed
Only apply config patches on resolve
1 parent 8178f22 commit 140a770

File tree

5 files changed

+113
-19
lines changed

5 files changed

+113
-19
lines changed

src/cargo/core/workspace.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::borrow::Cow;
12
use std::cell::RefCell;
23
use std::collections::hash_map::{Entry, HashMap};
34
use std::collections::{BTreeMap, BTreeSet, HashSet};
@@ -365,11 +366,46 @@ impl<'cfg> Workspace<'cfg> {
365366
/// Returns the root `[patch]` section of this workspace.
366367
///
367368
/// This may be from a virtual crate or an actual crate.
368-
pub fn root_patch(&self) -> &HashMap<Url, Vec<Dependency>> {
369-
match self.root_maybe() {
369+
pub fn root_patch(&self) -> Cow<'_, HashMap<Url, Vec<Dependency>>> {
370+
let from_manifest = match self.root_maybe() {
370371
MaybePackage::Package(p) => p.manifest().patch(),
371372
MaybePackage::Virtual(vm) => vm.patch(),
373+
};
374+
375+
let from_config = self
376+
.config
377+
.patch()
378+
.expect("config [patch] was never parsed");
379+
if from_config.is_empty() {
380+
return Cow::Borrowed(from_manifest);
381+
}
382+
if from_manifest.is_empty() {
383+
return Cow::Borrowed(from_config);
384+
}
385+
386+
// We could just chain from_manifest and from_config,
387+
// but that's not quite right as it won't deal with overlaps.
388+
let mut combined = from_manifest.clone();
389+
for (url, cdeps) in from_config {
390+
if let Some(deps) = combined.get_mut(url) {
391+
// We want from_manifest to take precedence for each patched name.
392+
// NOTE: This is inefficient if the number of patches is large!
393+
let mut left = cdeps.clone();
394+
for dep in &mut *deps {
395+
if let Some(i) = left.iter().position(|cdep| {
396+
// XXX: should this also take into account version numbers?
397+
dep.name_in_toml() == cdep.name_in_toml()
398+
}) {
399+
left.swap_remove(i);
400+
}
401+
}
402+
// Whatever is left does not exist in manifest dependencies.
403+
deps.extend(left);
404+
} else {
405+
combined.insert(url.clone(), cdeps.clone());
406+
}
372407
}
408+
Cow::Owned(combined)
373409
}
374410

375411
/// Returns an iterator over all packages in this workspace

src/cargo/ops/resolve.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ pub fn resolve_with_previous<'cfg>(
240240
// locked.
241241
let mut avoid_patch_ids = HashSet::new();
242242
if register_patches {
243-
for (url, patches) in ws.root_patch() {
243+
for (url, patches) in ws.root_patch().iter() {
244244
let previous = match previous {
245245
Some(r) => r,
246246
None => {

src/cargo/util/config/mod.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ use url::Url;
7474
use self::ConfigValue as CV;
7575
use crate::core::compiler::rustdoc::RustdocExternMap;
7676
use crate::core::shell::Verbosity;
77-
use crate::core::{nightly_features_allowed, CliUnstable, Shell, SourceId, Workspace};
77+
use crate::core::{nightly_features_allowed, CliUnstable, Dependency, Shell, SourceId, Workspace};
7878
use crate::ops;
7979
use crate::util::errors::{CargoResult, CargoResultExt};
8080
use crate::util::toml as cargo_toml;
@@ -176,6 +176,8 @@ pub struct Config {
176176
/// Lock, if held, of the global package cache along with the number of
177177
/// acquisitions so far.
178178
package_cache_lock: RefCell<Option<(Option<FileLock>, usize)>>,
179+
/// `[patch]` section parsed from configuration.
180+
patch: LazyCell<HashMap<Url, Vec<Dependency>>>,
179181
/// Cached configuration parsed by Cargo
180182
http_config: LazyCell<CargoHttpConfig>,
181183
net_config: LazyCell<CargoNetConfig>,
@@ -261,6 +263,7 @@ impl Config {
261263
upper_case_env,
262264
updated_sources: LazyCell::new(),
263265
package_cache_lock: RefCell::new(None),
266+
patch: LazyCell::new(),
264267
http_config: LazyCell::new(),
265268
net_config: LazyCell::new(),
266269
build_config: LazyCell::new(),
@@ -1291,6 +1294,17 @@ impl Config {
12911294
Ok(*(self.crates_io_source_id.try_borrow_with(f)?))
12921295
}
12931296

1297+
pub fn maybe_init_patch<F>(&self, f: F) -> CargoResult<&HashMap<Url, Vec<Dependency>>>
1298+
where
1299+
F: FnMut() -> CargoResult<HashMap<Url, Vec<Dependency>>>,
1300+
{
1301+
self.patch.try_borrow_with(f)
1302+
}
1303+
1304+
pub fn patch(&self) -> Option<&HashMap<Url, Vec<Dependency>>> {
1305+
self.patch.borrow()
1306+
}
1307+
12941308
pub fn creation_time(&self) -> Instant {
12951309
self.creation_time
12961310
}

src/cargo/util/toml/mod.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
44
use std::rc::Rc;
55
use std::str;
66

7-
use anyhow::{anyhow, bail};
7+
use anyhow::{anyhow, bail, Context as _};
88
use cargo_platform::Platform;
99
use log::{debug, trace};
1010
use semver::{self, VersionReq};
@@ -1558,22 +1558,20 @@ impl TomlManifest {
15581558
}
15591559

15601560
fn patch(&self, cx: &mut Context<'_, '_>) -> CargoResult<HashMap<Url, Vec<Dependency>>> {
1561-
let from_manifest = Self::patch_(self.patch.as_ref(), cx)?;
1562-
1563-
let config_patch: Option<BTreeMap<String, BTreeMap<String, TomlDependency>>> =
1564-
cx.config.get("patch")?;
1561+
let _ = cx.config.maybe_init_patch(|| {
1562+
// Parse out the patches from .config while we're at it.
1563+
let config_patch: Option<BTreeMap<String, BTreeMap<String, TomlDependency>>> =
1564+
cx.config.get("patch")?;
1565+
1566+
if config_patch.is_some() && !cx.config.cli_unstable().patch_in_config {
1567+
cx.warnings.push("`[patch]` in .cargo/config.toml ignored, the -Zpatch-in-config command-line flag is required".to_owned());
1568+
return Ok(HashMap::new());
1569+
}
15651570

1566-
if config_patch.is_some() && !cx.config.cli_unstable().patch_in_config {
1567-
cx.warnings.push("`[patch]` in .cargo/config.toml ignored, the -Zpatch-in-config command-line flag is required".to_owned());
1568-
return Ok(from_manifest);
1569-
}
1571+
Self::patch_(config_patch.as_ref(), cx)
1572+
}).context("parse `[patch]` from .cargo/config.toml")?;
15701573

1571-
let mut from_config = Self::patch_(config_patch.as_ref(), cx)?;
1572-
if from_config.is_empty() {
1573-
return Ok(from_manifest);
1574-
}
1575-
from_config.extend(from_manifest);
1576-
Ok(from_config)
1574+
Self::patch_(self.patch.as_ref(), cx)
15771575
}
15781576

15791577
/// Returns the path to the build script if one exists for this crate.

tests/testsuite/patch.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ fn from_config_without_z() {
109109
)
110110
.run();
111111
}
112+
112113
#[cargo_test]
113114
fn from_config() {
114115
Package::new("bar", "0.1.0").publish();
@@ -151,6 +152,51 @@ fn from_config() {
151152
.run();
152153
}
153154

155+
#[cargo_test]
156+
fn from_config_precedence() {
157+
Package::new("bar", "0.1.0").publish();
158+
159+
let p = project()
160+
.file(
161+
"Cargo.toml",
162+
r#"
163+
[package]
164+
name = "foo"
165+
version = "0.0.1"
166+
authors = []
167+
168+
[dependencies]
169+
bar = "0.1.0"
170+
171+
[patch.crates-io]
172+
bar = { path = 'bar' }
173+
"#,
174+
)
175+
.file(
176+
".cargo/config.toml",
177+
r#"
178+
[patch.crates-io]
179+
bar = { path = 'no-such-path' }
180+
"#,
181+
)
182+
.file("src/lib.rs", "")
183+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1"))
184+
.file("bar/src/lib.rs", r#""#)
185+
.build();
186+
187+
p.cargo("build -Zpatch-in-config")
188+
.masquerade_as_nightly_cargo()
189+
.with_stderr(
190+
"\
191+
[UPDATING] `[ROOT][..]` index
192+
[COMPILING] bar v0.1.1 ([..])
193+
[COMPILING] foo v0.0.1 ([CWD])
194+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
195+
",
196+
)
197+
.run();
198+
}
199+
154200
#[cargo_test]
155201
fn nonexistent() {
156202
Package::new("baz", "0.1.0").publish();

0 commit comments

Comments
 (0)