Repository navigation
feat(compdb): optionally add source files the compile commands #include - #48
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Dependency replay can silently omit compiled sources by discarding preprocessing inputs and additional architecture configurations.
3 open issues
What changed in this PR
Adds opt-in discovery of source files compiled through #include, extending the compilation-database frontend’s SBOM source coverage.
Changes:
- Adds compiler dependency scanning with command filtering and response-file expansion.
- Integrates source filtering, deduplication, and collision reporting.
- Adds documentation, tests, and CI coverage.
| File | Description |
|---|---|
| tests/test_included_sources.py | Tests command rewriting, dependency parsing, and frontend integration. |
| share/README.md | Documents the new scanner module. |
| share/frontends/included_sources.py | Implements dependency scanning and command rewriting. |
| share/frontends/compdb_sbom.py | Adds --included-sources and merges discovered sources. |
| .github/workflows/selftest.yml | Includes scanner checks and tests in CI. |
Review effort: Balanced
Frauschi
force-pushed
the
fix/srcs-included-sources
branch
from
October 20, 2026 15:35
bf82490 to
95e1838
Compare
Frauschi
force-pushed
the
fix/srcs-included-sources
branch
from
October 22, 2026 10:53
95e1838 to
be4a095
Compare
A source-set SBOM hashes the files listed as compile_commands.json entries. A product can also compile a .c file by including it from another one: wolfSSL compiles 29 such files, among them src/ssl_api_rw.c, src/x509.c and, when inlining is on, wolfcrypt/src/misc.c. None of those were listed, so editing one did not change the source-set hash or the SBOM. With the new --included-sources, compdb_sbom.py reruns every selected C, C++ and .S compile command with -M and adds each file the compiler read that has a source extension, lies under --root (else under the common directory of the listed sources), passes --include/--exclude and is not already listed. Headers stay out, as before. Every entry for a file is rerun, since a second target can compile it with other -D flags. Files are matched by device and inode, so a case-variant #include on macOS or Windows or a hardlink is not listed twice, paths through a symlinked directory resolve to the file the compiler read, and an included file whose basename clashes with another source is named in the error. The rerun is `<compiler> -M <flags> <source>` with only the flags that change what the preprocessor reads: -D, -U, -I and friends, -include, -imacros, -Wp, minus its -M and -o parts, --sysroot, --specs, -std, -m, -O, and the -f flags that set predefined macros (char signedness, -fshort-wchar, -ffreestanding, -fno-builtin, -fPIC, -fstack-protector, -fsanitize=, -fopenmp and the like). Every other -f flag is dropped, because several write files (SARIF, module caches, -fdeps-file). Options that take a separate argument are kept or dropped as a pair, so -mllvm, -framework and -undefined cannot split; preprocessor options passed through -Xclang or -Xpreprocessor (-D, -I, -include, -include-pch) are forwarded as they are, so a CMake precompiled header still applies, and -M sits in front so a misparsed option takes the source and fails instead of linking. Response files are expanded, nested ones too, and a missing one is an error; a ccache or distcc launcher is skipped; a universal build is scanned once per -arch, with that arch's -Xarch_ flags, and the results merged. Assembler entries (GNU as reads -M as --mri) are not rerun, and a -M run whose output does not list its own source is an error, so a compiler that gives -M another meaning cannot pass silently. The rerun never writes a file. It uses the build's own compiler, because only that resolves the target's include paths; a compiler that fails stops the run. The scan is opt-in. It runs the compilers compile_commands.json names, which the frontend otherwise treats as untrusted input, and the TI, IAR and CCRX compilers compdb also serves have no -M. On a CMake build of wolfSSL master the scan adds those 29 files to the 38 library sources in src/ and wolfcrypt/src/, in about 1.5 seconds. The scan lives in share/frontends/included_sources.py, imported only when the option is given, so the Zephyr frontend can use it too and loading compdb_sbom.py as a module does not need frontends/ on sys.path. New tests/test_included_sources.py covers the command rewrite, the -M output parser, and the frontend end to end (34 tests); selftest.yml runs it.
Frauschi
force-pushed
the
fix/srcs-included-sources
branch
from
October 22, 2026 12:37
be4a095 to
2a11b2e
Compare
sameehj
approved these changes
Oct 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Problem
A tier-E SBOM hashes the files listed as
compile_commands.jsonentries. Some sources are compiled by being#included from another.cfile, and those never appear as entries. wolfSSL has 29 of them, among themsrc/ssl_api_*.c,src/x509.cand, with inlining on,wolfcrypt/src/misc.c. Editing one of those changed neither the source-set hash nor the SBOM.Changes
compdb_sbom.py --included-sourcesreruns each selected C, C++ and.Scompile command as<compiler> -M <flags> <source>. It adds every file the compiler read that has a source extension, lies under--root(else under the common directory of the listed sources), passes--include/--excludeand is not already listed. Headers stay out.Every entry for a file is rerun, since a second target can compile it with other
-Dflags. Files are matched by device and inode, so a case-variant#includeor a hardlink is not listed twice. An included file whose basename clashes with another source is named in the error.The rerun keeps only the flags that affect preprocessing:
-D,-U, the-Ifamily,-include,-imacros,--sysroot,--specs,-std,-m,-O;-Wp,with its-Mand-oparts removed;-fflags that set predefined macros.Every other
-fflag is dropped, because several of them write files. Options that take a separate argument are kept or dropped as a pair. Preprocessor options passed through-Xclangor-Xpreprocessor(-D,-I,-include,-include-pch) are forwarded as they are, so a CMake precompiled header still applies.Response files are expanded, including nested ones. A missing one is an error. A
ccache/sccache/distcc/icecclauncher is skipped. A universal build is scanned once per-arch, with that architecture's-Xarch_flags, and the results are merged.Assembler entries are not rerun: GNU
asreads-Mas--mri. A-Mrun whose output does not list its own source stops the run, so a compiler that gives-Manother meaning cannot pass silently. A compiler that fails stops the run.The scan lives in the new
share/frontends/included_sources.py, which is imported only when the option is given, so the Zephyr frontend can reuse it.Why opt-in
The scan executes the compilers that
compile_commands.jsonnames, and the frontend otherwise treats that file as untrusted input. The TI, IAR and CCRX compilers that compdb also serves have no-M. Without the flag, nothing changes.Testing
tests/test_included_sources.pyhas 34 tests covering the command rewrite, the-Moutput parser and the frontend end to end;selftest.ymlruns it. They pass on macOS (Apple clang) and on Linux (gcc 13), where the case-variant test is skipped.On a CMake build of wolfSSL master the scan adds exactly the 29 included files to the 38 library sources in
src/andwolfcrypt/src/, in about 1.5 seconds. For the whole compile database, including examples and tests, it goes from 170 to 199 sources.Checked with real compilers:
-Wp,-o;arm-none-eabi-gccwith--specsand.Sfiles;arm-none-eabi-as;-undefined,-frameworkand SARIF output flags;-arch x86_64 -arch arm64) whose source includes a different.cfile per architecture, where both are listed.None of the reruns writes a file.
Notes
shlex, which mangles backslash paths, and some Windows shares report a zero inode. The Zephyr frontend in fix(zephyr): take module sources and settings from the west build #46 has a Windows-aware command splitter that compdb can share once fix(zephyr): take module sources and settings from the west build #46 lands.--included-sourceswill follow fix(zephyr): take module sources and settings from the west build #46.