1
1
use annotate_snippets:: { Level , Renderer , Snippet } ;
2
+ use cargo_util_schemas:: core:: PatchInfo ;
2
3
use std:: collections:: { BTreeMap , BTreeSet , HashMap } ;
3
4
use std:: ffi:: OsStr ;
4
5
use std:: path:: { Path , PathBuf } ;
@@ -27,6 +28,7 @@ use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, Worksp
27
28
use crate :: sources:: { CRATES_IO_INDEX , CRATES_IO_REGISTRY } ;
28
29
use crate :: util:: errors:: { CargoResult , ManifestError } ;
29
30
use crate :: util:: interning:: InternedString ;
31
+ use crate :: util:: CanonicalUrl ;
30
32
use crate :: util:: { self , context:: ConfigRelativePath , GlobalContext , IntoUrl , OptVersionReq } ;
31
33
32
34
mod embedded;
@@ -1185,7 +1187,7 @@ fn to_real_manifest(
1185
1187
) ?;
1186
1188
}
1187
1189
let replace = replace ( & resolved_toml, & mut manifest_ctx) ?;
1188
- let patch = patch ( & resolved_toml, & mut manifest_ctx) ?;
1190
+ let patch = patch ( & resolved_toml, & mut manifest_ctx, & features ) ?;
1189
1191
1190
1192
{
1191
1193
let mut names_sources = BTreeMap :: new ( ) ;
@@ -1448,7 +1450,7 @@ fn to_virtual_manifest(
1448
1450
} ;
1449
1451
(
1450
1452
replace ( & original_toml, & mut manifest_ctx) ?,
1451
- patch ( & original_toml, & mut manifest_ctx) ?,
1453
+ patch ( & original_toml, & mut manifest_ctx, & features ) ?,
1452
1454
)
1453
1455
} ;
1454
1456
if let Some ( profiles) = & original_toml. profile {
@@ -1527,7 +1529,7 @@ fn gather_dependencies(
1527
1529
1528
1530
for ( n, v) in dependencies. iter ( ) {
1529
1531
let resolved = v. resolved ( ) . expect ( "previously resolved" ) ;
1530
- let dep = dep_to_dependency ( & resolved, n, manifest_ctx, kind) ?;
1532
+ let dep = dep_to_dependency ( & resolved, n, manifest_ctx, kind, None ) ?;
1531
1533
manifest_ctx. deps . push ( dep) ;
1532
1534
}
1533
1535
Ok ( ( ) )
@@ -1561,7 +1563,7 @@ fn replace(
1561
1563
) ;
1562
1564
}
1563
1565
1564
- let mut dep = dep_to_dependency ( replacement, spec. name ( ) , manifest_ctx, None ) ?;
1566
+ let mut dep = dep_to_dependency ( replacement, spec. name ( ) , manifest_ctx, None , None ) ?;
1565
1567
let version = spec. version ( ) . ok_or_else ( || {
1566
1568
anyhow ! (
1567
1569
"replacements must specify a version \
@@ -1584,7 +1586,9 @@ fn replace(
1584
1586
fn patch (
1585
1587
me : & manifest:: TomlManifest ,
1586
1588
manifest_ctx : & mut ManifestContext < ' _ , ' _ > ,
1589
+ features : & Features ,
1587
1590
) -> CargoResult < HashMap < Url , Vec < Dependency > > > {
1591
+ let patch_files_enabled = features. require ( Feature :: patch_files ( ) ) . is_ok ( ) ;
1588
1592
let mut patch = HashMap :: new ( ) ;
1589
1593
for ( toml_url, deps) in me. patch . iter ( ) . flatten ( ) {
1590
1594
let url = match & toml_url[ ..] {
@@ -1601,7 +1605,7 @@ fn patch(
1601
1605
} ) ?,
1602
1606
} ;
1603
1607
patch. insert (
1604
- url,
1608
+ url. clone ( ) ,
1605
1609
deps. iter ( )
1606
1610
. map ( |( name, dep) | {
1607
1611
unused_dep_keys (
@@ -1610,14 +1614,21 @@ fn patch(
1610
1614
dep. unused_keys ( ) ,
1611
1615
& mut manifest_ctx. warnings ,
1612
1616
) ;
1613
- dep_to_dependency ( dep, name, manifest_ctx, None )
1617
+ dep_to_dependency (
1618
+ dep,
1619
+ name,
1620
+ manifest_ctx,
1621
+ None ,
1622
+ Some ( ( & url, patch_files_enabled) ) ,
1623
+ )
1614
1624
} )
1615
1625
. collect :: < CargoResult < Vec < _ > > > ( ) ?,
1616
1626
) ;
1617
1627
}
1618
1628
Ok ( patch)
1619
1629
}
1620
1630
1631
+ /// Transforms a `patch` entry to a [`Dependency`].
1621
1632
pub ( crate ) fn to_dependency < P : ResolveToPath + Clone > (
1622
1633
dep : & manifest:: TomlDependency < P > ,
1623
1634
name : & str ,
@@ -1627,27 +1638,26 @@ pub(crate) fn to_dependency<P: ResolveToPath + Clone>(
1627
1638
platform : Option < Platform > ,
1628
1639
root : & Path ,
1629
1640
kind : Option < DepKind > ,
1641
+ patch_source_url : & Url ,
1630
1642
) -> CargoResult < Dependency > {
1631
- dep_to_dependency (
1632
- dep,
1633
- name,
1634
- & mut ManifestContext {
1635
- deps : & mut Vec :: new ( ) ,
1636
- source_id,
1637
- gctx,
1638
- warnings,
1639
- platform,
1640
- root,
1641
- } ,
1642
- kind,
1643
- )
1643
+ let manifest_ctx = & mut ManifestContext {
1644
+ deps : & mut Vec :: new ( ) ,
1645
+ source_id,
1646
+ gctx,
1647
+ warnings,
1648
+ platform,
1649
+ root,
1650
+ } ;
1651
+ let patch_source_url = Some ( ( patch_source_url, gctx. cli_unstable ( ) . patch_files ) ) ;
1652
+ dep_to_dependency ( dep, name, manifest_ctx, kind, patch_source_url)
1644
1653
}
1645
1654
1646
1655
fn dep_to_dependency < P : ResolveToPath + Clone > (
1647
1656
orig : & manifest:: TomlDependency < P > ,
1648
1657
name : & str ,
1649
1658
manifest_ctx : & mut ManifestContext < ' _ , ' _ > ,
1650
1659
kind : Option < DepKind > ,
1660
+ patch_source_url : Option < ( & Url , bool ) > ,
1651
1661
) -> CargoResult < Dependency > {
1652
1662
match * orig {
1653
1663
manifest:: TomlDependency :: Simple ( ref version) => detailed_dep_to_dependency (
@@ -1658,9 +1668,10 @@ fn dep_to_dependency<P: ResolveToPath + Clone>(
1658
1668
name,
1659
1669
manifest_ctx,
1660
1670
kind,
1671
+ patch_source_url,
1661
1672
) ,
1662
1673
manifest:: TomlDependency :: Detailed ( ref details) => {
1663
- detailed_dep_to_dependency ( details, name, manifest_ctx, kind)
1674
+ detailed_dep_to_dependency ( details, name, manifest_ctx, kind, patch_source_url )
1664
1675
}
1665
1676
}
1666
1677
}
@@ -1670,6 +1681,7 @@ fn detailed_dep_to_dependency<P: ResolveToPath + Clone>(
1670
1681
name_in_toml : & str ,
1671
1682
manifest_ctx : & mut ManifestContext < ' _ , ' _ > ,
1672
1683
kind : Option < DepKind > ,
1684
+ patch_source_url : Option < ( & Url , bool ) > ,
1673
1685
) -> CargoResult < Dependency > {
1674
1686
if orig. version . is_none ( ) && orig. path . is_none ( ) && orig. git . is_none ( ) {
1675
1687
let msg = format ! (
@@ -1749,6 +1761,7 @@ fn detailed_dep_to_dependency<P: ResolveToPath + Clone>(
1749
1761
1750
1762
let version = orig. version . as_deref ( ) ;
1751
1763
let mut dep = Dependency :: parse ( pkg_name, version, new_source_id) ?;
1764
+
1752
1765
if orig. default_features . is_some ( ) && orig. default_features2 . is_some ( ) {
1753
1766
warn_on_deprecated (
1754
1767
"default-features" ,
@@ -1816,6 +1829,11 @@ fn detailed_dep_to_dependency<P: ResolveToPath + Clone>(
1816
1829
)
1817
1830
}
1818
1831
}
1832
+
1833
+ if let Some ( source_id) = patched_source_id ( orig, manifest_ctx, & dep, patch_source_url) ? {
1834
+ dep. set_source_id ( source_id) ;
1835
+ }
1836
+
1819
1837
Ok ( dep)
1820
1838
}
1821
1839
@@ -1912,6 +1930,88 @@ fn resolve_source_id_from_dependency<P: ResolveToPath + Clone>(
1912
1930
Ok ( new_source_id)
1913
1931
}
1914
1932
1933
+ // Handle `patches` field for `[patch]` table, if any.
1934
+ fn patched_source_id < P : ResolveToPath + Clone > (
1935
+ orig : & manifest:: TomlDetailedDependency < P > ,
1936
+ manifest_ctx : & mut ManifestContext < ' _ , ' _ > ,
1937
+ dep : & Dependency ,
1938
+ patch_source_url : Option < ( & Url , bool ) > ,
1939
+ ) -> CargoResult < Option < SourceId > > {
1940
+ let name_in_toml = dep. name_in_toml ( ) . as_str ( ) ;
1941
+ let message = "see https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#patch-files about the status of this feature." ;
1942
+ match ( patch_source_url, orig. patches . as_ref ( ) ) {
1943
+ ( _, None ) => {
1944
+ // not a SourceKind::Patched dep.
1945
+ Ok ( None )
1946
+ }
1947
+ ( None , Some ( _) ) => {
1948
+ let kind = dep. kind ( ) . kind_table ( ) ;
1949
+ manifest_ctx. warnings . push ( format ! (
1950
+ "unused manifest key: {kind}.{name_in_toml}.patches; {message}"
1951
+ ) ) ;
1952
+ Ok ( None )
1953
+ }
1954
+ ( Some ( ( url, false ) ) , Some ( _) ) => {
1955
+ manifest_ctx. warnings . push ( format ! (
1956
+ "ignoring `patches` on patch for `{name_in_toml}` in `{url}`; {message}"
1957
+ ) ) ;
1958
+ Ok ( None )
1959
+ }
1960
+ ( Some ( ( url, true ) ) , Some ( patches) ) => {
1961
+ let source_id = dep. source_id ( ) ;
1962
+ if !source_id. is_registry ( ) {
1963
+ bail ! (
1964
+ "patch for `{name_in_toml}` in `{url}` requires a registry source \
1965
+ when patching with patch files"
1966
+ ) ;
1967
+ }
1968
+ if & CanonicalUrl :: new ( url) ? != source_id. canonical_url ( ) {
1969
+ bail ! (
1970
+ "patch for `{name_in_toml}` in `{url}` must refer to the same source \
1971
+ when patching with patch files"
1972
+ )
1973
+ }
1974
+ let version = match dep. version_req ( ) . locked_version ( ) {
1975
+ Some ( v) => Some ( v. to_owned ( ) ) ,
1976
+ None if dep. version_req ( ) . is_exact ( ) => {
1977
+ // Remove the `=` exact operator.
1978
+ orig. version
1979
+ . as_deref ( )
1980
+ . map ( |v| v[ 1 ..] . trim ( ) . parse ( ) . ok ( ) )
1981
+ . flatten ( )
1982
+ }
1983
+ None => None ,
1984
+ } ;
1985
+ let Some ( version) = version else {
1986
+ bail ! (
1987
+ "patch for `{name_in_toml}` in `{url}` requires an exact version \
1988
+ when patching with patch files"
1989
+ ) ;
1990
+ } ;
1991
+ let patches: Vec < _ > = patches
1992
+ . iter ( )
1993
+ . map ( |path| {
1994
+ let path = path. resolve ( manifest_ctx. gctx ) ;
1995
+ let path = manifest_ctx. root . join ( path) ;
1996
+ // keep paths inside workspace relative to workspace, otherwise absolute.
1997
+ path. strip_prefix ( manifest_ctx. gctx . cwd ( ) )
1998
+ . map ( Into :: into)
1999
+ . unwrap_or_else ( |_| paths:: normalize_path ( & path) )
2000
+ } )
2001
+ . collect ( ) ;
2002
+ if patches. is_empty ( ) {
2003
+ bail ! (
2004
+ "patch for `{name_in_toml}` in `{url}` requires at least one patch file \
2005
+ when patching with patch files"
2006
+ ) ;
2007
+ }
2008
+ let pkg_name = dep. package_name ( ) . to_string ( ) ;
2009
+ let patch_info = PatchInfo :: new ( pkg_name, version. to_string ( ) , patches) ;
2010
+ SourceId :: for_patches ( source_id, patch_info) . map ( Some )
2011
+ }
2012
+ }
2013
+ }
2014
+
1915
2015
pub trait ResolveToPath {
1916
2016
fn resolve ( & self , gctx : & GlobalContext ) -> PathBuf ;
1917
2017
}
0 commit comments