From 1d7054c02290e96750295908d70db045ae3f1dbb Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 1 Sep 2022 22:13:08 +0100 Subject: [PATCH] Switch to potential new distutils environment variable --- cibuildwheel/windows.py | 227 +++++++++++++++------------------------- 1 file changed, 86 insertions(+), 141 deletions(-) diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py index c1c23062..2832e17b 100644 --- a/cibuildwheel/windows.py +++ b/cibuildwheel/windows.py @@ -133,50 +133,10 @@ def setup_setuptools_cross_compile( python_configuration: PythonConfiguration, python_libs_base: Path, env: dict[str, str], - cleanup_command_list: list[Callable[[], Any]], ) -> None: - # We write to distutils_cfg for distutils-based builds to override some - # settings. Ideally, we'd pass them on the command line, but since we don't - # know that setuptools is going to be used, we can't do it. - userprofile = os.getenv("USERPROFILE") - if not userprofile: - log.warning( - "Unable to configure setuptools for cross-compiling because USERPROFILE was not found" - ) - return - distutils_cfg = Path(userprofile) / "pydistutils.cfg" - if distutils_cfg.is_file(): - # More than 1000 backup files is likely a different issue, so don't - # bother going further than that. - for counter in range(1000): - distutils_bak = distutils_cfg.with_suffix(f".{counter}.bak") - if not distutils_bak.exists(): - break - else: - log.warning( - f"Unable to configure setuptools for cross-compiling because existing {distutils_cfg} file cannot be backed up: Too many existing backup files were found" - ) - return - - # Move the existing file and restore it when we exit - log.notice( - f"Preserving {distutils_cfg} as {distutils_bak.name}. It will be restored afterwards." - ) - try: - distutils_cfg.replace(distutils_bak) - except OSError as exc: - log.warning( - f"Unable to configure setuptools for cross-compiling because existing {distutils_cfg} file cannot be backed up. Error was {exc}" - ) - return - cleanup_command_list.append(lambda: distutils_bak.replace(distutils_cfg)) - elif distutils_cfg.is_dir(): - log.warning( - f"Unable to configure setuptools for cross-compiling because {distutils_cfg} is a directory" - ) - return - else: - cleanup_command_list.append(lambda: distutils_cfg.unlink()) + distutils_cfg = python_libs_base / "extra-setup.cfg" + env["DISTUTILS_EXTRA_CONFIG"] = str(distutils_cfg) + log.notice(f"Setting DISTUTILS_EXTRA_CONFIG={distutils_cfg} for cross-compilation") # Ensure our additional import libraries are made available, and explicitly # set the platform name @@ -186,8 +146,6 @@ def setup_setuptools_cross_compile( distutils_cfg.write_text( textwrap.dedent( f"""\ - # I am a temporary configuration file generated by cibuildwheel. - # Please remove me or I will likely break your other builds. [build] plat_name={plat_name} [build_ext] @@ -221,7 +179,6 @@ def setup_rust_cross_compile( python_configuration: PythonConfiguration, python_libs_base: Path, env: dict[str, str], - cleanup_command_list: list[Callable[[], Any]], ) -> None: # Assume that MSVC will be used, because we already know that we are # cross-compiling. MinGW users can set CARGO_BUILD_TARGET themselves @@ -252,7 +209,6 @@ def setup_python( dependency_constraint_flags: Sequence[PathOrStr], environment: ParsedEnvironment, build_frontend: BuildFrontend, - cleanup_command_list: list[Callable[[], Any]], ) -> dict[str, str]: tmp.mkdir() implementation_id = python_configuration.identifier.split("-")[0] @@ -374,10 +330,8 @@ def setup_python( if python_libs_base: # Set up the environment for various backends to enable cross-compilation - setup_setuptools_cross_compile( - python_configuration, python_libs_base, env, cleanup_command_list - ) - setup_rust_cross_compile(python_configuration, python_libs_base, env, cleanup_command_list) + setup_setuptools_cross_compile(python_configuration, python_libs_base, env) + setup_rust_cross_compile(python_configuration, python_libs_base, env) return env @@ -420,9 +374,6 @@ def build(options: Options, tmp_path: Path) -> None: build_options.dependency_constraints.get_for_python_version(config.version), ] - # list of callables to do any urgent cleanup, for example, - # config files that may bleed into other builds - cleanup_command_list: list[Callable[[], Any]] = [] # install Python env = setup_python( identifier_tmp_dir / "build", @@ -430,108 +381,102 @@ def build(options: Options, tmp_path: Path) -> None: dependency_constraint_flags, build_options.environment, build_options.build_frontend, - cleanup_command_list, ) - try: - compatible_wheel = find_compatible_wheel(built_wheels, config.identifier) - if compatible_wheel: - log.step_end() - print( - f"\nFound previously built wheel {compatible_wheel.name}, that's compatible with {config.identifier}. Skipping build step..." + compatible_wheel = find_compatible_wheel(built_wheels, config.identifier) + if compatible_wheel: + log.step_end() + print( + f"\nFound previously built wheel {compatible_wheel.name}, that's compatible with {config.identifier}. Skipping build step..." + ) + repaired_wheel = compatible_wheel + else: + # run the before_build command + if build_options.before_build: + log.step("Running before_build...") + before_build_prepared = prepare_command( + build_options.before_build, + project=".", + package=options.globals.package_dir, ) - repaired_wheel = compatible_wheel - else: - # run the before_build command - if build_options.before_build: - log.step("Running before_build...") - before_build_prepared = prepare_command( - build_options.before_build, - project=".", - package=options.globals.package_dir, + shell(before_build_prepared, env=env) + + log.step("Building wheel...") + built_wheel_dir.mkdir() + + verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity) + + if build_options.build_frontend == "pip": + # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org + # see https://github.com/pypa/cibuildwheel/pull/369 + call( + "python", + "-m", + "pip", + "wheel", + options.globals.package_dir.resolve(), + f"--wheel-dir={built_wheel_dir}", + "--no-deps", + *get_build_verbosity_extra_flags(build_options.build_verbosity), + env=env, + ) + elif build_options.build_frontend == "build": + config_setting = " ".join(verbosity_flags) + build_env = env.copy() + if build_options.dependency_constraints: + constraints_path = ( + build_options.dependency_constraints.get_for_python_version( + config.version + ) ) - shell(before_build_prepared, env=env) + # Bug in pip <= 21.1.3 - we can't have a space in the + # constraints file, and pip doesn't support drive letters + # in uhi. After probably pip 21.2, we can use uri. For + # now, use a temporary file. + if " " in str(constraints_path): + assert " " not in str(identifier_tmp_dir) + tmp_file = identifier_tmp_dir / "constraints.txt" + tmp_file.write_bytes(constraints_path.read_bytes()) + constraints_path = tmp_file - log.step("Building wheel...") - built_wheel_dir.mkdir() - - verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity) - - if build_options.build_frontend == "pip": - # Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org - # see https://github.com/pypa/cibuildwheel/pull/369 + build_env["PIP_CONSTRAINT"] = str(constraints_path) + build_env["VIRTUALENV_PIP"] = get_pip_version(env) call( "python", "-m", - "pip", - "wheel", - options.globals.package_dir.resolve(), - f"--wheel-dir={built_wheel_dir}", - "--no-deps", - *get_build_verbosity_extra_flags(build_options.build_verbosity), - env=env, + "build", + build_options.package_dir, + "--wheel", + f"--outdir={built_wheel_dir}", + f"--config-setting={config_setting}", + env=build_env, ) - elif build_options.build_frontend == "build": - config_setting = " ".join(verbosity_flags) - build_env = env.copy() - if build_options.dependency_constraints: - constraints_path = ( - build_options.dependency_constraints.get_for_python_version( - config.version - ) - ) - # Bug in pip <= 21.1.3 - we can't have a space in the - # constraints file, and pip doesn't support drive letters - # in uhi. After probably pip 21.2, we can use uri. For - # now, use a temporary file. - if " " in str(constraints_path): - assert " " not in str(identifier_tmp_dir) - tmp_file = identifier_tmp_dir / "constraints.txt" - tmp_file.write_bytes(constraints_path.read_bytes()) - constraints_path = tmp_file + else: + assert_never(build_options.build_frontend) - build_env["PIP_CONSTRAINT"] = str(constraints_path) - build_env["VIRTUALENV_PIP"] = get_pip_version(env) - call( - "python", - "-m", - "build", - build_options.package_dir, - "--wheel", - f"--outdir={built_wheel_dir}", - f"--config-setting={config_setting}", - env=build_env, - ) - else: - assert_never(build_options.build_frontend) + built_wheel = next(built_wheel_dir.glob("*.whl")) - built_wheel = next(built_wheel_dir.glob("*.whl")) + # repair the wheel + repaired_wheel_dir.mkdir() - # repair the wheel - repaired_wheel_dir.mkdir() + if built_wheel.name.endswith("none-any.whl"): + raise NonPlatformWheelError() - if built_wheel.name.endswith("none-any.whl"): - raise NonPlatformWheelError() + if build_options.repair_command: + log.step("Repairing wheel...") + repair_command_prepared = prepare_command( + build_options.repair_command, + wheel=built_wheel, + dest_dir=repaired_wheel_dir, + ) + shell(repair_command_prepared, env=env) + else: + shutil.move(str(built_wheel), repaired_wheel_dir) - if build_options.repair_command: - log.step("Repairing wheel...") - repair_command_prepared = prepare_command( - build_options.repair_command, - wheel=built_wheel, - dest_dir=repaired_wheel_dir, - ) - shell(repair_command_prepared, env=env) - else: - shutil.move(str(built_wheel), repaired_wheel_dir) + repaired_wheel = next(repaired_wheel_dir.glob("*.whl")) - repaired_wheel = next(repaired_wheel_dir.glob("*.whl")) - - if repaired_wheel.name in {wheel.name for wheel in built_wheels}: - raise AlreadyBuiltWheelError(repaired_wheel.name) - - finally: - while cleanup_command_list: - cleanup_command_list.pop(0)() + if repaired_wheel.name in {wheel.name for wheel in built_wheels}: + raise AlreadyBuiltWheelError(repaired_wheel.name) if build_options.test_command and options.globals.test_selector(config.identifier): if config.arch == "ARM64" != platform_module.machine():