Skip to content

Commit

Permalink
bootstrap: add initial implementation
Browse files Browse the repository at this point in the history
Has some rough edges, but seems to be working.

Signed-off-by: Filipe Laíns <[email protected]>
  • Loading branch information
FFY00 committed Oct 24, 2021
1 parent dea3859 commit 096d540
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 0 deletions.
138 changes: 138 additions & 0 deletions bootstrap/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# SPDX-License-Identifier: MIT

import atexit
import logging
import os
import pathlib
import shutil
import subprocess
import sys
import tempfile

from collections.abc import Collection, Mapping, Sequence
from typing import NamedTuple, Optional, Tuple


class Package(NamedTuple):
srcdir: pathlib.Path
module_path: pathlib.Path
module_sources: Collection[str]


ROOT = pathlib.Path(__file__).parent.parent
WORKING_DIR = ROOT / '.bootstrap'
MODULES = WORKING_DIR / 'modules'
TMPDIR = WORKING_DIR / 'tmp'
EXTERNAL = ROOT / 'external'

PACKAGES = {
# what we need
'build': Package(
EXTERNAL / 'build',
EXTERNAL / 'build' / 'src',
{'build'},
),
'installer': Package(
EXTERNAL / 'installer',
EXTERNAL / 'installer' / 'src',
{'installer'},
),
# dependencies
'setuptools': Package(
EXTERNAL / 'setuptools',
EXTERNAL / 'setuptools',
{'setuptools', 'pkg_resources', '_distutils_hack'},
),
'flit_core': Package(
EXTERNAL / 'flit' / 'flit_core',
EXTERNAL / 'flit' / 'flit_core',
{'flit_core'},
),
'wheel': Package(
EXTERNAL / 'wheel',
EXTERNAL / 'wheel' / 'src',
{'wheel'},
),
'tomli': Package(
EXTERNAL / 'tomli',
EXTERNAL / 'tomli',
{'tomli'},
),
'pep517': Package(
EXTERNAL / 'pep517',
EXTERNAL / 'pep517',
{'pep517'},
),
}

EXTRA_PATH = [str(package.module_path) for package in PACKAGES.values()]
PACKAGE_PATH_ENV = {
'PYTHONPATH': os.path.pathsep.join(EXTRA_PATH),
}


# copy sources to module dir and inject it into sys.path
MODULES.mkdir(parents=True)
for package in PACKAGES.values():
for path in package.module_sources:
shutil.copytree(
package.module_path / path,
MODULES / path,
)
atexit.register(shutil.rmtree, MODULES)
sys.path.insert(0, str(MODULES))


# import what we need from the inject modules
import build # noqa: E402
import build.env # noqa: E402
import pep517 # noqa: E402


_logger = logging.getLogger(__name__)


def log(msg):
_logger.info(msg)


# 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:
log(f'Building {name}...')

if name == 'setuptools':
# setuptools needs to think that wheel is installed, so we inject its metadata
wheel_src = PACKAGES['wheel'].srcdir
subprocess.check_call(
[sys.executable, 'setup.py', 'egg_info'],
env=os.environ | PACKAGE_PATH_ENV,
cwd=wheel_src
)
shutil.copytree(
wheel_src / 'src' / 'wheel.egg-info',
MODULES / 'wheel.egg-info',
)

srcdir = PACKAGES[name].srcdir
builder = build.ProjectBuilder(str(srcdir), runner=custom_runner)
wheel = builder.build('wheel', str(outdir))
return pathlib.Path(wheel)
57 changes: 57 additions & 0 deletions bootstrap/build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# 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 = {
package: bootstrap.build_package(package, outdir)
for package in bootstrap.PACKAGES
}
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')
12 changes: 12 additions & 0 deletions bootstrap/install.py
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')

0 comments on commit 096d540

Please sign in to comment.