2022-07-14 13:36:57 +02:00
from __future__ import annotations
2019-11-12 23:51:27 +00:00
import subprocess
import sys
import textwrap
2023-04-18 13:06:17 -04:00
from collections.abc import Iterable , Iterator , Sequence , Set
2022-04-28 09:19:27 -04:00
from dataclasses import dataclass
2022-05-24 17:35:46 -06:00
from pathlib import Path , PurePath , PurePosixPath
2023-06-14 00:02:10 +02:00
from typing import OrderedDict , Tuple
2017-03-19 21:27:29 +00:00
2024-05-27 23:04:25 +02:00
from packaging.version import Version
2024-06-10 15:36:20 +01:00
from . import errors
2023-06-14 00:02:10 +02:00
from ._compat.typing import assert_never
2021-01-22 09:33:22 -05:00
from .architecture import Architecture
2020-11-13 16:30:27 +00:00
from .logger import log
2024-05-20 07:47:10 +01:00
from .oci_container import OCIContainer , OCIContainerEngineConfig
from .options import BuildOptions , Options
2023-04-18 23:05:34 -04:00
from .typing import PathOrStr
2021-01-06 13:50:58 -05:00
from .util import (
2023-08-26 19:37:29 +01:00
BuildFrontendConfig ,
2021-01-06 13:50:58 -05:00
BuildSelector ,
2022-07-03 20:00:00 +02:00
find_compatible_wheel ,
2021-01-06 13:50:58 -05:00
get_build_verbosity_extra_flags ,
prepare_command ,
2021-01-09 15:40:40 -05:00
read_python_configs ,
2022-09-06 00:56:20 -04:00
split_config_settings ,
2022-11-06 00:01:30 +00:00
test_fail_cwd_file ,
2022-06-27 17:32:40 +01:00
unwrap ,
2021-01-06 13:50:58 -05:00
)
2020-02-21 06:42:19 -05:00
2022-04-28 09:19:27 -04:00
@dataclass ( frozen = True )
class PythonConfiguration :
2020-04-10 01:53:06 +02:00
version : str
identifier : str
2020-06-23 19:46:23 +01:00
path_str : str
@property
2022-05-24 17:35:46 -06:00
def path ( self ) -> PurePosixPath :
return PurePosixPath ( self . path_str )
2020-04-08 00:16:25 +02:00
2022-04-28 09:19:27 -04:00
@dataclass ( frozen = True )
class BuildStep :
2022-07-14 13:36:57 +02:00
platform_configs : list [ PythonConfiguration ]
2021-09-21 11:34:20 -04:00
platform_tag : str
2024-05-20 07:47:10 +01:00
container_engine : OCIContainerEngineConfig
2022-06-27 17:46:22 +01:00
container_image : str
2021-09-21 11:34:20 -04:00
2020-12-16 23:17:51 +00:00
def get_python_configurations (
2021-01-09 15:40:40 -05:00
build_selector : BuildSelector ,
2023-04-06 11:40:25 -07:00
architectures : Set [ Architecture ],
2022-07-14 13:36:57 +02:00
) -> list [ PythonConfiguration ]:
2021-05-03 11:45:43 -04:00
full_python_configs = read_python_configs ( "linux" )
2021-01-09 15:40:40 -05:00
python_configurations = [ PythonConfiguration ( ** item ) for item in full_python_configs ]
2020-12-12 21:23:27 +00:00
2021-01-01 16:53:45 -05:00
# return all configurations whose arch is in our `architectures` set,
2020-12-28 00:43:24 +00:00
# and match the build/skip rules
2020-12-12 21:23:27 +00:00
return [
2021-04-30 17:56:34 -04:00
c
for c in python_configurations
2020-12-31 16:35:26 +00:00
if any ( c . identifier . endswith ( arch . value ) for arch in architectures )
2020-12-12 21:23:27 +00:00
and build_selector ( c . identifier )
]
2017-04-11 22:57:42 +01:00
2019-04-20 18:52:01 +02:00
2024-05-20 07:47:10 +01:00
def container_image_for_python_configuration (
config : PythonConfiguration , build_options : BuildOptions
) -> str :
2021-10-12 02:05:47 +01:00
# e.g
# identifier is 'cp310-manylinux_x86_64'
# platform_tag is 'manylinux_x86_64'
# platform_arch is 'x86_64'
_ , platform_tag = config . identifier . split ( "-" , 1 )
_ , platform_arch = platform_tag . split ( "_" , 1 )
assert build_options . manylinux_images is not None
assert build_options . musllinux_images is not None
return (
build_options . manylinux_images [ platform_arch ]
if platform_tag . startswith ( "manylinux" )
else build_options . musllinux_images [ platform_arch ]
)
2021-09-23 15:27:58 -04:00
def get_build_steps (
2022-07-14 13:36:57 +02:00
options : Options , python_configurations : list [ PythonConfiguration ]
2021-09-22 10:51:38 -04:00
) -> Iterator [ BuildStep ]:
2021-10-12 02:05:47 +01:00
"""
Groups PythonConfigurations into BuildSteps. Each BuildStep represents a
2022-06-27 18:30:49 +01:00
separate container instance.
2021-10-12 02:05:47 +01:00
"""
2024-05-20 07:47:10 +01:00
steps = OrderedDict [ Tuple [ str , str , str , OCIContainerEngineConfig ], BuildStep ]()
2021-09-20 12:17:42 -04:00
2021-10-12 02:05:47 +01:00
for config in python_configurations :
_ , platform_tag = config . identifier . split ( "-" , 1 )
2021-09-20 12:17:42 -04:00
2024-05-20 07:47:10 +01:00
build_options = options . build_options ( config . identifier )
2021-10-12 02:05:47 +01:00
2024-05-20 07:47:10 +01:00
before_all = build_options . before_all
container_image = container_image_for_python_configuration ( config , build_options )
container_engine = build_options . container_engine
step_key = ( platform_tag , container_image , before_all , container_engine )
2021-10-12 02:05:47 +01:00
if step_key in steps :
steps [ step_key ] . platform_configs . append ( config )
else :
steps [ step_key ] = BuildStep (
2022-06-27 17:46:22 +01:00
platform_configs = [ config ],
platform_tag = platform_tag ,
2024-05-20 07:47:10 +01:00
container_engine = container_engine ,
2022-06-27 17:46:22 +01:00
container_image = container_image ,
2021-10-12 02:05:47 +01:00
)
yield from steps . values ()
2021-09-20 12:17:42 -04:00
2022-10-09 16:44:14 +02:00
def check_all_python_exist (
2023-04-18 13:06:17 -04:00
* , platform_configs : Iterable [ PythonConfiguration ], container : OCIContainer
2022-10-09 16:44:14 +02:00
) -> None :
exist = True
2023-10-01 13:51:21 +02:00
has_manylinux_interpreters = True
2022-10-09 16:44:14 +02:00
messages = []
2023-10-01 13:51:21 +02:00
try :
# use capture_output to keep quiet
container . call ([ "manylinux-interpreters" , "--help" ], capture_output = True )
except subprocess . CalledProcessError :
has_manylinux_interpreters = False
2022-10-09 16:44:14 +02:00
for config in platform_configs :
python_path = config . path / "bin" / "python"
try :
2023-10-01 13:51:21 +02:00
if has_manylinux_interpreters :
container . call ([ "manylinux-interpreters" , "ensure" , config . path . name ])
2022-10-09 16:44:14 +02:00
container . call ([ "test" , "-x" , python_path ])
except subprocess . CalledProcessError :
messages . append (
f " ' { python_path } ' executable doesn't exist in image ' { container . image } ' to build ' { config . identifier } '."
)
exist = False
if not exist :
message = " \n " . join ( messages )
2024-06-10 15:36:20 +01:00
raise errors . FatalError ( message )
2022-10-09 16:44:14 +02:00
2022-06-27 18:30:49 +01:00
def build_in_container (
2021-10-12 02:05:47 +01:00
* ,
options : Options ,
2023-04-18 13:06:17 -04:00
platform_configs : Sequence [ PythonConfiguration ],
2022-06-27 18:30:49 +01:00
container : OCIContainer ,
2021-09-20 12:28:05 -04:00
container_project_path : PurePath ,
container_package_dir : PurePath ,
) -> None :
2022-05-24 17:35:46 -06:00
container_output_dir = PurePosixPath ( "/output" )
2021-09-20 12:28:05 -04:00
2022-10-09 16:44:14 +02:00
check_all_python_exist ( platform_configs = platform_configs , container = container )
2022-06-27 18:30:49 +01:00
log . step ( "Copying project into container..." )
container . copy_into ( Path . cwd (), container_project_path )
2021-09-20 12:28:05 -04:00
2021-10-12 02:05:47 +01:00
before_all_options_identifier = platform_configs [ 0 ] . identifier
before_all_options = options . build_options ( before_all_options_identifier )
if before_all_options . before_all :
2021-09-20 12:28:05 -04:00
log . step ( "Running before_all..." )
2022-06-27 18:30:49 +01:00
env = container . get_environment ()
2021-09-20 12:28:05 -04:00
env [ "PATH" ] = f '/opt/python/cp38-cp38/bin: { env [ "PATH" ] } '
env [ "PIP_DISABLE_PIP_VERSION_CHECK" ] = "1"
2022-10-11 10:06:35 -04:00
env [ "PIP_ROOT_USER_ACTION" ] = "ignore"
2021-10-12 02:05:47 +01:00
env = before_all_options . environment . as_dictionary (
2022-06-27 18:30:49 +01:00
env , executor = container . environment_executor
2021-10-12 02:05:47 +01:00
)
2021-09-20 12:28:05 -04:00
before_all_prepared = prepare_command (
2021-10-12 02:05:47 +01:00
before_all_options . before_all ,
2021-09-20 12:28:05 -04:00
project = container_project_path ,
package = container_package_dir ,
)
2022-06-27 18:30:49 +01:00
container . call ([ "sh" , "-c" , before_all_prepared ], env = env )
2021-09-20 12:28:05 -04:00
2022-07-14 13:36:57 +02:00
built_wheels : list [ PurePosixPath ] = []
2022-04-18 14:24:53 +02:00
2021-09-20 12:28:05 -04:00
for config in platform_configs :
log . build_start ( config . identifier )
2021-10-12 02:05:47 +01:00
build_options = options . build_options ( config . identifier )
2023-08-26 19:37:29 +01:00
build_frontend = build_options . build_frontend or BuildFrontendConfig ( "pip" )
2024-06-09 15:45:31 -04:00
use_uv = build_frontend . name == "build[uv]" and Version ( config . version ) >= Version ( "3.8" )
pip = [ "uv" , "pip" ] if use_uv else [ "pip" ]
2021-09-20 12:28:05 -04:00
2022-07-14 13:36:57 +02:00
dependency_constraint_flags : list [ PathOrStr ] = []
2021-09-20 12:28:05 -04:00
2021-10-12 02:05:47 +01:00
if build_options . dependency_constraints :
constraints_file = build_options . dependency_constraints . get_for_python_version (
config . version
)
2023-08-14 08:30:33 +02:00
container_constraints_file = PurePosixPath ( "/constraints.txt" )
2021-09-20 12:28:05 -04:00
2022-06-27 18:30:49 +01:00
container . copy_into ( constraints_file , container_constraints_file )
2021-09-20 12:28:05 -04:00
dependency_constraint_flags = [ "-c" , container_constraints_file ]
log . step ( "Setting up build environment..." )
2022-06-27 18:30:49 +01:00
env = container . get_environment ()
2022-10-13 22:29:52 -04:00
env [ "PIP_DISABLE_PIP_VERSION_CHECK" ] = "1"
env [ "PIP_ROOT_USER_ACTION" ] = "ignore"
2021-09-20 12:28:05 -04:00
# put this config's python top of the list
python_bin = config . path / "bin"
env [ "PATH" ] = f ' { python_bin } : { env [ "PATH" ] } '
2022-06-27 18:30:49 +01:00
env = build_options . environment . as_dictionary ( env , executor = container . environment_executor )
2021-09-20 12:28:05 -04:00
# check config python is still on PATH
2022-06-27 18:30:49 +01:00
which_python = container . call ([ "which" , "python" ], env = env , capture_output = True ) . strip ()
2022-05-24 17:35:46 -06:00
if PurePosixPath ( which_python ) != python_bin / "python" :
2024-06-10 15:36:20 +01:00
msg = "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."
raise errors . FatalError ( msg )
2021-09-20 12:28:05 -04:00
2024-06-09 15:45:31 -04:00
if use_uv :
which_uv = container . call ([ "which" , "uv" ], env = env , capture_output = True ) . strip ()
if not which_uv :
2024-06-10 15:36:20 +01:00
msg = "uv not found on PATH. You must use a supported manylinux or musllinux environment with uv."
raise errors . FatalError ( msg )
2024-06-09 15:45:31 -04:00
else :
which_pip = container . call ([ "which" , "pip" ], env = env , capture_output = True ) . strip ()
if PurePosixPath ( which_pip ) != python_bin / "pip" :
2024-06-10 15:36:20 +01:00
msg = "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."
raise errors . FatalError ( msg )
2021-09-20 12:28:05 -04:00
2022-07-03 20:00:00 +02:00
compatible_wheel = find_compatible_wheel ( built_wheels , config . identifier )
if compatible_wheel :
2022-04-18 14:24:53 +02:00
log . step_end ()
print (
2022-07-03 20:00:00 +02:00
f " \n Found previously built wheel { compatible_wheel . name } , that's compatible with { config . identifier } . Skipping build step..."
2021-09-20 12:28:05 -04:00
)
2022-07-03 20:00:00 +02:00
repaired_wheels = [ compatible_wheel ]
2021-09-20 12:28:05 -04:00
else :
2022-04-18 14:24:53 +02:00
if build_options . before_build :
log . step ( "Running before_build..." )
before_build_prepared = prepare_command (
build_options . before_build ,
project = container_project_path ,
package = container_package_dir ,
)
2022-06-27 18:30:49 +01:00
container . call ([ "sh" , "-c" , before_build_prepared ], env = env )
2021-09-20 12:28:05 -04:00
2022-04-18 14:24:53 +02:00
log . step ( "Building wheel..." )
2021-09-20 12:28:05 -04:00
2022-05-24 17:35:46 -06:00
temp_dir = PurePosixPath ( "/tmp/cibuildwheel" )
2022-04-18 14:24:53 +02:00
built_wheel_dir = temp_dir / "built_wheel"
2022-06-27 18:30:49 +01:00
container . call ([ "rm" , "-rf" , built_wheel_dir ])
container . call ([ "mkdir" , "-p" , built_wheel_dir ])
2021-09-20 12:28:05 -04:00
2023-08-26 19:37:29 +01:00
extra_flags = split_config_settings ( build_options . config_settings , build_frontend . name )
extra_flags += build_frontend . args
2021-09-20 12:28:05 -04:00
2023-08-26 19:37:29 +01:00
if build_frontend . name == "pip" :
2023-04-17 11:06:45 -07:00
extra_flags += get_build_verbosity_extra_flags ( build_options . build_verbosity )
2022-06-27 18:30:49 +01:00
container . call (
2022-04-18 14:24:53 +02:00
[
"python" ,
"-m" ,
"pip" ,
"wheel" ,
container_package_dir ,
f "--wheel-dir= { built_wheel_dir } " ,
"--no-deps" ,
2022-09-06 00:56:20 -04:00
* extra_flags ,
2022-04-18 14:24:53 +02:00
],
env = env ,
)
2024-06-09 15:45:31 -04:00
elif build_frontend . name == "build" or build_frontend . name == "build[uv]" :
2023-04-17 11:06:45 -07:00
if not 0 <= build_options . build_verbosity < 2 :
msg = f "build_verbosity { build_options . build_verbosity } is not supported for build frontend. Ignoring."
log . warning ( msg )
2024-07-01 20:15:09 -04:00
if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags :
2024-06-09 15:45:31 -04:00
extra_flags += [ "--installer=uv" ]
2022-06-27 18:30:49 +01:00
container . call (
2022-04-18 14:24:53 +02:00
[
"python" ,
"-m" ,
"build" ,
container_package_dir ,
"--wheel" ,
f "--outdir= { built_wheel_dir } " ,
2022-09-06 00:56:20 -04:00
* extra_flags ,
2022-04-18 14:24:53 +02:00
],
env = env ,
)
else :
2023-04-07 15:51:12 -07:00
assert_never ( build_frontend )
2022-04-18 14:24:53 +02:00
2022-06-27 18:30:49 +01:00
built_wheel = container . glob ( built_wheel_dir , "*.whl" )[ 0 ]
2022-04-18 14:24:53 +02:00
repaired_wheel_dir = temp_dir / "repaired_wheel"
2022-06-27 18:30:49 +01:00
container . call ([ "rm" , "-rf" , repaired_wheel_dir ])
container . call ([ "mkdir" , "-p" , repaired_wheel_dir ])
2022-04-18 14:24:53 +02:00
if built_wheel . name . endswith ( "none-any.whl" ):
2024-07-07 09:47:03 +02:00
raise errors . NonPlatformWheelError ()
2022-04-18 14:24:53 +02:00
if build_options . repair_command :
log . step ( "Repairing wheel..." )
repair_command_prepared = prepare_command (
build_options . repair_command , wheel = built_wheel , dest_dir = repaired_wheel_dir
)
2022-06-27 18:30:49 +01:00
container . call ([ "sh" , "-c" , repair_command_prepared ], env = env )
2022-04-18 14:24:53 +02:00
else :
2022-06-27 18:30:49 +01:00
container . call ([ "mv" , built_wheel , repaired_wheel_dir ])
2022-04-18 14:24:53 +02:00
2022-06-27 18:30:49 +01:00
repaired_wheels = container . glob ( repaired_wheel_dir , "*.whl" )
2021-09-20 12:28:05 -04:00
2022-06-19 18:03:05 +02:00
for repaired_wheel in repaired_wheels :
if repaired_wheel . name in { wheel . name for wheel in built_wheels }:
2024-07-07 09:47:03 +02:00
raise errors . AlreadyBuiltWheelError ( repaired_wheel . name )
2022-06-19 18:03:05 +02:00
2021-10-12 02:05:47 +01:00
if build_options . test_command and build_options . test_selector ( config . identifier ):
2021-09-20 12:28:05 -04:00
log . step ( "Testing wheel..." )
# set up a virtual environment to install and test from, to make sure
# there are no dependencies that were pulled in at build time.
2024-06-09 15:45:31 -04:00
if not use_uv :
container . call (
[ "pip" , "install" , "virtualenv" , * dependency_constraint_flags ], env = env
)
2022-11-06 00:01:30 +00:00
testing_temp_dir = PurePosixPath (
container . call ([ "mktemp" , "-d" ], capture_output = True ) . strip ()
2022-06-27 18:30:49 +01:00
)
2022-11-06 00:01:30 +00:00
venv_dir = testing_temp_dir / "venv"
2021-09-20 12:28:05 -04:00
2024-06-09 15:45:31 -04:00
if use_uv :
container . call ([ "uv" , "venv" , venv_dir ], env = env )
else :
# Use embedded dependencies from virtualenv to ensure determinism
venv_args = [ "--no-periodic-update" , "--pip=embed" ]
# In Python<3.12, setuptools & wheel are installed as well
if Version ( config . version ) < Version ( "3.12" ):
venv_args . extend (( "--setuptools=embed" , "--wheel=embed" ))
container . call ([ "python" , "-m" , "virtualenv" , * venv_args , venv_dir ], env = env )
2021-09-20 12:28:05 -04:00
virtualenv_env = env . copy ()
virtualenv_env [ "PATH" ] = f " { venv_dir / 'bin' } : { virtualenv_env [ 'PATH' ] } "
2024-05-29 02:20:02 -04:00
virtualenv_env [ "VIRTUAL_ENV" ] = str ( venv_dir )
2021-09-20 12:28:05 -04:00
2021-10-12 02:05:47 +01:00
if build_options . before_test :
2021-09-20 12:28:05 -04:00
before_test_prepared = prepare_command (
2021-10-12 02:05:47 +01:00
build_options . before_test ,
2021-09-20 12:28:05 -04:00
project = container_project_path ,
package = container_package_dir ,
)
2022-06-27 18:30:49 +01:00
container . call ([ "sh" , "-c" , before_test_prepared ], env = virtualenv_env )
2021-09-20 12:28:05 -04:00
# Install the wheel we just built
# Note: If auditwheel produced two wheels, it's because the earlier produced wheel
# conforms to multiple manylinux standards. These multiple versions of the wheel are
# functionally the same, differing only in name, wheel metadata, and possibly include
# different external shared libraries. so it doesn't matter which one we run the tests on.
# Let's just pick the first one.
wheel_to_test = repaired_wheels [ 0 ]
2022-06-27 18:30:49 +01:00
container . call (
2024-06-09 15:45:31 -04:00
[ * pip , "install" , str ( wheel_to_test ) + build_options . test_extras ],
2021-09-20 12:28:05 -04:00
env = virtualenv_env ,
)
# Install any requirements to run the tests
2021-10-12 02:05:47 +01:00
if build_options . test_requires :
2024-06-09 15:45:31 -04:00
container . call ([ * pip , "install" , * build_options . test_requires ], env = virtualenv_env )
2021-09-20 12:28:05 -04:00
# Run the tests from a different directory
test_command_prepared = prepare_command (
2021-10-12 02:05:47 +01:00
build_options . test_command ,
2021-09-20 12:28:05 -04:00
project = container_project_path ,
package = container_package_dir ,
2023-06-23 08:29:58 -04:00
wheel = wheel_to_test ,
2021-09-20 12:28:05 -04:00
)
2022-11-06 00:01:30 +00:00
test_cwd = testing_temp_dir / "test_cwd"
container . call ([ "mkdir" , "-p" , test_cwd ])
container . copy_into ( test_fail_cwd_file , test_cwd / "test_fail.py" )
container . call ([ "sh" , "-c" , test_command_prepared ], cwd = test_cwd , env = virtualenv_env )
2021-09-20 12:28:05 -04:00
# clean up test environment
2022-11-06 00:01:30 +00:00
container . call ([ "rm" , "-rf" , testing_temp_dir ])
2021-09-20 12:28:05 -04:00
# move repaired wheels to output
2022-07-03 20:00:00 +02:00
if compatible_wheel is None :
2022-06-27 18:30:49 +01:00
container . call ([ "mkdir" , "-p" , container_output_dir ])
container . call ([ "mv" , * repaired_wheels , container_output_dir ])
2022-04-18 14:24:53 +02:00
built_wheels . extend (
container_output_dir / repaired_wheel . name for repaired_wheel in repaired_wheels
)
2021-09-20 12:28:05 -04:00
log . build_end ()
log . step ( "Copying wheels back to host..." )
# copy the output back into the host
2022-06-27 18:30:49 +01:00
container . copy_out ( container_output_dir , options . globals . output_dir )
2021-09-20 12:28:05 -04:00
log . step_end ()
2023-01-30 15:53:44 -05:00
def build ( options : Options , tmp_path : Path ) -> None : # noqa: ARG001
2021-09-19 00:19:28 -04:00
python_configurations = get_python_configurations (
2021-10-12 02:05:47 +01:00
options . globals . build_selector , options . globals . architectures
2021-09-19 00:19:28 -04:00
)
2017-04-11 22:57:42 +01:00
2020-06-17 00:18:40 +02:00
cwd = Path . cwd ()
2021-10-12 02:05:47 +01:00
abs_package_dir = options . globals . package_dir . resolve ()
2020-06-17 00:18:40 +02:00
if cwd != abs_package_dir and cwd not in abs_package_dir . parents :
2022-09-05 13:11:46 -04:00
msg = "package_dir must be inside the working directory"
raise Exception ( msg )
2020-03-13 13:45:14 +01:00
2022-05-24 17:35:46 -06:00
container_project_path = PurePosixPath ( "/project" )
2020-07-23 20:24:19 +02:00
container_package_dir = container_project_path / abs_package_dir . relative_to ( cwd )
2020-03-13 13:45:14 +01:00
2021-10-12 02:05:47 +01:00
for build_step in get_build_steps ( options , python_configurations ):
2024-05-20 07:47:10 +01:00
try :
# check the container engine is installed
subprocess . run (
[ build_step . container_engine . name , "--version" ],
check = True ,
stdout = subprocess . DEVNULL ,
)
2024-06-10 15:36:20 +01:00
except subprocess . CalledProcessError as error :
msg = unwrap (
f """
cibuildwheel: { build_step . container_engine . name } not found. An
OCI exe like Docker or Podman is required to run Linux builds.
If you're building on Travis CI, add `services: [docker]` to
your .travis.yml. If you're building on Circle CI in Linux,
add a `setup_remote_docker` step to your .circleci/config.yml.
If you're building on Cirrus CI, use `docker_builder` task.
"""
2024-05-20 07:47:10 +01:00
)
2024-06-10 15:36:20 +01:00
raise errors . ConfigurationError ( msg ) from error
2024-05-20 07:47:10 +01:00
2018-09-09 16:11:42 +02:00
try :
2021-10-12 02:05:47 +01:00
ids_to_build = [ x . identifier for x in build_step . platform_configs ]
2022-06-27 18:30:49 +01:00
log . step ( f "Starting container image { build_step . container_image } ..." )
print ( f "info: This container will host the build for { ', ' . join ( ids_to_build ) } ..." )
2021-09-21 11:34:20 -04:00
2022-06-27 17:46:22 +01:00
with OCIContainer (
image = build_step . container_image ,
2023-09-07 19:05:58 +02:00
enforce_32_bit = build_step . platform_tag . endswith ( "i686" ),
2021-04-30 17:56:34 -04:00
cwd = container_project_path ,
2024-05-20 07:47:10 +01:00
engine = build_step . container_engine ,
2022-06-27 18:30:49 +01:00
) as container :
build_in_container (
2021-10-12 02:05:47 +01:00
options = options ,
platform_configs = build_step . platform_configs ,
2022-06-27 18:30:49 +01:00
container = container ,
2021-10-12 02:05:47 +01:00
container_project_path = container_project_path ,
container_package_dir = container_package_dir ,
2021-09-20 12:28:05 -04:00
)
2020-11-01 11:43:28 +00:00
2020-04-10 14:18:09 +01:00
except subprocess . CalledProcessError as error :
2021-10-12 02:05:47 +01:00
troubleshoot ( options , error )
2024-06-10 15:36:20 +01:00
msg = f "Command { error . cmd } failed with code { error . returncode } . { error . stdout or '' } "
raise errors . FatalError ( msg ) from error
2020-02-02 16:03:33 +00:00
2021-08-28 00:44:26 +00:00
2023-04-18 13:06:17 -04:00
def _matches_prepared_command ( error_cmd : Sequence [ str ], command_template : str ) -> bool :
2021-08-26 15:23:57 -07:00
if len ( error_cmd ) < 3 or error_cmd [ 0 : 2 ] != [ "sh" , "-c" ]:
return False
command_prefix = command_template . split ( "{" , maxsplit = 1 )[ 0 ] . strip ()
return error_cmd [ 2 ] . startswith ( command_prefix )
2021-10-12 02:05:47 +01:00
def troubleshoot ( options : Options , error : Exception ) -> None :
2021-06-23 10:47:18 -04:00
if isinstance ( error , subprocess . CalledProcessError ) and (
error . cmd [ 0 : 4 ] == [ "python" , "-m" , "pip" , "wheel" ]
or error . cmd [ 0 : 3 ] == [ "python" , "-m" , "build" ]
2021-09-19 00:19:28 -04:00
or _matches_prepared_command (
2021-10-12 02:05:47 +01:00
error . cmd , options . build_options ( None ) . repair_command
) # TODO allow matching of overrides too?
2021-06-23 10:47:18 -04:00
):
2021-10-12 02:05:47 +01:00
# the wheel build step or the repair step failed
so_files = list ( options . globals . package_dir . glob ( "**/*.so" ))
2020-02-02 14:33:36 +00:00
if so_files :
2021-04-30 17:56:34 -04:00
print (
textwrap . dedent (
2021-05-03 11:45:43 -04:00
"""
2021-05-02 16:37:38 +02:00
NOTE: Shared object (.so) files found in this project.
2020-02-02 14:33:36 +00:00
2021-05-02 16:37:38 +02:00
These files might be built against the wrong OS, causing problems with
2021-08-26 15:23:57 -07:00
auditwheel. If possible, run cibuildwheel in a clean checkout.
2020-02-02 14:33:36 +00:00
2021-05-02 16:37:38 +02:00
If you're using Cython and have previously done an in-place build,
remove those build files (*.so and *.c) before starting cibuildwheel.
2021-08-26 15:23:57 -07:00
setuptools uses the build/ folder to store its build cache. It
may be necessary to remove those build files (*.so and *.o) before
starting cibuildwheel.
Files that belong to a virtual environment are probably not an issue
unless you used a custom command telling cibuildwheel to activate it.
2021-05-03 11:45:43 -04:00
"""
2021-04-30 17:56:34 -04:00
),
file = sys . stderr ,
)
2020-02-02 14:33:36 +00:00
2021-05-03 11:45:43 -04:00
print ( " Files detected:" )
print ( " \n " . join ( f " { f } " for f in so_files ))
2022-10-07 08:47:31 -04:00
print ()