-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
bootstrap: add initial implementation
Has some rough edges, but seems to be working. Signed-off-by: Filipe Laíns <[email protected]>
- Loading branch information
Showing
3 changed files
with
202 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# SPDX-License-Identifier: MIT | ||
|
||
import logging | ||
import os | ||
import pathlib | ||
import subprocess | ||
import sys | ||
import tempfile | ||
|
||
from collections.abc import Iterable, Mapping, Sequence | ||
from typing import NamedTuple, Optional, Tuple, Union | ||
|
||
|
||
class Package(NamedTuple): | ||
srcdir: pathlib.Path | ||
module_path: pathlib.Path | ||
|
||
|
||
ROOT = pathlib.Path(__file__).parent.parent | ||
TMPDIR = ROOT / '.bootstrap' | ||
EXTERNAL = ROOT / 'external' | ||
|
||
PACKAGES = { | ||
# what we need | ||
'build': Package( | ||
EXTERNAL / 'build', | ||
EXTERNAL / 'build' / 'src', | ||
), | ||
'installer': Package( | ||
EXTERNAL / 'installer', | ||
EXTERNAL / 'installer' / 'src', | ||
), | ||
# dependencies | ||
'setuptools': Package( | ||
EXTERNAL / 'setuptools', | ||
EXTERNAL / 'setuptools', | ||
), | ||
'flit': Package( | ||
EXTERNAL / 'flit', | ||
EXTERNAL / 'flit', | ||
), | ||
'flit_core': Package( | ||
EXTERNAL / 'flit' / 'flit_core', | ||
EXTERNAL / 'flit' / 'flit_core', | ||
), | ||
'wheel': Package( | ||
EXTERNAL / 'wheel', | ||
EXTERNAL / 'wheel', | ||
), | ||
'tomli': Package( | ||
EXTERNAL / 'tomli', | ||
EXTERNAL / 'tomli', | ||
), | ||
'pep517': Package( | ||
EXTERNAL / 'pep517', | ||
EXTERNAL / 'pep517', | ||
), | ||
} | ||
|
||
EXTRA_PATH = [str(package.module_path) for package in PACKAGES.values()] | ||
PACKAGE_PATH_ENV = { | ||
'PYTHONPATH': os.path.pathsep.join(EXTRA_PATH), | ||
} | ||
|
||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
|
||
# inject extra sources into sys.path and import what we need | ||
sys.path = EXTRA_PATH + sys.path | ||
import build # noqa: E402 | ||
import build.env # noqa: E402 | ||
import pep517 # noqa: E402 | ||
|
||
|
||
# not needed after https://github.com/pypa/build/pull/361 | ||
def create_isolated_env_venv_no_pip(path: str) -> Tuple[str, str]: | ||
import venv | ||
|
||
venv.EnvBuilder(symlinks=build.env._fs_supports_symlink()).create(path) | ||
executable, script_dir, purelib = build.env._find_executable_and_scripts(path) | ||
|
||
return executable, script_dir | ||
|
||
|
||
def custom_runner( | ||
cmd: Sequence[str], | ||
cwd: Optional[str] = None, | ||
extra_environ: Optional[Mapping[str, str]] = None, | ||
) -> None: | ||
extra_environ = dict(extra_environ) if extra_environ else {} | ||
extra_environ.update(PACKAGE_PATH_ENV) | ||
pep517.default_subprocess_runner(cmd, cwd, extra_environ) | ||
|
||
|
||
def build_package(name: str, outdir: pathlib.Path) -> pathlib.Path: | ||
_logger.info(f'Building {name}...') | ||
srcdir = PACKAGES[name].srcdir | ||
builder = build.ProjectBuilder(srcdir, runner=custom_runner) | ||
wheel = builder.build('wheel', str(outdir)) | ||
return pathlib.Path(wheel) | ||
|
||
|
||
def build_package_setuptools( | ||
outdir: pathlib.Path, | ||
wheel_whl: pathlib.Path, | ||
) -> pathlib.Path: | ||
# setuptools needs wheel installed | ||
_logger.info('Building setuptools...') | ||
srcdir = PACKAGES['setuptools'].srcdir | ||
TMPDIR.mkdir(exist_ok=True) | ||
with tempfile.TemporaryDirectory(prefix='setuptools-venv-', dir=TMPDIR) as venv_path: | ||
executable, scripts_dir = create_isolated_env_venv_no_pip(venv_path) | ||
|
||
# install wheel | ||
subprocess.check_call( | ||
[executable, '-m', 'installer', str(wheel_whl)], | ||
env=os.environ | PACKAGE_PATH_ENV, | ||
) | ||
|
||
# build setuptools | ||
builder = build.ProjectBuilder(srcdir, runner=custom_runner) | ||
builder.python_executable = executable | ||
builder.scripts_dir = scripts_dir | ||
wheel = builder.build('wheel', str(outdir)) | ||
return pathlib.Path(wheel) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# SPDX-License-Identifier: MIT | ||
|
||
import argparse | ||
import json | ||
import logging | ||
import pathlib | ||
import shutil | ||
import sys | ||
|
||
from typing import Dict, Optional | ||
from collections.abc import Sequence | ||
|
||
import bootstrap | ||
|
||
|
||
def main_parser() -> argparse.ArgumentParser: | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument( | ||
'--outdir', | ||
'-o', | ||
type=str, | ||
default='dist', | ||
help='output directory (defaults to `dist`)', | ||
) | ||
return parser | ||
|
||
|
||
def main(cli_args: Sequence[str], prog: Optional[str] = None): | ||
parser = main_parser() | ||
if prog: | ||
parser.prog = prog | ||
args, unknown = parser.parse_known_args(cli_args) | ||
|
||
logging.basicConfig(level=logging.INFO) | ||
|
||
outdir = pathlib.Path(args.outdir).absolute() | ||
if outdir.exists(): | ||
if not outdir.is_dir(): | ||
raise NotADirectoryError(f"{str(outdir)} exists and it's not a directory") | ||
shutil.rmtree(outdir) | ||
outdir.mkdir(parents=True) | ||
|
||
artifacts: Dict[str, pathlib.Path] = {} | ||
# build wheel first because setuptools needs it installed | ||
artifacts['wheel'] = bootstrap.build_package('wheel', outdir) | ||
# build setuptools via the custom builder | ||
bootstrap.build_package_setuptools(outdir, artifacts['wheel']) | ||
# build the rest | ||
for package in bootstrap.PACKAGES: | ||
if package in ('wheel', 'setuptools'): | ||
continue | ||
artifacts[package] = bootstrap.build_package(package, outdir) | ||
# write artifact metadata | ||
outdir.joinpath('artifacts.json').write_text(json.dumps({ | ||
package: path.name for package, path in artifacts.items() | ||
})) | ||
|
||
print(f'Written wheels to `{str(args.outdir)}`:' + ''.join(sorted( | ||
f'\n\t{artifact.name}' for artifact in artifacts.values() | ||
))) | ||
|
||
|
||
if __name__ == '__main__': | ||
main(sys.argv[1:], 'python -m bootstra.build') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# SPDX-License-Identifier: MIT | ||
|
||
import sys | ||
|
||
import bootstrap # noqa: F401 | ||
|
||
# bootstrap injects the extra sources | ||
import installer.__main__ # noqa: E402 | ||
|
||
|
||
if __name__ == '__main__': | ||
installer.__main__.main(sys.argv[1:], 'python -m bootstrap.install') |