Import a CMake project

September 5, 2026 · View on GitHub

CMake is used only to import or refresh build records. Normal builds and certifying verification do not invoke it. After init, setup, source review, and placing the reference binary at its configured path, run:

rbit import cmake .

This guide owns the import and refresh workflows. The generated option table lists every flag and default.

This one command creates the minimal initial build records and empty per-source review shards, configures the existing CMake project without building it, and saves the direct compiler and linker graph. It does not edit CMakeLists.txt. In the single rbit init command, repeat --target NAME for every binary the project produces. ReproBit target IDs map to CMake targets with the same name by default. When the ReproBit ID is program but the CMake target and output are app, declare both facts explicitly. The --oracle option names the reference binary:

rbit init . --target program --artifact build/app.exe --oracle reference/program.exe
# After setup, source review, and placing the reference binary:
rbit import cmake . --target program=app

For the simplest path, pass the real CMake target name to rbit init --target when starting the project; the default rebuilt-output and reference filenames then follow that name. Use the mapping form only when the ReproBit ID intentionally differs, and make sure the artifact declared at init matches the target's real output path.

For example, initialize a target whose real output name is GAME.EXE with:

rbit init . --target game --artifact build/GAME.EXE --oracle reference/GAME.EXE

Pass ordinary CMake cache settings explicitly when configuration needs them:

rbit import cmake . \
  --cmake-define BUILD_SHARED_LIBS=OFF \
  --cmake-define GAME_LANGUAGE=en

Repeat --cmake-define NAME=VALUE for each setting. ReproBit passes each value as one CMake argument and refuses duplicate names or settings it owns itself. The import records these values, the selected CMake program, the configuration, the timeout, and directive inputs so a later refresh can replay the same setup. Passing one of those options to --refresh intentionally replaces its saved value for the refreshed graph. Use --clear-cmake-defines or --clear-directive-inputs when a refresh should replace a saved list with no values.

The generated files are ordinary JSON that can be reviewed and committed. A failed first import removes only its new scaffold and retains the temporary workspace for diagnosis. ReproBit builds use only the committed graph.

Refresh after adding or removing source files

Start with a read-only preview whenever the reviewed source list changes:

rbit source preview .

For an existing CMake import, preview prints the complete refresh command. Run that command directly—do not lock the new list separately first:

rbit import cmake . --refresh

If preview used repeated --path values, its refresh command repeats the same values in the same order. Without them, both commands use the Git index.

Refresh prepares the new source list and CMake records privately, keeps saved adjustments and checks for compiler steps that are still compatible, starts new or changed steps with empty review files, and retires steps CMake no longer produces. It then verifies every target from scratch and publishes the complete update at once: source and build records, verified binaries, matching debug files, and the JSON and HTML report. An ambiguous compiler step, a failed verification, or a concurrent project change leaves the published project unchanged. --target is only for the first import.

Use --jobs COUNT to set the refresh verification's parallelism and the shared --initialization-timeout, --compile-timeout, --link-timeout, and --cleanup-timeout options to adjust its execution limits. These options require --refresh and apply only to this run; they do not become part of the saved CMake recipe. --timeout separately controls CMake configuration.

Older recorded graphs may not contain the original CMake options. They remain valid for builds and verification, but refresh will ask you to run one ordinary rbit import cmake . with those options first.

Advanced: how the CMake import works and how to split it for CI

The import materializes the reviewed source, then performs one bounded configure—never a project build—in a fresh workspace. It uses Unix Makefiles on POSIX and the provisioned, authenticated NMake Makefiles frontend on native Windows. Both paths request CMAKE_EXPORT_COMPILE_COMMANDS, the locked compiler-role frontends, and reprobit-target-plan.json.

Run rbit cmake-module --file to inspect the installed ReproBit.cmake. The guided import supplies that module through CMAKE_PROJECT_INCLUDE and defers its generated target plan until the project has declared its targets. Projects that deliberately use the lower-level split commands can instead contain this thin import hook:

if(REPROBIT_PROJECT_PLAN)
  include("${REPROBIT_CMAKE_MODULE}")
  include("${REPROBIT_PROJECT_PLAN}")
endif()

