1
1
use annotate_snippets:: { Level , Snippet } ;
2
+ use cargo_util_schemas:: core:: PatchInfo ;
2
3
use std:: collections:: { BTreeMap , BTreeSet , HashMap , HashSet } ;
3
4
use std:: ffi:: OsStr ;
4
5
use std:: path:: { Path , PathBuf } ;
@@ -28,6 +29,7 @@ use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, Worksp
28
29
use crate :: sources:: { CRATES_IO_INDEX , CRATES_IO_REGISTRY } ;
29
30
use crate :: util:: errors:: { CargoResult , ManifestError } ;
30
31
use crate :: util:: interning:: InternedString ;
32
+ use crate :: util:: CanonicalUrl ;
31
33
use crate :: util:: { self , context:: ConfigRelativePath , GlobalContext , IntoUrl , OptVersionReq } ;
32
34
33
35
mod embedded;
@@ -1326,7 +1328,7 @@ fn to_real_manifest(
1326
1328
) ?;
1327
1329
}
1328
1330
let replace = replace ( & resolved_toml, & mut manifest_ctx) ?;
1329
- let patch = patch ( & resolved_toml, & mut manifest_ctx) ?;
1331
+ let patch = patch ( & resolved_toml, & mut manifest_ctx, & features ) ?;
1330
1332
1331
1333
{
1332
1334
let mut names_sources = BTreeMap :: new ( ) ;
@@ -1585,7 +1587,7 @@ fn to_virtual_manifest(
1585
1587
} ;
1586
1588
(
1587
1589
replace ( & original_toml, & mut manifest_ctx) ?,
1588
- patch ( & original_toml, & mut manifest_ctx) ?,
1590
+ patch ( & original_toml, & mut manifest_ctx, & features ) ?,
1589
1591
)
1590
1592
} ;
1591
1593
if let Some ( profiles) = & original_toml. profile {
@@ -1664,7 +1666,7 @@ fn gather_dependencies(
1664
1666
1665
1667
for ( n, v) in dependencies. iter ( ) {
1666
1668
let resolved = v. resolved ( ) . expect ( "previously resolved" ) ;
1667
- let dep = dep_to_dependency ( & resolved, n, manifest_ctx, kind) ?;
1669
+ let dep = dep_to_dependency ( & resolved, n, manifest_ctx, kind, None ) ?;
1668
1670
manifest_ctx. deps . push ( dep) ;
1669
1671
}
1670
1672
Ok ( ( ) )
@@ -1698,7 +1700,7 @@ fn replace(
1698
1700
) ;
1699
1701
}
1700
1702
1701
- let mut dep = dep_to_dependency ( replacement, spec. name ( ) , manifest_ctx, None ) ?;
1703
+ let mut dep = dep_to_dependency ( replacement, spec. name ( ) , manifest_ctx, None , None ) ?;
1702
1704
let version = spec. version ( ) . ok_or_else ( || {
1703
1705
anyhow ! (
1704
1706
"replacements must specify a version \
@@ -1721,7 +1723,9 @@ fn replace(
1721
1723
fn patch (
1722
1724
me : & manifest:: TomlManifest ,
1723
1725
manifest_ctx : & mut ManifestContext < ' _ , ' _ > ,
1726
+ features : & Features ,
1724
1727
) -> CargoResult < HashMap < Url , Vec < Dependency > > > {
1728
+ let patch_files_enabled = features. require ( Feature :: patch_files ( ) ) . is_ok ( ) ;
1725
1729
let mut patch = HashMap :: new ( ) ;
1726
1730
for ( toml_url, deps) in me. patch . iter ( ) . flatten ( ) {
1727
1731
let url = match & toml_url[ ..] {
@@ -1738,7 +1742,7 @@ fn patch(
1738
1742
} ) ?,
1739
1743
} ;
1740
1744
patch. insert (
1741
- url,
1745
+ url. clone ( ) ,
1742
1746
deps. iter ( )
1743
1747
. map ( |( name, dep) | {
1744
1748
unused_dep_keys (
@@ -1747,14 +1751,21 @@ fn patch(
1747
1751
dep. unused_keys ( ) ,
1748
1752
& mut manifest_ctx. warnings ,
1749
1753
) ;
1750
- dep_to_dependency ( dep, name, manifest_ctx, None )
1754
+ dep_to_dependency (
1755
+ dep,
1756
+ name,
1757
+ manifest_ctx,
1758
+ None ,
1759
+ Some ( ( & url, patch_files_enabled) ) ,
1760
+ )
1751
1761
} )
1752
1762
. collect :: < CargoResult < Vec < _ > > > ( ) ?,
1753
1763
) ;
1754
1764
}
1755
1765
Ok ( patch)
1756
1766
}
1757
1767
1768
+ /// Transforms a `patch` entry to a [`Dependency`].
1758
1769
pub ( crate ) fn to_dependency < P : ResolveToPath + Clone > (
1759
1770
dep : & manifest:: TomlDependency < P > ,
1760
1771
name : & str ,
@@ -1764,27 +1775,26 @@ pub(crate) fn to_dependency<P: ResolveToPath + Clone>(
1764
1775
platform : Option < Platform > ,
1765
1776
root : & Path ,
1766
1777
kind : Option < DepKind > ,
1778
+ patch_source_url : & Url ,
1767
1779
) -> CargoResult < Dependency > {
1768
- dep_to_dependency (
1769
- dep,
1770
- name,
1771
- & mut ManifestContext {
1772
- deps : & mut Vec :: new ( ) ,
1773
- source_id,
1774
- gctx,
1775
- warnings,
1776
- platform,
1777
- root,
1778
- } ,
1779
- kind,
1780
- )
1780
+ let manifest_ctx = & mut ManifestContext {
1781
+ deps : & mut Vec :: new ( ) ,
1782
+ source_id,
1783
+ gctx,
1784
+ warnings,
1785
+ platform,
1786
+ root,
1787
+ } ;
1788
+ let patch_source_url = Some ( ( patch_source_url, gctx. cli_unstable ( ) . patch_files ) ) ;
1789
+ dep_to_dependency ( dep, name, manifest_ctx, kind, patch_source_url)
1781
1790
}
1782
1791
1783
1792
fn dep_to_dependency < P : ResolveToPath + Clone > (
1784
1793
orig : & manifest:: TomlDependency < P > ,
1785
1794
name : & str ,
1786
1795
manifest_ctx : & mut ManifestContext < ' _ , ' _ > ,
1787
1796
kind : Option < DepKind > ,
1797
+ patch_source_url : Option < ( & Url , bool ) > ,
1788
1798
) -> CargoResult < Dependency > {
1789
1799
match * orig {
1790
1800
manifest:: TomlDependency :: Simple ( ref version) => detailed_dep_to_dependency (
@@ -1795,9 +1805,10 @@ fn dep_to_dependency<P: ResolveToPath + Clone>(
1795
1805
name,
1796
1806
manifest_ctx,
1797
1807
kind,
1808
+ patch_source_url,
1798
1809
) ,
1799
1810
manifest:: TomlDependency :: Detailed ( ref details) => {
1800
- detailed_dep_to_dependency ( details, name, manifest_ctx, kind)
1811
+ detailed_dep_to_dependency ( details, name, manifest_ctx, kind, patch_source_url )
1801
1812
}
1802
1813
}
1803
1814
}
@@ -1807,6 +1818,7 @@ fn detailed_dep_to_dependency<P: ResolveToPath + Clone>(
1807
1818
name_in_toml : & str ,
1808
1819
manifest_ctx : & mut ManifestContext < ' _ , ' _ > ,
1809
1820
kind : Option < DepKind > ,
1821
+ patch_source_url : Option < ( & Url , bool ) > ,
1810
1822
) -> CargoResult < Dependency > {
1811
1823
if orig. version . is_none ( ) && orig. path . is_none ( ) && orig. git . is_none ( ) {
1812
1824
anyhow:: bail!(
@@ -1942,6 +1954,11 @@ fn detailed_dep_to_dependency<P: ResolveToPath + Clone>(
1942
1954
)
1943
1955
}
1944
1956
}
1957
+
1958
+ if let Some ( source_id) = patched_source_id ( orig, manifest_ctx, & dep, patch_source_url) ? {
1959
+ dep. set_source_id ( source_id) ;
1960
+ }
1961
+
1945
1962
Ok ( dep)
1946
1963
}
1947
1964
@@ -2030,6 +2047,88 @@ fn to_dependency_source_id<P: ResolveToPath + Clone>(
2030
2047
}
2031
2048
}
2032
2049
2050
+ // Handle `patches` field for `[patch]` table, if any.
2051
+ fn patched_source_id < P : ResolveToPath + Clone > (
2052
+ orig : & manifest:: TomlDetailedDependency < P > ,
2053
+ manifest_ctx : & mut ManifestContext < ' _ , ' _ > ,
2054
+ dep : & Dependency ,
2055
+ patch_source_url : Option < ( & Url , bool ) > ,
2056
+ ) -> CargoResult < Option < SourceId > > {
2057
+ let name_in_toml = dep. name_in_toml ( ) . as_str ( ) ;
2058
+ let message = "see https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#patch-files about the status of this feature." ;
2059
+ match ( patch_source_url, orig. patches . as_ref ( ) ) {
2060
+ ( _, None ) => {
2061
+ // not a SourceKind::Patched dep.
2062
+ Ok ( None )
2063
+ }
2064
+ ( None , Some ( _) ) => {
2065
+ let kind = dep. kind ( ) . kind_table ( ) ;
2066
+ manifest_ctx. warnings . push ( format ! (
2067
+ "unused manifest key: {kind}.{name_in_toml}.patches; {message}"
2068
+ ) ) ;
2069
+ Ok ( None )
2070
+ }
2071
+ ( Some ( ( url, false ) ) , Some ( _) ) => {
2072
+ manifest_ctx. warnings . push ( format ! (
2073
+ "ignoring `patches` on patch for `{name_in_toml}` in `{url}`; {message}"
2074
+ ) ) ;
2075
+ Ok ( None )
2076
+ }
2077
+ ( Some ( ( url, true ) ) , Some ( patches) ) => {
2078
+ let source_id = dep. source_id ( ) ;
2079
+ if !source_id. is_registry ( ) {
2080
+ bail ! (
2081
+ "patch for `{name_in_toml}` in `{url}` requires a registry source \
2082
+ when patching with patch files"
2083
+ ) ;
2084
+ }
2085
+ if & CanonicalUrl :: new ( url) ? != source_id. canonical_url ( ) {
2086
+ bail ! (
2087
+ "patch for `{name_in_toml}` in `{url}` must refer to the same source \
2088
+ when patching with patch files"
2089
+ )
2090
+ }
2091
+ let version = match dep. version_req ( ) . locked_version ( ) {
2092
+ Some ( v) => Some ( v. to_owned ( ) ) ,
2093
+ None if dep. version_req ( ) . is_exact ( ) => {
2094
+ // Remove the `=` exact operator.
2095
+ orig. version
2096
+ . as_deref ( )
2097
+ . map ( |v| v[ 1 ..] . trim ( ) . parse ( ) . ok ( ) )
2098
+ . flatten ( )
2099
+ }
2100
+ None => None ,
2101
+ } ;
2102
+ let Some ( version) = version else {
2103
+ bail ! (
2104
+ "patch for `{name_in_toml}` in `{url}` requires an exact version \
2105
+ when patching with patch files"
2106
+ ) ;
2107
+ } ;
2108
+ let patches: Vec < _ > = patches
2109
+ . iter ( )
2110
+ . map ( |path| {
2111
+ let path = path. resolve ( manifest_ctx. gctx ) ;
2112
+ let path = manifest_ctx. root . join ( path) ;
2113
+ // keep paths inside workspace relative to workspace, otherwise absolute.
2114
+ path. strip_prefix ( manifest_ctx. gctx . cwd ( ) )
2115
+ . map ( Into :: into)
2116
+ . unwrap_or_else ( |_| paths:: normalize_path ( & path) )
2117
+ } )
2118
+ . collect ( ) ;
2119
+ if patches. is_empty ( ) {
2120
+ bail ! (
2121
+ "patch for `{name_in_toml}` in `{url}` requires at least one patch file \
2122
+ when patching with patch files"
2123
+ ) ;
2124
+ }
2125
+ let pkg_name = dep. package_name ( ) . to_string ( ) ;
2126
+ let patch_info = PatchInfo :: new ( pkg_name, version. to_string ( ) , patches) ;
2127
+ SourceId :: for_patches ( source_id, patch_info) . map ( Some )
2128
+ }
2129
+ }
2130
+ }
2131
+
2033
2132
pub trait ResolveToPath {
2034
2133
fn resolve ( & self , gctx : & GlobalContext ) -> PathBuf ;
2035
2134
}
0 commit comments