There is a significant "clash of philosophies" regarding Header Units in the standard proposal for module dependency scanning P1689 (it's not standard yet because it doesn't belong to the language standard and the whole ecosystem is thrown to trash by now but it's de facto) that seems to be a major blocker for universal tooling support.
# The Problem
When scanning a file that uses header units, how should the dependency graph be constructed? Consider this scenario:
// a.hh
import "b.hh";
// b.hh
// (whatever)
// c.cc
import "a.hh";
When we scan
c.cc, what should the scanner output?Option 1: The "Module" Model (Opaque/Non-transitive) The scanner reports that
c.cc requires a.hh. It stops there. The build system is then responsible for scanning a.hh separately to discover it needs b.hh.Rationale: This treats a header unit exactly like a named module. It keeps the build DAG clean and follows the logic that `import` is an encapsulated dependency.
Option 2: The "Header" Model (Transitive/Include-like) The scanner resolves the whole tree and reports that `c.cc` requires both `a.hh` and `b.hh`.
Rationale: Header units are still headers. They can export macros and preprocessor state. Importing
a.hh is semantically similar to including it, so the scanner should resolve everything as early as possible (most likely using traditional -I paths), or the impact on the importing translation unit is not clear.# Current Implementation Chaos
Right now, the "Big Three" are all over the place, making it impossible to write a universal build rule:
1. Clang (
clang-scan-deps): Currently lacks support for header unit scanning.2. GCC (
-M -Mmodules): It essentially deadlocks. It aborts if the Compiled Module Interface (CMI) of the imported header unit isn't already there. But we are scanning specifically to find out what we need to build!3. MSVC: Follows Option 2. It resolves and reports every level of header units using traditional include-style lookup and aborts if the physical header files cannot be found.
# The Two Core Questions
1. What is the scanning strategy? Should
import "a.hh" be an opaque entry as it is in the DAG, or should the scanner be forced to look through it to find b.hh?2. Looking-up-wise, is
import "header" a fancy #include or a module?If it's a fancy include: Compilers should use `-I` (include paths) to resolve them during the scan. Then we think of other ways to consume their CMIs during the compilation.
If it's a module: They should be found via module-mapping mechanics (like MSVC's
/reference or GCC's module mapper).# Why this matters
We can't have a universal dependency scanning format (P1689) if every compiler requires a different set of filesystem preconditions to successfully scan a file, or if each of them has their own philosophy for scanning things.
If you are a build system maintainer or a compiler dev, how do you see this being resolved? Should header units be forced into the "Module" mold for the sake of implementation clarity, or must we accept that they are "Legacy+" and require full textual resolution?
I'd love to hear some thoughts before this (hopefully) gets addressed in a future revision of the proposal.
https://redd.it/1qx7zex
@r_cpp