Loading a project scans the same sources once per Configuration: a Crafter.Graphics consumer spends ~10 s before anything builds #47
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#47
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::GetInterfacesAndImplementationsreads and regex-scans every source it is given, every time it is called, with nothing cached between calls. When a project declares many configurations over the same files, each file is scanned once per configuration. Crafter.Graphics gives each of its tests the library's whole interface and implementation list, so its sources are scanned dozens of times.Every consumer pays this on every load, including
crafter-build run --listandcrafter-build -r, because a dependency'sproject.cpp, test declarations included, runs whenever its consumer is loaded. In a Crafter.Graphics consumer it is about 10 s before anything is built or run.Line numbers refer to
a3fe1bd(5.0.0).Measured
On 2026-11-01, with
flatpak run net.catcrafts.CrafterBuild run --listin 3DForts, which depends on Crafter.Graphics:--unshare=network, plus agitwrapper that makespulla no-op), and 20–28 s online.strace -f -Tonflock,fcntl,openatandexecveshows no call and no gap over 0.5 s, so this is not a lock wait.Window.cppm,WebGPU.cppmandWebGPUComputeShader.cppmare each opened 59 times.crafter-build -ron a Crafter.Graphics example spends about 14 s before the program starts.git pulls run one after another, about 0.2 s each. That is a small share of the total.Where
implementations/Crafter.Build-Clang.cpp:81,GetInterfacesAndImplementations. For each interface (:89-100) and each implementation (:188-196), it reads the whole file, strips comments with twostd::regex_replacepasses, and then runs the module and import patterns (:110,:123,:153,:164,:179,:201,:208,:223,:238). Nothing remembers a file from one call to the next.project.cpp:389, in the lambda that declares each test:c.GetInterfacesAndImplementations(ifaces, sources), wheresourcesis the library's implementations plus the test'smain. It runs once per test, for 61 test directories.Proposal
Cache the per-file scan for the life of the process, keyed on the absolute path plus size and mtime, or a content hash. Store what the scan finds, the module or partition declared and the imports, and let every Configuration that names the file reuse it. Resolving those imports against the configuration's own dependencies (
ResolveImportName) stays per configuration; only reading and matching are shared.#45 would replace the regexes with
clang-scan-deps, a preprocessor run per file. That makes a per-file cache more important, not less, and the two fit together: the cache stores whatever the scanner returns.Independent of this, a library can avoid the cost for its consumers by declaring its tests only when
IsTopLevelProject(), as Crafter.LintRules does for its test. That would be a change in Crafter.Graphics, not here.