Skip to content

codex: add CMake integration - #68

Open
saidctb wants to merge 8 commits into
mainfrom
cmake
Open

codex: add CMake integration#68
saidctb wants to merge 8 commits into
mainfrom
cmake

Conversation

@saidctb

@saidctb saidctb commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.78704% with 83 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
prik/cmake.py 80.28% 26 Missing and 16 partials ⚠️
prik/pipeline/build.py 80.48% 17 Missing and 7 partials ⚠️
prik/cli.py 69.09% 10 Missing and 7 partials ⚠️

📢 Thoughts on this report? Let us know!

saidctb and others added 7 commits September 10, 2026 17:32
Two narrow correctness fixes in the standalone CLI -> CMake translation and
its CMake target properties; the pipeline stages themselves are unchanged.

generate --cmake resolved prebuilt objects, archives, and shared libraries to
absolute paths and then rewrote every absolute item back to a relative string,
so a real file path reached target_link_libraries() as a bare token and the
linker read it as a library name (-l../lib/libnative.a). Path-backed items now
render as "${CMAKE_CURRENT_LIST_DIR}/<relative>", keeping the three categories
distinct - object/archive/shared library to a path, named library to a name,
arg: to a linker argument - in their original order.

--native-library-dir became LINK_OPTIONS "-L<dir>", which carries no runtime
meaning, so an extension could link and then fail to import. prik_add_module()
gains LIBRARY_DIRS, mapped to target_link_directories() plus the target's
BUILD_RPATH, and generate --cmake emits LIBRARY_DIRS instead of a raw -L.
INSTALL_RPATH stays under project control.

Also fixes a bare Python float in the installed-wheel CMake test: PRIK's
documented scalar policy requires numpy.float64, and CI reached that assertion
once the isolated wheel install succeeded.

Changed: prik/cmake.py (link-item rendering, LIBRARY_DIRS emission),
cmake/UsePRIK.cmake (LIBRARY_DIRS argument, link directories, BUILD_RPATH),
CMake guide and CLI reference docs, CHANGELOG.

Tests: two end-to-end regressions in tests/fortran/infrastructure/building/
end_to_end/test_cmake_builds.py that configure, build, import, and call - one
linking a real prebuilt .o and .a from a sibling directory, one importing an
extension backed by a shared library in a non-system directory with no loader
path able to resolve it. The ordered-link-item ordering test is kept.

Verified: all five suite roots green (fortran -m "not real_library" 2534, c
646, docs 745, tools/workflows 64) plus the CI toolchain-smoke lane (8) and a
CMake-file rerun with LD_LIBRARY_PATH set. Static gate clean: ruff check and
format, version check, bandit, vulture, radon policy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrtPVjqMKNLJ1cYsteWiKG
Three narrow fixes in the packaged CMake helper; the pipeline stages are
unchanged.

The native OBJECT target filtered LINK_LIBRARIES through if(TARGET ...), which
forwarded "debug Foo optimized Bar" as plain Foo and Bar with their keywords
stripped, so both configurations' usage requirements reached the native
sources at once, and dropped generator-expression entries entirely because
they are not targets. PRIK_LINK_LIBRARIES now passes through unchanged, so
CMake applies the caller's own selection rules. The extension target keeps the
same LINK_LIBRARIES for the final link.

CMake compiles .C as C++ while PRIK plans such a source as C, so the two would
disagree about the compiler. prik_add_module() and generate --cmake now reject
that suffix with a message naming the reason, and generation fails before a
project is written rather than at CMake configure time.

A module contributing native Fortran sources now checks that CMake's Fortran
language is enabled, instead of failing later with a less direct error.

Changed: cmake/UsePRIK.cmake (native link forwarding, suffix validation,
Fortran language check), prik/cmake.py (fail-fast .C rejection), CMake guide
docs, CHANGELOG.

Tests: five regressions in tests/fortran/infrastructure/building/end_to_end/
test_cmake_builds.py - a parametrized Debug/Release pair whose native source
#errors when the wrong configuration's definition arrives or the right one is
missing, .C rejection at both the helper and CLI level, and the Fortran
language diagnostic. All five fail against the pre-fix helper and pass with
it.

Verified: test_cmake_builds.py plus tests/docs 782 passed, CI toolchain-smoke
lane 8 passed, static gate clean (ruff check and format, version check,
bandit, vulture, radon policy).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrtPVjqMKNLJ1cYsteWiKG
CMake configuration ran PRIK's whole semantic pipeline -- preprocessing,
parsing, semantic IR, policy completion, wrapper planning, C and Fortran
lowering, and a compiled Fortran type probe -- purely to discover which files
it would later generate, then ran all of it again at build time to actually
write them. Configuration now asks a structural question instead, and the
pipeline runs exactly once, during the build.

The structural query derives everything CMake needs from the module name and
its declared shape: the generated filenames, the link driver, and the
mandatory ABI flags its compiler profile states. Vendor detection is a lookup
on the driver's name, so no compiler runs. Querying a 300-procedure module's
plan drops from 2.54s to 0.41s, and the query no longer grows with source
size.

Configuration cannot read a source, so the generated file list must not depend
on what analysis finds. PRIK now names the optional compilation units up front
and always writes them: a module needing no collision adapter still gets
<module>_adapters.c, and a Fortran module needing no bridge still gets
bind_c_<module>_wrapper.f90. An unused unit holds a placeholder that defines
no symbol and compiles clean under -Wall -Wextra -pedantic -Werror. A semantic
edit therefore changes a file's contents, not the build graph, and never needs
a reconfigure. Configure-time and build-time calls share one function, so the
files CMake declares are exactly the files generation fills.

Transitive semantic inputs -- nested C headers, Fortran INCLUDE files,
imported contracts -- now reach CMake through a depfile written during
generation, replacing the CMAKE_CONFIGURE_DEPENDS wiring that existed because
a bridge source could appear or disappear. That uses add_custom_command
DEPFILE, so the packaged helper requires CMake 3.21; verified working under
both Ninja and Unix Makefiles.

UsePRIK.cmake drops from 560 to 482 lines: gone are the plan-tree path
rebasing, the per-compilation-unit JSON application, the semantic dependency
wiring, and every semantic JSON read. Compile flags now use compile-language
generator expressions, so no source properties remain and cross-target flag
leakage is structurally impossible. The internal heavy --plan mode had no
other consumer and is removed.

Changed: prik/naming/generated_files.py (new, sole owner of generated
filenames, replacing inline spellings in planning, codegen, and build
integration), prik/cmake.py (StructuralLayout), prik/cli.py (--cmake-plan,
--declared-layout, --depfile), prik/pipeline/build.py (placeholder units,
depfile writer, --plan removal), cmake/UsePRIK.cmake, CMake guide, CHANGELOG.

Tests: configure succeeds for a source PRIK cannot parse and fails only at
build time; the bridge and adapter filenames stay fixed while their contents
round-trip between placeholder and real without a reconfigure; placeholders
compile under strict conformance flags; an unchanged rebuild reruns nothing;
and --lto reaches both target kinds' compile lines and the link. The existing
nested-header and nested-INCLUDE tests now exercise the depfile. The obsolete
"reconfigures when a bridge appears" test is replaced by its inverse.

Verified: full CI-shaped suite 3990 passed, CMake suite 42 passed, static gate
clean. The installed-wheel test cannot run on this machine and is unchanged;
packaging picks up the new module through packages.find.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrtPVjqMKNLJ1cYsteWiKG
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