|
"""Build a project using PEP 517 hooks. |
|
""" |
|
import argparse |
|
import logging |
|
import os |
|
import shutil |
|
import tempfile |
|
|
|
from ._compat import tomllib |
|
from .envbuild import BuildEnvironment |
|
from .wrappers import Pep517HookCaller |
|
|
|
log = logging.getLogger(__name__) |
|
|
|
|
|
def validate_system(system): |
|
""" |
|
Ensure build system has the requisite fields. |
|
""" |
|
required = {'requires', 'build-backend'} |
|
if not (required <= set(system)): |
|
message = "Missing required fields: {missing}".format( |
|
missing=required-set(system), |
|
) |
|
raise ValueError(message) |
|
|
|
|
|
def load_system(source_dir): |
|
""" |
|
Load the build system from a source dir (pyproject.toml). |
|
""" |
|
pyproject = os.path.join(source_dir, 'pyproject.toml') |
|
with open(pyproject, 'rb') as f: |
|
pyproject_data = tomllib.load(f) |
|
return pyproject_data['build-system'] |
|
|
|
|
|
def compat_system(source_dir): |
|
""" |
|
Given a source dir, attempt to get a build system backend |
|
and requirements from pyproject.toml. Fallback to |
|
setuptools but only if the file was not found or a build |
|
system was not indicated. |
|
""" |
|
try: |
|
system = load_system(source_dir) |
|
except (FileNotFoundError, KeyError): |
|
system = {} |
|
system.setdefault( |
|
'build-backend', |
|
'setuptools.build_meta:__legacy__', |
|
) |
|
system.setdefault('requires', ['setuptools', 'wheel']) |
|
return system |
|
|
|
|
|
def _do_build(hooks, env, dist, dest): |
|
get_requires_name = 'get_requires_for_build_{dist}'.format(**locals()) |
|
get_requires = getattr(hooks, get_requires_name) |
|
reqs = get_requires({}) |
|
log.info('Got build requires: %s', reqs) |
|
|
|
env.pip_install(reqs) |
|
log.info('Installed dynamic build dependencies') |
|
|
|
with tempfile.TemporaryDirectory() as td: |
|
log.info('Trying to build %s in %s', dist, td) |
|
build_name = 'build_{dist}'.format(**locals()) |
|
build = getattr(hooks, build_name) |
|
filename = build(td, {}) |
|
source = os.path.join(td, filename) |
|
shutil.move(source, os.path.join(dest, os.path.basename(filename))) |
|
|
|
|
|
def build(source_dir, dist, dest=None, system=None): |
|
system = system or load_system(source_dir) |
|
dest = os.path.join(source_dir, dest or 'dist') |
|
os.makedirs(dest, exist_ok=True) |
|
|
|
validate_system(system) |
|
hooks = Pep517HookCaller( |
|
source_dir, system['build-backend'], system.get('backend-path') |
|
) |
|
|
|
with BuildEnvironment() as env: |
|
env.pip_install(system['requires']) |
|
_do_build(hooks, env, dist, dest) |
|
|
|
|
|
parser = argparse.ArgumentParser() |
|
parser.add_argument( |
|
'source_dir', |
|
help="A directory containing pyproject.toml", |
|
) |
|
parser.add_argument( |
|
'--binary', '-b', |
|
action='store_true', |
|
default=False, |
|
) |
|
parser.add_argument( |
|
'--source', '-s', |
|
action='store_true', |
|
default=False, |
|
) |
|
parser.add_argument( |
|
'--out-dir', '-o', |
|
help="Destination in which to save the builds relative to source dir", |
|
) |
|
|
|
|
|
def main(args): |
|
log.warning('pep517.build is deprecated. ' |
|
'Consider switching to https://pypi.org/project/build/') |
|
|
|
|
|
dists = list(filter(None, ( |
|
'sdist' if args.source or not args.binary else None, |
|
'wheel' if args.binary or not args.source else None, |
|
))) |
|
|
|
for dist in dists: |
|
build(args.source_dir, dist, args.out_dir) |
|
|
|
|
|
if __name__ == '__main__': |
|
main(parser.parse_args()) |
|
|