From 17b896cd95f59a9b5aece457917e6723a54b2f88 Mon Sep 17 00:00:00 2001 From: John Sirois Date: Thu, 7 Nov 2024 19:53:12 -0800 Subject: [PATCH] Add support for gathering all requirements needed to build a wheel for a project. --- pex/build_system/__init__.py | 1 + pex/build_system/pep_517.py | 27 +++++++++++++++++++++------ pex/build_system/pep_518.py | 28 +++++++++++++++++++++------- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/pex/build_system/__init__.py b/pex/build_system/__init__.py index 254ca6d60..46db28c49 100644 --- a/pex/build_system/__init__.py +++ b/pex/build_system/__init__.py @@ -10,3 +10,4 @@ # # See: https://peps.python.org/pep-0517/#source-trees DEFAULT_BUILD_BACKEND = "setuptools.build_meta:__legacy__" +DEFAULT_BUILD_REQUIRES = ("setuptools",) diff --git a/pex/build_system/pep_517.py b/pex/build_system/pep_517.py index 5f1573813..50015a434 100644 --- a/pex/build_system/pep_517.py +++ b/pex/build_system/pep_517.py @@ -10,10 +10,11 @@ from pex import third_party from pex.build_system import DEFAULT_BUILD_BACKEND -from pex.build_system.pep_518 import BuildSystem, load_build_system +from pex.build_system.pep_518 import BuildSystem, load_build_system, load_build_system_table from pex.common import safe_mkdtemp from pex.dist_metadata import DistMetadata, Distribution, MetadataType from pex.jobs import Job, SpawnedJob +from pex.orderedset import OrderedSet from pex.pip.version import PipVersion, PipVersionValue from pex.resolve.resolvers import Resolver from pex.result import Error, try_ @@ -22,7 +23,7 @@ from pex.typing import TYPE_CHECKING, cast if TYPE_CHECKING: - from typing import Any, Dict, Iterable, List, Mapping, Optional, Set, Text, Union + from typing import Any, Dict, Iterable, List, Mapping, Optional, Set, Text, Tuple, Union _DEFAULT_BUILD_SYSTEMS = {} # type: Dict[PipVersionValue, BuildSystem] @@ -248,15 +249,16 @@ def build_sdist( return os.path.join(dist_dir, sdist_relpath) -def spawn_prepare_metadata( +def get_requires_for_build_wheel( project_directory, # type: str target, # type: Target resolver, # type: Resolver pip_version=None, # type: Optional[PipVersionValue] ): - # type: (...) -> SpawnedJob[DistMetadata] + # type: (...) -> Tuple[str, ...] - extra_requirements = [] + build_system_table = try_(load_build_system_table(project_directory)) + requires = OrderedSet(build_system_table.requires) spawned_job = try_( _invoke_build_hook( project_directory, @@ -267,11 +269,24 @@ def spawn_prepare_metadata( ) ) try: - extra_requirements.extend(spawned_job.await_result()) + requires.update(spawned_job.await_result()) except Job.Error as e: if e.exitcode != _HOOK_UNAVAILABLE_EXIT_CODE: raise e + return tuple(requires) + +def spawn_prepare_metadata( + project_directory, # type: str + target, # type: Target + resolver, # type: Resolver + pip_version=None, # type: Optional[PipVersionValue] +): + # type: (...) -> SpawnedJob[DistMetadata] + + extra_requirements = get_requires_for_build_wheel( + project_directory, target, resolver, pip_version=pip_version + ) build_dir = os.path.join(safe_mkdtemp(), "build") os.mkdir(build_dir) spawned_job = try_( diff --git a/pex/build_system/pep_518.py b/pex/build_system/pep_518.py index 09387dfce..2cedc6a16 100644 --- a/pex/build_system/pep_518.py +++ b/pex/build_system/pep_518.py @@ -7,7 +7,7 @@ import subprocess from pex import toml -from pex.build_system import DEFAULT_BUILD_BACKEND +from pex.build_system import DEFAULT_BUILD_BACKEND, DEFAULT_BUILD_REQUIRES from pex.common import REPRODUCIBLE_BUILDS_ENV, CopyMode from pex.dist_metadata import Distribution from pex.interpreter import PythonInterpreter @@ -159,6 +159,25 @@ def create( env = attr.ib() # type: Mapping[str, str] +def _maybe_load_build_system_table(project_directory): + # type: (str) -> Union[Optional[BuildSystemTable], Error] + + # The interface is spec'd here: https://peps.python.org/pep-0518/ + pyproject_toml = os.path.join(project_directory, "pyproject.toml") + if not os.path.isfile(pyproject_toml): + return None + return _read_build_system_table(pyproject_toml) + + +def load_build_system_table(project_directory): + # type: (str) -> Union[BuildSystemTable, Error] + + maybe_build_system_table_or_error = _maybe_load_build_system_table(project_directory) + if maybe_build_system_table_or_error is not None: + return maybe_build_system_table_or_error + return BuildSystemTable(requires=DEFAULT_BUILD_REQUIRES, build_backend=DEFAULT_BUILD_BACKEND) + + def load_build_system( target, # type: Target resolver, # type: Resolver @@ -167,12 +186,7 @@ def load_build_system( ): # type: (...) -> Union[Optional[BuildSystem], Error] - # The interface is spec'd here: https://peps.python.org/pep-0518/ - pyproject_toml = os.path.join(project_directory, "pyproject.toml") - if not os.path.isfile(pyproject_toml): - return None - - maybe_build_system_table_or_error = _read_build_system_table(pyproject_toml) + maybe_build_system_table_or_error = _maybe_load_build_system_table(project_directory) if not isinstance(maybe_build_system_table_or_error, BuildSystemTable): return maybe_build_system_table_or_error build_system_table = maybe_build_system_table_or_error