def test_empty_var():
    environment_recipe = parse_environment("CFLAGS=")

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={"CFLAGS": "-Wall"})

    assert environment_dict == {"CFLAGS": ""}
def test_no_vars_pass_through():
    environment_recipe = parse_environment("")

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={"CIBUILDWHEEL": "awesome"})

    assert environment_dict == {"CIBUILDWHEEL": "awesome"}
def test_shell_eval_and_env():
    environment_recipe = parse_environment('VAR="$(echo "$PREV_VAR" string)"')

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={"PREV_VAR": "1 2 3"})

    assert environment_dict == {"PREV_VAR": "1 2 3", "VAR": "1 2 3 string"}
def test_no_vars_pass_through():
    environment_recipe = parse_environment('')

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={'CIBUILDWHEEL': 'awesome'})

    assert environment_dict == {'CIBUILDWHEEL': 'awesome'}
Example #5
0
def test_operators_inside_eval():
    environment_recipe = parse_environment('SOMETHING="$(echo a; echo b; echo c)"')

    # pass the existing process env so PATH is available
    environment_dict = environment_recipe.as_dictionary(os.environ.copy())

    assert environment_dict.get('SOMETHING') == 'a\nb\nc'
Example #6
0
def test_awkwardly_quoted_variable():
    environment_recipe = parse_environment('VAR2=something"like this""$VAR1"$VAR1$(echo "theres more")"$(echo "and more!")"')

    # pass the existing process env so PATH is available
    environment_dict = environment_recipe.as_dictionary({'VAR1': 'but wait'})

    assert environment_dict.get('VAR2') == 'somethinglike thisbut waitbut waittheres moreand more!'
def test_inheritance():
    environment_recipe = parse_environment("PATH=$PATH:/usr/local/bin")

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={"PATH": "/usr/bin"})

    assert environment_dict == {"PATH": "/usr/bin:/usr/local/bin"}
def test_quotes():
    environment_recipe = parse_environment(
        "A=1 VAR=\"1 NOT_A_VAR=2\" VBR='vbr'")

    environment_dict = environment_recipe.as_dictionary(prev_environment={})

    assert environment_dict == {"A": "1", "VAR": "1 NOT_A_VAR=2", "VBR": "vbr"}
Example #9
0
def test_substitution_with_backslash():
    environment_recipe = parse_environment('PATH2="somewhere_else;$PATH1"')

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={"PATH1": "c:\\folder\\"})

    assert environment_dict.get("PATH2") == "somewhere_else;c:\\folder\\"
Example #10
0
def test_basic_parsing():
    environment_recipe = parse_environment("VAR=1 VBR=2")

    environment_dict = environment_recipe.as_dictionary(prev_environment={})
    environment_cmds = environment_recipe.as_shell_commands()

    assert environment_dict == {"VAR": "1", "VBR": "2"}
    assert environment_cmds == ["export VAR=1", "export VBR=2"]
Example #11
0
def test_substitution_with_backslash():
    environment_recipe = parse_environment('PATH2="somewhere_else;$PATH1"')

    # pass the existing process env so PATH is available
    environment_dict = environment_recipe.as_dictionary(
        prev_environment={"PATH1": "c:\\folder\\"})

    assert environment_dict.get("PATH2") == "somewhere_else;c:\\folder\\"
def test_no_vars():
    environment_recipe = parse_environment('')

    environment_dict = environment_recipe.as_dictionary(prev_environment={})
    environment_cmds = environment_recipe.as_shell_commands()

    assert environment_dict == {}
    assert environment_cmds == []
def test_basic_parsing():
    environment_recipe = parse_environment('VAR=1 VBR=2')

    environment_dict = environment_recipe.as_dictionary(prev_environment={})
    environment_cmds = environment_recipe.as_shell_commands()

    assert environment_dict == {'VAR': '1', 'VBR': '2'}
    assert environment_cmds == ['export VAR=1', 'export VBR=2']
def test_empty_var():
    environment_recipe = parse_environment('CFLAGS=')

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={'CFLAGS': '-Wall'})
    environment_cmds = environment_recipe.as_shell_commands()

    assert environment_dict == {'CFLAGS': ''}
    assert environment_cmds == ['export CFLAGS=']
