Skip to content

Commit f3c0292

Browse files
etrclaude
andcommitted
TASK-007: share staged install across check-local; skip make check on hygiene CI matrix
- check-local: build one DESTDIR=.shared-check-stage and pass it to both check-install-layout and check-hygiene via CHECK_*_SHARED=yes, halving the install cost of `make check`. Standalone invocations still do their own install. - check-hygiene: gate the staged install behind a $(HYGIENE_STAMP) mtime sentinel so repeated standalone runs are no-ops when public headers haven't changed; bypassed when CHECK_HYGIENE_SHARED=yes. - check-hygiene grep: anchor HEADER_HYGIENE_FORBIDDEN to a leading "/" so leak detection only matches absolute paths, not arbitrary substrings. - clean-local: remove the stage directories on `make clean`. - CI: header-hygiene matrix entry skips the unconditional `make check` step (the dedicated `make check-hygiene` step is the gate for that job). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6018559 commit f3c0292

2 files changed

Lines changed: 59 additions & 18 deletions

File tree

.github/workflows/verify-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ jobs:
650650
run: |
651651
cd build ;
652652
make check;
653-
if: ${{ matrix.build-type != 'iwyu' && matrix.compiler-family != 'arm-cross' }}
653+
if: ${{ matrix.build-type != 'iwyu' && matrix.compiler-family != 'arm-cross' && matrix.build-type != 'header-hygiene' }}
654654

655655
- name: Run header-hygiene check
656656
# TASK-007: dedicated public-header hygiene gate. Runs the

Makefile.am

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -132,36 +132,38 @@ CHECK_INSTALL_STAGE = $(abs_top_builddir)/.install-stage
132132

133133
check-install-layout:
134134
@echo "=== check-install-layout: staged install must hide details/ and *_impl.hpp ==="
135-
@rm -rf $(CHECK_INSTALL_STAGE)
136-
@$(MAKE) $(AM_MAKEFLAGS) install DESTDIR=$(CHECK_INSTALL_STAGE) >check-install.log 2>&1 || { \
137-
echo "FAIL: staged install failed"; \
138-
cat check-install.log; \
139-
rm -f check-install.log; \
135+
@if test "$(CHECK_INSTALL_SHARED)" != "yes"; then \
140136
rm -rf $(CHECK_INSTALL_STAGE); \
141-
exit 1; \
142-
}
143-
@rm -f check-install.log
137+
$(MAKE) $(AM_MAKEFLAGS) install DESTDIR=$(CHECK_INSTALL_STAGE) >check-install.log 2>&1 || { \
138+
echo "FAIL: staged install failed"; \
139+
cat check-install.log; \
140+
rm -f check-install.log; \
141+
rm -rf $(CHECK_INSTALL_STAGE); \
142+
exit 1; \
143+
}; \
144+
rm -f check-install.log; \
145+
fi
144146
@leaked_details=`find $(CHECK_INSTALL_STAGE) -type d -name details 2>/dev/null`; \
145147
if test -n "$$leaked_details"; then \
146148
echo "FAIL: details/ directory leaked into install:"; \
147149
echo "$$leaked_details"; \
148-
rm -rf $(CHECK_INSTALL_STAGE); \
150+
if test "$(CHECK_INSTALL_SHARED)" != "yes"; then rm -rf $(CHECK_INSTALL_STAGE); fi; \
149151
exit 1; \
150152
fi
151153
@leaked_impl=`find $(CHECK_INSTALL_STAGE) -name '*_impl.hpp' 2>/dev/null`; \
152154
if test -n "$$leaked_impl"; then \
153155
echo "FAIL: *_impl.hpp file leaked into install:"; \
154156
echo "$$leaked_impl"; \
155-
rm -rf $(CHECK_INSTALL_STAGE); \
157+
if test "$(CHECK_INSTALL_SHARED)" != "yes"; then rm -rf $(CHECK_INSTALL_STAGE); fi; \
156158
exit 1; \
157159
fi
158160
@umbrella_count=`find $(CHECK_INSTALL_STAGE) -name 'httpserver.hpp' | wc -l | tr -d ' '`; \
159161
if test "$$umbrella_count" != "1"; then \
160162
echo "FAIL: expected exactly 1 installed httpserver.hpp, got $$umbrella_count"; \
161-
rm -rf $(CHECK_INSTALL_STAGE); \
163+
if test "$(CHECK_INSTALL_SHARED)" != "yes"; then rm -rf $(CHECK_INSTALL_STAGE); fi; \
162164
exit 1; \
163165
fi
164-
@rm -rf $(CHECK_INSTALL_STAGE)
166+
@if test "$(CHECK_INSTALL_SHARED)" != "yes"; then rm -rf $(CHECK_INSTALL_STAGE); fi
165167
@echo " PASS: staged install layout is clean"
166168

167169
# ---------------------------------------------------------------------------
@@ -200,13 +202,33 @@ CHECK_HYGIENE_STAGE = $(abs_top_builddir)/.hygiene-stage
200202
CHECK_HYGIENE_CXX = $(CXX) -std=c++20 -E -I$(CHECK_HYGIENE_STAGE)$(includedir) $(CPPFLAGS)
201203
HEADER_HYGIENE_STRICT ?= no
202204

