from __future__ import annotations from pathlib import Path import os import sys import sysconfig import platform from functools import lru_cache from typing import Optional, Union, List 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.as_posix()) def _compute_best(cls, *arg, **kwargs): from scikit_build_core.builder.wheel_tag import WheelTag 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[import,attr-defined] return distutils._msvccompiler._get_vc_env(vc_arch) # type: ignore[no-any-return] except AttributeError: from setuptools._distutils import ( _msvccompiler, # type: ignore[import,attr-defined] ) return _msvccompiler._get_vc_env(vc_arch) # type: ignore[no-any-return,attr-defined] def _fix_env(): config = get_config() if sys.platform.startswith("win"): if config.build.ninja: from setuptools import msvc 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 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) -> Optional[dict[str, Union[str, List[str]]]]: 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 print(f"LIEF does not provide precompiled wheels for '{sys.platform} - {platform.machine()}'", 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: Optional[dict[str, Union[str, List[str]]]] = None, ) -> list[str]: return _get_build_requirements(is_editable=False) def prepare_metadata_for_build_wheel( metadata_directory: str, config_settings: Optional[dict[str, Union[str, List[str]]]] = 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: Optional[dict[str, Union[str, List[str]]]] = None, ) -> List[str]: return _get_build_requirements(is_editable=True) def prepare_metadata_for_build_editable( metadata_directory: str, config_settings: Optional[dict[str, Union[str, List[str]]]] = 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: Optional[dict[str, Union[str, List[str]]]] = None, metadata_directory: Optional[str] = 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: Optional[dict[str, Union[str, List[str]]]] = None, ) -> str: from scikit_build_core.build.sdist import build_sdist as _impl config_settings = _get_hooked_config(is_editable=False) config_settings['sdist.exclude'].extend([ '*' ]) 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)