Skip to content

Enforce header self containment - #2202

Open
ispeters wants to merge 3 commits into
NVIDIA:mainfrom
ispeters:enforce-header-self-containment
Open

Enforce header self containment#2202
ispeters wants to merge 3 commits into
NVIDIA:mainfrom
ispeters:enforce-header-self-containment

Conversation

@ispeters

Copy link
Copy Markdown
Contributor

Claude wrote this diff for me to add CMake's auto-generated tests that stdexec's headers are self-contained to CI.

There are a handful of headers that are deliberately not self-contained so they're excluded from the auto-generated validation with set_source_files_properties(<file> PROPERTIES SKIP_LINTING ON) (documented in CMakeLists.txt). __parallel_scheduler_default_impl_entry.hpp is almost self-contained—it has a precondition that the includer define STDEXEC_PARALLEL_SCHEDULER_INLINE before including it but it is otherwise self-contained so there's a "hand-written" .cpp file that does nothing but define STDEXEC_PARALLEL_SCHEDULER_INLINE and include the header, which is added as a dependency to the auto-generated tests.

Turn the dormant VERIFY_INTERFACE_HEADER_SETS check (all_verify_interface_header_sets,
excluded from the default ALL target by CMake design) into an explicit CI step, run once
in the plain clang 22 Debug build.

A handful of headers are not self-contained by design and are excluded from
verification accordingly:
  - __detail/__epilogue.hpp pops warning/pragma state that __detail/__prologue.hpp is
    documented to push, so it cannot compile standalone.
  - __detail/__parallel_scheduler_default_impl_entry.hpp has a documented precondition
    that includers define STDEXEC_PARALLEL_SCHEDULER_INLINE first.
  - exec/tbb/tbb_thread_pool.hpp and exec/taskflow/taskflow_thread_pool.hpp
    unconditionally include an optional external dependency; only verify them when the
    corresponding STDEXEC_ENABLE_TBB / STDEXEC_ENABLE_TASKFLOW option is on.
  - exec/windows/filetime_clock.hpp and exec/windows/windows_thread_pool.hpp are
    Windows-only.

Verified locally (gcc 13, ASIO via Boost, TBB/Taskflow off, non-Windows): before this
change, all_verify_interface_header_sets reports 7 failures against main, 6 of which are
the by-design cases above; the 7th is __detail/__when_all.hpp's missing dependency on
__just.hpp, fixed separately in NVIDIA#2195. With this change applied, the by-design failures
are gone and __when_all.hpp is the only remaining failure until NVIDIA#2195 lands; applying
NVIDIA#2195's fix on top of this branch yields a fully clean run (178/178).
…ult_impl_entry.hpp

__detail/__parallel_scheduler_default_impl_entry.hpp is excluded from
VERIFY_INTERFACE_HEADER_SETS because it has a documented precondition
(includers must define STDEXEC_PARALLEL_SCHEDULER_INLINE before including
it) that the automatic verification can't express. The only place that
precondition is otherwise exercised is src/parallel_scheduler/
parallel_scheduler.cpp, which is gated behind STDEXEC_BUILD_PARALLEL_SCHEDULER
(default OFF, never enabled in CI), so as of the previous commit this header
had no coverage in CI at all.

Add a small OBJECT-library translation unit that defines
STDEXEC_PARALLEL_SCHEDULER_INLINE and includes the header, so a regression
still fails a normal build. Tried wiring this in as an extra dependency of
CMake's auto-generated all_verify_interface_header_sets target instead, but
that target doesn't exist yet at the point test/CMakeLists.txt runs (checked
directly with if(TARGET ...)); it's created later, once the whole build has
been configured. A plain always-built OBJECT library sidesteps that and
costs one extra small translation unit per build.

Verified locally: builds clean as-is; deliberately removing the
STDEXEC_PARALLEL_SCHEDULER_INLINE definition reproduces the expected
'must be defined before including this header' compile error, confirming
the check actually catches a regression.
@copy-pr-bot

copy-pr-bot Bot commented Aug 15, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@ericniebler

Copy link
Copy Markdown
Collaborator

/ok to test 5698323

Comment thread .github/workflows/ci.cpu.yml Outdated
Comment on lines +131 to +134
# Verify that public headers are self-contained. Header
# verification is independent of the rest of the build matrix, so
# it only needs to run once; pick a single plain (non-modules,
# non-sanitizer) Debug build to run it in.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is perhaps a lie. Some of the excluded files are excluded for being Windows-only, or similar. This diff is certainly adding value, but maybe it makes sense to attach this validation to more elements of the matrix to ensure at least one build includes each of the conditionally-excluded files.

Ian's review comment on the CI step (added in 25af571): the claim that header
verification 'is independent of the rest of the build matrix, so it only
needs to run once' is wrong for the conditionally-excluded headers
specifically. Running the check only on the Linux clang-22 Debug job meant
exec/windows/filetime_clock.hpp, exec/windows/windows_thread_pool.hpp, and
exec/taskflow/taskflow_thread_pool.hpp were SKIP_LINTING'd there (correctly,
per their own guards) but never compiled anywhere else in CI either, so they
had no coverage at all despite the PR's stated goal.

- Force-enable STDEXEC_ENABLE_TASKFLOW on the same clang-22 Debug job that
  already runs the verification target, alongside the existing
  STDEXEC_ENABLE_TBB. Verified locally that this is a safe, self-contained
  CPM fetch (same mechanism as the existing Boost/ASIO dependency) and that
  all_verify_interface_header_sets goes from 178 to 179 checked headers with
  it on, all passing.
- Add the equivalent verification step to test-windows.ps1, gated to Debug
  configs so it doesn't run twice per compiler/toolset matrix entry. This is
  the only place in CI where WIN32 is true, so it's the only place the two
  Windows-only headers actually get compiled and checked. Not locally
  verifiable — no Windows toolchain available in this environment — but the
  change follows the existing script's structure and conventions closely.

Between the two, every SKIP_LINTING exclusion added in 1e7de1a now has
somewhere in CI that actually compiles it: TBB and Taskflow on the Linux
job, the two Windows-only headers on the Windows Debug jobs. __epilogue.hpp
and __parallel_scheduler_default_impl_entry.hpp were already covered
unconditionally (the former isn't gated at all; the latter has its own
hand-written check from 5698323).
@ericniebler

Copy link
Copy Markdown
Collaborator

/ok to test 637d5ca

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants