PEP 517: update backend example (#310)

- update hook names and signatures
- clearly separate sdist building & wheel building
- add support for out-of-tree wheel builds
- clarify build_wheel spec based on updated example
- be explicit that out-of-tree builds should match
  the results of building via sdist
This commit is contained in:
Nick Coghlan 2017-07-16 15:12:00 +10:00 committed by GitHub
parent 20c110f591
commit 0189da7b60
1 changed files with 82 additions and 30 deletions

View File

@ -190,25 +190,38 @@ metadata. The directory passed in by the build frontend MUST be
identical to the directory created by ``prepare_wheel_metadata``,
including any unrecognized files it created.
If build_directory is not None, it is a unicode string containing the
path to a directory where intermediate build artifacts may be stored.
This may be empty, or it may contain artifacts from a previous build to
be used as a cache. The backend is responsible for determining whether
any cached artifacts are outdated. When a build_directory is provided,
the backend should not create or modify any files in the source
directory (the working directory where the hook is called). If the
backend cannot reliably avoid modifying the directory it builds from, it
should copy any files it needs to build_directory and perform the build
there.
Backends which do not provide the ``prepare_wheel_metadata`` hook may either
silently ignore the ``metadata_directory`` parameter to ``build_wheel``, or
else raise an exception when it is set to anything other than ``None``.
If build_directory is None, the backend may do an 'in place' build which
modifies the source directory. The semantics of this are not specified
here.
If ``build_directory`` is not None, it is a unicode string containing the
path to a directory other than the source directory (the working directory where
the hook is called) where intermediate build artifacts may be stored.
Whatever the value of build_directory, the backend may also store intermediates
in other cache locations or temporary directories, which it is responsible for
managing. The presence or absence of any caches should not make a
material difference to the final result of the build.
These out-of-tree builds should have the same result as first building an sdist
and then building a wheel from that sdist. If the backend cannot otherwise
ensure that unexpected files won't end up in the built wheel archive, it should
copy any essential input files it needs to ``build_directory`` and perform the
build there.
The provided build directory may be empty, or it may contain artifacts from a
previous build to be used as a cache. The backend is responsible for determining
whether any cached artifacts are outdated.
If ``build_directory`` is None, the backend may do an 'in place' build which
modifies the source directory and may produce different results from those that
would be obtained by exporting an sdist first. The exact semantics of this will
depend on the build system in use, and hence are not specified here.
To guard against frontends that are not fully compliant with this specification,
defensively coded backends may treat
``os.path.samefile(build_directory, os.getcwd())`` as a request for an in place
build.
Whatever the value of ``build_directory``, the backend may also store
intermediate artifacts in other cache locations or temporary directories, which
it is responsible for managing. The presence or absence of any caches should not
make a material difference to the final result of the build.
build_sdist
-----------
@ -618,16 +631,15 @@ build backend::
# mypackage_custom_build_backend.py
import os.path
import pathlib
import shutil
def get_build_requires(config_settings, config_directory):
return ["wheel"]
SDIST_NAME = "mypackage-0.1"
SDIST_FILENAME = SDIST_NAME + ".tar.gz"
WHEEL_FILENAME = "mypackage-0.1-py2.py3-none-any.whl"
def build_wheel(wheel_directory, config_settings, config_directory=None):
from wheel.archive import archive_wheelfile
filename = "mypackage-0.1-py2.py3-none-any"
path = os.path.join(wheel_directory, filename)
archive_wheelfile(path, "src/")
return filename
#################
# sdist creation
#################
def _exclude_hidden_and_special_files(archive_entry):
"""Tarfile filter to exclude hidden and special files from the archive"""
@ -636,14 +648,54 @@ build backend::
return archive_entry
return None
def build_sdist(sdist_dir, config_settings):
sdist_subdir = "mypackage-0.1"
sdist_path = pathlib.Path(sdist_dir) / (sdist_subdir + ".tar.gz")
def _make_sdist(sdist_dir):
"""Make an sdist and return both the Python object and its filename"""
sdist_path = pathlib.Path(sdist_dir) / SDIST_FILENAME
sdist = tarfile.open(sdist_path, "w:gz", format=tarfile.PAX_FORMAT)
# Tar up the whole directory, minus hidden and special files
sdist.add(os.getcwd(), arcname=sdist_subdir,
sdist.add(os.getcwd(), arcname=SDIST_NAME,
filter=_exclude_hidden_and_special_files)
return sdist_subdir + ".tar.gz"
return sdist, SDIST_FILENAME
def build_sdist(sdist_dir, config_settings):
"""PEP 517 sdist creation hook"""
sdist, sdist_filename = _make_sdist(sdist_dir)
return sdist_filename
#################
# wheel creation
#################
def get_requires_for_build_wheel(config_settings):
"""PEP 517 wheel building dependency definition hook"""
# As a simple static requirement, this could also just be
# listed in the project's build system dependencies instead
return ["wheel"]
def _prepare_out_of_tree_build(build_directory):
"""Prepare out-of-tree build by way of unpacking the sdist"""
sdist, sdist_filename = _make_sdist(build_directory)
os.chdir(build_directory)
if os.path.exists(SDIST_NAME):
# Prevent caching of stale input files
shutil.rmtree(SDIST_NAME)
sdist.extractall()
os.remove(sdist_filename)
os.chdir(SDIST_NAME)
def build_wheel(wheel_directory, build_directory=None,
metadata_directory=None, config_settings=None):
"""PEP 517 wheel creation hook"""
# First check if the frontend has requested an out-of-tree build
out_of_tree_build = (build_directory is not None and
not os.path.samefile(build_directory, os.getcwd())
if out_of_tree_build:
_prepare_out_of_tree_build(build_directory)
# Continue with assembling the wheel archive
from wheel.archive import archive_wheelfile
path = os.path.join(wheel_directory, WHEEL_FILENAME)
archive_wheelfile(path, "src/")
return WHEEL_FILENAME
Of course, this is a *terrible* build backend: it requires the user to
have manually set up the wheel metadata in