Skip to content

Commit 9ca68d8

Browse files
authored
Merge pull request #27 from pypa/feature/build-command
Add build command
2 parents 72d377b + 83e8c6d commit 9ca68d8

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

README.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,13 @@ To test the build backend for a project, run in a system shell:
5353
.. code-block:: shell
5454
5555
python3 -m pep517.check path/to/source # source dir containing pyproject.toml
56+
57+
To build a backend into source and/or binary distributions, run in a shell:
58+
59+
.. code-block:: shell
60+
61+
python -m pep517.build path/to/source # source dir containing pyproject.toml
62+
63+
This 'build' module should be considered experimental while the PyPA `decides
64+
on the best place for this functionality
65+
<https://github.com/pypa/packaging-problems/issues/219>`_.

pep517/build.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"""Build a project using PEP 517 hooks.
2+
"""
3+
import argparse
4+
import logging
5+
import os
6+
import contextlib
7+
import pytoml
8+
import shutil
9+
import errno
10+
import tempfile
11+
12+
from .envbuild import BuildEnvironment
13+
from .wrappers import Pep517HookCaller
14+
15+
log = logging.getLogger(__name__)
16+
17+
18+
@contextlib.contextmanager
19+
def tempdir():
20+
td = tempfile.mkdtemp()
21+
try:
22+
yield td
23+
finally:
24+
shutil.rmtree(td)
25+
26+
27+
def _do_build(hooks, env, dist, dest):
28+
get_requires_name = 'get_requires_for_build_{dist}'.format(**locals())
29+
get_requires = getattr(hooks, get_requires_name)
30+
reqs = get_requires({})
31+
log.info('Got build requires: %s', reqs)
32+
33+
env.pip_install(reqs)
34+
log.info('Installed dynamic build dependencies')
35+
36+
with tempdir() as td:
37+
log.info('Trying to build %s in %s', dist, td)
38+
build_name = 'build_{dist}'.format(**locals())
39+
build = getattr(hooks, build_name)
40+
filename = build(td, {})
41+
source = os.path.join(td, filename)
42+
shutil.move(source, os.path.join(dest, os.path.basename(filename)))
43+
44+
45+
def mkdir_p(*args, **kwargs):
46+
"""Like `mkdir`, but does not raise an exception if the
47+
directory already exists.
48+
"""
49+
try:
50+
return os.mkdir(*args, **kwargs)
51+
except OSError as exc:
52+
if exc.errno != errno.EEXIST:
53+
raise
54+
55+
56+
def build(source_dir, dist, dest=None):
57+
pyproject = os.path.join(source_dir, 'pyproject.toml')
58+
dest = os.path.join(source_dir, dest or 'dist')
59+
mkdir_p(dest)
60+
61+
with open(pyproject) as f:
62+
pyproject_data = pytoml.load(f)
63+
# Ensure the mandatory data can be loaded
64+
buildsys = pyproject_data['build-system']
65+
requires = buildsys['requires']
66+
backend = buildsys['build-backend']
67+
68+
hooks = Pep517HookCaller(source_dir, backend)
69+
70+
with BuildEnvironment() as env:
71+
env.pip_install(requires)
72+
_do_build(hooks, env, dist, dest)
73+
74+
75+
parser = argparse.ArgumentParser()
76+
parser.add_argument(
77+
'source_dir',
78+
help="A directory containing pyproject.toml",
79+
)
80+
parser.add_argument(
81+
'--binary', '-b',
82+
action='store_true',
83+
default=False,
84+
)
85+
parser.add_argument(
86+
'--source', '-s',
87+
action='store_true',
88+
default=False,
89+
)
90+
parser.add_argument(
91+
'--out-dir', '-o',
92+
help="Destination in which to save the builds relative to source dir",
93+
)
94+
95+
96+
def main(args):
97+
# determine which dists to build
98+
dists = list(filter(None, (
99+
'sdist' if args.source or not args.binary else None,
100+
'wheel' if args.binary or not args.source else None,
101+
)))
102+
103+
for dist in dists:
104+
build(args.source_dir, dist, args.out_dir)
105+
106+
107+
if __name__ == '__main__':
108+
main(parser.parse_args())

0 commit comments

Comments
 (0)