from __future__ import annotations import os import platform import sys import sysconfig from functools import lru_cache from pathlib import Path CWD = Path(__file__).parent ROOT_DIR = CWD / ".." / ".." / ".." BINDING_DIR = ROOT_DIR / "api" / "python" SRC_DIR = ROOT_DIR.resolve().absolute() DEFAULT_CONF = CWD / ".." / "config-default.toml" _WHEEL_TAG_COMPUTE_BEST = None @lru_cache(maxsize=1) def get_config(): from config import Config if path := os.getenv("PYLIEF_CONF"): config_path = Path(path).resolve().absolute() return Config.from_file(config_path) return Config.from_file(DEFAULT_CONF) def _compute_best(cls, *arg, **kwargs): from scikit_build_core.builder.wheel_tag import WheelTag assert _WHEEL_TAG_COMPUTE_BEST is not None best_tag = _WHEEL_TAG_COMPUTE_BEST(cls, *arg, **kwargs) config = get_config() pyvers = best_tag.pyvers abis = best_tag.abis archs = best_tag.archs if version := config.cross_compilation.pyversion: pyvers = [version] if abi := config.cross_compilation.abi: abis = [abi] if platform := config.cross_compilation.platform: archs = [platform] return WheelTag(pyvers, abis, archs) def _get_vc_env(vc_arch: str) -> dict[str, str]: try: from setuptools import distutils # type: ignore return distutils._msvccompiler._get_vc_env(vc_arch) except AttributeError: from setuptools._distutils import _msvccompiler # type: ignore return _msvccompiler._get_vc_env(vc_arch) # type: ignore def _fix_env(): config = get_config() if sys.platform.startswith("win"): if config.build.ninja: is64 = sys.maxsize > 2**32 arch = "x64" if is64 else "x86" ninja_env = _get_vc_env(arch) os.environ.update(ninja_env) if sys.platform.startswith("darwin"): deployment_target = os.getenv( "MACOSX_DEPLOYMENT_TARGET", None ) or sysconfig.get_config_var("MACOSX_DEPLOYMENT_TARGET") os.environ["_PYTHON_HOST_PLATFORM"] = ( f"macosx-{deployment_target}-{config.osx_arch}" ) # scikit_build_core is aware of ARCHFLAGS for the wheel arch: # builder/builder.py:get_archs() os.environ["ARCHFLAGS"] = f"-arch {config.osx_arch}" if platform := config.cross_compilation.platform: global _WHEEL_TAG_COMPUTE_BEST from scikit_build_core.builder.wheel_tag import WheelTag _WHEEL_TAG_COMPUTE_BEST = WheelTag.compute_best WheelTag.compute_best = _compute_best # type: ignore os.environ["_PYTHON_HOST_PLATFORM"] = platform if os.name == "nt" and platform == "win32": os.environ["VSCMD_ARG_TGT_ARCH"] = "x86" sys.version = sys.version.lower().replace("amd64", "") sys.platform = "win32" def _get_hooked_config(is_editable: bool) -> dict[str, str | list[str]] | None: from config import Config config = get_config() if config is None: raise RuntimeError("Can't get the configuration") if config.build.py_api.startswith("cp3"): # https://github.com/scikit-build/scikit-build-core/blob/9ac2e35aa888b70e9f1999a75dfafb7a5d709f88/src/scikit_build_core/builder/wheel_tag.py#L83-L85 # Make sure that this check is not silently warned since it could lead # to a wrong .whl filename minor = int(config.build.py_api[3:]) if sys.implementation.name != "cpython" or minor > sys.version_info.minor: msg = f"Ignoring py-api, not a CPython interpreter ({sys.implementation.name}) or version (3.{minor}) is too high" raise RuntimeError(msg) config_settings = { "logging.level": "DEBUG", "build-dir": config.build_dir, "build.targets": config.build.targets, "install.strip": config.strip, "backport.find-python": "0", "wheel.py-api": config.build.py_api, "cmake.source-dir": SRC_DIR.as_posix(), "cmake.build-type": config.build.build_type, "cmake.args": [ *config.cmake_generator, *config.get_cmake_args(is_editable), ], "sdist.exclude": Config.DEFAULT_EXCLUDE, } return config_settings def _get_build_requirements(is_editable: bool) -> list[str]: build_req_file = BINDING_DIR / "build-requirements.txt" if not build_req_file.is_file() and not (BINDING_DIR / "src").is_dir(): # By convention this is an mocked sdist impl = sys.implementation.name version = "{0.major}.{0.minor}.{0.micro}".format(sys.version_info) free_threaded = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) ft_status = "free-threaded" if free_threaded else "GIL-enabled" print( f"LIEF does not provide precompiled wheels for " f"'{sys.platform} - {platform.machine()}' " f"(Python: {impl} {version} [{ft_status}])", file=sys.stderr, ) sys.exit(1) reqs = [ line for line in build_req_file.read_text().splitlines() if not line.startswith("#") ] return reqs def get_requires_for_build_wheel( config_settings: dict[str, str | list[str]] | None = None, ) -> list[str]: return _get_build_requirements(is_editable=False) def prepare_metadata_for_build_wheel( metadata_directory: str, config_settings: dict[str, str | list[str]] | None = None, ) -> str: from scikit_build_core.build import prepare_metadata_for_build_wheel as _impl _fix_env() return _impl(metadata_directory, config_settings) def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): from scikit_build_core.build import build_wheel as _impl config_settings = _get_hooked_config(is_editable=False) _fix_env() return _impl(wheel_directory, config_settings, metadata_directory) def get_requires_for_build_editable( config_settings: dict[str, str | list[str]] | None = None, ) -> list[str]: return _get_build_requirements(is_editable=True) def prepare_metadata_for_build_editable( metadata_directory: str, config_settings: dict[str, str | list[str]] | None = None, ) -> str: from scikit_build_core.build import prepare_metadata_for_build_editable as _impl _fix_env() return _impl(metadata_directory, config_settings) def build_editable( wheel_directory: str, config_settings: dict[str, str | list[str]] | None = None, metadata_directory: str | None = None, ) -> str: from scikit_build_core.build import build_editable as _impl config_settings = _get_hooked_config(is_editable=True) _fix_env() return _impl(wheel_directory, config_settings, metadata_directory) def build_sdist( sdist_directory: str, config_settings: dict[str, str | list[str]] | None = None, ) -> str: from scikit_build_core.build.sdist import build_sdist as _impl config_settings = _get_hooked_config(is_editable=False) assert config_settings is not None config_settings["sdist.exclude"].extend(["*"]) # type: ignore config_settings["sdist.include"] = [ "pyproject.toml", "backend/setup.py", "backend/versioning.py", "backend/config.py", "backend/dynamic_provider.py", ] return _impl(sdist_directory, config_settings)