Skip to content

Commit 75d4fb2

Browse files
Ozaqclaude
andcommitted
Add compile-time test to prevent header regressions
The IWYU cleanup made all public headers self-contained, but nothing prevents regressions. Add a CMake test that compiles each header in isolation — if a future change breaks self-containedness, the build fails immediately pointing to the offending header. Gated behind -DENABLE_TEST_SELF_CONTAINED_HEADERS=ON to avoid the extra compile cost on regular builds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9206ef5 commit 75d4fb2

File tree

3 files changed

+76
-0
lines changed

3 files changed

+76
-0
lines changed

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,12 @@ ecbuild_add_option( FEATURE HIP
266266
DESCRIPTION "HIP GPU linear algebra operations"
267267
REQUIRED_PACKAGES hip hipsparse )
268268

269+
### Self-contained header compilation test
270+
271+
ecbuild_add_option( FEATURE TEST_SELF_CONTAINED_HEADERS
272+
DEFAULT OFF
273+
DESCRIPTION "Test that each public header compiles standalone" )
274+
269275
### Performance tests
270276

271277
ecbuild_add_option( FEATURE EXTRA_TESTS

tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,5 @@ endif()
4040
if( eckit_HAVE_EXPERIMENTAL )
4141
add_subdirectory( experimental )
4242
endif()
43+
44+
add_subdirectory( headers )

tests/headers/CMakeLists.txt

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Self-contained header compilation test
2+
#
3+
# Every public header in eckit must compile on its own, without requiring
4+
# the consumer to include other headers first or in a particular order.
5+
# This is essential for a library: downstream projects include eckit
6+
# headers in arbitrary combinations and orders, and a header that silently
7+
# depends on a transitive include will break when unrelated changes
8+
# elsewhere shuffle include graphs. Self-contained headers eliminate this
9+
# class of fragile, hard-to-diagnose build failures for all users.
10+
#
11+
# This test generates a .cc file per header (containing only that single
12+
# #include) and compiles them into one target. A compilation failure
13+
# immediately identifies the offending header.
14+
#
15+
# Gate: -DENABLE_TEST_SELF_CONTAINED_HEADERS=ON (off by default to avoid
16+
# the extra compile cost on every regular build).
17+
18+
# Glob all public headers under src/eckit/
19+
file( GLOB_RECURSE _all_headers
20+
RELATIVE ${PROJECT_SOURCE_DIR}/src
21+
${PROJECT_SOURCE_DIR}/src/eckit/*.h )
22+
23+
# Exclusions
24+
list( FILTER _all_headers EXCLUDE REGEX "^eckit/contrib/" )
25+
26+
set( _skip_headers
27+
# Must be included via Matrix.h (backend selector guard)
28+
eckit/maths/MatrixEigen.h
29+
eckit/maths/MatrixLapack.h
30+
# Require optional external libraries not always available
31+
eckit/linalg/detail/CUDA.h # CUDA runtime
32+
eckit/linalg/detail/HIP.h # HIP runtime
33+
eckit/io/rados/RadosCluster.h # librados (Ceph)
34+
eckit/io/rados/RadosHandle.h # librados (Ceph)
35+
eckit/utils/MD4.h # OpenSSL
36+
eckit/utils/SHA1.h # OpenSSL
37+
eckit/geo/area/library/Shapefile.h # shapelib
38+
# Require MPI headers (only available when ENABLE_MPI=ON)
39+
eckit/mpi/Parallel.h
40+
eckit/mpi/ParallelGroup.h
41+
eckit/mpi/ParallelRequest.h
42+
eckit/mpi/ParallelStatus.h
43+
# Coupled to ODB — reference odb:: types from a separate project
44+
eckit/sql/expression/function/FunctionMATCH.h
45+
eckit/sql/expression/ShiftedColumnExpression.h
46+
eckit/sql/SQLIteratorOutput.h
47+
eckit/sql/SQLMATCHSubquerySession.h
48+
eckit/sql/SQLMATCHSubquerySessionOutput.h
49+
)
50+
list( REMOVE_ITEM _all_headers ${_skip_headers} )
51+
52+
# Generate one .cc per header
53+
set( _generated_sources )
54+
foreach( _header ${_all_headers} )
55+
string( REPLACE "/" "_" _stem ${_header} )
56+
string( REPLACE "." "_" _stem ${_stem} )
57+
set( _src "${CMAKE_CURRENT_BINARY_DIR}/${_stem}.cc" )
58+
file( GENERATE OUTPUT ${_src}
59+
CONTENT "#include \"${_header}\"\n" )
60+
list( APPEND _generated_sources ${_src} )
61+
endforeach()
62+
63+
# Compile-only check: an OBJECT library verifies each header compiles
64+
# standalone without needing a main() or link step.
65+
if( HAVE_TEST_SELF_CONTAINED_HEADERS )
66+
add_library( eckit_test_headers_selfcontained OBJECT ${_generated_sources} )
67+
target_link_libraries( eckit_test_headers_selfcontained PUBLIC eckit )
68+
endif()

0 commit comments

Comments
 (0)