1
1
"""Vendor Rust crates into an sdist"""
2
2
3
+ import enum
3
4
import json
4
5
import logging
5
6
import os
22
23
},
23
24
}
24
25
25
- # vendor when project depends on a Rust build system
26
- RUST_BUILD_REQUIRES = frozenset ({"setuptools-rust" , "maturin" })
26
+
27
+ class RustBuildSystem (enum .StrEnum ):
28
+ maturin = "maturin"
29
+ setuptools_rust = "setuptools-rust"
27
30
28
31
29
32
def _cargo_vendor (
@@ -86,28 +89,32 @@ def _cargo_config(project_dir: pathlib.Path) -> None:
86
89
tomlkit .dump (cfg , f )
87
90
88
91
89
- def _should_vendor_rust (req : Requirement , project_dir : pathlib .Path ) -> bool :
90
- """Detect if project has build requirement on Rust
92
+ def _detect_rust_build_backend (
93
+ req : Requirement , pyproject_toml : dict [str , typing .Any ]
94
+ ) -> RustBuildSystem | None :
95
+ """Detect Rust requirement and return Rust build system
91
96
92
97
Detects setuptools-rust and maturin.
93
98
"""
94
- pyproject_toml = dependencies .get_pyproject_contents (project_dir )
95
- if not pyproject_toml :
96
- logger .debug (f"{ req .name } : has no pyproject.toml" )
97
- return False
98
-
99
- build_backend = dependencies .get_build_backend (pyproject_toml )
99
+ build_system = dependencies .get_build_backend (pyproject_toml )
100
+ if build_system ["build-backend" ] == RustBuildSystem .maturin :
101
+ return RustBuildSystem .maturin
100
102
101
- for reqstring in build_backend ["requires" ]:
103
+ for reqstring in build_system ["requires" ]:
102
104
req = Requirement (reqstring )
103
- if req .name in RUST_BUILD_REQUIRES :
105
+ try :
106
+ # StrEnum.__contains__ does not work with str type
107
+ rbs = RustBuildSystem (req .name )
108
+ except ValueError :
109
+ pass
110
+ else :
104
111
logger .debug (
105
- f"{ req .name } : build-system requires { req .name } , vendoring crates"
112
+ f"{ req .name } : build-system requires ' { req .name } ' , vendoring crates"
106
113
)
107
- return True
114
+ return rbs
108
115
109
116
logger .debug (f"{ req .name } : no Rust build plugin detected" )
110
- return False
117
+ return None
111
118
112
119
113
120
def vendor_rust (
@@ -119,14 +126,52 @@ def vendor_rust(
119
126
``setuptools-rust`` or ``maturin``, and has a ``Cargo.toml``, otherwise
120
127
``False``.
121
128
"""
122
- if not _should_vendor_rust (req , project_dir ):
129
+ pyproject_toml = dependencies .get_pyproject_contents (project_dir )
130
+ if not pyproject_toml :
131
+ logger .debug (f"{ req .name } : has no pyproject.toml" )
123
132
return False
124
133
125
- # check for Cargo.toml
126
- manifests = list (project_dir .glob ("**/Cargo.toml" ))
134
+ backend = _detect_rust_build_backend (req , pyproject_toml )
135
+ manifests : list [pathlib .Path ] = []
136
+ # By default, maturin and setuptools-rust use Cargo.toml from project
137
+ # root directory. Projects can specify a different file in optional
138
+ # "tool.setuptools-rust" or "tool.maturin" entries.
139
+ match backend :
140
+ case RustBuildSystem .maturin :
141
+ try :
142
+ tool_maturin : dict [str , typing .Any ] = pyproject_toml ["tool" ]["maturin" ]
143
+ except KeyError as e :
144
+ logger .debug (f"{ req .name } : No additional maturin settings: { e } " )
145
+ else :
146
+ if "manifest-path" in tool_maturin :
147
+ manifests .append (project_dir / tool_maturin ["manifest-path" ])
148
+ case RustBuildSystem .setuptools_rust :
149
+ ext_modules : list [dict [str , typing .Any ]]
150
+ try :
151
+ ext_modules = pyproject_toml ["tool" ]["setuptools-rust" ]["ext-modules" ]
152
+ except KeyError as e :
153
+ logger .debug (f"{ req .name } : No additional setuptools-rust settings: { e } " )
154
+ else :
155
+ for ext_module in ext_modules :
156
+ if "path" in ext_module :
157
+ manifests .append (project_dir / ext_module ["path" ])
158
+ case None :
159
+ logger .debug (f"{ req .name } : no Rust build system detected" )
160
+ return False
161
+ case _ as unreachable :
162
+ typing .assert_never (unreachable )
163
+
127
164
if not manifests :
128
- logger .debug (f"{ req .name } : has no Cargo.toml files" )
129
- return False
165
+ # check for Cargo.toml in project root
166
+ root_cargo_toml = project_dir / "Cargo.toml"
167
+ if root_cargo_toml .is_file ():
168
+ manifests .append (root_cargo_toml )
169
+ else :
170
+ logger .warning (
171
+ f"{ req .name } : Rust build backend { backend } detected, but "
172
+ "no Cargo.toml files found."
173
+ )
174
+ return False
130
175
131
176
the_manifests = sorted (str (d .relative_to (project_dir )) for d in manifests )
132
177
logger .debug (f"{ req .name } : { project_dir } has cargo manifests: { the_manifests } " )
0 commit comments