Example #15
0
def test_shell_eval_and_env():
    environment_recipe = parse_environment(
        f'VAR="$({PYTHON_ECHO} "$PREV_VAR" string)"')

    prev_environment = {**os.environ, "PREV_VAR": "1 2 3"}
    environment_dict = environment_recipe.as_dictionary(
        prev_environment=prev_environment)

    assert environment_dict == {**prev_environment, "VAR": "1 2 3 string"}
Example #16
0
def test_inheritance():
    environment_recipe = parse_environment("PATH=$PATH:/usr/local/bin")

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={"PATH": "/usr/bin"})
    environment_cmds = environment_recipe.as_shell_commands()

    assert environment_dict == {"PATH": "/usr/bin:/usr/local/bin"}
    assert environment_cmds == ["export PATH=$PATH:/usr/local/bin"]
Example #17
0
def test_operators_inside_eval():
    environment_recipe = parse_environment(
        f'SOMETHING="$({PYTHON_ECHO} a; {PYTHON_ECHO} b; {PYTHON_ECHO} c)"')

    # pass the existing process env so subcommands can be run in the evaluation
    environment_dict = environment_recipe.as_dictionary(
        prev_environment=os.environ.copy())

    assert environment_dict.get("SOMETHING") == "a\nb\nc"
Example #18
0
def test_empty_var():
    environment_recipe = parse_environment("CFLAGS=")

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={"CFLAGS": "-Wall"})
    environment_cmds = environment_recipe.as_shell_commands()

    assert environment_dict == {"CFLAGS": ""}
    assert environment_cmds == ["export CFLAGS="]
def test_shell_eval_and_env():
    environment_recipe = parse_environment('VAR="$(echo "$PREV_VAR" string)"')

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={'PREV_VAR': '1 2 3'})
    environment_cmds = environment_recipe.as_shell_commands()

    assert environment_dict == {'PREV_VAR': '1 2 3', 'VAR': '1 2 3 string'}
    assert environment_cmds == ['export VAR="$(echo "$PREV_VAR" string)"']
def test_inheritance():
    environment_recipe = parse_environment('PATH=$PATH:/usr/local/bin')

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={'PATH': '/usr/bin'})
    environment_cmds = environment_recipe.as_shell_commands()

    assert environment_dict == {'PATH': '/usr/bin:/usr/local/bin'}
    assert environment_cmds == ['export PATH=$PATH:/usr/local/bin']
Example #21
0
def test_shell_eval():
    environment_recipe = parse_environment('VAR="$(echo "a   test" string)"')

    env_copy = os.environ.copy()
    env_copy.pop("VAR", None)

    environment_dict = environment_recipe.as_dictionary(
        prev_environment=env_copy)

    assert environment_dict["VAR"] == "a   test string"
def test_quotes():
    environment_recipe = parse_environment('A=1 VAR="1 NOT_A_VAR=2" VBR=\'vbr\'')

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={}
    )
    environment_cmds = environment_recipe.as_shell_commands()

    assert environment_dict == {'A': '1', 'VAR': '1 NOT_A_VAR=2', 'VBR': 'vbr'}
    assert environment_cmds == ['export A=1', 'export VAR="1 NOT_A_VAR=2"', 'export VBR=\'vbr\'']
def test_shell_eval():
    environment_recipe = parse_environment('VAR="$(echo "a test" string)"')

    environment_dict = environment_recipe.as_dictionary(
        prev_environment={}
    )
    environment_cmds = environment_recipe.as_shell_commands()

    assert environment_dict == {'VAR': 'a test string'}
    assert environment_cmds == ['export VAR="$(echo "a test" string)"']
Example #24
0
def test_quotes():
    environment_recipe = parse_environment(
        "A=1 VAR=\"1 NOT_A_VAR=2\" VBR='vbr'")

    environment_dict = environment_recipe.as_dictionary(prev_environment={})
    environment_cmds = environment_recipe.as_shell_commands()

    assert environment_dict == {"A": "1", "VAR": "1 NOT_A_VAR=2", "VBR": "vbr"}
    assert environment_cmds == [
        "export A=1", 'export VAR="1 NOT_A_VAR=2"', "export VBR='vbr'"
    ]
