Skip to content

feat(compdb): optionally add source files the compile commands #include - #48

Merged
sameehj merged 1 commit into
masterfrom
fix/srcs-included-sources
Oct 22, 2026
Merged

sameehj merged 1 commit into
masterfrom
fix/srcs-included-sources

Conversation

@Frauschi

@Frauschi Frauschi commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Problem

A tier-E SBOM hashes the files listed as compile_commands.json entries. Some sources are compiled by being #included from another .c file, and those never appear as entries. wolfSSL has 29 of them, among them src/ssl_api_*.c, src/x509.c and, 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-sources reruns each selected C, C++ and .S compile 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/--exclude and is not already listed. Headers stay out.

  • 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 or 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 -I family, -include, -imacros, --sysroot, --specs, -std, -m, -O;
    • -Wp, with its -M and -o parts removed;
    • the -f flags that set predefined macros.

    Every other -f flag 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 -Xclang or -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/icecc launcher 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 as reads -M as --mri. A -M run whose output does not list its own source stops the run, so a compiler that gives -M another 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.json names, 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.py has 34 tests covering the command rewrite, the -M output parser and the frontend end to end; selftest.yml runs 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/ and wolfcrypt/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:

    • a CMake + Ninja build with clang precompiled headers;
    • gcc-16 with -Wp,-o;
    • arm-none-eabi-gcc with --specs and .S files;
    • arm-none-eabi-as;
    • Apple clang with -undefined, -framework and SARIF output flags;
    • an Apple clang universal build (-arch x86_64 -arch arm64) whose source includes a different .c file per architecture, where both are listed.

    None of the reruns writes a file.

Notes

@Frauschi Frauschi self-assigned this Oct 20, 2026
Copilot AI balanced review requested due to automatic review settings October 20, 2026 15:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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

Comment thread share/frontends/included_sources.py Outdated
Comment thread share/frontends/included_sources.py Outdated
Comment thread share/frontends/included_sources.py Outdated
@Frauschi
Frauschi force-pushed the fix/srcs-included-sources branch from bf82490 to 95e1838 Compare October 20, 2026 15:35
@Frauschi Frauschi assigned wolfSSL-Bot and unassigned Frauschi Oct 20, 2026
@Frauschi
Frauschi requested a review from sameehj October 20, 2026 15:37
@Frauschi
Frauschi force-pushed the fix/srcs-included-sources branch from 95e1838 to be4a095 Compare October 22, 2026 10:53
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
Frauschi force-pushed the fix/srcs-included-sources branch from be4a095 to 2a11b2e Compare October 22, 2026 12:37
@sameehj
sameehj merged commit 18226b1 into master Oct 22, 2026
3 checks passed
@Frauschi
Frauschi deleted the fix/srcs-included-sources branch October 22, 2026 12:59
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.

4 participants