diff --git a/doc/bootstrap.rst b/doc/bootstrap.rst index 02da8945..19d7d923 100644 --- a/doc/bootstrap.rst +++ b/doc/bootstrap.rst @@ -13,24 +13,29 @@ from source? The key piece is ``flit_core``. This is a package which can build itself using nothing except Python and the standard library. From an unpacked source archive, -you can run ``python build_dists.py``, of which the crucial part is:: +you can make a wheel by running:: - from flit_core import buildapi - whl_fname = buildapi.build_wheel('dist/') - print(os.path.join('dist', whl_fname)) + python -m flit_core.wheel -This produces a ``.whl`` wheel file, which you can unzip into your -``site-packages`` folder (or equivalent) to make ``flit_core`` available for -building other packages. (You could also just copy ``flit_core`` from the -source directory, but without the ``.dist-info`` folder, tools like pip won't -know that it's installed.) +And then you can install this wheel with the ``bootstrap_install.py`` script +included in the sdist (or by unzipping it to the correct directory):: + + # Install to site-packages for this Python: + python bootstrap_install.py dist/flit_core-*.whl + + # Install somewhere else: + python bootstrap_install.py --installdir /path/to/site-packages dist/flit_core-*.whl As of version 3.6, flit_core bundles the ``tomli`` TOML parser, to avoid a dependency cycle. If you need to unbundle it, you will need to special-case installing flit_core and/or tomli to get around that cycle. -I recommend that you get the `build `_ and -`installer `_ packages (and their -dependencies) installed as the goal of the bootstrapping phase. These tools -together can be used to install any other Python packages: ``build`` to create -wheels and ``installer`` to install them. +After ``flit_core``, I recommend that you get `installer +`_ set up. You can use +``python -m flit_core.wheel`` again to make a wheel, and then use installer +itself (from the source directory) to install it. + +After that, you probably want to get `build `_ +and its dependencies installed as the goal of the bootstrapping phase. You can +then use ``build`` to create wheels of any other Python packages, and +``installer`` to install them. diff --git a/flit_core/bootstrap_install.py b/flit_core/bootstrap_install.py new file mode 100644 index 00000000..e04a007e --- /dev/null +++ b/flit_core/bootstrap_install.py @@ -0,0 +1,48 @@ +"""Install flit_core without using any other tools. + +Normally, you would install flit_core with pip like any other Python package. +This script is meant to help with 'bootstrapping' other packaging +systems, where you may need flit_core to build other packaging tools. + +Use 'python -m flit_core.wheel' to make a wheel, then: + + python bootstrap_install.py flit_core-3.6.0-py3-none-any.whl + +To install for something other than the Python running the script, pass a +site-packages or equivalent directory with the --installdir option. +""" +import argparse +import sys +import sysconfig +from pathlib import Path +from zipfile import ZipFile + +def extract_wheel(whl_path, dest): + print("Installing to", dest) + with ZipFile(whl_path) as zf: + zf.extractall(dest) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + 'wheel', + type=Path, + help=f'flit_core wheel to install (.whl file)', + ) + purelib = Path(sysconfig.get_path('purelib')).resolve() + parser.add_argument( + '--installdir', + '-i', + type=Path, + default=purelib, + help=f'installdir directory (defaults to {purelib})', + ) + + args = parser.parse_args() + + if not args.wheel.name.startswith('flit_core-'): + sys.exit("Use this script only for flit_core wheels") + if not args.installdir.is_dir(): + sys.exit(f"{args.installdir} is not a directory") + + extract_wheel(args.wheel, args.installdir) diff --git a/flit_core/flit_core/tests/test_wheel.py b/flit_core/flit_core/tests/test_wheel.py index 51d6def7..310f9c6c 100644 --- a/flit_core/flit_core/tests/test_wheel.py +++ b/flit_core/flit_core/tests/test_wheel.py @@ -3,7 +3,7 @@ from testpath import assert_isfile -from flit_core.wheel import make_wheel_in +from flit_core.wheel import make_wheel_in, main samples_dir = Path(__file__).parent / 'samples' @@ -31,6 +31,15 @@ def test_zero_timestamp(tmp_path, monkeypatch): assert zf.getinfo('module1a.py').date_time == (1980, 1, 1, 0, 0, 0) +def test_main(tmp_path): + main(['--outdir', str(tmp_path), str(samples_dir / 'pep621')]) + wheels = list(tmp_path.glob('*.whl')) + assert len(wheels) == 1 + # Minimum value for zip timestamps is 1980-1-1 + with ZipFile(wheels[0], 'r') as zf: + assert 'module1a.py' in zf.namelist() + + def test_data_dir(tmp_path): info = make_wheel_in(samples_dir / 'with_data_dir' / 'pyproject.toml', tmp_path) assert_isfile(info.file) diff --git a/flit_core/flit_core/wheel.py b/flit_core/flit_core/wheel.py index d921f7cc..08cb70ae 100644 --- a/flit_core/flit_core/wheel.py +++ b/flit_core/flit_core/wheel.py @@ -1,3 +1,4 @@ +import argparse from base64 import urlsafe_b64encode import contextlib from datetime import datetime @@ -8,6 +9,7 @@ import os.path as osp import stat import tempfile +from pathlib import Path from types import SimpleNamespace from typing import Optional import zipfile @@ -228,3 +230,30 @@ def make_wheel_in(ini_path, wheel_directory, editable=False): log.info("Built wheel: %s", wheel_path) return SimpleNamespace(builder=wb, file=wheel_path) + + +def main(argv=None): + parser = argparse.ArgumentParser() + parser.add_argument( + 'srcdir', + type=Path, + nargs='?', + default=Path.cwd(), + help='source directory (defaults to current directory)', + ) + + parser.add_argument( + '--outdir', + '-o', + help='output directory (defaults to {srcdir}/dist)', + ) + args = parser.parse_args(argv) + outdir = args.srcdir / 'dist' if args.outdir is None else Path(args.outdir) + print("Building wheel from", args.srcdir) + pyproj_toml = args.srcdir / 'pyproject.toml' + outdir.mkdir(parents=True, exist_ok=True) + info = make_wheel_in(pyproj_toml, outdir) + print("Wheel built", outdir / info.file.name) + +if __name__ == "__main__": + main() diff --git a/flit_core/pyproject.toml b/flit_core/pyproject.toml index ca3f46b2..3652f9a3 100644 --- a/flit_core/pyproject.toml +++ b/flit_core/pyproject.toml @@ -19,3 +19,6 @@ dynamic = ["version"] [project.urls] Source = "https://github.com/pypa/flit" + +[tool.flit.sdist] +include = ["bootstrap_install.py", "build_dists.py"]