2017-03-19 20:57:51 +00:00
from __future__ import print_function
2018-04-08 19:11:38 +01:00
import argparse , os , subprocess , sys , textwrap
2017-03-19 20:57:51 +00:00
2017-04-13 15:02:04 +01:00
import cibuildwheel
import cibuildwheel.linux , cibuildwheel.windows , cibuildwheel.macos
2017-09-01 13:33:29 +01:00
from cibuildwheel.environment import parse_environment , EnvironmentParseError
2018-09-16 15:16:30 +02:00
from cibuildwheel.util import BuildSelector , Unbuffered
2017-03-19 20:57:51 +00:00
2018-07-05 09:58:05 +01:00
def get_option_from_environment ( option_name , platform = None , default = None ):
2017-04-10 21:42:12 +01:00
'''
Returns an option from the environment, optionally scoped by the platform.
Example:
get_option_from_environment('CIBW_COLOR', platform='macos')
This will return the value of CIBW_COLOR_MACOS if it exists, otherwise the value of
CIBW_COLOR.
'''
if platform :
option = os . environ . get ( ' %s _ %s ' % ( option_name , platform . upper ()))
if option is not None :
return option
2018-07-05 09:58:05 +01:00
return os . environ . get ( option_name , default )
2017-04-10 21:42:12 +01:00
2017-03-19 20:57:51 +00:00
def main ():
parser = argparse . ArgumentParser (
description = 'Build wheels for all the platforms.' ,
epilog = ( 'Most options are supplied via environment variables. '
'See https://github.com/joerick/cibuildwheel#options for info.' ))
parser . add_argument ( '--platform' ,
choices = [ 'auto' , 'linux' , 'macos' , 'windows' ],
2017-03-23 11:19:14 +00:00
default = os . environ . get ( 'CIBW_PLATFORM' , 'auto' ),
2017-03-19 20:57:51 +00:00
help = ( 'Platform to build for. For "linux" you need docker running, on Mac '
'or Linux. For "macos", you need a Mac machine, and note that this '
'script is going to automatically install MacPython on your system, '
'so don \' t run on your development machine. For "windows", you need to '
'run in Windows, and it will build and test for all versions of '
'Python at C: \\ PythonXX[-x64]. Default: auto.' ))
parser . add_argument ( '--output-dir' ,
default = os . environ . get ( 'CIBW_OUTPUT_DIR' , 'wheelhouse' ),
help = 'Destination folder for the wheels.' )
parser . add_argument ( 'project_dir' ,
default = '.' ,
nargs = '?' ,
help = ( 'Path to the project that you want wheels for. Default: the current '
'directory.' ))
2019-04-20 18:52:01 +02:00
parser . add_argument ( '--print-build-identifiers' ,
action = 'store_true' ,
help = 'Print the build identifiers matched by the current invocation and exit.' )
2017-03-19 20:57:51 +00:00
args = parser . parse_args ()
2019-08-30 18:31:02 +02:00
detect_obsolete_options ()
2017-04-10 21:42:12 +01:00
if args . platform != 'auto' :
platform = args . platform
else :
2018-09-05 04:15:32 -04:00
platform = None
2017-04-10 21:42:12 +01:00
if os . environ . get ( 'TRAVIS_OS_NAME' ) == 'linux' :
platform = 'linux'
elif os . environ . get ( 'TRAVIS_OS_NAME' ) == 'osx' :
platform = 'macos'
elif 'APPVEYOR' in os . environ :
platform = 'windows'
2017-09-10 22:02:36 +01:00
elif 'BITRISE_BUILD_NUMBER' in os . environ :
platform = 'macos'
2018-09-05 04:15:32 -04:00
elif os . environ . get ( 'CIRCLECI' ):
if sys . platform . startswith ( 'linux' ):
platform = 'linux'
elif sys . platform . startswith ( 'darwin' ):
platform = 'macos'
2019-03-10 23:07:14 +00:00
elif 'AZURE_HTTP_USER_AGENT' in os . environ :
if os . environ [ 'AGENT_OS' ] == 'Linux' :
platform = 'linux'
elif os . environ [ 'AGENT_OS' ] == 'Darwin' :
platform = 'macos'
2019-03-10 23:19:22 +00:00
elif os . environ [ 'AGENT_OS' ] == 'Windows_NT' :
2019-03-10 23:07:14 +00:00
platform = 'windows'
2018-09-05 04:15:32 -04:00
if platform is None :
2017-06-27 18:18:20 +01:00
print ( 'cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server, '
2019-06-10 20:27:32 +10:00
'Travis CI, AppVeyor, and CircleCI are supported. You can run on your development '
2017-04-10 21:42:12 +01:00
'machine using the --platform argument. Check --help output for more '
'information.' ,
file = sys . stderr )
exit ( 2 )
2017-03-19 20:57:51 +00:00
output_dir = args . output_dir
2018-07-05 09:58:05 +01:00
test_command = get_option_from_environment ( 'CIBW_TEST_COMMAND' , platform = platform )
test_requires = get_option_from_environment ( 'CIBW_TEST_REQUIRES' , platform = platform , default = '' ) . split ()
2019-09-07 17:17:21 +01:00
test_extras = get_option_from_environment ( 'CIBW_TEST_EXTRAS' , platform = platform , default = '' )
2017-03-19 20:57:51 +00:00
project_dir = args . project_dir
2017-04-10 21:42:12 +01:00
before_build = get_option_from_environment ( 'CIBW_BEFORE_BUILD' , platform = platform )
2018-07-05 09:58:05 +01:00
build_verbosity = get_option_from_environment ( 'CIBW_BUILD_VERBOSITY' , platform = platform , default = '' )
2018-09-16 15:16:30 +02:00
build_config , skip_config = os . environ . get ( 'CIBW_BUILD' , '*' ), os . environ . get ( 'CIBW_SKIP' , '' )
2018-07-05 09:58:05 +01:00
environment_config = get_option_from_environment ( 'CIBW_ENVIRONMENT' , platform = platform , default = '' )
2017-07-24 22:39:37 +01:00
2019-09-07 17:17:21 +01:00
if test_extras :
test_extras = '[ {0} ]' . format ( test_extras )
2018-04-08 21:24:08 +02:00
try :
build_verbosity = min ( 3 , max ( - 3 , int ( build_verbosity )))
except ValueError :
build_verbosity = 0
2017-09-01 13:33:29 +01:00
try :
environment = parse_environment ( environment_config )
except ( EnvironmentParseError , ValueError ) as e :
2017-09-06 19:18:28 +01:00
print ( 'cibuildwheel: Malformed environment option " %s "' % environment_config , file = sys . stderr )
2017-09-01 13:33:29 +01:00
import traceback
traceback . print_exc ( None , sys . stderr )
exit ( 2 )
2017-04-11 22:57:42 +01:00
2018-09-16 22:49:38 +02:00
build_selector = BuildSelector ( build_config , skip_config )
2017-03-19 20:57:51 +00:00
2017-08-22 21:41:13 +02:00
# Add CIBUILDWHEEL environment variable
# This needs to be passed on to the docker container in linux.py
os . environ [ 'CIBUILDWHEEL' ] = '1'
2018-11-29 23:20:38 +01:00
if not os . path . exists ( os . path . join ( project_dir , 'setup.py' )):
print ( 'cibuildwheel: Could not find setup.py at root of project' , file = sys . stderr )
2018-11-29 02:44:19 +00:00
exit ( 2 )
2019-04-20 18:52:01 +02:00
if args . print_build_identifiers :
print_build_identifiers ( platform , build_selector )
exit ( 0 )
2017-04-13 15:02:04 +01:00
build_options = dict (
2017-03-19 20:57:51 +00:00
project_dir = project_dir ,
output_dir = output_dir ,
test_command = test_command ,
2017-04-10 21:42:12 +01:00
test_requires = test_requires ,
2019-09-07 17:17:21 +01:00
test_extras = test_extras ,
2017-04-10 21:42:12 +01:00
before_build = before_build ,
2018-04-08 21:24:08 +02:00
build_verbosity = build_verbosity ,
2018-09-16 22:49:38 +02:00
build_selector = build_selector ,
2017-07-24 22:39:37 +01:00
environment = environment ,
2017-03-19 20:57:51 +00:00
)
2017-11-19 23:18:10 +01:00
if platform == 'linux' :
2019-08-14 18:29:15 +02:00
# TODO Add documentation on CIBW_ENVIRONMENT and possibility of AUDITWHEEL_PLAT if not defined by custom manylinux image
2019-08-27 16:03:09 +02:00
manylinux_x86_64_image = os . environ . get ( 'CIBW_MANYLINUX_X86_64_IMAGE' , 'manylinux2010' )
2019-09-16 12:48:15 +02:00
manylinux_i686_image = os . environ . get ( 'CIBW_MANYLINUX_I686_IMAGE' , 'manylinux1' )
2019-08-27 16:03:09 +02:00
default_manylinux_images_x86_64 = { 'manylinux1' : 'quay.io/pypa/manylinux1_x86_64' ,
'manylinux2010' : 'quay.io/pypa/manylinux2010_x86_64' }
default_manylinux_images_i686 = { 'manylinux1' : 'quay.io/pypa/manylinux1_i686' }
2017-11-19 23:18:10 +01:00
build_options . update (
2019-08-14 18:29:15 +02:00
manylinux_images = { 'x86_64' : default_manylinux_images_x86_64 . get ( manylinux_x86_64_image ) or manylinux_x86_64_image ,
'i686' : default_manylinux_images_i686 . get ( manylinux_i686_image ) or manylinux_i686_image },
2017-11-19 23:18:10 +01:00
)
elif platform == 'macos' :
pass
elif platform == 'windows' :
pass
2018-08-02 12:04:39 +02:00
# Python is buffering by default when running on the CI platforms, giving problems interleaving subprocess call output with unflushed calls to 'print'
sys . stdout = Unbuffered ( sys . stdout )
2017-04-13 15:02:04 +01:00
print_preamble ( platform , build_options )
2017-07-02 18:02:27 -05:00
if not os . path . exists ( output_dir ):
2017-08-31 09:49:57 +02:00
os . makedirs ( output_dir )
2017-07-02 18:02:27 -05:00
2017-03-19 20:57:51 +00:00
if platform == 'linux' :
2017-11-19 23:18:10 +01:00
cibuildwheel . linux . build ( ** build_options )
2017-03-19 20:57:51 +00:00
elif platform == 'windows' :
2017-04-13 15:02:04 +01:00
cibuildwheel . windows . build ( ** build_options )
2017-03-19 20:57:51 +00:00
elif platform == 'macos' :
2017-04-13 15:02:04 +01:00
cibuildwheel . macos . build ( ** build_options )
2017-03-19 20:57:51 +00:00
else :
2017-03-19 21:27:29 +00:00
raise Exception ( 'Unsupported platform' )
2017-03-19 20:57:51 +00:00
2019-04-20 18:52:01 +02:00
2019-08-30 18:31:02 +02:00
def detect_obsolete_options ():
# Check the old 'MANYLINUX1_*_IMAGE' options
for ( deprecated , alternative ) in [( 'CIBW_MANYLINUX1_X86_64_IMAGE' , 'CIBW_MANYLINUX_X86_64_IMAGE' ),
( 'CIBW_MANYLINUX1_I686_IMAGE' , 'CIBW_MANYLINUX_I686_IMAGE' )]:
if deprecated in os . environ :
print ( "' {} ' has been deprecated, and will be removed in a future release. Use the option ' {} ' instead." . format ( deprecated , alternative ))
if alternative not in os . environ :
print ( "Using value of option ' {} ' as replacement for ' {} '" . format ( deprecated , alternative ))
os . environ [ alternative ] = os . environ [ deprecated ]
else :
print ( "Option ' {} ' is not empty. Please unset ' {} '" . format ( alternative , deprecated ))
exit ( 2 )
# Check for 'manylinux1' in the 'CIBW_BUILD' and 'CIBW_SKIP' options
for deprecated in [ 'CIBW_BUILD' , 'CIBW_SKIP' ]:
if deprecated in os . environ and 'manylinux1' in os . environ [ deprecated ]:
print ( "Build identifiers with 'manylinux1' been deprecated. Replacing all occurences of 'manylinux1' by 'manylinux' in the option ' {} '" . format ( deprecated ))
os . environ [ deprecated ] = os . environ [ deprecated ] . replace ( 'manylinux1' , 'manylinux' )
2017-04-13 15:02:04 +01:00
def print_preamble ( platform , build_options ):
print ( textwrap . dedent ( '''
_ _ _ _ _ _ _
___|_| |_ _ _|_| |_| |_ _ _| |_ ___ ___| |
| _| | . | | | | | . | | | | | -_| -_| |
|___|_|___|___|_|_|___|_____|_|_|___|___|_|
''' ))
print ( 'cibuildwheel version %s \n ' % cibuildwheel . __version__ )
print ( 'Build options:' )
print ( ' platform: %r ' % platform )
2018-11-30 00:02:18 +01:00
for option , value in sorted ( build_options . items ()):
2017-04-13 15:02:04 +01:00
print ( ' %s : %r ' % ( option , value ))
2018-04-08 18:42:47 +01:00
warnings = detect_warnings ( platform , build_options )
if warnings :
print ( ' \n Warnings:' )
for warning in warnings :
print ( ' ' + warning )
2017-04-13 15:02:04 +01:00
print ( ' \n Here we go! \n ' )
2019-04-20 18:52:01 +02:00
def print_build_identifiers ( platform , build_selector ):
if platform == 'linux' :
python_configurations = cibuildwheel . linux . get_python_configurations ( build_selector )
elif platform == 'windows' :
python_configurations = cibuildwheel . windows . get_python_configurations ( build_selector )
elif platform == 'macos' :
python_configurations = cibuildwheel . macos . get_python_configurations ( build_selector )
else :
python_configurations = []
for config in python_configurations :
print ( config . identifier )
2018-04-08 18:42:47 +01:00
def detect_warnings ( platform , build_options ):
warnings = []
# warn about deprecated {python} and {pip}
for option_name in [ 'test_command' , 'before_build' ]:
2018-04-08 23:38:54 +01:00
option_value = build_options . get ( option_name )
2018-04-08 18:42:47 +01:00
2018-04-08 23:38:54 +01:00
if option_value :
if ' {python} ' in option_value or ' {pip} ' in option_value :
warnings . append ( option_name + ": ' {python} ' and ' {pip} ' are no longer needed, and will be removed in a future release. Simply use 'python' or 'pip' instead." )
2018-04-08 18:42:47 +01:00
return warnings
2017-03-19 20:57:51 +00:00
if __name__ == '__main__' :
main ()