Skip to content

Commit 8178f22

Browse files
author
Jon Gjengset
committed
Support [patch] in .cargo/config files
This patch adds support for `[patch]` sections in `.cargo/config.toml` files. Patches from config files defer to `[patch]` in `Cargo.toml` if both provide a patch for the same crate. The current implementation merge config patches into the workspace manifest patches. It's unclear if that's the right long-term plan, or whether these patches should be stored separately (though likely still in the manifest). Regardless, they _should_ likely continue to be parsed when the manifest is parsed so that errors and such occur in the same place regardless of where a patch is specified. Fixes #5539.
1 parent 572e201 commit 8178f22

File tree

3 files changed

+243
-2
lines changed

3 files changed

+243
-2
lines changed

src/cargo/core/features.rs

+2
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ pub struct CliUnstable {
551551
pub extra_link_arg: bool,
552552
pub credential_process: bool,
553553
pub configurable_env: bool,
554+
pub patch_in_config: bool,
554555
}
555556

556557
const STABILIZED_COMPILE_PROGRESS: &str = "The progress bar is now always \
@@ -707,6 +708,7 @@ impl CliUnstable {
707708
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
708709
"jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?,
709710
"configurable-env" => self.configurable_env = parse_empty(k, v)?,
711+
"patch-in-config" => self.patch_in_config = parse_empty(k, v)?,
710712
"features" => {
711713
// For now this is still allowed (there are still some
712714
// unstable options like "compare"). This should be removed at

src/cargo/util/toml/mod.rs

+24-2
Original file line numberDiff line numberDiff line change
@@ -1531,9 +1531,12 @@ impl TomlManifest {
15311531
Ok(replace)
15321532
}
15331533

1534-
fn patch(&self, cx: &mut Context<'_, '_>) -> CargoResult<HashMap<Url, Vec<Dependency>>> {
1534+
fn patch_(
1535+
table: Option<&BTreeMap<String, BTreeMap<String, TomlDependency>>>,
1536+
cx: &mut Context<'_, '_>,
1537+
) -> CargoResult<HashMap<Url, Vec<Dependency>>> {
15351538
let mut patch = HashMap::new();
1536-
for (url, deps) in self.patch.iter().flatten() {
1539+
for (url, deps) in table.into_iter().flatten() {
15371540
let url = match &url[..] {
15381541
CRATES_IO_REGISTRY => CRATES_IO_INDEX.parse().unwrap(),
15391542
_ => cx
@@ -1554,6 +1557,25 @@ impl TomlManifest {
15541557
Ok(patch)
15551558
}
15561559

1560+
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")?;
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(from_manifest);
1569+
}
1570+
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)
1577+
}
1578+
15571579
/// Returns the path to the build script if one exists for this crate.
15581580
fn maybe_custom_build(
15591581
&self,

tests/testsuite/patch.rs

+217
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,91 @@ fn replace() {
6666
p.cargo("build").with_stderr("[FINISHED] [..]").run();
6767
}
6868

69+
#[cargo_test]
70+
fn from_config_without_z() {
71+
Package::new("bar", "0.1.0").publish();
72+
73+
let p = project()
74+
.file(
75+
"Cargo.toml",
76+
r#"
77+
[package]
78+
name = "foo"
79+
version = "0.0.1"
80+
authors = []
81+
82+
[dependencies]
83+
bar = "0.1.0"
84+
"#,
85+
)
86+
.file(
87+
".cargo/config.toml",
88+
r#"
89+
[patch.crates-io]
90+
bar = { path = 'bar' }
91+
"#,
92+
)
93+
.file("src/lib.rs", "")
94+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1"))
95+
.file("bar/src/lib.rs", r#""#)
96+
.build();
97+
98+
p.cargo("build")
99+
.with_stderr(
100+
"\
101+
[WARNING] `[patch]` in .cargo/config.toml ignored, the -Zpatch-in-config command-line flag is required
102+
[UPDATING] `[ROOT][..]` index
103+
[DOWNLOADING] crates ...
104+
[DOWNLOADED] bar v0.1.0 ([..])
105+
[COMPILING] bar v0.1.0
106+
[COMPILING] foo v0.0.1 ([CWD])
107+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
108+
",
109+
)
110+
.run();
111+
}
112+
#[cargo_test]
113+
fn from_config() {
114+
Package::new("bar", "0.1.0").publish();
115+
116+
let p = project()
117+
.file(
118+
"Cargo.toml",
119+
r#"
120+
[package]
121+
name = "foo"
122+
version = "0.0.1"
123+
authors = []
124+
125+
[dependencies]
126+
bar = "0.1.0"
127+
"#,
128+
)
129+
.file(
130+
".cargo/config.toml",
131+
r#"
132+
[patch.crates-io]
133+
bar = { path = 'bar' }
134+
"#,
135+
)
136+
.file("src/lib.rs", "")
137+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1"))
138+
.file("bar/src/lib.rs", r#""#)
139+
.build();
140+
141+
p.cargo("build -Zpatch-in-config")
142+
.masquerade_as_nightly_cargo()
143+
.with_stderr(
144+
"\
145+
[UPDATING] `[ROOT][..]` index
146+
[COMPILING] bar v0.1.1 ([..])
147+
[COMPILING] foo v0.0.1 ([CWD])
148+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
149+
",
150+
)
151+
.run();
152+
}
153+
69154
#[cargo_test]
70155
fn nonexistent() {
71156
Package::new("baz", "0.1.0").publish();
@@ -268,6 +353,78 @@ fn unused() {
268353
);
269354
}
270355

356+
#[cargo_test]
357+
fn unused_from_config() {
358+
Package::new("bar", "0.1.0").publish();
359+
360+
let p = project()
361+
.file(
362+
"Cargo.toml",
363+
r#"
364+
[package]
365+
name = "foo"
366+
version = "0.0.1"
367+
authors = []
368+
369+
[dependencies]
370+
bar = "0.1.0"
371+
"#,
372+
)
373+
.file(
374+
".cargo/config.toml",
375+
r#"
376+
[patch.crates-io]
377+
bar = { path = "bar" }
378+
"#,
379+
)
380+
.file("src/lib.rs", "")
381+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.2.0"))
382+
.file("bar/src/lib.rs", "not rust code")
383+
.build();
384+
385+
p.cargo("build -Zpatch-in-config")
386+
.masquerade_as_nightly_cargo()
387+
.with_stderr(
388+
"\
389+
[UPDATING] `[ROOT][..]` index
390+
[WARNING] Patch `bar v0.2.0 ([CWD]/bar)` was not used in the crate graph.
391+
[..]
392+
[..]
393+
[..]
394+
[..]
395+
[DOWNLOADING] crates ...
396+
[DOWNLOADED] bar v0.1.0 [..]
397+
[COMPILING] bar v0.1.0
398+
[COMPILING] foo v0.0.1 ([CWD])
399+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
400+
",
401+
)
402+
.run();
403+
p.cargo("build -Zpatch-in-config")
404+
.masquerade_as_nightly_cargo()
405+
.with_stderr(
406+
"\
407+
[WARNING] Patch `bar v0.2.0 ([CWD]/bar)` was not used in the crate graph.
408+
[..]
409+
[..]
410+
[..]
411+
[..]
412+
[FINISHED] [..]
413+
",
414+
)
415+
.run();
416+
417+
// unused patch should be in the lock file
418+
let lock = p.read_lockfile();
419+
let toml: toml::Value = toml::from_str(&lock).unwrap();
420+
assert_eq!(toml["patch"]["unused"].as_array().unwrap().len(), 1);
421+
assert_eq!(toml["patch"]["unused"][0]["name"].as_str(), Some("bar"));
422+
assert_eq!(
423+
toml["patch"]["unused"][0]["version"].as_str(),
424+
Some("0.2.0")
425+
);
426+
}
427+
271428
#[cargo_test]
272429
fn unused_git() {
273430
Package::new("bar", "0.1.0").publish();
@@ -395,6 +552,66 @@ fn add_patch() {
395552
p.cargo("build").with_stderr("[FINISHED] [..]").run();
396553
}
397554

555+
#[cargo_test]
556+
fn add_patch_from_config() {
557+
Package::new("bar", "0.1.0").publish();
558+
559+
let p = project()
560+
.file(
561+
"Cargo.toml",
562+
r#"
563+
[package]
564+
name = "foo"
565+
version = "0.0.1"
566+
authors = []
567+
568+
[dependencies]
569+
bar = "0.1.0"
570+
"#,
571+
)
572+
.file("src/lib.rs", "")
573+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
574+
.file("bar/src/lib.rs", r#""#)
575+
.build();
576+
577+
p.cargo("build")
578+
.with_stderr(
579+
"\
580+
[UPDATING] `[ROOT][..]` index
581+
[DOWNLOADING] crates ...
582+
[DOWNLOADED] bar v0.1.0 [..]
583+
[COMPILING] bar v0.1.0
584+
[COMPILING] foo v0.0.1 ([CWD])
585+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
586+
",
587+
)
588+
.run();
589+
p.cargo("build").with_stderr("[FINISHED] [..]").run();
590+
591+
p.change_file(
592+
".cargo/config.toml",
593+
r#"
594+
[patch.crates-io]
595+
bar = { path = 'bar' }
596+
"#,
597+
);
598+
599+
p.cargo("build -Zpatch-in-config")
600+
.masquerade_as_nightly_cargo()
601+
.with_stderr(
602+
"\
603+
[COMPILING] bar v0.1.0 ([CWD]/bar)
604+
[COMPILING] foo v0.0.1 ([CWD])
605+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
606+
",
607+
)
608+
.run();
609+
p.cargo("build -Zpatch-in-config")
610+
.masquerade_as_nightly_cargo()
611+
.with_stderr("[FINISHED] [..]")
612+
.run();
613+
}
614+
398615
#[cargo_test]
399616
fn add_ignored_patch() {
400617
Package::new("bar", "0.1.0").publish();

0 commit comments

Comments
 (0)