Add the buildthings build backend.
Review Request #14951 — Created March 20, 2026 and submitted
Buildthings is a setuptools-based Python build backend that makes it
easier to work with projects with more complex build requirements,
particularly those involving NPM or packages that may still be in
development in local source trees.It's based on the build backends we introduced in Review Board and
Djblets, but more refined and capable of being expanded for additional
use cases down the road.This initial version supports:
-
.local-packages/, mapping dependency names to local source trees,
used in isolated build environments (available per-build type). -
Options for including dev- and install-time dependencies within the
isolated build environments (available per-build type). -
An option for excluding certain, more complex dependencies from the
isolated environments (available per-build type). -
Options for enabling NPM installs and for managing workspaces based on
other Python packages. -
Support for extra build steps for editables, wheels, and source
distributions.
Much of this behaves the way it did in our in-tree build backends, but
with several behavior issues fixed. For example, development
dependencies will now map to local paths if found in .local-packages/.
Further, depending on an editable package using this backend will no
longer error out when it in turn depends on other local trees.
Full documentation for all the options are available in the README.
Converted Djblets and Review Board over to buildthings.
Tested building in new virtualenvs using editable mode, ensuring
that.local-packageswas picked up, and using the install deps
options.Checked that when building Review Board, the Djblets
.local-packages
was used.Verified that the local packages weren't installed as package
dependencies (which would override any editable installs).Tested various combinations of the options and overrides.
Tested building sdist and wheel distributions, with and without
.local-packagesand other deps turned on.Tested the build steps support.
Tested that when building packages with
include-{dev,install}-deps,
those packages were installed in the environment but that the
development deps didn't become part of the final package's dependency
list.
| Summary | ID |
|---|---|
| 507d56edb28f26676642250128dd531a02ad1abb |
| Description | From | Last Updated |
|---|---|---|
|
We need to add toml as a dependency for python_version<3.11 |
|
|
|
There's no override of prepare_metadata_for_build_sdist. Should we have one? |
|
|
|
This should probably have a line of ===== above it too. |
|
|
|
'from setuptools.build_meta import *' used; unable to detect undefined names Column: 1 Error code: F403 |
|
|
|
'setuptools.build_meta.*' imported but unused Column: 1 Error code: F401 |
|
|
|
Looks like some of this might have been copy/pasted from sdist |
|
|
|
Are these things actually necessary for the metadata operation? This is pretty heavyweight, and seems like it could be limited … |
|
|
|
The local_packages_path in isolation_config is str | None but apply_local_dep_paths types this as str |
|
|
|
This will end up computing npm_workspaces_dir relative to buildthings/backend.py. I think we want the cwd instead. |
|
|
|
Because command is an str, this needs shell=True |
|
|
|
We should pass text=True to subprocess.run(), otherwise the error will look like this: Failed to install npm packages: b'...' |
|
|
|
Shouldn't this be ImportError? |
|
|
|
This cache works when we're doing a single type of build, but in the case where a package has different … |
|
|
|
This drops invalid requirements lines silently. |
|
|
|
This should be able to just be < instead of <= |
|
|
|
'from setuptools.build_meta import *' used; unable to detect undefined names Column: 1 Error code: F403 |
|
|
|
'setuptools.build_meta.*' imported but unused Column: 1 Error code: F401 |
|
|
|
Was this supposed to be included here alongside the build types? |
|
|
|
This is meant to be tool.buildthings.editable.extra-build-steps |
|
|
|
Typo "doen't". |
|
|
|
This should just be [tool.buildthings.isolation]. |
|
|
|
Same here, we can remove the word "editable" since this function is used with any build type. |
|
|
|
This is missing in the Args section of the docstring. |
|
|
|
Do we wanna use logger here instead of a print? |
|
|
|
This needs a Raises section in the docs. |
|
|
|
This needs a Raises docs section too. |
|
|
|
This needs a Raises docs section too. |
|
|
|
exclude_deps contains package names, but deps has full dependency specifiers. We probably need to parse the items in deps and … |
|
|
|
This shouldn't talk about Djblets and Review Board. |
|
|
|
For now this is okay, but keying off only the leaf node might bite us in the future. Is there … |
|
|
|
Can we document in here the stuff about different build types having different local packages, but that's okay because the … |
|
|
|
This will quietly swallow requirements lines like -r some-other-file.txt, -e ., or --index-url .... We probably want to document that … |
|
- Testing Done:
-
+ Converted Djblets and Review Board over to buildthings.
+ + Tested building in new virtualenvs using editable mode, ensuring
+ that .local-packageswas picked up, and using the install deps+ options. + + Checked that when building Review Board, the Djblets
.local-packages+ was used. + + Verified that the local packages weren't installed as package
+ dependencies (which would override any editable installs). + + Tested various combinations of the options and overrides.
+ + Tested building sdist and wheel distributions, with and without
+ .local-packagesand other deps turned on.+ + Tested the build steps support.
- Testing Done:
-
Converted Djblets and Review Board over to buildthings.
Tested building in new virtualenvs using editable mode, ensuring
that .local-packageswas picked up, and using the install depsoptions. Checked that when building Review Board, the Djblets
.local-packageswas used. Verified that the local packages weren't installed as package
dependencies (which would override any editable installs). Tested various combinations of the options and overrides.
Tested building sdist and wheel distributions, with and without
.local-packagesand other deps turned on.Tested the build steps support.
+ + Tested that when building packages with
include-{dev,install}-deps,+ those packages were installed in the environment but that the + development deps didn't become part of the final package's dependency + list.
-
-
-
-
-
-
Are these things actually necessary for the metadata operation? This is pretty heavyweight, and seems like it could be limited to run only in the actual build_* hooks.
-
The local_packages_path in isolation_config is
str | Nonebutapply_local_dep_pathstypes this asstr -
This will end up computing
npm_workspaces_dirrelative to buildthings/backend.py. I think we want the cwd instead. -
-
We should pass
text=Truetosubprocess.run(), otherwise the error will look like this:Failed to install npm packages: b'...'
-
-
This cache works when we're doing a single type of build, but in the case where a package has different local_packages for different targets, we could mess up (for example,
python -m builddoes both wheel and sdist by default, I believe without reloading the build backend). I think we need to have this key off the build type as well. -
- Change Summary:
-
- Added
tomlas a dependency for Python < 3.11. - Added logging when encountering an invalid requirement line in a
requirements.txt-type file. - Removed npm management during
prepare_metadata_for_build_wheel(). - Passed
shell=Truewhen running build steps. - Passed
text=Truewhen runningnpm install, to help with error reporting. - Fixed some doc issues.
- Fixed
apply_local_dep_pathsto take aNonelocal_packages_path. - Fixed the
npm_workspaces_dirto be based on the current working directory of the tree. - Fixed an
IOErrorthat should have beenImportError.
- Added
- Commits:
-
Summary ID 4027a0f9a480988426a486cea07259604217ef5d 316b1b344bd10a11858b4c6e91671dabb102d409
- Change Summary:
-
- Simplified requirement parsing code.
tool.buildthings.dependenciescan now only be dynamic if specified explicitly intool.buildthings.dynamic. This allows for static dependencies here.- Fixed the Python version range for the
tomldependency. - Fixed up
pyproject.tomlfor buildthings to work with the latest changes.
- Commits:
-
Summary ID 316b1b344bd10a11858b4c6e91671dabb102d409 55006baa013819908c4b98a8293931379764a95d
Checks run (2 succeeded)
- Change Summary:
-
- Fixed up several issues with docs, including missing arguments and exceptions, typos, and other issues.
- Switched to logger for print output.
- Added
tomlas a build-time requirement for this package, using the proper Python version range.
- Commits:
-
Summary ID 55006baa013819908c4b98a8293931379764a95d 8e0955df7c99f82b349b3f87595a60e0a438d591
Checks run (2 succeeded)
-
-
exclude_depscontains package names, butdepshas full dependency specifiers. We probably need to parse the items indepsand canonicalize bothdepsandexclude_depsnames before comparing them. -
-
For now this is okay, but keying off only the leaf node might bite us in the future. Is there any reason to not use the full key in self.dynamic?
-
Can we document in here the stuff about different build types having different local packages, but that's okay because the build backend gets reloaded?
-
This will quietly swallow requirements lines like
-r some-other-file.txt,-e ., or--index-url .... We probably want to document that limitation in here.