The generated plan is declarative CMake data. It may use these checked graph operations:

  • reprobit_insert_generated_source inserts a freshly materialized source at a zero-based target index. It checks the existing before/after neighbours, the file SHA-256 and size, project-root containment, and that neither the file nor its seat is redirected.
  • reprobit_insert_link_item inserts an existing link item at a checked index and validates its exact neighbours.
  • reprobit_register_target records the selected output and optional PDB for a target.
  • reprobit_add_link_admission records a typed produced-object admission, including its exact index or neighbour selector and expected symbol.
  • reprobit_write_plan emits resolved target and admission metadata at CMake generate time. Python re-parses it with duplicate-key and unknown-field rejection before it becomes proof input.

For example, an adapter-generated include can contain:

reprobit_insert_generated_source(
  TARGET program
  SOURCE generated/carrier.cpp
  INDEX 4
  AFTER src/third.cpp
  BEFORE src/fifth.cpp
  LANGUAGE CXX
  SHA256 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
  SIZE 128
)

reprobit_insert_link_item(
  TARGET program
  ITEM generated_supplier
  INDEX 2
  AFTER first_library
  BEFORE second_library
)

Projects should not duplicate recipe dispatch, source rendering, path transport, or provenance logic in CMake. Keep target definitions project-specific and let the adapter own the generated plan.

Create the ignored configure tree, then extract the closed graph using the exact roots reported by the first command:

rbit graph configure . \
  --workspace-root .reprobit-state/import \
  --toolchain-root /opt/toolchains/msvc42 \
  --compiler-transport /opt/toolchains/msvc42/wine/x86/cl \
  --resource-transport /opt/toolchains/msvc42/wine/x86/rc \
  --cmake-define FEATURE_SET=classic
rbit graph extract . \
  --configured-build-root .reprobit-state/import/build \
  --effective-source-root .reprobit-state/import/source \
  --effective-source-digest SHA256_FROM_CONFIGURE \
  --toolchain-root /opt/toolchains/msvc42 \
  --directive-input config=mfcs42 \
  --directive-input config=msvcprt.lib

Replace SHA256_FROM_CONFIGURE with the effective-source digest printed by graph configure. The workspace must be absent or empty; ReproBit will not erase or reuse a configured tree. The configure command seals the effective source tree before and after CMake, rejects a changed target universe, and reports a configure log and command digest for review. The default target-plan path is .reprobit-state/import/build/reprobit-target-plan.json; use --target-plan only for another path beneath the configured build root. The extractor reads compile_commands.json, resource rules, link.txt, and bounded response files, then rejects commands or inputs outside the effective source, configured build, and admitted toolchain roots. It publishes a canonical reprobit/producer-graph.json only after validating the complete candidate project tree and rechecking all authority files transactionally.

Older MSVC COFF objects and archive members can contribute /DEFAULTLIB controls through their .drectve sections even though those libraries do not appear in the extracted linker command. Such edges are never inferred or authorized at certification time. Run a prelink audit, review its missing-edge diagnostic, and repeat --directive-input TARGET=LIBRARY once for every approved target and bare library. The extractor lowercases the name, adds .lib when omitted, canonicalizes the references as system-library/*.lib, and rejects unknown targets, paths, and duplicates. A certifying run requires every effective DEFAULTLIB to match exactly one argv-derived or committed directive input; its failure prints the exact flags needed for the next reviewed extraction.

Review and commit that graph, then discard the ignored configure tree. Later rbit build (warm by default), rbit build --cold, and rbit verify runs expand only its symbolic ${SOURCE}, ${BUILD}, and ${TOOLCHAIN} seats and invoke the locked producer roles directly. Graph schema v3 requires every source edge to resolve through the source manifest or a reviewed overlay output. Unrelated manifest entries may be added or removed without changing the command DAG; removing a graph input, or changing the toolchain lock, logical-path profile, target set, terminal artifact path, or producer commands requires a new import extraction.

Warm build stores non-certifying node artifacts in the leased project-local CAS. The first run normally misses; an unchanged second run should be all-hit with zero backend runtime starts. build --cold and verify, which always builds from scratch, bypass the cache entirely.