コード例 #1
0
    def __init__(self) -> None:
        if sys.platform == 'win32' and hasattr(sys.stdout, 'reconfigure'):
            # the encoding on Windows can be a 1-byte charmap, but all CIs
            # support utf8, so we hardcode that
            sys.stdout.reconfigure(encoding='utf8')

        self.unicode_enabled = file_supports_unicode(sys.stdout)

        ci_provider = detect_ci_provider()

        if ci_provider == CIProvider.azure_pipelines:
            self.fold_mode = 'azure'
            self.colors_enabled = True

        elif ci_provider == CIProvider.github_actions:
            self.fold_mode = 'github'
            self.colors_enabled = True

        elif ci_provider == CIProvider.travis_ci:
            self.fold_mode = 'travis'
            self.colors_enabled = True

        elif ci_provider == CIProvider.appveyor:
            self.fold_mode = 'disabled'
            self.colors_enabled = True

        else:
            self.fold_mode = 'disabled'
            self.colors_enabled = file_supports_color(sys.stdout)
コード例 #2
0
ファイル: oci_container.py プロジェクト: pypa/cibuildwheel
    def __enter__(self) -> OCIContainer:

        self.name = f"cibuildwheel-{uuid.uuid4()}"

        # work-around for Travis-CI PPC64le Docker runs since 2021:
        # this avoids network splits
        # https://github.com/pypa/cibuildwheel/issues/904
        # https://github.com/conda-forge/conda-smithy/pull/1520
        network_args = []
        if detect_ci_provider() == CIProvider.travis_ci and platform.machine() == "ppc64le":
            network_args = ["--network=host"]

        shell_args = ["linux32", "/bin/bash"] if self.simulate_32_bit else ["/bin/bash"]

        subprocess.run(
            [
                self.engine,
                "create",
                "--env=CIBUILDWHEEL",
                f"--name={self.name}",
                "--interactive",
                "--volume=/:/host",  # ignored on CircleCI
                *network_args,
                self.image,
                *shell_args,
            ],
            check=True,
        )

        self.process = subprocess.Popen(
            [
                self.engine,
                "start",
                "--attach",
                "--interactive",
                self.name,
            ],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
        )

        assert self.process.stdin and self.process.stdout
        self.bash_stdin = self.process.stdin
        self.bash_stdout = self.process.stdout

        # run a noop command to block until the container is responding
        self.call(["/bin/true"], cwd="/")

        if self.cwd:
            # Although `docker create -w` does create the working dir if it
            # does not exist, podman does not. There does not seem to be a way
            # to setup a workdir for a container running in podman.
            self.call(["mkdir", "-p", os.fspath(self.cwd)], cwd="/")

        return self
コード例 #3
0
    def __enter__(self) -> "DockerContainer":
        self.name = f"cibuildwheel-{uuid.uuid4()}"
        cwd_args = ["-w", str(self.cwd)] if self.cwd else []

        # work-around for Travis-CI PPC64le Docker runs since 2021:
        # this avoids network splits
        # https://github.com/pypa/cibuildwheel/issues/904
        # https://github.com/conda-forge/conda-smithy/pull/1520
        network_args = []
        if detect_ci_provider() == CIProvider.travis_ci and platform.machine() == "ppc64le":
            network_args = ["--network=host"]

        shell_args = ["linux32", "/bin/bash"] if self.simulate_32_bit else ["/bin/bash"]
        subprocess.run(
            [
                "docker",
                "create",
                "--env=CIBUILDWHEEL",
                f"--name={self.name}",
                "--interactive",
                "--volume=/:/host",  # ignored on CircleCI
                *network_args,
                *cwd_args,
                self.docker_image,
                *shell_args,
            ],
            check=True,
        )
        self.process = subprocess.Popen(
            [
                "docker",
                "start",
                "--attach",
                "--interactive",
                self.name,
            ],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
        )

        assert self.process.stdin and self.process.stdout
        self.bash_stdin = self.process.stdin
        self.bash_stdout = self.process.stdout

        # run a noop command to block until the container is responding
        self.call(["/bin/true"])

        return self
