Files
cibuildwheel/cibuildwheel/__main__.py
T

280 lines
9.2 KiB
Python
Raw Normal View History

2019-11-12 23:51:27 +00:00
import argparse
import os
import sys
import textwrap
2020-06-15 01:53:31 +02:00
from pathlib import Path
from typing import List, Set, Union
2021-01-31 17:14:15 -05:00
2017-04-13 15:02:04 +01:00
import cibuildwheel
2019-11-12 23:51:27 +00:00
import cibuildwheel.linux
import cibuildwheel.macos
2021-02-04 14:27:54 -05:00
import cibuildwheel.util
2019-11-12 23:51:27 +00:00
import cibuildwheel.windows
2021-01-22 09:33:22 -05:00
from cibuildwheel.architecture import Architecture, allowed_architectures_check
from cibuildwheel.options import compute_options
2021-01-09 15:40:40 -05:00
from cibuildwheel.typing import PLATFORMS, PlatformName, assert_never
from cibuildwheel.util import (
2021-09-19 00:19:28 -04:00
AllBuildOptions,
BuildSelector,
Unbuffered,
2020-11-23 21:00:22 +00:00
detect_ci_provider,
)
def main() -> None:
2021-01-09 15:40:40 -05:00
platform: PlatformName
2017-03-19 20:57:51 +00:00
parser = argparse.ArgumentParser(
2021-05-03 11:45:43 -04:00
description="Build wheels for all the platforms.",
epilog="""
2021-06-21 12:26:46 -04:00
Most options are supplied via environment variables or in
--config-file (pyproject.toml usually). See
https://github.com/pypa/cibuildwheel#options for info.
2021-05-03 11:45:43 -04:00
""",
2021-04-30 17:56:34 -04:00
)
2017-03-19 20:57:51 +00:00
2021-04-30 17:56:34 -04:00
parser.add_argument(
2021-05-03 11:45:43 -04:00
"--platform",
choices=["auto", "linux", "macos", "windows"],
default=os.environ.get("CIBW_PLATFORM", "auto"),
help="""
2021-05-02 16:37:38 +02:00
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.
2021-05-03 11:45:43 -04:00
""",
2021-04-30 17:56:34 -04:00
)
2020-12-16 23:17:51 +00:00
2021-04-29 20:21:42 -04:00
arch_list_str = ", ".join(a.name for a in Architecture)
2021-04-30 17:56:34 -04:00
parser.add_argument(
2021-05-03 11:45:43 -04:00
"--archs",
2021-04-30 17:56:34 -04:00
default=None,
2021-05-03 11:45:43 -04:00
help=f"""
2021-05-02 16:37:38 +02:00
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}
2021-05-03 11:45:43 -04:00
""",
2021-04-30 17:56:34 -04:00
)
2021-04-29 20:21:42 -04:00
2021-04-30 17:56:34 -04:00
parser.add_argument(
2021-05-03 11:45:43 -04:00
"--output-dir",
help="Destination folder for the wheels.",
2021-04-30 17:56:34 -04:00
)
2021-04-29 20:21:42 -04:00
2021-06-21 12:26:46 -04:00
parser.add_argument(
"--config-file",
help="""
TOML config file for cibuildwheel. Defaults to pyproject.toml, but
can be overridden with this option.
""",
)
2021-04-30 17:56:34 -04:00
parser.add_argument(
2021-05-03 11:45:43 -04:00
"package_dir",
default=".",
nargs="?",
help="""
2021-05-02 16:37:38 +02:00
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.
2021-05-03 11:45:43 -04:00
""",
2021-04-30 17:56:34 -04:00
)
2017-03-19 20:57:51 +00:00
2021-04-30 17:56:34 -04:00
parser.add_argument(
2021-05-03 11:45:43 -04:00
"--print-build-identifiers",
action="store_true",
help="Print the build identifiers matched by the current invocation and exit.",
2021-04-30 17:56:34 -04:00
)
2021-04-29 20:21:42 -04:00
2021-04-30 17:56:34 -04:00
parser.add_argument(
2021-05-03 11:45:43 -04:00
"--allow-empty",
action="store_true",
help="Do not report an error code if the build does not match any wheels.",
2021-04-30 17:56:34 -04:00
)
parser.add_argument(
"--prerelease-pythons",
action="store_true",
help="Enable pre-release Python versions if available.",
)
2017-03-19 20:57:51 +00:00
args = parser.parse_args()
2021-05-03 11:45:43 -04:00
if args.platform != "auto":
platform = args.platform
else:
2020-11-23 21:00:22 +00:00
ci_provider = detect_ci_provider()
if ci_provider is None:
2021-04-30 17:56:34 -04:00
print(
textwrap.dedent(
2021-05-03 11:45:43 -04:00
"""
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.
2021-05-03 11:45:43 -04:00
"""
2021-04-30 17:56:34 -04:00
),
file=sys.stderr,
)
2021-01-17 14:13:26 -05:00
sys.exit(2)
2021-05-03 11:45:43 -04:00
if sys.platform.startswith("linux"):
platform = "linux"
elif sys.platform == "darwin":
platform = "macos"
elif sys.platform == "win32":
platform = "windows"
else:
2021-04-30 17:56:34 -04:00
print(
'cibuildwheel: Unable to detect platform from "sys.platform" in a CI environment. You can run '
2021-05-03 11:45:43 -04:00
"cibuildwheel using the --platform argument. Check --help output for more information.",
2021-04-30 17:56:34 -04:00
file=sys.stderr,
)
2021-01-17 14:13:26 -05:00
sys.exit(2)
2021-01-09 15:40:40 -05:00
if platform not in PLATFORMS:
2021-05-03 11:45:43 -04:00
print(f"cibuildwheel: Unsupported platform: {platform}", file=sys.stderr)
2021-01-17 14:13:26 -05:00
sys.exit(2)
2020-06-15 01:53:31 +02:00
package_dir = Path(args.package_dir)
2021-09-19 00:19:28 -04:00
output_dir = Path(
args.output_dir
if args.output_dir is not None
else os.environ.get("CIBW_OUTPUT_DIR", "wheelhouse")
)
2021-09-19 00:19:28 -04:00
all_build_options, build_options_by_selector = compute_options(
platform, package_dir, output_dir, args.config_file, args.archs, args.prerelease_pythons
2021-09-19 00:19:28 -04:00
)
identifiers = get_build_identifiers(
2021-09-19 00:19:28 -04:00
platform, all_build_options.build_selector, all_build_options.architectures
)
2021-01-18 00:09:11 -05:00
if args.print_build_identifiers:
2021-01-18 00:09:11 -05:00
for identifier in identifiers:
print(identifier)
2021-01-17 14:13:26 -05:00
sys.exit(0)
2021-09-19 00:19:28 -04:00
build_options = AllBuildOptions(all_build_options, build_options_by_selector, identifiers)
2021-09-19 00:19:28 -04:00
# Add CIBUILDWHEEL environment variable
# This needs to be passed on to the docker container in linux.py
os.environ["CIBUILDWHEEL"] = "1"
2017-03-19 20:57:51 +00: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) # type: ignore[no-untyped-call,assignment]
2017-04-13 15:02:04 +01:00
print_preamble(platform, build_options)
2021-01-22 09:23:52 -05:00
try:
2021-01-22 09:33:22 -05:00
allowed_architectures_check(platform, build_options.architectures)
2021-01-22 09:23:52 -05:00
except ValueError as err:
print("cibuildwheel:", *err.args, file=sys.stderr)
sys.exit(4)
2021-01-22 08:54:19 -05:00
if not identifiers:
print(
f"cibuildwheel: No build identifiers selected: {build_options.build_selector}",
file=sys.stderr,
)
2021-01-22 08:54:19 -05:00
if not args.allow_empty:
sys.exit(3)
2020-06-15 01:53:31 +02:00
if not output_dir.exists():
output_dir.mkdir(parents=True)
2017-07-02 18:02:27 -05:00
2021-04-30 17:56:34 -04:00
with cibuildwheel.util.print_new_wheels(
"\n{n} wheels produced in {m:.0f} minutes:", output_dir
):
2021-05-03 11:45:43 -04:00
if platform == "linux":
2021-02-04 14:27:54 -05:00
cibuildwheel.linux.build(build_options)
2021-05-03 11:45:43 -04:00
elif platform == "windows":
2021-02-04 14:27:54 -05:00
cibuildwheel.windows.build(build_options)
2021-05-03 11:45:43 -04:00
elif platform == "macos":
2021-02-04 14:27:54 -05:00
cibuildwheel.macos.build(build_options)
else:
assert_never(platform)
2017-03-19 20:57:51 +00:00
2021-09-19 00:19:28 -04:00
def print_preamble(platform: str, build_options: AllBuildOptions) -> None:
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
_ _ _ _ _ _ _
___|_| |_ _ _|_| |_| |_ _ _| |_ ___ ___| |
| _| | . | | | | | . | | | | | -_| -_| |
|___|_|___|___|_|_|___|_____|_|_|___|___|_|
2021-05-03 11:45:43 -04:00
"""
2021-04-30 17:56:34 -04:00
)
)
2017-04-13 15:02:04 +01:00
2021-05-03 11:45:43 -04:00
print(f"cibuildwheel version {cibuildwheel.__version__}\n")
2017-04-13 15:02:04 +01:00
2021-05-03 11:45:43 -04:00
print("Build options:")
print(f" platform: {platform!r}")
2021-09-18 17:46:55 -04:00
print(textwrap.indent(str(build_options), " "))
2017-04-13 15:02:04 +01:00
warnings = detect_warnings(platform, build_options)
if warnings:
2021-05-03 11:45:43 -04:00
print("\nWarnings:")
for warning in warnings:
2021-05-03 11:45:43 -04:00
print(" " + warning)
2021-05-03 11:45:43 -04:00
print("\nHere we go!\n")
2017-04-13 15:02:04 +01:00
2021-01-18 00:09:11 -05:00
def get_build_identifiers(
platform: PlatformName, build_selector: BuildSelector, architectures: Set[Architecture]
) -> List[str]:
2021-04-30 17:56:34 -04:00
python_configurations: Union[
List[cibuildwheel.linux.PythonConfiguration],
List[cibuildwheel.windows.PythonConfiguration],
List[cibuildwheel.macos.PythonConfiguration],
]
2021-01-31 17:14:15 -05:00
2021-05-03 11:45:43 -04:00
if platform == "linux":
2021-04-30 17:56:34 -04:00
python_configurations = cibuildwheel.linux.get_python_configurations(
build_selector, architectures
)
2021-05-03 11:45:43 -04:00
elif platform == "windows":
2021-04-30 17:56:34 -04:00
python_configurations = cibuildwheel.windows.get_python_configurations(
build_selector, architectures
)
2021-05-03 11:45:43 -04:00
elif platform == "macos":
2021-04-30 17:56:34 -04:00
python_configurations = cibuildwheel.macos.get_python_configurations(
build_selector, architectures
)
2021-01-18 00:09:11 -05:00
else:
assert_never(platform)
2021-01-18 00:09:11 -05:00
return [config.identifier for config in python_configurations]
2021-09-19 00:19:28 -04:00
def detect_warnings(platform: str, all_options: AllBuildOptions) -> List[str]:
warnings = []
# warn about deprecated {python} and {pip}
2021-09-19 00:19:28 -04:00
for build_options in all_options.values():
for option_name in ["test_command", "before_build"]:
option_value = getattr(build_options, option_name)
2021-09-19 00:19:28 -04:00
if option_value and ("{python}" in option_value or "{pip}" in option_value):
# Reminder: in an f-string, double braces means literal single brace
msg = (
f"{option_name}: '{{python}}' and '{{pip}}' are no longer needed, "
"and will be removed in a future release. Simply use 'python' or 'pip' instead."
)
warnings.append(msg)
return warnings
2021-05-03 11:45:43 -04:00
if __name__ == "__main__":
2017-03-19 20:57:51 +00:00
main()