Skip to content

CMake frontend: repo-relative source keys, relative -I roots, deterministic TU union - #23

Open
bluecmd wants to merge 3 commits into
EngFlow:mainfrom
bluecmd:up-cmake-path-keys
Open

bluecmd wants to merge 3 commits into
EngFlow:mainfrom
bluecmd:up-cmake-path-keys

Conversation

@bluecmd

@bluecmd bluecmd commented Sep 27, 2026

Copy link
Copy Markdown

AI-generated: the code, tests and this description were written by Claude (Anthropic) agents and reviewed by a Claude session; a human (@bluecmd) directed the work and decided to send it. We found these while using any2bazel to migrate ~60 CMake/Meson packages from an OpenWrt image, plus zlib, fmt, spdlog, tinyxml2, jansson and expat on the host toolchain.

Three small CMake-frontend fixes. Each one made a correct BUILD file look wrong, or made the diff results change between runs.

  • Key sources relative to repo_root, not the CMake source dir. When the CMake project is a subdirectory of the repo (vendored third_party/foo, a monorepo), the File API spells avl.c while Bazel spells third_party/foo/avl.c. Every TU showed up as missing_tu/extra_tu. Sources are now anchored on the codemodel's paths.source and re-relativized against repo_root.
  • Deterministic TU-union representative. _union_tus walked library names from a set, so when a source is compiled by a shared/static twin with different flags (-Dfoo_EXPORTS, -fPIC), the reported defines_diff flipped between runs with Python's hash seed. It now walks them sorted.
  • Resolve relative -I roots. add_definitions(-I..) or a relative include_directories() in a subdirectory reaches the File API verbatim as ... It is now resolved against the target's source dir, normalised and re-relativized, so no per-package include_map entry is needed. Absolute roots are unchanged.

Fixtures without codemodel paths keep the old behaviour. New tests are in tests/test_engine.py and tests/test_extractors.py, and all existing tests pass.

Not tested here: the Ladybird/Dolphin case studies. The first and third fixes only change keys when the CMake source dir differs from repo_root or a root is relative.

🤖 Generated with Claude Code

bluecmd and others added 3 commits September 27, 2026 08:07
…e dir

The File API spells sources relative to the top-level CMake source dir. When
that dir is a subdirectory of the repo root (a vendored third_party/ package,
or a monorepo holding several CMake projects) the CMake side keyed a TU as
'avl.c' while the Bazel side keyed the same file as 'third_party/libubox/avl.c',
so every TU showed up as missing_tu/extra_tu and the diff was meaningless.

Anchor each source on the codemodel's paths.source and re-relativize against
repo_root; sources outside the repo stay absolute as before. Fixtures without
paths.source keep the old behaviour.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
_union_tus keeps the first TU seen per source, and the library names it walks
come from a set comprehension, so when one source is compiled by two library
targets with different flags (a shared/static twin: -Dfoo_EXPORTS, -fPIC) the
representative -- and therefore the reported defines_diff/flags_diff -- flipped
between runs with Python's per-process hash seed. Walk the names sorted.

Found by the model during the libubox (OpenWrt) migration: the same diff run
alternated between reporting ubox_EXPORTS as cmake-only and not at all.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A subdirectory CMakeLists that does ADD_DEFINITIONS(-I..) (libubox lua/,
ubus lua/ and examples/, uci lua/) reaches the codemodel as the literal
token `-I..` in compileCommandFragments. Stored verbatim, the CMake side
carried `..` as an include root while the Bazel side spells the same
directory `third_party/<pkg>`, so every such package needed the same
include_map entry (`..` -> `third_party/<pkg>`) before it could converge.

`..` only means something relative to the directory that spelled it.
CMake itself defines a relative include_directories() entry as relative to
CMAKE_CURRENT_SOURCE_DIR, and the raw -I.. is the hand-spelled version of
that intent (it only names a real header root for in-source builds, which
is OpenWrt's default: cmake.mk sets CMAKE_BINARY_DIR = CMAKE_SOURCE_DIR).
So resolve relative -I/-isystem/-iquote/-idirafter roots -- joined or
split, in fragments or in includes[].path -- against the target's source
dir (codemodel paths.source joined with the target's paths.source),
normalize (lua/.. -> package root) and re-relativize against repo_root,
the same way the previous commit anchors sources. Absolute roots are untouched: the
canonicalizer already normalizes those (`examples/..` from
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) collapses there).
Replies without codemodel paths keep the verbatim spelling.

With this, libubox, ubus and uci converge with their include_map entries
removed. The `.` -> `third_party/<pkg>` entries those configs also carried
were never load-bearing: `.` only ever appeared on the Bazel side (the
toolchain's `-iquote .` for the workspace root), which the asymmetric
include check already tolerates as bazel_only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

1 participant