Example #25
0
def test_awkwardly_quoted_variable():
    environment_recipe = parse_environment(
        f'VAR2=something"like this""$VAR1"$VAR1$({PYTHON_ECHO} "there is more")"$({PYTHON_ECHO} "and more!")"'
    )

    prev_environment = {**os.environ, "VAR1": "but wait"}
    environment_dict = environment_recipe.as_dictionary(
        prev_environment=prev_environment)

    assert (environment_dict.get("VAR2") ==
            "somethinglike thisbut waitbut waitthere is moreand more!")
def test_shell_eval():
    environment_recipe = parse_environment('VAR="$(echo "a test" string)"')

    env_copy = os.environ.copy()
    env_copy.pop('VAR', None)

    environment_dict = environment_recipe.as_dictionary(
        prev_environment=env_copy)
    environment_cmds = environment_recipe.as_shell_commands()

    assert environment_dict['VAR'] == 'a test string'
    assert environment_cmds == ['export VAR="$(echo "a test" string)"']
Example #27
0
def test_options_1(tmp_path, monkeypatch):
    with tmp_path.joinpath("pyproject.toml").open("w") as f:
        f.write(PYPROJECT_1)

    args = get_default_command_line_arguments()
    args.package_dir = str(tmp_path)

    monkeypatch.setattr(platform_module, "machine", lambda: "x86_64")

    options = Options(platform="linux", command_line_arguments=args)

    identifiers = get_build_identifiers(
        platform="linux",
        build_selector=options.globals.build_selector,
        architectures=options.globals.architectures,
    )

    override_display = """\
test_command: 'pyproject'
  cp37-manylinux_x86_64: 'pyproject-override'"""

    print(options.summary(identifiers))

    assert override_display in options.summary(identifiers)

    default_build_options = options.build_options(identifier=None)

    assert default_build_options.environment == parse_environment('FOO="BAR"')

    all_pinned_docker_images = _get_pinned_docker_images()
    pinned_x86_64_docker_image = all_pinned_docker_images["x86_64"]

    local = options.build_options("cp38-manylinux_x86_64")
    assert local.manylinux_images is not None
    assert local.test_command == "pyproject"
    assert local.manylinux_images["x86_64"] == pinned_x86_64_docker_image[
        "manylinux1"]

    local = options.build_options("cp37-manylinux_x86_64")
    assert local.manylinux_images is not None
    assert local.test_command == "pyproject-override"
    assert local.manylinux_images["x86_64"] == pinned_x86_64_docker_image[
        "manylinux2014"]
