| """Build a project using PEP 517 hooks. |
| """ |
| import argparse |
| import io |
| import logging |
| import os |
| import shutil |
| |
| from .envbuild import BuildEnvironment |
| from .wrappers import Pep517HookCaller |
| from .dirtools import tempdir, mkdir_p |
| from .compat import FileNotFoundError, toml_load |
| |
| 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 io.open(pyproject, 'rb') as f: |
| pyproject_data = toml_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 tempdir() 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') |
| mkdir_p(dest) |
| |
| 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/') |
| |
| # determine which dists to 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()) |