2
2
import os
3
3
import platform
4
4
import re
5
- import shlex
6
5
import shutil
7
6
import subprocess
8
7
import sys
9
8
import tempfile
10
9
from pathlib import Path
11
- from typing import (
12
- Any ,
13
- Dict ,
14
- Iterator ,
15
- List ,
16
- NamedTuple ,
17
- Optional ,
18
- Sequence ,
19
- Set ,
20
- Tuple ,
21
- cast ,
22
- )
10
+ from typing import Any , Dict , Iterator , List , NamedTuple , Sequence , Set , Tuple , cast
23
11
24
12
from filelock import FileLock
25
13
33
21
BuildFrontend ,
34
22
BuildSelector ,
35
23
NonPlatformWheelError ,
24
+ call ,
36
25
detect_ci_provider ,
37
26
download ,
38
27
get_build_verbosity_extra_flags ,
39
28
get_pip_version ,
40
29
install_certifi_script ,
41
30
prepare_command ,
42
31
read_python_configs ,
32
+ shell ,
43
33
unwrap ,
44
34
venv ,
45
35
)
46
36
47
37
48
- def call (
49
- args : Sequence [PathOrStr ],
50
- env : Optional [Dict [str , str ]] = None ,
51
- cwd : Optional [PathOrStr ] = None ,
52
- shell : bool = False ,
53
- ) -> None :
54
- # print the command executing for the logs
55
- if shell :
56
- print (f"+ { args } " )
57
- else :
58
- print ("+ " + " " .join (shlex .quote (str (a )) for a in args ))
59
-
60
- subprocess .run (args , env = env , cwd = cwd , shell = shell , check = True )
61
-
62
-
63
38
def get_macos_version () -> Tuple [int , int ]:
64
39
"""
65
40
Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0)
@@ -75,13 +50,7 @@ def get_macos_version() -> Tuple[int, int]:
75
50
76
51
77
52
def get_macos_sdks () -> List [str ]:
78
- output = subprocess .run (
79
- ["xcodebuild" , "-showsdks" ],
80
- universal_newlines = True ,
81
- check = True ,
82
- stdout = subprocess .PIPE ,
83
- ).stdout
84
-
53
+ output = call ("xcodebuild" , "-showsdks" , text = True )
85
54
return [m .group (1 ) for m in re .finditer (r"-sdk (macosx\S+)" , output )]
86
55
87
56
@@ -111,9 +80,7 @@ def get_python_configurations(
111
80
112
81
113
82
def install_cpython (version : str , url : str ) -> Path :
114
- installed_system_packages = subprocess .run (
115
- ["pkgutil" , "--pkgs" ], universal_newlines = True , check = True , stdout = subprocess .PIPE
116
- ).stdout .splitlines ()
83
+ installed_system_packages = call ("pkgutil" , "--pkgs" , text = True ).splitlines ()
117
84
118
85
# if this version of python isn't installed, get it from python.org and install
119
86
python_package_identifier = f"org.python.Python.PythonFramework-{ version } "
@@ -134,8 +101,8 @@ def install_cpython(version: str, url: str) -> Path:
134
101
# download the pkg
135
102
download (url , Path ("/tmp/Python.pkg" ))
136
103
# install
137
- call ([ "sudo" , "installer" , "-pkg" , "/tmp/Python.pkg" , "-target" , "/" ] )
138
- call ([ "sudo" , str ( installation_path / "bin" / "python3" ), str ( install_certifi_script )] )
104
+ call ("sudo" , "installer" , "-pkg" , "/tmp/Python.pkg" , "-target" , "/" )
105
+ call ("sudo" , installation_path / "bin" / "python3" , install_certifi_script )
139
106
140
107
return installation_path
141
108
@@ -150,7 +117,7 @@ def install_pypy(version: str, url: str) -> Path:
150
117
downloaded_tar_bz2 = Path ("/tmp" ) / pypy_tar_bz2
151
118
download (url , downloaded_tar_bz2 )
152
119
installation_path .parent .mkdir (parents = True , exist_ok = True )
153
- call ([ "tar" , "-C" , installation_path .parent , "-xf" , downloaded_tar_bz2 ] )
120
+ call ("tar" , "-C" , installation_path .parent , "-xf" , downloaded_tar_bz2 )
154
121
155
122
return installation_path
156
123
@@ -196,20 +163,18 @@ def setup_python(
196
163
requires_reinstall = not (venv_bin_path / "pip" ).exists ()
197
164
if requires_reinstall :
198
165
# maybe pip isn't installed at all. ensurepip resolves that.
199
- call ([ "python" , "-m" , "ensurepip" ] , env = env , cwd = venv_path )
166
+ call ("python" , "-m" , "ensurepip" , env = env , cwd = venv_path )
200
167
201
168
# upgrade pip to the version matching our constraints
202
169
# if necessary, reinstall it to ensure that it's available on PATH as 'pip'
203
170
call (
204
- [
205
- "python" ,
206
- "-m" ,
207
- "pip" ,
208
- "install" ,
209
- "--force-reinstall" if requires_reinstall else "--upgrade" ,
210
- "pip" ,
211
- * dependency_constraint_flags ,
212
- ],
171
+ "python" ,
172
+ "-m" ,
173
+ "pip" ,
174
+ "install" ,
175
+ "--force-reinstall" if requires_reinstall else "--upgrade" ,
176
+ "pip" ,
177
+ * dependency_constraint_flags ,
213
178
env = env ,
214
179
cwd = venv_path ,
215
180
)
@@ -219,11 +184,9 @@ def setup_python(
219
184
220
185
# check what pip version we're on
221
186
assert (venv_bin_path / "pip" ).exists ()
222
- call (["which" , "pip" ], env = env )
223
- call (["pip" , "--version" ], env = env )
224
- which_pip = subprocess .run (
225
- ["which" , "pip" ], env = env , universal_newlines = True , check = True , stdout = subprocess .PIPE
226
- ).stdout .strip ()
187
+ call ("which" , "pip" , env = env )
188
+ call ("pip" , "--version" , env = env )
189
+ which_pip = call ("which" , "pip" , env = env , text = True ).strip ()
227
190
if which_pip != str (venv_bin_path / "pip" ):
228
191
print (
229
192
"cibuildwheel: pip available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert pip above it." ,
@@ -232,15 +195,9 @@ def setup_python(
232
195
sys .exit (1 )
233
196
234
197
# check what Python version we're on
235
- call (["which" , "python" ], env = env )
236
- call (["python" , "--version" ], env = env )
237
- which_python = subprocess .run (
238
- ["which" , "python" ],
239
- env = env ,
240
- universal_newlines = True ,
241
- check = True ,
242
- stdout = subprocess .PIPE ,
243
- ).stdout .strip ()
198
+ call ("which" , "python" , env = env )
199
+ call ("python" , "--version" , env = env )
200
+ which_python = call ("which" , "python" , env = env , text = True ).strip ()
244
201
if which_python != str (venv_bin_path / "python" ):
245
202
print (
246
203
"cibuildwheel: python available on PATH doesn't match our installed instance. If you have modified PATH, ensure that you don't overwrite cibuildwheel's entry or insert python above it." ,
@@ -295,27 +252,23 @@ def setup_python(
295
252
log .step ("Installing build tools..." )
296
253
if build_frontend == "pip" :
297
254
call (
298
- [
299
- "pip" ,
300
- "install" ,
301
- "--upgrade" ,
302
- "setuptools" ,
303
- "wheel" ,
304
- "delocate" ,
305
- * dependency_constraint_flags ,
306
- ],
255
+ "pip" ,
256
+ "install" ,
257
+ "--upgrade" ,
258
+ "setuptools" ,
259
+ "wheel" ,
260
+ "delocate" ,
261
+ * dependency_constraint_flags ,
307
262
env = env ,
308
263
)
309
264
elif build_frontend == "build" :
310
265
call (
311
- [
312
- "pip" ,
313
- "install" ,
314
- "--upgrade" ,
315
- "delocate" ,
316
- "build[virtualenv]" ,
317
- * dependency_constraint_flags ,
318
- ],
266
+ "pip" ,
267
+ "install" ,
268
+ "--upgrade" ,
269
+ "delocate" ,
270
+ "build[virtualenv]" ,
271
+ * dependency_constraint_flags ,
319
272
env = env ,
320
273
)
321
274
else :
@@ -344,7 +297,7 @@ def build(options: Options) -> None:
344
297
before_all_prepared = prepare_command (
345
298
before_all_options .before_all , project = "." , package = before_all_options .package_dir
346
299
)
347
- call ([ before_all_prepared ], shell = True , env = env )
300
+ shell ( before_all_prepared , env = env )
348
301
349
302
for config in python_configurations :
350
303
build_options = options .build_options (config .identifier )
@@ -372,7 +325,7 @@ def build(options: Options) -> None:
372
325
before_build_prepared = prepare_command (
373
326
build_options .before_build , project = "." , package = build_options .package_dir
374
327
)
375
- call (before_build_prepared , env = env , shell = True )
328
+ shell (before_build_prepared , env = env )
376
329
377
330
log .step ("Building wheel..." )
378
331
if built_wheel_dir .exists ():
@@ -385,16 +338,14 @@ def build(options: Options) -> None:
385
338
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
386
339
# see https://github.com/pypa/cibuildwheel/pull/369
387
340
call (
388
- [
389
- "python" ,
390
- "-m" ,
391
- "pip" ,
392
- "wheel" ,
393
- build_options .package_dir .resolve (),
394
- f"--wheel-dir={ built_wheel_dir } " ,
395
- "--no-deps" ,
396
- * verbosity_flags ,
397
- ],
341
+ "python" ,
342
+ "-m" ,
343
+ "pip" ,
344
+ "wheel" ,
345
+ build_options .package_dir .resolve (),
346
+ f"--wheel-dir={ built_wheel_dir } " ,
347
+ "--no-deps" ,
348
+ * verbosity_flags ,
398
349
env = env ,
399
350
)
400
351
elif build_options .build_frontend == "build" :
@@ -409,15 +360,13 @@ def build(options: Options) -> None:
409
360
build_env ["PIP_CONSTRAINT" ] = constraint_path .as_uri ()
410
361
build_env ["VIRTUALENV_PIP" ] = get_pip_version (env )
411
362
call (
412
- [
413
- "python" ,
414
- "-m" ,
415
- "build" ,
416
- build_options .package_dir ,
417
- "--wheel" ,
418
- f"--outdir={ built_wheel_dir } " ,
419
- f"--config-setting={ config_setting } " ,
420
- ],
363
+ "python" ,
364
+ "-m" ,
365
+ "build" ,
366
+ build_options .package_dir ,
367
+ "--wheel" ,
368
+ f"--outdir={ built_wheel_dir } " ,
369
+ f"--config-setting={ config_setting } " ,
421
370
env = build_env ,
422
371
)
423
372
else :
@@ -448,7 +397,7 @@ def build(options: Options) -> None:
448
397
dest_dir = repaired_wheel_dir ,
449
398
delocate_archs = delocate_archs ,
450
399
)
451
- call (repair_command_prepared , env = env , shell = True )
400
+ shell (repair_command_prepared , env = env )
452
401
else :
453
402
shutil .move (str (built_wheel ), repaired_wheel_dir )
454
403
@@ -513,9 +462,7 @@ def build(options: Options) -> None:
513
462
514
463
# set up a virtual environment to install and test from, to make sure
515
464
# there are no dependencies that were pulled in at build time.
516
- call (
517
- ["pip" , "install" , "virtualenv" , * dependency_constraint_flags ], env = env
518
- )
465
+ call ("pip" , "install" , "virtualenv" , * dependency_constraint_flags , env = env )
519
466
venv_dir = Path (tempfile .mkdtemp ())
520
467
521
468
arch_prefix = []
@@ -529,17 +476,17 @@ def build(options: Options) -> None:
529
476
)
530
477
531
478
# define a custom 'call' function that adds the arch prefix each time
532
- def call_with_arch (args : Sequence [ PathOrStr ] , ** kwargs : Any ) -> None :
533
- if isinstance ( args , str ):
534
- args = " " . join ( arch_prefix ) + " " + args
535
- else :
536
- args = [ * arch_prefix , * args ]
537
- call ( args , ** kwargs )
479
+ def call_with_arch (* args : PathOrStr , ** kwargs : Any ) -> None :
480
+ call ( * arch_prefix , * args , ** kwargs )
481
+
482
+ def shell_with_arch ( command : str , ** kwargs : Any ) -> None :
483
+ command = " " . join ( arch_prefix ) + " " + command
484
+ shell ( command , ** kwargs )
538
485
539
486
# Use --no-download to ensure determinism by using seed libraries
540
487
# built into virtualenv
541
488
call_with_arch (
542
- [ "python" , "-m" , "virtualenv" , "--no-download" , venv_dir ] , env = env
489
+ "python" , "-m" , "virtualenv" , "--no-download" , venv_dir , env = env
543
490
)
544
491
545
492
virtualenv_env = env .copy ()
@@ -551,26 +498,28 @@ def call_with_arch(args: Sequence[PathOrStr], **kwargs: Any) -> None:
551
498
)
552
499
553
500
# check that we are using the Python from the virtual environment
554
- call_with_arch ([ "which" , "python" ] , env = virtualenv_env )
501
+ call_with_arch ("which" , "python" , env = virtualenv_env )
555
502
556
503
if build_options .before_test :
557
504
before_test_prepared = prepare_command (
558
505
build_options .before_test ,
559
506
project = "." ,
560
507
package = build_options .package_dir ,
561
508
)
562
- call_with_arch (before_test_prepared , env = virtualenv_env , shell = True )
509
+ shell_with_arch (before_test_prepared , env = virtualenv_env )
563
510
564
511
# install the wheel
565
512
call_with_arch (
566
- ["pip" , "install" , f"{ repaired_wheel } { build_options .test_extras } " ],
513
+ "pip" ,
514
+ "install" ,
515
+ f"{ repaired_wheel } { build_options .test_extras } " ,
567
516
env = virtualenv_env ,
568
517
)
569
518
570
519
# test the wheel
571
520
if build_options .test_requires :
572
521
call_with_arch (
573
- [ "pip" , "install" ] + build_options .test_requires , env = virtualenv_env
522
+ "pip" , "install" , * build_options .test_requires , env = virtualenv_env
574
523
)
575
524
576
525
# run the tests from $HOME, with an absolute path in the command
@@ -581,11 +530,10 @@ def call_with_arch(args: Sequence[PathOrStr], **kwargs: Any) -> None:
581
530
project = Path ("." ).resolve (),
582
531
package = build_options .package_dir .resolve (),
583
532
)
584
- call_with_arch (
533
+ shell_with_arch (
585
534
test_command_prepared ,
586
535
cwd = os .environ ["HOME" ],
587
536
env = virtualenv_env ,
588
- shell = True ,
589
537
)
590
538
591
539
# clean up
0 commit comments