Example #28
0
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'],
                        default=os.environ.get('CIBW_PLATFORM', 'auto'),
                        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. 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.')

    args = parser.parse_args()

    detect_obsolete_options()

    if args.platform != 'auto':
        platform = args.platform
    else:
        ci = strtobool(os.environ.get('CI', 'false')) or 'BITRISE_BUILD_NUMBER' in os.environ or 'AZURE_HTTP_USER_AGENT' in os.environ
        if not ci:
            print('cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server, '
                  'Travis CI, AppVeyor, Azure Pipelines and CircleCI are supported. You can run on your '
                  'development machine or other CI providers using the --platform argument. Check --help '
                  'output for more information.',
                  file=sys.stderr)
            exit(2)
        if sys.platform.startswith('linux'):
            platform = 'linux'
        elif sys.platform == 'darwin':
            platform = 'macos'
        elif sys.platform == 'win32':
            platform = 'windows'
        else:
            print('cibuildwheel: Unable to detect platform from "sys.platform" in a CI environment. You can run '
                  'cibuildwheel using the --platform argument. Check --help output for more information.',
                  file=sys.stderr)
            exit(2)

    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='')
    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', '')
    if platform == 'linux':
        repair_command_default = 'auditwheel repair -w {dest_dir} {wheel}'
    elif platform == 'macos':
        repair_command_default = 'delocate-listdeps {wheel} && delocate-wheel --require-archs x86_64 -w {dest_dir} {wheel}'
    else:
        repair_command_default = ''
    repair_command = get_option_from_environment('CIBW_REPAIR_WHEEL_COMMAND', platform=platform, default=repair_command_default)
    environment_config = get_option_from_environment('CIBW_ENVIRONMENT', platform=platform, default='')

    if test_extras:
        test_extras = '[{0}]'.format(test_extras)

    try:
        build_verbosity = min(3, max(-3, int(build_verbosity)))
    except ValueError:
        build_verbosity = 0

    try:
        environment = parse_environment(environment_config)
    except (EnvironmentParseError, ValueError):
        print('cibuildwheel: Malformed environment option "%s"' % environment_config, file=sys.stderr)
        traceback.print_exc(None, sys.stderr)
        exit(2)

    build_selector = BuildSelector(build_config, skip_config)

    # 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)

    build_options = dict(
        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,
        repair_command=repair_command,
        environment=environment,
    )

    if platform == 'linux':
        manylinux_x86_64_image = os.environ.get('CIBW_MANYLINUX_X86_64_IMAGE', 'manylinux2010')
        manylinux_i686_image = os.environ.get('CIBW_MANYLINUX_I686_IMAGE', 'manylinux2010')
        manylinux_pypy_x86_64_image = os.environ.get('CIBW_MANYLINUX_PYPY_X86_64_IMAGE', 'manylinux2010')
        manylinux_aarch64_image = os.environ.get('CIBW_MANYLINUX_AARCH64_IMAGE', 'manylinux2014')
        manylinux_ppc64le_image = os.environ.get('CIBW_MANYLINUX_PPC64LE_IMAGE', 'manylinux2014')
        manylinux_s390x_image = os.environ.get('CIBW_MANYLINUX_S390X_IMAGE', 'manylinux2014')

        default_manylinux_images_x86_64 = {'manylinux1': 'quay.io/pypa/manylinux1_x86_64',
                                           'manylinux2010': 'quay.io/pypa/manylinux2010_x86_64',
                                           'manylinux2014': 'quay.io/pypa/manylinux2014_x86_64'}
        default_manylinux_images_i686 = {'manylinux1': 'quay.io/pypa/manylinux1_i686',
                                         'manylinux2010': 'quay.io/pypa/manylinux2010_i686',
                                         'manylinux2014': 'quay.io/pypa/manylinux2014_i686'}
        default_manylinux_images_pypy_x86_64 = {'manylinux2010': 'pypywheels/manylinux2010-pypy_x86_64'}
        default_manylinux_images_aarch64 = {'manylinux2014': 'quay.io/pypa/manylinux2014_aarch64'}
        default_manylinux_images_ppc64le = {'manylinux2014': 'quay.io/pypa/manylinux2014_ppc64le'}
        default_manylinux_images_s390x = {'manylinux2014': 'quay.io/pypa/manylinux2014_s390x'}

        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,
                              'pypy_x86_64': default_manylinux_images_pypy_x86_64.get(manylinux_pypy_x86_64_image) or manylinux_pypy_x86_64_image,
                              'aarch64': default_manylinux_images_aarch64.get(manylinux_aarch64_image) or manylinux_aarch64_image,
                              'ppc64le': default_manylinux_images_ppc64le.get(manylinux_ppc64le_image) or manylinux_ppc64le_image,
                              's390x': default_manylinux_images_s390x.get(manylinux_s390x_image) or manylinux_s390x_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)

    print_preamble(platform, build_options)

    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    if platform == 'linux':
        cibuildwheel.linux.build(**build_options)
    elif platform == 'windows':
        cibuildwheel.windows.build(**build_options)
    elif platform == 'macos':
        cibuildwheel.macos.build(**build_options)
    else:
        print('cibuildwheel: Unsupported platform: {}'.format(platform), file=sys.stderr)
        exit(2)