203-
check-hygiene:
204-
@echo "=== check-hygiene: <httpserver.hpp> must not transitively include backend headers ==="
205+
# Sentinel file: only re-run the staged install when headers have changed.
206+
# This is an mtime gate used exclusively for standalone `make check-hygiene`
207+
# invocations — it avoids paying a full `make install` cost on every
208+
# repeated standalone run. When check-local drives check-hygiene it sets
209+
# CHECK_HYGIENE_SHARED=yes and passes CHECK_HYGIENE_STAGE pointing at its
210+
# own pre-built shared stage, so this stamp target is bypassed entirely.
211+
HYGIENE_STAMP = $(CHECK_HYGIENE_STAGE)/.hygiene-stamp
212+
213+
$(HYGIENE_STAMP): $(wildcard $(top_srcdir)/src/httpserver/*.hpp)
205214
@rm -rf $(CHECK_HYGIENE_STAGE)
206215
@$(MAKE) $(AM_MAKEFLAGS) install DESTDIR=$(CHECK_HYGIENE_STAGE) >check-hygiene-install.log 2>&1 || { \
207216
echo "FAIL: staged install failed"; cat check-hygiene-install.log; \
208217
rm -f check-hygiene-install.log; rm -rf $(CHECK_HYGIENE_STAGE); exit 1; }
209218
@rm -f check-hygiene-install.log
219+
@touch $(HYGIENE_STAMP)
220+
221+
check-hygiene:
222+
@echo "=== check-hygiene: <httpserver.hpp> must not transitively include backend headers ==="
223+
@if test "$(CHECK_HYGIENE_SHARED)" != "yes"; then \
224+
$(MAKE) $(AM_MAKEFLAGS) $(HYGIENE_STAMP); \
225+
else \
226+
if ! test -d "$(CHECK_HYGIENE_STAGE)"; then \
227+
echo "FAIL: CHECK_HYGIENE_SHARED=yes but stage dir '$(CHECK_HYGIENE_STAGE)' does not exist."; \
228+
echo " Always pair CHECK_HYGIENE_SHARED=yes with CHECK_HYGIENE_STAGE=<valid-dir>."; \
229+
exit 1; \
230+
fi; \
231+
fi
210232
@status=0; \
211233
if ! $(CHECK_HYGIENE_CXX) $(top_srcdir)/test/headers/consumer_umbrella_no_backend.cpp >check-hygiene.i 2>check-hygiene.err; then \
212234
if test "$(HEADER_HYGIENE_STRICT)" = "yes"; then \
@@ -220,7 +242,7 @@ check-hygiene:
220242
sed 's/^/ /' check-hygiene.err | tail -10; \
221243
fi; \
222244
else \
223-
leaks=`grep -hE '^# [0-9]+ "[^"]*($(HEADER_HYGIENE_FORBIDDEN))"' check-hygiene.i | awk '{print $$3}' | sort -u`; \
245+
leaks=`grep -hE '^# [0-9]+ "[^"]*/($(HEADER_HYGIENE_FORBIDDEN))"' check-hygiene.i | awk '{print $$3}' | sort -u`; \
224246
if test -n "$$leaks"; then \
225247
if test "$(HEADER_HYGIENE_STRICT)" = "yes"; then \
226248
echo "FAIL: forbidden headers leaked through <httpserver.hpp>:"; \
@@ -235,16 +257,35 @@ check-hygiene:
235257
fi; \
236258
fi; \
237259
rm -f check-hygiene.i check-hygiene.err; \
238-
rm -rf $(CHECK_HYGIENE_STAGE); \
239260
exit $$status
240261

241-
check-local: check-headers check-install-layout check-hygiene
262+
# check-local runs check-install-layout and check-hygiene against a single
263+
# shared staged install to avoid paying two full `make install` costs on
264+
# every `make check`. Both sub-checks can still be invoked standalone (they
265+
# will do their own install when CHECK_*_SHARED is not set).
266+
check-local: check-headers
267+
@echo "=== Shared staged install for check-install-layout and check-hygiene ==="
268+
@rm -rf $(abs_top_builddir)/.shared-check-stage
269+
@$(MAKE) $(AM_MAKEFLAGS) install DESTDIR=$(abs_top_builddir)/.shared-check-stage >check-shared-install.log 2>&1 || { \
270+
echo "FAIL: shared staged install failed"; cat check-shared-install.log; \
271+
rm -f check-shared-install.log; rm -rf $(abs_top_builddir)/.shared-check-stage; exit 1; }
272+
@rm -f check-shared-install.log
273+
@$(MAKE) $(AM_MAKEFLAGS) check-install-layout \
274+
CHECK_INSTALL_STAGE=$(abs_top_builddir)/.shared-check-stage \
275+
CHECK_INSTALL_SHARED=yes
276+
@$(MAKE) $(AM_MAKEFLAGS) check-hygiene \
277+
CHECK_HYGIENE_STAGE=$(abs_top_builddir)/.shared-check-stage \
278+
CHECK_HYGIENE_SHARED=yes
279+
@rm -rf $(abs_top_builddir)/.shared-check-stage
242280

243281
.PHONY: check-headers check-install-layout check-hygiene
244282

245283
MOSTLYCLEANFILES = $(DX_CLEANFILES) *.gcda *.gcno *.gcov
246284
DISTCLEANFILES = DIST_REVISION
247285

286+
clean-local:
287+
rm -rf $(CHECK_HYGIENE_STAGE) $(abs_top_builddir)/.shared-check-stage $(CHECK_INSTALL_STAGE)
288+
248289
pkgconfigdir = $(libdir)/pkgconfig
249290
pkgconfig_DATA = libhttpserver.pc
250291

0 commit comments

Comments
 (0)