コード例 #4
0
ファイル: __main__.py プロジェクト: pypa/cibuildwheel
def build_in_directory(args: CommandLineArguments) -> None:
    platform: PlatformName

    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)

    options = compute_options(platform=platform, command_line_arguments=args)

    package_dir = options.globals.package_dir
    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))
        msg = f"cibuildwheel: Could not find any of {{{names}}} at root of package"
        print(msg, file=sys.stderr)
        sys.exit(2)

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

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

    # Add CIBUILDWHEEL environment variable
    os.environ["CIBUILDWHEEL"] = "1"

    # 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[assignment]

    # create the cache dir before it gets printed & builds performed
    CIBW_CACHE_PATH.mkdir(parents=True, exist_ok=True)

    print_preamble(platform=platform, options=options, identifiers=identifiers)

    try:
        options.check_for_invalid_configuration(identifiers)
        allowed_architectures_check(platform, options.globals.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: {options.globals.build_selector}",
            file=sys.stderr,
        )
        if not args.allow_empty:
            sys.exit(3)

    output_dir = options.globals.output_dir

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

    tmp_path = Path(mkdtemp(prefix="cibw-run-")).resolve(strict=True)
    try:
        with cibuildwheel.util.print_new_wheels(
            "\n{n} wheels produced in {m:.0f} minutes:", output_dir
        ):
            if platform == "linux":
                cibuildwheel.linux.build(options, tmp_path)
            elif platform == "windows":
                cibuildwheel.windows.build(options, tmp_path)
            elif platform == "macos":
                cibuildwheel.macos.build(options, tmp_path)
            else:
                assert_never(platform)
    finally:
        shutil.rmtree(tmp_path, ignore_errors=sys.platform.startswith("win"))
        if tmp_path.exists():
            log.warning(f"Can't delete temporary folder '{str(tmp_path)}'")
コード例 #5
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)
コード例 #6
0
def main() -> None:
    platform: PlatformName

    parser = argparse.ArgumentParser(
        description="Build wheels for all the platforms.",
        epilog="""
            Most options are supplied via environment variables or in
            --config-file (pyproject.toml usually). See
            https://github.com/pypa/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. Use this option to override the
            auto-detected platform or to run cibuildwheel on your development
            machine. Specifying "macos" or "windows" only works on that
            operating system, but "linux" works on all three, as long as
            Docker is installed. Default: auto.
        """,
    )

    arch_list_str = ", ".join(a.name for a in Architecture)
    parser.add_argument(
        "--archs",
        default=None,
        help=f"""
            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, {arch_list_str}
        """,
    )

    parser.add_argument(
        "--output-dir",
        help="Destination folder for the wheels. Default: wheelhouse.",
    )

    parser.add_argument(
        "--config-file",
        default="",
        help="""
            TOML config file. Default: "", meaning {package}/pyproject.toml,
            if it exists.
        """,
    )

    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.",
    )

    parser.add_argument(
        "--prerelease-pythons",
        action="store_true",
        help="Enable pre-release Python versions if available.",
    )

    args = parser.parse_args(namespace=CommandLineArguments())

    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)

    options = compute_options(platform=platform, command_line_arguments=args)

    package_dir = options.globals.package_dir
    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))
        msg = f"cibuildwheel: Could not find any of {{{names}}} at root of package"
        print(msg, file=sys.stderr)
        sys.exit(2)

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

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

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

    # 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[assignment]

    # create the cache dir before it gets printed & builds performed
    CIBW_CACHE_PATH.mkdir(parents=True, exist_ok=True)

    print_preamble(platform=platform, options=options, identifiers=identifiers)

    try:
        options.check_for_invalid_configuration(identifiers)
        allowed_architectures_check(platform, options.globals.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: {options.globals.build_selector}",
            file=sys.stderr,
        )
        if not args.allow_empty:
            sys.exit(3)

    output_dir = options.globals.output_dir

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

    tmp_path = Path(mkdtemp(prefix="cibw-run-")).resolve(strict=True)
    try:
        with cibuildwheel.util.print_new_wheels(
            "\n{n} wheels produced in {m:.0f} minutes:", output_dir
        ):
            if platform == "linux":
                cibuildwheel.linux.build(options, tmp_path)
            elif platform == "windows":
                cibuildwheel.windows.build(options, tmp_path)
            elif platform == "macos":
                cibuildwheel.macos.build(options, tmp_path)
            else:
                assert_never(platform)
    finally:
        shutil.rmtree(tmp_path, ignore_errors=sys.platform.startswith("win"))
        if tmp_path.exists():
            log.warning(f"Can't delete temporary folder '{str(tmp_path)}'")