Example #29
0
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'],
                        default=os.environ.get('CIBW_PLATFORM', 'auto'),
                        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.'))

    args = parser.parse_args()

    if args.platform != 'auto':
        platform = args.platform
    else:
        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'
        elif 'BITRISE_BUILD_NUMBER' in os.environ:
            platform = 'macos'
        else:
            print('cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server, '
                  'Travis CI and Appveyor are supported. You can run on your development '
                  'machine using the --platform argument. Check --help output for more '
                  'information.',
                  file=sys.stderr)
            exit(2)

    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()
    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='')
    skip_config = os.environ.get('CIBW_SKIP', '')
    environment_config = get_option_from_environment('CIBW_ENVIRONMENT', platform=platform, default='')

    try:
        build_verbosity = min(3, max(-3, int(build_verbosity)))
    except ValueError:
        build_verbosity = 0

    try:
        environment = parse_environment(environment_config)
    except (EnvironmentParseError, ValueError) as e:
        print('cibuildwheel: Malformed environment option "%s"' % environment_config, file=sys.stderr)
        import traceback
        traceback.print_exc(None, sys.stderr)
        exit(2)

    skip = BuildSkipper(skip_config)

    # Add CIBUILDWHEEL environment variable
    # This needs to be passed on to the docker container in linux.py
    os.environ['CIBUILDWHEEL'] = '1'

    try:
        project_setup_py = os.path.join(project_dir, 'setup.py')
        name_output = subprocess.check_output([sys.executable, project_setup_py, '--name'],
                                              universal_newlines=True)
        # the last line of output is the name
        package_name = name_output.strip().splitlines()[-1]
    except subprocess.CalledProcessError as err:
        if not os.path.exists(project_setup_py):
            print('cibuildwheel: Could not find setup.py at root of project', file=sys.stderr)
            exit(2)
        else:
            print(err.output)
            print('cibuildwheel: Failed to get name of the package. Command was %s' % err.cmd,
                  file=sys.stderr)
            exit(err.returncode)

    if package_name == '' or package_name == 'UNKNOWN':
        print('cibuildwheel: Invalid package name "%s". Check your setup.py' % package_name,
              file=sys.stderr)
        exit(2)

    build_options = dict(
        project_dir=project_dir,
        package_name=package_name,
        output_dir=output_dir,
        test_command=test_command,
        test_requires=test_requires,
        before_build=before_build,
        build_verbosity=build_verbosity,
        skip=skip,
        environment=environment,
    )

    if platform == 'linux':
        manylinux1_x86_64_image = os.environ.get('CIBW_MANYLINUX1_X86_64_IMAGE', None)
        manylinux1_i686_image = os.environ.get('CIBW_MANYLINUX1_I686_IMAGE', None)

        build_options.update(
            manylinux1_images={'x86_64': manylinux1_x86_64_image, 'i686': manylinux1_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)

    print_preamble(platform, build_options)

    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    if platform == 'linux':
        cibuildwheel.linux.build(**build_options)
    elif platform == 'windows':
        cibuildwheel.windows.build(**build_options)
    elif platform == 'macos':
        cibuildwheel.macos.build(**build_options)
    else:
        raise Exception('Unsupported platform')
Example #30
0
def main() -> None:
    platform: PlatformName

    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'],
                        default=os.environ.get('CIBW_PLATFORM', 'auto'),
                        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. Default: auto.
                        ''')

    parser.add_argument('--archs',
                        default=None,
                        help='''
                            Comma-separated list of CPU architectures to build for.
                            When set to 'auto', builds the architectures natively supported
                            on this machine. Set this option to build an architecture
                            via emulation, for example, using binfmt_misc and QEMU.
                            Default: auto.
                            Choices: auto, auto64, auto32, native, all, {}
                        '''.format(", ".join(a.name for a in Architecture)))
    parser.add_argument('--output-dir',
                        default=os.environ.get('CIBW_OUTPUT_DIR',
                                               'wheelhouse'),
                        help='Destination folder for the wheels.')
    parser.add_argument('package_dir',
                        default='.',
                        nargs='?',
                        help='''
                            Path to the package that you want wheels for. Must be a subdirectory of
                            the working directory. When set, the working directory is still
                            considered the 'project' and is copied into the Docker container on
                            Linux. Default: the working directory.
                        ''')

    parser.add_argument(
        '--print-build-identifiers',
        action='store_true',
        help=
        'Print the build identifiers matched by the current invocation and exit.'
    )
    parser.add_argument(
        '--allow-empty',
        action='store_true',
        help=
        'Do not report an error code if the build does not match any wheels.')

    args = parser.parse_args()

    detect_obsolete_options()

    if args.platform != 'auto':
        platform = args.platform
    else:
        ci_provider = detect_ci_provider()
        if ci_provider is None:
            print(textwrap.dedent('''
                cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server;
                Travis CI, AppVeyor, Azure Pipelines, GitHub Actions, CircleCI, and Gitlab are
                supported. You can run on your development machine or other CI providers using the
                --platform argument. Check --help output for more information.
            '''),
                  file=sys.stderr)
            sys.exit(2)
        if sys.platform.startswith('linux'):
            platform = 'linux'
        elif sys.platform == 'darwin':
            platform = 'macos'
        elif sys.platform == 'win32':
            platform = 'windows'
        else:
            print(
                'cibuildwheel: Unable to detect platform from "sys.platform" in a CI environment. You can run '
                'cibuildwheel using the --platform argument. Check --help output for more information.',
                file=sys.stderr)
            sys.exit(2)

    if platform not in PLATFORMS:
        print(f'cibuildwheel: Unsupported platform: {platform}',
              file=sys.stderr)
        sys.exit(2)

    package_dir = Path(args.package_dir)
    output_dir = Path(args.output_dir)

    if platform == 'linux':
        repair_command_default = 'auditwheel repair -w {dest_dir} {wheel}'
    elif platform == 'macos':
        repair_command_default = 'delocate-listdeps {wheel} && delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}'
    elif platform == 'windows':
        repair_command_default = 'delvewheel repair -w {dest_dir} {wheel}'
    else:
        assert_never(platform)

    build_config = os.environ.get('CIBW_BUILD') or '*'
    skip_config = os.environ.get('CIBW_SKIP', '')
    test_skip = os.environ.get('CIBW_TEST_SKIP', '')
    environment_config = get_option_from_environment('CIBW_ENVIRONMENT',
                                                     platform=platform,
                                                     default='')
    before_all = get_option_from_environment('CIBW_BEFORE_ALL',
                                             platform=platform,
                                             default='')
    before_build = get_option_from_environment('CIBW_BEFORE_BUILD',
                                               platform=platform)
    repair_command = get_option_from_environment(
        'CIBW_REPAIR_WHEEL_COMMAND',
        platform=platform,
        default=repair_command_default)
    dependency_versions = get_option_from_environment(
        'CIBW_DEPENDENCY_VERSIONS', platform=platform, default='pinned')
    test_command = get_option_from_environment('CIBW_TEST_COMMAND',
                                               platform=platform)
    before_test = get_option_from_environment('CIBW_BEFORE_TEST',
                                              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='')
    build_verbosity_str = get_option_from_environment('CIBW_BUILD_VERBOSITY',
                                                      platform=platform,
                                                      default='')

    package_files = {'setup.py', 'setup.cfg', 'pyproject.toml'}

    if not any(package_dir.joinpath(name).exists() for name in package_files):
        names = ', '.join(sorted(package_files, reverse=True))
        print(
            f'cibuildwheel: Could not find any of {{{names}}} at root of package',
            file=sys.stderr)
        sys.exit(2)

    # Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py
    requires_python_str: Optional[str] = os.environ.get(
        'CIBW_PROJECT_REQUIRES_PYTHON') or get_requires_python_str(package_dir)
    requires_python = None if requires_python_str is None else SpecifierSet(
        requires_python_str)

    build_selector = BuildSelector(build_config=build_config,
                                   skip_config=skip_config,
                                   requires_python=requires_python)
    test_selector = TestSelector(skip_config=test_skip)

    try:
        environment = parse_environment(environment_config)
    except (EnvironmentParseError, ValueError):
        print(
            f'cibuildwheel: Malformed environment option "{environment_config}"',
            file=sys.stderr)
        traceback.print_exc(None, sys.stderr)
        sys.exit(2)

    if dependency_versions == 'pinned':
        dependency_constraints: Optional[
            DependencyConstraints] = DependencyConstraints.with_defaults()
    elif dependency_versions == 'latest':
        dependency_constraints = None
    else:
        dependency_versions_path = Path(dependency_versions)
        dependency_constraints = DependencyConstraints(
            dependency_versions_path)

    if test_extras:
        test_extras = f'[{test_extras}]'

    try:
        build_verbosity = min(3, max(-3, int(build_verbosity_str)))
    except ValueError:
        build_verbosity = 0

    # Add CIBUILDWHEEL environment variable
    # This needs to be passed on to the docker container in linux.py
    os.environ['CIBUILDWHEEL'] = '1'

    if args.archs is not None:
        archs_config_str = args.archs
    else:
        archs_config_str = get_option_from_environment('CIBW_ARCHS',
                                                       platform=platform,
                                                       default='auto')

    archs = Architecture.parse_config(archs_config_str, platform=platform)

    identifiers = get_build_identifiers(platform, build_selector, archs)

    if args.print_build_identifiers:
        for identifier in identifiers:
            print(identifier)
        sys.exit(0)

    manylinux_images: Optional[Dict[str, str]] = None
    if platform == 'linux':
        pinned_docker_images_file = resources_dir / 'pinned_docker_images.cfg'
        all_pinned_docker_images = ConfigParser()
        all_pinned_docker_images.read(pinned_docker_images_file)
        # all_pinned_docker_images looks like a dict of dicts, e.g.
        # { 'x86_64': {'manylinux1': '...', 'manylinux2010': '...', 'manylinux2014': '...'},
        #   'i686': {'manylinux1': '...', 'manylinux2010': '...', 'manylinux2014': '...'},
        #   'pypy_x86_64': {'manylinux2010': '...' }
        #   ... }

        manylinux_images = {}

        for build_platform in [
                'x86_64', 'i686', 'pypy_x86_64', 'aarch64', 'ppc64le', 's390x'
        ]:
            pinned_images = all_pinned_docker_images[build_platform]

            config_name = f'CIBW_MANYLINUX_{build_platform.upper()}_IMAGE'
            config_value = os.environ.get(config_name)

            if config_value is None:
                # default to manylinux2010 if it's available, otherwise manylinux2014
                image = pinned_images.get(
                    'manylinux2010') or pinned_images.get('manylinux2014')
            elif config_value in pinned_images:
                image = pinned_images[config_value]
            else:
                image = config_value

            manylinux_images[build_platform] = image

    build_options = BuildOptions(
        architectures=archs,
        package_dir=package_dir,
        output_dir=output_dir,
        test_command=test_command,
        test_requires=test_requires,
        test_extras=test_extras,
        before_test=before_test,
        before_build=before_build,
        before_all=before_all,
        build_verbosity=build_verbosity,
        build_selector=build_selector,
        test_selector=test_selector,
        repair_command=repair_command,
        environment=environment,
        dependency_constraints=dependency_constraints,
        manylinux_images=manylinux_images,
    )

    # 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)  # type: ignore

    print_preamble(platform, build_options)

    try:
        allowed_architectures_check(platform, build_options.architectures)
    except ValueError as err:
        print("cibuildwheel:", *err.args, file=sys.stderr)
        sys.exit(4)

    if not identifiers:
        print(f'cibuildwheel: No build identifiers selected: {build_selector}',
              file=sys.stderr)
        if not args.allow_empty:
            sys.exit(3)

    if not output_dir.exists():
        output_dir.mkdir(parents=True)

    with cibuildwheel.util.print_new_wheels(
            "\n{n} wheels produced in {m:.0f} minutes:", output_dir):
        if platform == 'linux':
            cibuildwheel.linux.build(build_options)
        elif platform == 'windows':
            cibuildwheel.windows.build(build_options)
        elif platform == 'macos':
            cibuildwheel.macos.build(build_options)
        else:
            assert_never(platform)