Skip to content

Commit ed57538

Browse files
authored
Merge pull request #580 from dhellmann/bootstrap-hook
add hook entry point for bootstrap
2 parents 9d0b9a9 + 41f924c commit ed57538

8 files changed

Lines changed: 155 additions & 13 deletions

File tree

.github/workflows/test.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ jobs:
9797
- migrate_graph
9898
- override
9999
- pep517_build_sdist
100+
- post_bootstrap_hook
100101
- prebuilt_wheels_alt_server
101102
- report_missing_dependency
102103
- rust_vendor

.mergify.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pull_request_rules:
2626
- name: Automatic merge on approval
2727
conditions:
2828
- and:
29+
- "-draft"
2930
- check-success=e2e (3.11, 1.75, bootstrap, ubuntu-latest)
3031
- check-success=e2e (3.11, 1.75, bootstrap_build_tags, ubuntu-latest)
3132
- check-success=e2e (3.11, 1.75, bootstrap_cache, ubuntu-latest)
@@ -49,6 +50,7 @@ pull_request_rules:
4950
- check-success=e2e (3.11, 1.75, optimize_build, ubuntu-latest)
5051
- check-success=e2e (3.11, 1.75, override, ubuntu-latest)
5152
- check-success=e2e (3.11, 1.75, pep517_build_sdist, ubuntu-latest)
53+
- check-success=e2e (3.11, 1.75, post_bootstrap_hook, ubuntu-latest)
5254
- check-success=e2e (3.11, 1.75, prebuilt_wheel_hook, ubuntu-latest)
5355
- check-success=e2e (3.11, 1.75, prebuilt_wheels_alt_server, ubuntu-latest)
5456
- check-success=e2e (3.11, 1.75, report_missing_dependency, ubuntu-latest)
@@ -99,6 +101,8 @@ pull_request_rules:
99101
- check-success=e2e (3.12, 1.75, override, ubuntu-latest)
100102
- check-success=e2e (3.12, 1.75, pep517_build_sdist, macos-latest)
101103
- check-success=e2e (3.12, 1.75, pep517_build_sdist, ubuntu-latest)
104+
- check-success=e2e (3.12, 1.75, post_bootstrap_hook, macos-latest)
105+
- check-success=e2e (3.12, 1.75, post_bootstrap_hook, ubuntu-latest)
102106
- check-success=e2e (3.12, 1.75, prebuilt_wheel_hook, macos-latest)
103107
- check-success=e2e (3.12, 1.75, prebuilt_wheel_hook, ubuntu-latest)
104108
- check-success=e2e (3.12, 1.75, prebuilt_wheels_alt_server, macos-latest)
@@ -107,7 +111,6 @@ pull_request_rules:
107111
- check-success=e2e (3.12, 1.75, report_missing_dependency, ubuntu-latest)
108112
- check-success=e2e (3.12, 1.75, rust_vendor, macos-latest)
109113
- check-success=e2e (3.12, 1.75, rust_vendor, ubuntu-latest)
110-
- "-draft"
111114

112115
# At least 1 reviewer
113116
- "#approved-reviews-by>=1"

