Files
cibuildwheel/cibuildwheel/__main__.py
T

257 lines
11 KiB
Python
Raw Normal View History

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
from cibuildwheel.util import BuildSelector, Unbuffered
2017-03-19 20:57:51 +00:00
def get_option_from_environment(option_name, platform=None, default=None):
'''
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
return os.environ.get(option_name, default)
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.'))
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()
detect_obsolete_options()
if args.platform != 'auto':
platform = args.platform
else:
2018-09-05 04:15:32 -04:00
platform = None
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, '
'Travis CI, AppVeyor, and CircleCI are supported. You can run on your development '
'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
test_command = get_option_from_environment('CIBW_TEST_COMMAND', platform=platform)
test_requires = get_option_from_environment('CIBW_TEST_REQUIRES', platform=platform, default='').split()
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
before_build = get_option_from_environment('CIBW_BEFORE_BUILD', platform=platform)
build_verbosity = get_option_from_environment('CIBW_BUILD_VERBOSITY', platform=platform, default='')
build_config, skip_config = os.environ.get('CIBW_BUILD', '*'), os.environ.get('CIBW_SKIP', '')
environment_config = get_option_from_environment('CIBW_ENVIRONMENT', platform=platform, default='')
2017-07-24 22:39:37 +01:00
if test_extras:
test_extras = '[{0}]'.format(test_extras)
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
build_selector = BuildSelector(build_config, skip_config)
2017-03-19 20:57:51 +00:00
# Add CIBUILDWHEEL environment variable
# This needs to be passed on to the docker container in linux.py
os.environ['CIBUILDWHEEL'] = '1'
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)
exit(2)
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,
test_requires=test_requires,
test_extras=test_extras,
before_build=before_build,
build_verbosity=build_verbosity,
build_selector=build_selector,
2017-07-24 22:39:37 +01:00
environment=environment,
2017-03-19 20:57:51 +00:00
)
if platform == 'linux':
# TODO Add documentation on CIBW_ENVIRONMENT and possibility of AUDITWHEEL_PLAT if not defined by custom manylinux image
manylinux_x86_64_image = os.environ.get('CIBW_MANYLINUX_X86_64_IMAGE', 'manylinux2010')
manylinux_i686_image = os.environ.get('CIBW_MANYLINUX_I686_IMAGE', 'manylinux1')
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'}
build_options.update(
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},
)
elif platform == 'macos':
pass
elif platform == 'windows':
pass
# 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):
os.makedirs(output_dir)
2017-07-02 18:02:27 -05:00
2017-03-19 20:57:51 +00:00
if platform == 'linux':
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
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)
for option, value in sorted(build_options.items()):
2017-04-13 15:02:04 +01:00
print(' %s: %r' % (option, value))
warnings = detect_warnings(platform, build_options)
if warnings:
print('\nWarnings:')
for warning in warnings:
print(' ' + warning)
2017-04-13 15:02:04 +01:00
print('\nHere we go!\n')
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)
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 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.")
return warnings
2017-03-19 20:57:51 +00:00
if __name__ == '__main__':
main()