Scan for modules and imports with clang-scan-deps instead of regexes #45
Labels
No labels
bug
claude:blocked
claude:done
claude:failed
claude:in-progress
claude:ready
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Catcrafts/Crafter.Build#45
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Configuration::GetInterfacesAndImplementationsfinds a project's module declarations and imports by running regexes over each source's text, after deleting comments with two more regexes. Those regexes skip neither string literals nor comments that span lines, ignore the preprocessor, and expect exactly one space afterimport. So a file can gain a module or an import it does not have, or lose one it does. The fix proposed here is clang's own dependency scanner,clang-scan-deps -format=p1689, which preprocesses each file with its real compile command.Line numbers refer to
909e027.Details
implementations/Crafter.Build-Clang.cpp:98-99and:195-196delete comments with//[^\n]*and/\*.*?\*/. Instd::regex,.does not match a newline, so a block comment that spans lines stays in the text.:110and:123findexport module X;andexport module X:P;, and:201finds an implementation unit'smodule X;.:153,:164,:179,:208,:223and:238findimport X;andimport :P;. Every pattern has a single literal space aftermoduleorimport, and the import patterns allow no space before the;.The same patterns, run on a few inputs:
"export module std;"inside a string literalmodule std;import Foo;on its own line inside a/* … */that spans linesFooimport Foo;between#if 0and#endifFooimport Foo;(two spaces) orimport Foo ;A declaration read where there is none becomes a spurious dependency, or a hard error when no such module is in the build. A real import that is missed is a missing edge: on a clean build, the unit can compile before the module it imports has a BMI.
Reproduction
Found in PR #44.
tests/MingwSysroot/main.cppwrites a stand-in std module withWrite(..., "export module std;\n"). The pattern at:201matchedmodule std;inside the literal and took the test for an implementation unit of a modulestd, socrafter-build test MingwSysrootstopped before compiling anything:The test now writes that text with
std::format("export module {};\n", "std"), with a comment saying why.Suggested fix
Take each unit's provided and required modules from
clang-scan-deps -format=p1689. It runs the preprocessor with the unit's compile command, so strings, comments, spacing, macros and#ifcome out the way clang sees them when it compiles the file, and it reports partitions asX:P. Both toolchains ship it:/usr/bin/clang-scan-depson Arch, and/app/bin/clang-scan-depsin the Flatpak.To settle:
GetInterfacesAndImplementationsruns insideCrafterBuildProject, often before the project has set its defines and flags, and the scan needs the final compile command.Build()already resolves imports late, inResolvePendingImports(). The scan could move there, leavingGetInterfacesAndImplementationsto record the files.clang-scan-depsper file adds a clang start-up per source to every build. It can also scan a whole compilation database in one process (-compilation-database=… -j N). Results can be kept per file, keyed on its bytes and its command, so unchanged files are not scanned again.std::formatworkaround intests/MingwSysroot/main.cpp, fix the doubled "not found not found" at:235, and pin the behaviour with a test of the four inputs above.A cheaper alternative was considered and set aside: blank out comments and string and character literals before the regexes, and match declarations only at the start of a line, where C++20 requires them. That fixes the first, second and fourth rows of the table, but not
#if.