You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

build.py 2.6KB

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