docs/customization.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,40 @@ def prebuilt_wheel(
336336
f"{req.name}: running prebuilt wheel hook for {wheel_filename}"
337337
)
338338
```
339+
340+
### post_bootstrap
341+
342+
The `post_bootstrap` hook runs after a package is bootstrapped, before its
343+
installation dependencies are bootstrapped. It can be used to perform validation
344+
checks for the sdist and wheel that results from the bootstrap operation.
345+
346+
Configure a `post_bootstrap` hook in your `pyproject.toml` like this:
347+
348+
```toml
349+
[project.entry-points."fromager.hooks"]
350+
post_bootstrap = "package_plugins.module:function"
351+
```
352+
353+
The input arguments to the `post_bootstrap` hook are the `WorkContext`,
354+
`Requirement` being built, the distribution name and version, and the sdist and
355+
wheel filenames.
356+
357+
NOTE: The files should not be renamed or moved.
358+
359+
NOTE: The `sdist_filename` argument can be None if the wheel is pre-built and
360+
the `wheel_filename` argument can be None if bootstrapping is running in
361+
sdist-only mode.
362+
363+
```python
364+
def post_bootstrap(
365+
ctx: context.WorkContext,
366+
req: Requirement,
367+
dist_name: str,
368+
dist_version: str,
369+
sdist_filename: pathlib.Path | None,
370+
wheel_filename: pathlib.Path | None,
371+
):
372+
logger.info(
373+
f"{req.name}: running post bootstrap hook for {sdist_filename} and {wheel_filename}"
374+
)
375+
```

e2e/fromager_hooks/pyproject.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "flit-core-overrides"
7-
authors = [
8-
{name = "Doug Hellmann", email="dhellmann@redhat.com"},
9-
]
7+
authors = [{ name = "Doug Hellmann", email = "dhellmann@redhat.com" }]
108
description = "test package"
119
dynamic = ["version"]
1210
classifiers = [
@@ -31,3 +29,4 @@ dependencies = []
3129
[project.entry-points."fromager.hooks"]
3230
post_build = "package_plugins.hooks:after_build_wheel"
3331
prebuilt_wheel = "package_plugins.hooks:after_prebuilt_wheel"
32+
post_bootstrap = "package_plugins.hooks:after_bootstrap"

e2e/fromager_hooks/src/package_plugins/hooks.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,22 @@ def after_build_wheel(
2424
test_file.write_text(f"{dist_name}=={dist_version}")
2525

2626

27+
def after_bootstrap(
28+
ctx: context.WorkContext,
29+
req: Requirement,
30+
dist_name: str,
31+
dist_version: str,
32+
sdist_filename: pathlib.Path | None,
33+
wheel_filename: pathlib.Path | None,
34+
):
35+
logger.info(
36+
f"{req.name}: running post bootstrap hook in {__name__} for {sdist_filename} and {wheel_filename}"
37+
)
38+
test_file = ctx.work_dir / "test-output-file.txt"
39+
logger.info(f"{req.name}: post-bootstrap hook writing to {test_file}")
40+
test_file.write_text(f"{dist_name}=={dist_version}")
41+
42+
2743
def after_prebuilt_wheel(
2844
ctx: context.WorkContext,
2945
req: Requirement,
@@ -34,6 +50,6 @@ def after_prebuilt_wheel(
3450
logger.info(
3551
f"{req.name}: running post build hook in {__name__} for {wheel_filename}"
3652
)
37-
test_file = ctx.work_dir / "test-prebuilt.txt"
53+
test_file = ctx.work_dir / "test-prebuilt.txt"
3854
logger.info(f"{req.name}: prebuilt-wheel hook writing to {test_file}")
3955
test_file.write_text(f"{dist_name}=={dist_version}")

e2e/test_post_bootstrap_hook.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/bash
2+
# -*- indent-tabs-mode: nil; tab-width: 2; sh-indentation: 2; -*-
3+
4+
# Test post-bootstrap hook
5+
6+
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
7+
source "$SCRIPTDIR/common.sh"
8+
9+
# What are we building?
10+
DIST="setuptools"
11+
VERSION="75.8.0"
12+
13+
# Install hook for test
14+
pip install e2e/fromager_hooks
15+
16+
# Bootstrap the project
17+
fromager \
18+
--debug \
19+
--sdists-repo="$OUTDIR/sdists-repo" \
20+
--wheels-repo="$OUTDIR/wheels-repo" \
21+
--work-dir="$OUTDIR/work-dir" \
22+
--settings-dir="$SCRIPTDIR/prebuilt_settings" \
23+
bootstrap "${DIST}==${VERSION}"
24+
25+
26+
27+
EXPECTED_FILES="
28+
work-dir/test-output-file.txt
29+
"
30+
31+
pass=true
32+
for f in $EXPECTED_FILES; do
33+
if [ ! -f "$OUTDIR/$f" ]; then
34+
echo "FAIL: Did not find $OUTDIR/$f" 1>&2
35+
pass=false
36+
fi
37+
done
38+
39+
cat $OUTDIR/work-dir/test-output-file.txt
40+
41+
if $pass; then
42+
if ! grep -q "${DIST}==${VERSION}" $OUTDIR/work-dir/test-output-file.txt; then
43+
echo "FAIL: Did not find content in post-bootstrap hook output file" 1>&2
44+
pass=false
45+
fi
46+
fi
47+
48+
$pass

src/fromager/bootstrapper.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
build_environment,
1818
dependencies,
1919
finders,
20+
hooks,
2021
progress,
2122
resolver,
2223
server,
@@ -116,6 +117,7 @@ def bootstrap(self, req: Requirement, req_type: RequirementType) -> Version:
116117
build_env: build_environment.BuildEnvironment | None = None
117118
sdist_root_dir: pathlib.Path | None = None
118119
wheel_filename: pathlib.Path | None = None
120+
sdist_filename: pathlib.Path | None = None
119121

120122
if pbi.pre_built:
121123
wheel_filename, unpack_dir = self._download_prebuilt(
@@ -172,7 +174,7 @@ def bootstrap(self, req: Requirement, req_type: RequirementType) -> Version:
172174
f"{req.name}=={resolved_version} ({req_type})"
173175
)
174176
wheel_filename = None
175-
build_env = self._build_sdist(
177+
sdist_filename, build_env = self._build_sdist(
176178
req, resolved_version, sdist_root_dir, build_dependencies
177179
)
178180
else:
@@ -181,10 +183,19 @@ def bootstrap(self, req: Requirement, req_type: RequirementType) -> Version:
181183
f"{req.name}: building wheel {req.name}=={resolved_version} "
182184
f"to get install requirements ({req_type})"
183185
)
184-
wheel_filename, build_env = self._build_wheel(
186+
wheel_filename, sdist_filename, build_env = self._build_wheel(
185187
req, resolved_version, sdist_root_dir, build_dependencies
186188
)
187189

190+
hooks.run_post_bootstrap_hooks(
191+
ctx=self.ctx,
192+
req=req,
193+
dist_name=canonicalize_name(req.name),
194+
dist_version=str(resolved_version),
195+
sdist_filename=sdist_filename,
196+
wheel_filename=wheel_filename,
197+
)
198+
188199
if build_sdist_only:
189200
if typing.TYPE_CHECKING:
190201
assert build_env is not None
@@ -251,7 +262,7 @@ def _build_sdist(
251262
resolved_version: Version,
252263
sdist_root_dir: pathlib.Path,
253264
build_dependencies: set[Requirement],
254-
) -> build_environment.BuildEnvironment:
265+
) -> tuple[pathlib.Path, build_environment.BuildEnvironment]:
255266
build_env = build_environment.BuildEnvironment(
256267
self.ctx,
257268
sdist_root_dir.parent,
@@ -262,29 +273,30 @@ def _build_sdist(
262273
self.ctx, self.ctx.sdists_builds, req, str(resolved_version)
263274
)
264275
if not find_sdist_result:
265-
sources.build_sdist(
276+
sdist_filename = sources.build_sdist(
266277
ctx=self.ctx,
267278
req=req,
268279
version=resolved_version,
269280
sdist_root_dir=sdist_root_dir,
270281
build_env=build_env,
271282
)
272283
else:
284+
sdist_filename = find_sdist_result
273285
logger.info(
274286
f"{req.name} have sdist version {resolved_version}: {find_sdist_result}"
275287
)
276288
except Exception as err:
277289
logger.warning(f"{req.name}: failed to build source distribution: {err}")
278-
return build_env
290+
return (sdist_filename, build_env)
279291

280292
def _build_wheel(
281293
self,
282294
req: Requirement,
283295
resolved_version: Version,
284296
sdist_root_dir: pathlib.Path,
285297
build_dependencies: set[Requirement],
286-
) -> tuple[pathlib.Path, build_environment.BuildEnvironment]:
287-
build_env = self._build_sdist(
298+
) -> tuple[pathlib.Path, pathlib.Path, build_environment.BuildEnvironment]:
299+
sdist_filename, build_env = self._build_sdist(
288300
req, resolved_version, sdist_root_dir, build_dependencies
289301
)
290302

@@ -303,7 +315,7 @@ def _build_wheel(
303315
logger.info(
304316
f"{req.name}: built wheel for version {resolved_version}: {wheel_filename}"
305317
)
306-
return wheel_filename, build_env
318+
return wheel_filename, sdist_filename, build_env
307319

308320
def _prepare_build_dependencies(
309321
self, req: Requirement, sdist_root_dir: pathlib.Path

src/fromager/hooks.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,32 @@ def run_post_build_hooks(
6161
)
6262

6363

64+
def run_post_bootstrap_hooks(
65+
ctx: context.WorkContext,
66+
req: Requirement,
67+
dist_name: str,
68+
dist_version: str,
69+
sdist_filename: pathlib.Path | None,
70+
wheel_filename: pathlib.Path | None,
71+
) -> None:
72+
hook_mgr = _get_hooks("post_bootstrap")
73+
if hook_mgr.names():
74+
logger.info(
75+
f"{req.name}: starting post-bootstrap hooks for sdist {sdist_filename} and wheel {wheel_filename}"
76+
)
77+
for ext in hook_mgr:
78+
# NOTE: Each hook is responsible for doing its own logging for
79+
# start/stop because we don't have a good name to use here.
80+
ext.plugin(
81+
ctx=ctx,
82+
req=req,
83+
dist_name=dist_name,
84+
dist_version=dist_version,
85+
sdist_filename=sdist_filename,
86+
wheel_filename=wheel_filename,
87+
)
88+
89+
6490
def run_prebuilt_wheel_hooks(
6591
ctx: context.WorkContext,
6692
req: Requirement,

0 commit comments

Comments
 (0)