Skip to content

# feat(api,cli): generate CMakeLists.txt for standalone C/C++ nodes and support cmake find_package#1763

Closed
Ts-sound wants to merge 26 commits into
dora-rs:mainfrom
Ts-sound:feat/standalone-node-cmake
Closed

# feat(api,cli): generate CMakeLists.txt for standalone C/C++ nodes and support cmake find_package#1763
Ts-sound wants to merge 26 commits into
dora-rs:mainfrom
Ts-sound:feat/standalone-node-cmake

Conversation

@Ts-sound
Copy link
Copy Markdown
Contributor

@Ts-sound Ts-sound commented Apr 28, 2026

Summary

Add CMake find_package support for all C/C++ API crates (dora-node-api-c, dora-operator-api-c, dora-node-api-cxx, dora-operator-api-cxx), enable dora new --kind node --lang c/cxx and --kind dataflow to generate ready-to-build CMakeLists.txt, replace shell-based staging with a Rust xtask tool, and publish unified library archives with cmake config files.

Problem

Running dora new --kind node --lang c or --lang cxx produced only source files without a build system. A // TODO: Makefile? comment remained in both template modules, requiring users to manually write their own build setup.

Related

Solution

Generic CMake Template

Single dora-api-config.cmake.in with @PACKAGE@, @TARGET@, @LIB_UNIX@, @LIB_WIN@, @CXX_BRIDGE_FILES@ placeholders shared by all 4 crates.

build.rs Staging

Each crate's build.rs writes cmake config + headers to predictable paths:

  • C crates: target/<profile>/<crate>/lib/cmake/<crate>/*.cmake + include/*.h
  • C++ crates: target/cxxbridge/<crate>/lib/cmake/<crate>/*.cmake + include/*.h + src/*.cc

xtask stage

Replaced shell scripts with cargo run -p xtask -- stage <crate> <target_dir> <prefix> — cross-platform, handles all 4 crates.

dora new Templates

Template Content
--kind node --lang c node.c + CMakeLists.txt (find_package(dora-node-api-c))
--kind node --lang cxx node.cc + CMakeLists.txt (find_package(dora-node-api-cxx) + CXX_BRIDGE_FILES)
--kind dataflow --lang c 3 nodes + root CMakeLists.txt (find_package + add_subdirectory)
--kind dataflow --lang cxx 3 nodes + root CMakeLists.txt (find_package + add_subdirectory + CXX_BRIDGE_FILES)

Tests

  • test-c-cpp-libraries.yml : CI test
  • scripts/c_cpp_lib/stage_test.sh — verifies staging for debug, release, --target
  • scripts/c_cpp_lib/build_node_test.shdora new --kind node + cmake build
  • scripts/c_cpp_lib/build_dataflow_test.shdora new --kind dataflow + cmake build

Documentation

Rewrote apis/C-CPP-LIBRARIES.md with Obtaining the API Libraries (download / xtask), Using dora new, and CMake Integration sections.

Key Changes

  • apis/c/node/cmake/dora-api-config.cmake.in — generic cmake template
  • apis/{c/operator,c++/node,c++/operator}/build.rs — cmake export
  • apis/c/{node,operator}/build.rs — stage to target/<profile>/<crate>/
  • xtask/cargo run -p xtask -- stage <crate> <target_dir> <prefix>
  • binaries/cli/src/template/c/{node,node/CMakeLists,cmake,mod}.rs — C node + dataflow templates
  • binaries/cli/src/template/cxx/{node,node/CMakeLists,cmake,node-template,mod}.rs — C++ node + dataflow templates
  • .github/workflows/{publish,test}-c-cpp-libraries.yml — replace shell scripts with xtask
  • scripts/c_cpp_lib/{stage_test,build_node_test,build_dataflow_test}.sh — build verification
  • apis/C-CPP-LIBRARIES.md — complete rewrite

Expected Flow

# Option 1: Download pre-built archives from GitHub releases
tar xzf dora-c-libraries-x86_64-unknown-linux-gnu.tar.gz

# Option 2: Build with xtask
cargo build --release -p dora-node-api-c
cargo run -p xtask -- stage dora-node-api-c target/release dora-c-libraries-x86_64-unknown-linux-gnu

# Create and build a C node
dora new my-robot --kind node --lang c
cd my-robot && mkdir build && cd build
cmake .. -DCMAKE_PREFIX_PATH=../../dora-c-libraries-x86_64-unknown-linux-gnu
cmake --build .

# Create and build a C++ dataflow
dora new my-dataflow --kind dataflow --lang cxx
cd my-dataflow && mkdir build && cd build
cmake .. -DCMAKE_PREFIX_PATH=../../dora-cpp-libraries-x86_64-unknown-linux-gnu
cmake --build .

Tasks

  • Generic cmake template for all 4 crates
  • build.rs staging to predictable target directory
  • xtask stage command replacing shell scripts
  • C node CMakeLists.txt template
  • C++ node CMakeLists.txt template with CXX_BRIDGE_FILES
  • C dataflow CMakeLists.txt (find_package + add_subdirectory)
  • C++ dataflow CMakeLists.txt (find_package + add_subdirectory)
  • CI publish workflow update
  • CI test workflow update
  • Build test scripts (node + dataflow)
  • Documentation rewrite (C-CPP-LIBRARIES.md)
  • C++ node template fix (correct cxxbridge API)

Notes

  • CXX_BRIDGE_FILES is only populated for C++ crates. C crates set it to empty.
  • C++ .cc files are NOT included in the static library — they come from find_package and must be compiled alongside user code.
  • Dataflow root CMakeLists.txt uses add_subdirectory() for each node, delegating to per-node CMakeLists.txt.

Ts-sound added 10 commits April 28, 2026 04:13
- Add build.rs to generate CMake config files on cargo build
- Create cmake templates for Config and ConfigVersion files
- Version automatically derived from CARGO_PKG_VERSION
- Library symlinked to lib/ for standard CMake layout
- Users can use: find_package(dora-node-api-c REQUIRED)
- Add CMakeLists-template.txt for standalone C nodes
- `dora new --kind node --lang c` now generates CMakeLists.txt
- Uses find_package(dora-node-api-c REQUIRED) pattern
- Remove unused dora-node-api-c/dora-operator-api-c deps from cli
- Resolve TODO: Makefile? comment
…-c::extra

- Add dora-node-api-c::extra interface library for platform deps
- Linux: pthread dl m rt
- macOS: CoreServices Security frameworks + pthread m c resolv
- Windows: system libs (advapi32, kernel32, ws2_32, etc.)
- CLI template now links extra target instead of explicit m
- Add platform/arch detection at cmake config load time
- Embed built target info from build.rs (@target@ placeholder)
- Use STREQUAL for exact string matching (not MATCHES regex)
- Add target mismatch FATAL_ERROR for cross-platform safety
- Fix symlink creation: use `let _ = remove_file` to ignore errors
- Print built target info during cargo build
- Add cmake config directory to package structure
- Copy dora-node-api-c cmake files (generated by build.rs)
- Unix: cp -r target/{target}/release/lib/cmake/dora-node-api-c
- Windows: Copy-Item -Recurse cmake/dora-node-api-c
- Package structure now supports find_package(dora-node-api-c)
- Check TARGET env var when building with `--target` flag
- cmake files now generated at correct path:
  - Without --target: target/{profile}/lib/cmake/dora-node-api-c/
  - With --target: target/{target}/{profile}/lib/cmake/dora-node-api-c/
- Fixes CI workflow `cp` error when packaging published libraries
- cmake already shows target info via message(STATUS) during configuration
- cargo build output is now clean without warnings
- OUT_DIR is managed by cargo, respects --target-dir and config
- Works correctly with or without explicit --target flag
- Fixes dangling symlink issue in default builds
- Eliminates 3-parent guess from CARGO_MANIFEST_DIR
- Handle arch name mismatch: AMD64 vs x86_64, arm64 vs aarch64
- Normalize at cmake side: AMD64/amd64/x86_64 -> x86_64
- Normalize at cmake side: ARM64/arm64/aarch64 -> aarch64
- Fixes target matching on Windows/macOS where names differ from Cargo
- Test dora new --lang c → cmake → build on Linux/Windows/macOS
- Trigger on changes to apis/c/node or cli c template
- Verify complete find_package workflow end-to-end
@trunk-io
Copy link
Copy Markdown
Contributor

trunk-io Bot commented Apr 28, 2026

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

- Remove '-node' suffix from CMake executable target name
- Update cmake-smoke.yml to verify correct build output path
- Executable now named 'test-node' instead of 'test-node-node'
@Ts-sound
Copy link
Copy Markdown
Contributor Author

@phil-opp Could you please review the changes (about apis/c/node)?
If approved, I'll apply consistent changes to c/operator and all cxx/* accordingly.

@phil-opp
Copy link
Copy Markdown
Collaborator

Claude found the following issue:

🔴 Windows: the static lib never makes it into lib/ on a clean build

In apis/c/node/build.rs:

#[cfg(windows)]
fn link_library(lib_dir: &Path) {
    let lib_symlink = lib_dir.join("dora_node_api_c.lib");
    let _ = fs::remove_file(&lib_symlink);
    let target_dir = lib_dir.parent().expect("failed to get parent");
    let lib_target = target_dir.join("dora_node_api_c.lib");
    if lib_target.exists() {
        fs::copy(&lib_target, &lib_symlink).expect("failed to copy library");
    }
}

build.rs runs before rustc compiles the crate, so on a fresh build target/release/dora_node_api_c.lib does not exist yet → lib_target.exists() is false → the copy is silently skipped. After compilation the .lib is on disk but never gets staged into target/release/lib/. find_package then resolves IMPORTED_LOCATION to a missing file and CMake fails at link time.

The Unix path happens to work because dangling symlinks are legal and cargo writes the .a later, resolving the link. Windows fs::copy doesn't have that property.

The CI smoke job should surface this on windows-latest — and the single cargo build --release --package dora-node-api-c step won't trigger a second build-script run (this PR also drops dora-node-api-c from binaries/cli/Cargo.toml, so cargo install --path binaries/cli doesn't re-invoke it).

Suggested fix: move lib staging out of build.rs into a post-build step (an xtask or a CI step that runs after cargo build). build.rs would then only emit the cmake/ and include/ files, and lib staging becomes a release-time concern — which is closer to what cargo's build-script contract expects anyway. As a bonus this also removes the Unix symlink, so local-dev and release-archive layouts stay identical.

@Ts-sound
Copy link
Copy Markdown
Contributor Author

Ts-sound commented Apr 28, 2026

OK, I will add this task as a follow-up to publish-c-cpp-libraries.yml .

- build.rs now only generates cmake config and header to OUT_DIR
- Add scripts/c-cpp-libraries/stage-c-node.sh for Unix staging
- Add scripts/c-cpp-libraries/stage-c-node.ps1 for Windows staging
- Update publish workflow to use staging scripts
- Rename cmake-smoke.yml to test-c-cpp-libraries.yml
- Staging happens in CI, not in build.rs (cargo contract compliant)
@phil-opp
Copy link
Copy Markdown
Collaborator

We also have an existing cmake-templates.txt file. How does this compare to it? Doesn't this lead to duplication?

- Scripts now receive TARGET_DIR (target/release) not BUILD_DIR + find OUT_DIR
- build.rs writes directly to TARGET_DIR/lib/cmake/ and TARGET_DIR/include/
- Remove unreliable find + grep OUT_DIR discovery
- Parameter renamed: BuildDir -> TargetDir for clarity
@Ts-sound
Copy link
Copy Markdown
Contributor Author

This file is used to simultaneously compile talker_1 and talker_2, listener_1 )for dataflow-template.yml). Is it necessary to continue using cmake-templates.txt?

- Use broader paths: apis/**, binaries/**, scripts/**, .github/workflows/**
- Remove invalid 'apis/c++/**' path pattern (++ causes parse error)
- Fix parameter name: BuildDir -> TargetDir in publish workflow
- Consistent with staging script changes
@Ts-sound
Copy link
Copy Markdown
Contributor Author

This file can be simplified.

project(cxx-dataflow LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "-fPIC")

find_package(dora-node-api-c REQUIRED)

## listener_1
add_executable(listener_1 listener_1/node.cc)
target_link_libraries(listener_1 PRIVATE dora-node-api-c::dora-node-api-c dora-node-api-c::extra)

## talker_1,talker_2
...

- Use CARGO_BUILD_TARGET to detect if --target flag was used
- Output structure:
  - Without --target: target/<profile>/lib/cmake/, include/
  - With --target: target/<target>/<profile>/lib/cmake/, include/
- Staging script now works correctly with TARGET_DIR parameter
- Removed dependency on OUT_DIR for final output location
@Ts-sound
Copy link
Copy Markdown
Contributor Author

Ts-sound commented Apr 28, 2026

@phil-opp
In Rust, the build.rs output path is provided via OUT_DIR, which is a temporary, dynamically generated directory (target/<profile>/build/<crate>-*/out). Currently, it is difficult for xtask scripts or other post-build scripts to reliably locate this path.

- build.rs now writes cmake config and headers to target/<profile>/dora-node-api-c/
  alongside the static library, enabling predictable artifact paths without search
- add xtask crate with stage-c-node command for cross-platform staging
- update CI workflows to use cargo run -p xtask instead of shell scripts
- delete scripts/c-cpp-libraries/stage-c-node.sh and .ps1
- add scripts/c_cpp_lib/test-stage.sh to verify debug, release, and --target builds
…/C++ crates

- Replace crate-specific cmake template with generic dora-api-config.cmake.in
  supporting @Package@, @target@, @LIB_UNIX@, @LIB_WIN@ placeholders
- Add cmake config generation to dora-operator-api-c, dora-node-api-cxx,
  dora-operator-api-cxx build.rs (previously only dora-node-api-c)
- C crates stage to TARGET_DIR/<crate>/ alongside .a; C++ crates stage to
  target/cxxbridge/<crate>/ (cmake + header only, no .cc)
- Generalize xtask stage command to support all 4 crates via single subcommand
- Update publish-c-cpp-libraries.yml: replace all manual cp with xtask stage
- Update test-stage.sh to support --all flag and 4 crate parameters
…plate

- Add CXX_BRIDGE_FILES variable to cmake config for C++ crates (populated
  with .cc path; empty for C crates) so users can add it to add_executable
- Stage cxxbridge .cc source files via xtask stage command
- Fix CMakeLists-template.txt for C++ (uses CXX_BRIDGE_FILES variable)
- Fix C standard in C template (20 -> 11, CMake only supports 90/99/11/17/23)
- Add CMakeLists.txt generation to cxx template module (parity with C)
- Fix cxx node template to use correct cxxbridge API (next_event, event_type,
  event_as_input instead of next_input)
- Add build-test.sh to verify C and C++ nodes build end-to-end with
  dora new + CMake (temp/ directory preserved for inspection)
…bdirectory

- Replace ExternalProject_Add in dataflow cmake-template.txt with
  find_package(dora-node-api-c/cxx REQUIRED) + add_subdirectory for
  talker_1, talker_2, listener_1
- Simplify create_cmakefile() to only replace name placeholder (remove
  DORA_ROOT_DIR / workspace path logic)
- Rename build-test.sh -> build_node_test.sh, test-stage.sh -> stage_test.sh
- Add build_dataflow_test.sh to verify C/C++ dataflow builds end-to-end
- Update directory structure to show lib/cmake/ layout
- Replace manual CMake examples with find_package(dora-node-api-c/cxx)
- Add Using dora new section with node and dataflow workflows
- Update Building from Source with xtask stage instructions
- Retain Manual Linking as advanced reference
@Ts-sound Ts-sound marked this pull request as ready for review April 29, 2026 05:40
@Ts-sound
Copy link
Copy Markdown
Contributor Author

@phil-opp Could you please review the changes?

@heyong4725
Copy link
Copy Markdown
Collaborator

Closing as superseded-in-part by #1851, with thanks to @Ts-sound for laying out the full design.

Why close

Three weeks after this PR opened, #1851 (rescue of #1514) merged the CLI-template piece of #1756 using a different approach: ExternalProject_Add (build dora from source via DORA_ROOT_DIR) instead of find_package(dora-node-api-c REQUIRED) (consume installed libraries). Both are legitimate UX models, but only one ships in dora new. A panel-faithful rescue of this PR's 1561 lines would have to either replace #1851 or design coexistence, plus rebase against three weeks of unrelated evolution, plus address @phil-opp's two unresolved review concerns.

What this PR's contribution split into

Already on main (via #1851 / pre-existing infra):

Not on main, real value, surgical follow-up coming:

  • find_package(dora-node-api-c REQUIRED) support — build.rs in the 4 C/C++ API crates generates CMake config + version files at standard layout (<prefix>/lib/cmake/<crate>/)
  • xtask stage tool — cross-platform Rust replacement for shell staging scripts (also fixes @phil-opp's Windows build.rs lib-staging bug by moving staging out of build.rs into a post-build step)
  • test-c-cpp-libraries.yml CI workflow

The residual scope (find_package infra + xtask + test CI, with Phil's Windows fix applied) will land as a focused follow-up PR — roughly 400-600 lines instead of 1561, with no conflict against #1851's templates. I'll credit you with Co-authored-by since the design and most of the code is yours.

Tracking

Thanks for the thorough design write-up — it made the triage straightforward.

@heyong4725 heyong4725 closed this May 19, 2026
heyong4725 added a commit that referenced this pull request May 19, 2026
…-up of #1763)

Adds CMake `find_package(dora-node-api-c)` / `find_package(dora-node-api-cxx)`
support for consumers of the published static libraries, without
disturbing #1851's `dora new --kind node --lang c/cxx` CLI templates
(which serve the orthogonal source-tree-build workflow). Closes the
residual piece of #1756 left over after #1763 was closed in favour of
this narrower rescue. Co-authored-by @Ts-sound (tong), whose #1763
contributed the design and most of the code.

What is added
=============

* `apis/c/node/build.rs` (new), `apis/c/operator/build.rs`,
  `apis/c++/node/build.rs`, `apis/c++/operator/build.rs` — each
  build.rs now emits a `<crate>Config.cmake` + `<crate>ConfigVersion.cmake`
  at the standard `<prefix>/lib/cmake/<crate>/` layout, copies the public
  headers into `<prefix>/include/`, and for cxx crates also stages the
  generated `.cc` so consumers can compile it alongside their code.
  The config files import the static lib as a `<crate>::<crate>` target
  and an `<crate>::extra` interface target carrying the platform link
  dependencies (winapi libs / Apple frameworks / pthread+dl+m+rt) so
  downstream CMakeLists never have to list them by hand. Built-target vs
  current-target is checked at configure time, so an
  archive-extracted-on-wrong-architecture is caught before the link step.

* `apis/c/node/cmake/dora-api-config.cmake.in`,
  `apis/c/node/cmake/dora-api-version.cmake.in` — the generic cmake
  templates shared by all four crates (placeholders: `@PACKAGE@`,
  `@TARGET@`, `@LIB_UNIX@`, `@LIB_WIN@`, `@CXX_BRIDGE_FILES@`,
  `@VERSION@`).

* `xtask/` — new workspace crate, `cargo run -p xtask -- stage <crate>
  <target_dir> <prefix>`, assembles the staging output produced by
  build.rs into a find_package-ready prefix directory. Handles both
  C-style (single staging dir under target_dir) and cxx-style (cmake
  config in cxxbridge tree, static lib in target_dir, cxxbridge .h/.cc
  also copied) layouts. Walks ancestor directories of `target_dir` to
  locate the cxxbridge tree, since the cxx tool always emits to
  `target/<triple>/cxxbridge/` while the static lib may be either
  `target/<profile>/` or `target/<triple>/<profile>/` depending on
  whether `--target` was passed.

* `.github/workflows/test-c-cpp-libraries.yml` — new test workflow.
  Builds each C/C++ API crate, stages it via xtask, then runs `cmake
  configure + cmake build` against a checked-in fixture
  (`tests/cmake-find-package-c/`, `tests/cmake-find-package-cxx/`).
  Runs on ubuntu-22.04, macos-latest, windows-latest. Path-scoped so it
  only runs when the C/C++ surface changes.

* `tests/cmake-find-package-c/`, `tests/cmake-find-package-cxx/` —
  tiny fixtures that exercise the find_package contract: minimal
  CMakeLists.txt + main.{c,cc} that take the address of a symbol from
  the dora C/C++ API. If the cmake config files are missing or the
  staging layout drifts, these fail at cmake configure or link time.

* `.github/workflows/publish-c-cpp-libraries.yml` — staging step
  rewritten to delegate to `xtask stage` instead of carrying parallel
  Unix shell / pwsh `cp` blocks. Addresses @phil-opp's review concern
  from #1763 — staging now happens strictly *after* `cargo build`
  finishes the static lib, never during build.rs (where the .lib does
  not yet exist on a fresh Windows build).

* `apis/C-CPP-LIBRARIES.md` — adds a "Recommended: find_package"
  section showing the canonical consumer recipe. Demotes the existing
  hand-rolled include/link-path examples to a "Legacy" section. Adds a
  "build from source with xtask" recipe so a local build produces the
  same layout consumers see in the GitHub release artefacts.

What is NOT added (deliberately out of scope)
=============================================

* No changes to `binaries/cli/src/template/{c,cxx}/*-cmake-template.txt`.
  Those templates (added by #1851 / rescue of #1514) use
  `ExternalProject_Add` against a source-tree checkout, which serves
  developers cloning the repo. The find_package idiom here serves the
  orthogonal "downloaded a release tarball" workflow. The two coexist:
  a future PR may add a `dora new --cmake find-package` flag that emits
  a find_package-style CMakeLists, but that is a separate UX decision.

* No new public Rust API. The reply enum stays compatible. No semver-
  affecting type changes.

* No CLI surface changes. `dora new --kind node --lang c/cxx` still
  emits the same template.

* Phil's `cmake-templates.txt` duplication concern from #1763:
  inspected and confirmed not duplicative — the CLI template
  scaffolds into user projects (per-project CMakeLists), while
  `dora-api-config.cmake.in` is the package config consumed by
  find_package. Different layers.

Verification
============

  cargo fmt --all -- --check
  cargo clippy --all --exclude dora-{node-api,operator-api,ros2-bridge}-python -- -D warnings
  cargo test -p xtask -p dora-node-api-c -p dora-operator-api-c \
                     -p dora-node-api-cxx -p dora-operator-api-cxx
  cargo check --examples

End-to-end find_package round-trip exercised locally on macos-aarch64:

  cargo build --release -p dora-node-api-c
  cargo run -p xtask -- stage dora-node-api-c target/release dora-c-prefix
  cmake -S tests/cmake-find-package-c -B build-c \
        -DCMAKE_PREFIX_PATH=$(pwd)/dora-c-prefix
  cmake --build build-c                # built smoke executable; exit 0

  cargo build --release -p dora-node-api-cxx --target aarch64-apple-darwin
  cargo run -p xtask -- stage dora-node-api-cxx \
                                 target/aarch64-apple-darwin/release \
                                 dora-cpp-prefix
  cmake -S tests/cmake-find-package-cxx -B build-cxx \
        -DCMAKE_PREFIX_PATH=$(pwd)/dora-cpp-prefix
  cmake --build build-cxx              # built smoke executable; exit 0

CI test workflow drives the same recipe on linux/macos/windows.

Co-authored-by: Ts-sound <Ts-sound@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
heyong4725 added a commit that referenced this pull request May 19, 2026
…-up of #1763)

Adds CMake `find_package(dora-node-api-c)` / `find_package(dora-node-api-cxx)`
support for consumers of the published static libraries, without
disturbing #1851's `dora new --kind node --lang c/cxx` CLI templates
(which serve the orthogonal source-tree-build workflow). Closes the
residual piece of #1756 left over after #1763 was closed in favour of
this narrower rescue. Co-authored-by @Ts-sound (tong), whose #1763
contributed the design and most of the code.

What is added
=============

* `apis/c/node/build.rs` (new), `apis/c/operator/build.rs`,
  `apis/c++/node/build.rs`, `apis/c++/operator/build.rs` — each
  build.rs now emits a `<crate>Config.cmake` + `<crate>ConfigVersion.cmake`
  at the standard `<prefix>/lib/cmake/<crate>/` layout, copies the public
  headers into `<prefix>/include/`, and for cxx crates also stages the
  generated `.cc` so consumers can compile it alongside their code.
  The config files import the static lib as a `<crate>::<crate>` target
  and an `<crate>::extra` interface target carrying the platform link
  dependencies (winapi libs / Apple frameworks / pthread+dl+m+rt) so
  downstream CMakeLists never have to list them by hand. Built-target vs
  current-target is checked at configure time, so an
  archive-extracted-on-wrong-architecture is caught before the link step.

* `apis/c/node/cmake/dora-api-config.cmake.in`,
  `apis/c/node/cmake/dora-api-version.cmake.in` — the generic cmake
  templates shared by all four crates (placeholders: `@PACKAGE@`,
  `@TARGET@`, `@LIB_UNIX@`, `@LIB_WIN@`, `@CXX_BRIDGE_FILES@`,
  `@VERSION@`).

* `xtask/` — new workspace crate, `cargo run -p xtask -- stage <crate>
  <target_dir> <prefix>`, assembles the staging output produced by
  build.rs into a find_package-ready prefix directory. Handles both
  C-style (single staging dir under target_dir) and cxx-style (cmake
  config in cxxbridge tree, static lib in target_dir, cxxbridge .h/.cc
  also copied) layouts. Walks ancestor directories of `target_dir` to
  locate the cxxbridge tree, since the cxx tool always emits to
  `target/<triple>/cxxbridge/` while the static lib may be either
  `target/<profile>/` or `target/<triple>/<profile>/` depending on
  whether `--target` was passed.

* `.github/workflows/test-c-cpp-libraries.yml` — new test workflow.
  Builds each C/C++ API crate, stages it via xtask, then runs `cmake
  configure + cmake build` against a checked-in fixture
  (`tests/cmake-find-package-c/`, `tests/cmake-find-package-cxx/`).
  Runs on ubuntu-22.04, macos-latest, windows-latest. Path-scoped so it
  only runs when the C/C++ surface changes.

* `tests/cmake-find-package-c/`, `tests/cmake-find-package-cxx/` —
  tiny fixtures that exercise the find_package contract: minimal
  CMakeLists.txt + main.{c,cc} that take the address of a symbol from
  the dora C/C++ API. If the cmake config files are missing or the
  staging layout drifts, these fail at cmake configure or link time.

* `.github/workflows/publish-c-cpp-libraries.yml` — staging step
  rewritten to delegate to `xtask stage` instead of carrying parallel
  Unix shell / pwsh `cp` blocks. Addresses @phil-opp's review concern
  from #1763 — staging now happens strictly *after* `cargo build`
  finishes the static lib, never during build.rs (where the .lib does
  not yet exist on a fresh Windows build).

* `apis/C-CPP-LIBRARIES.md` — adds a "Recommended: find_package"
  section showing the canonical consumer recipe. Demotes the existing
  hand-rolled include/link-path examples to a "Legacy" section. Adds a
  "build from source with xtask" recipe so a local build produces the
  same layout consumers see in the GitHub release artefacts.

What is NOT added (deliberately out of scope)
=============================================

* No changes to `binaries/cli/src/template/{c,cxx}/*-cmake-template.txt`.
  Those templates (added by #1851 / rescue of #1514) use
  `ExternalProject_Add` against a source-tree checkout, which serves
  developers cloning the repo. The find_package idiom here serves the
  orthogonal "downloaded a release tarball" workflow. The two coexist:
  a future PR may add a `dora new --cmake find-package` flag that emits
  a find_package-style CMakeLists, but that is a separate UX decision.

* No new public Rust API. The reply enum stays compatible. No semver-
  affecting type changes.

* No CLI surface changes. `dora new --kind node --lang c/cxx` still
  emits the same template.

* Phil's `cmake-templates.txt` duplication concern from #1763:
  inspected and confirmed not duplicative — the CLI template
  scaffolds into user projects (per-project CMakeLists), while
  `dora-api-config.cmake.in` is the package config consumed by
  find_package. Different layers.

Verification
============

  cargo fmt --all -- --check
  cargo clippy --all --exclude dora-{node-api,operator-api,ros2-bridge}-python -- -D warnings
  cargo test -p xtask -p dora-node-api-c -p dora-operator-api-c \
                     -p dora-node-api-cxx -p dora-operator-api-cxx
  cargo check --examples

End-to-end find_package round-trip exercised locally on macos-aarch64:

  cargo build --release -p dora-node-api-c
  cargo run -p xtask -- stage dora-node-api-c target/release dora-c-prefix
  cmake -S tests/cmake-find-package-c -B build-c \
        -DCMAKE_PREFIX_PATH=$(pwd)/dora-c-prefix
  cmake --build build-c                # built smoke executable; exit 0

  cargo build --release -p dora-node-api-cxx --target aarch64-apple-darwin
  cargo run -p xtask -- stage dora-node-api-cxx \
                                 target/aarch64-apple-darwin/release \
                                 dora-cpp-prefix
  cmake -S tests/cmake-find-package-cxx -B build-cxx \
        -DCMAKE_PREFIX_PATH=$(pwd)/dora-cpp-prefix
  cmake --build build-cxx              # built smoke executable; exit 0

CI test workflow drives the same recipe on linux/macos/windows.

Co-authored-by: Ts-sound <Ts-sound@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
heyong4725 added a commit that referenced this pull request May 19, 2026
…-up of #1763)

Adds CMake `find_package(dora-node-api-c)` / `find_package(dora-node-api-cxx)`
support for consumers of the published static libraries, without
disturbing #1851's `dora new --kind node --lang c/cxx` CLI templates
(which serve the orthogonal source-tree-build workflow). Closes the
residual piece of #1756 left over after #1763 was closed in favour of
this narrower rescue. Co-authored-by @Ts-sound (tong), whose #1763
contributed the design and most of the code.

What is added
=============

* `apis/c/node/build.rs` (new), `apis/c/operator/build.rs`,
  `apis/c++/node/build.rs`, `apis/c++/operator/build.rs` — each
  build.rs now emits a `<crate>Config.cmake` + `<crate>ConfigVersion.cmake`
  at the standard `<prefix>/lib/cmake/<crate>/` layout, copies the public
  headers into `<prefix>/include/`, and for cxx crates also stages the
  generated `.cc` so consumers can compile it alongside their code.
  The config files import the static lib as a `<crate>::<crate>` target
  and an `<crate>::extra` interface target carrying the platform link
  dependencies (winapi libs / Apple frameworks / pthread+dl+m+rt) so
  downstream CMakeLists never have to list them by hand. Built-target vs
  current-target is checked at configure time, so an
  archive-extracted-on-wrong-architecture is caught before the link step.

* `apis/c/node/cmake/dora-api-config.cmake.in`,
  `apis/c/node/cmake/dora-api-version.cmake.in` — the generic cmake
  templates shared by all four crates (placeholders: `@PACKAGE@`,
  `@TARGET@`, `@LIB_UNIX@`, `@LIB_WIN@`, `@CXX_BRIDGE_FILES@`,
  `@VERSION@`).

* `xtask/` — new workspace crate, `cargo run -p xtask -- stage <crate>
  <target_dir> <prefix>`, assembles the staging output produced by
  build.rs into a find_package-ready prefix directory. Handles both
  C-style (single staging dir under target_dir) and cxx-style (cmake
  config in cxxbridge tree, static lib in target_dir, cxxbridge .h/.cc
  also copied) layouts. Walks ancestor directories of `target_dir` to
  locate the cxxbridge tree, since the cxx tool always emits to
  `target/<triple>/cxxbridge/` while the static lib may be either
  `target/<profile>/` or `target/<triple>/<profile>/` depending on
  whether `--target` was passed.

* `.github/workflows/test-c-cpp-libraries.yml` — new test workflow.
  Builds each C/C++ API crate, stages it via xtask, then runs `cmake
  configure + cmake build` against a checked-in fixture
  (`tests/cmake-find-package-c/`, `tests/cmake-find-package-cxx/`).
  Runs on ubuntu-22.04, macos-latest, windows-latest. Path-scoped so it
  only runs when the C/C++ surface changes.

* `tests/cmake-find-package-c/`, `tests/cmake-find-package-cxx/` —
  tiny fixtures that exercise the find_package contract: minimal
  CMakeLists.txt + main.{c,cc} that take the address of a symbol from
  the dora C/C++ API. If the cmake config files are missing or the
  staging layout drifts, these fail at cmake configure or link time.

* `.github/workflows/publish-c-cpp-libraries.yml` — staging step
  rewritten to delegate to `xtask stage` instead of carrying parallel
  Unix shell / pwsh `cp` blocks. Addresses @phil-opp's review concern
  from #1763 — staging now happens strictly *after* `cargo build`
  finishes the static lib, never during build.rs (where the .lib does
  not yet exist on a fresh Windows build).

* `apis/C-CPP-LIBRARIES.md` — adds a "Recommended: find_package"
  section showing the canonical consumer recipe. Demotes the existing
  hand-rolled include/link-path examples to a "Legacy" section. Adds a
  "build from source with xtask" recipe so a local build produces the
  same layout consumers see in the GitHub release artefacts.

What is NOT added (deliberately out of scope)
=============================================

* No changes to `binaries/cli/src/template/{c,cxx}/*-cmake-template.txt`.
  Those templates (added by #1851 / rescue of #1514) use
  `ExternalProject_Add` against a source-tree checkout, which serves
  developers cloning the repo. The find_package idiom here serves the
  orthogonal "downloaded a release tarball" workflow. The two coexist:
  a future PR may add a `dora new --cmake find-package` flag that emits
  a find_package-style CMakeLists, but that is a separate UX decision.

* No new public Rust API. The reply enum stays compatible. No semver-
  affecting type changes.

* No CLI surface changes. `dora new --kind node --lang c/cxx` still
  emits the same template.

* Phil's `cmake-templates.txt` duplication concern from #1763:
  inspected and confirmed not duplicative — the CLI template
  scaffolds into user projects (per-project CMakeLists), while
  `dora-api-config.cmake.in` is the package config consumed by
  find_package. Different layers.

Verification
============

  cargo fmt --all -- --check
  cargo clippy --all --exclude dora-{node-api,operator-api,ros2-bridge}-python -- -D warnings
  cargo test -p xtask -p dora-node-api-c -p dora-operator-api-c \
                     -p dora-node-api-cxx -p dora-operator-api-cxx
  cargo check --examples

End-to-end find_package round-trip exercised locally on macos-aarch64:

  cargo build --release -p dora-node-api-c
  cargo run -p xtask -- stage dora-node-api-c target/release dora-c-prefix
  cmake -S tests/cmake-find-package-c -B build-c \
        -DCMAKE_PREFIX_PATH=$(pwd)/dora-c-prefix
  cmake --build build-c                # built smoke executable; exit 0

  cargo build --release -p dora-node-api-cxx --target aarch64-apple-darwin
  cargo run -p xtask -- stage dora-node-api-cxx \
                                 target/aarch64-apple-darwin/release \
                                 dora-cpp-prefix
  cmake -S tests/cmake-find-package-cxx -B build-cxx \
        -DCMAKE_PREFIX_PATH=$(pwd)/dora-cpp-prefix
  cmake --build build-cxx              # built smoke executable; exit 0

CI test workflow drives the same recipe on linux/macos/windows.

Co-authored-by: Ts-sound <Ts-sound@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
heyong4725 added a commit that referenced this pull request May 19, 2026
…-up of #1763)

Adds CMake `find_package(dora-node-api-c)` / `find_package(dora-node-api-cxx)`
support for consumers of the published static libraries, without
disturbing #1851's `dora new --kind node --lang c/cxx` CLI templates
(which serve the orthogonal source-tree-build workflow). Closes the
residual piece of #1756 left over after #1763 was closed in favour of
this narrower rescue. Co-authored-by @Ts-sound (tong), whose #1763
contributed the design and most of the code.

What is added
=============

* `apis/c/node/build.rs` (new), `apis/c/operator/build.rs`,
  `apis/c++/node/build.rs`, `apis/c++/operator/build.rs` — each
  build.rs now emits a `<crate>Config.cmake` + `<crate>ConfigVersion.cmake`
  at the standard `<prefix>/lib/cmake/<crate>/` layout, copies the public
  headers into `<prefix>/include/`, and for cxx crates also stages the
  generated `.cc` so consumers can compile it alongside their code.
  The config files import the static lib as a `<crate>::<crate>` target
  and an `<crate>::extra` interface target carrying the platform link
  dependencies (winapi libs / Apple frameworks / pthread+dl+m+rt) so
  downstream CMakeLists never have to list them by hand. Built-target vs
  current-target is checked at configure time, so an
  archive-extracted-on-wrong-architecture is caught before the link step.

* `apis/c/node/cmake/dora-api-config.cmake.in`,
  `apis/c/node/cmake/dora-api-version.cmake.in` — the generic cmake
  templates shared by all four crates (placeholders: `@PACKAGE@`,
  `@TARGET@`, `@LIB_UNIX@`, `@LIB_WIN@`, `@CXX_BRIDGE_FILES@`,
  `@VERSION@`).

* `xtask/` — new workspace crate, `cargo run -p xtask -- stage <crate>
  <target_dir> <prefix>`, assembles the staging output produced by
  build.rs into a find_package-ready prefix directory. Handles both
  C-style (single staging dir under target_dir) and cxx-style (cmake
  config in cxxbridge tree, static lib in target_dir, cxxbridge .h/.cc
  also copied) layouts. Walks ancestor directories of `target_dir` to
  locate the cxxbridge tree, since the cxx tool always emits to
  `target/<triple>/cxxbridge/` while the static lib may be either
  `target/<profile>/` or `target/<triple>/<profile>/` depending on
  whether `--target` was passed.

* `.github/workflows/test-c-cpp-libraries.yml` — new test workflow.
  Builds each C/C++ API crate, stages it via xtask, then runs `cmake
  configure + cmake build` against a checked-in fixture
  (`tests/cmake-find-package-c/`, `tests/cmake-find-package-cxx/`).
  Runs on ubuntu-22.04, macos-latest, windows-latest. Path-scoped so it
  only runs when the C/C++ surface changes.

* `tests/cmake-find-package-c/`, `tests/cmake-find-package-cxx/` —
  tiny fixtures that exercise the find_package contract: minimal
  CMakeLists.txt + main.{c,cc} that take the address of a symbol from
  the dora C/C++ API. If the cmake config files are missing or the
  staging layout drifts, these fail at cmake configure or link time.

* `.github/workflows/publish-c-cpp-libraries.yml` — staging step
  rewritten to delegate to `xtask stage` instead of carrying parallel
  Unix shell / pwsh `cp` blocks. Addresses @phil-opp's review concern
  from #1763 — staging now happens strictly *after* `cargo build`
  finishes the static lib, never during build.rs (where the .lib does
  not yet exist on a fresh Windows build).

* `apis/C-CPP-LIBRARIES.md` — adds a "Recommended: find_package"
  section showing the canonical consumer recipe. Demotes the existing
  hand-rolled include/link-path examples to a "Legacy" section. Adds a
  "build from source with xtask" recipe so a local build produces the
  same layout consumers see in the GitHub release artefacts.

What is NOT added (deliberately out of scope)
=============================================

* No changes to `binaries/cli/src/template/{c,cxx}/*-cmake-template.txt`.
  Those templates (added by #1851 / rescue of #1514) use
  `ExternalProject_Add` against a source-tree checkout, which serves
  developers cloning the repo. The find_package idiom here serves the
  orthogonal "downloaded a release tarball" workflow. The two coexist:
  a future PR may add a `dora new --cmake find-package` flag that emits
  a find_package-style CMakeLists, but that is a separate UX decision.

* No new public Rust API. The reply enum stays compatible. No semver-
  affecting type changes.

* No CLI surface changes. `dora new --kind node --lang c/cxx` still
  emits the same template.

* Phil's `cmake-templates.txt` duplication concern from #1763:
  inspected and confirmed not duplicative — the CLI template
  scaffolds into user projects (per-project CMakeLists), while
  `dora-api-config.cmake.in` is the package config consumed by
  find_package. Different layers.

Verification
============

  cargo fmt --all -- --check
  cargo clippy --all --exclude dora-{node-api,operator-api,ros2-bridge}-python -- -D warnings
  cargo test -p xtask -p dora-node-api-c -p dora-operator-api-c \
                     -p dora-node-api-cxx -p dora-operator-api-cxx
  cargo check --examples

End-to-end find_package round-trip exercised locally on macos-aarch64:

  cargo build --release -p dora-node-api-c
  cargo run -p xtask -- stage dora-node-api-c target/release dora-c-prefix
  cmake -S tests/cmake-find-package-c -B build-c \
        -DCMAKE_PREFIX_PATH=$(pwd)/dora-c-prefix
  cmake --build build-c                # built smoke executable; exit 0

  cargo build --release -p dora-node-api-cxx --target aarch64-apple-darwin
  cargo run -p xtask -- stage dora-node-api-cxx \
                                 target/aarch64-apple-darwin/release \
                                 dora-cpp-prefix
  cmake -S tests/cmake-find-package-cxx -B build-cxx \
        -DCMAKE_PREFIX_PATH=$(pwd)/dora-cpp-prefix
  cmake --build build-cxx              # built smoke executable; exit 0

CI test workflow drives the same recipe on linux/macos/windows.

Co-authored-by: Ts-sound <Ts-sound@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
heyong4725 added a commit that referenced this pull request May 20, 2026
…-up of #1763) (#1875)

Adds CMake `find_package(dora-node-api-c)` / `find_package(dora-node-api-cxx)`
support for consumers of the published static libraries, without
disturbing #1851's `dora new --kind node --lang c/cxx` CLI templates
(which serve the orthogonal source-tree-build workflow). Closes the
residual piece of #1756 left over after #1763 was closed in favour of
this narrower rescue. Co-authored-by @Ts-sound (tong), whose #1763
contributed the design and most of the code.

What is added
=============

* `apis/c/node/build.rs` (new), `apis/c/operator/build.rs`,
  `apis/c++/node/build.rs`, `apis/c++/operator/build.rs` — each
  build.rs now emits a `<crate>Config.cmake` + `<crate>ConfigVersion.cmake`
  at the standard `<prefix>/lib/cmake/<crate>/` layout, copies the public
  headers into `<prefix>/include/`, and for cxx crates also stages the
  generated `.cc` so consumers can compile it alongside their code.
  The config files import the static lib as a `<crate>::<crate>` target
  and an `<crate>::extra` interface target carrying the platform link
  dependencies (winapi libs / Apple frameworks / pthread+dl+m+rt) so
  downstream CMakeLists never have to list them by hand. Built-target vs
  current-target is checked at configure time, so an
  archive-extracted-on-wrong-architecture is caught before the link step.

* `apis/c/node/cmake/dora-api-config.cmake.in`,
  `apis/c/node/cmake/dora-api-version.cmake.in` — the generic cmake
  templates shared by all four crates (placeholders: `@PACKAGE@`,
  `@TARGET@`, `@LIB_UNIX@`, `@LIB_WIN@`, `@CXX_BRIDGE_FILES@`,
  `@VERSION@`).

* `xtask/` — new workspace crate, `cargo run -p xtask -- stage <crate>
  <target_dir> <prefix>`, assembles the staging output produced by
  build.rs into a find_package-ready prefix directory. Handles both
  C-style (single staging dir under target_dir) and cxx-style (cmake
  config in cxxbridge tree, static lib in target_dir, cxxbridge .h/.cc
  also copied) layouts. Walks ancestor directories of `target_dir` to
  locate the cxxbridge tree, since the cxx tool always emits to
  `target/<triple>/cxxbridge/` while the static lib may be either
  `target/<profile>/` or `target/<triple>/<profile>/` depending on
  whether `--target` was passed.

* `.github/workflows/test-c-cpp-libraries.yml` — new test workflow.
  Builds each C/C++ API crate, stages it via xtask, then runs `cmake
  configure + cmake build` against a checked-in fixture
  (`tests/cmake-find-package-c/`, `tests/cmake-find-package-cxx/`).
  Runs on ubuntu-22.04, macos-latest, windows-latest. Path-scoped so it
  only runs when the C/C++ surface changes.

* `tests/cmake-find-package-c/`, `tests/cmake-find-package-cxx/` —
  tiny fixtures that exercise the find_package contract: minimal
  CMakeLists.txt + main.{c,cc} that take the address of a symbol from
  the dora C/C++ API. If the cmake config files are missing or the
  staging layout drifts, these fail at cmake configure or link time.

* `.github/workflows/publish-c-cpp-libraries.yml` — staging step
  rewritten to delegate to `xtask stage` instead of carrying parallel
  Unix shell / pwsh `cp` blocks. Addresses @phil-opp's review concern
  from #1763 — staging now happens strictly *after* `cargo build`
  finishes the static lib, never during build.rs (where the .lib does
  not yet exist on a fresh Windows build).

* `apis/C-CPP-LIBRARIES.md` — adds a "Recommended: find_package"
  section showing the canonical consumer recipe. Demotes the existing
  hand-rolled include/link-path examples to a "Legacy" section. Adds a
  "build from source with xtask" recipe so a local build produces the
  same layout consumers see in the GitHub release artefacts.

What is NOT added (deliberately out of scope)
=============================================

* No changes to `binaries/cli/src/template/{c,cxx}/*-cmake-template.txt`.
  Those templates (added by #1851 / rescue of #1514) use
  `ExternalProject_Add` against a source-tree checkout, which serves
  developers cloning the repo. The find_package idiom here serves the
  orthogonal "downloaded a release tarball" workflow. The two coexist:
  a future PR may add a `dora new --cmake find-package` flag that emits
  a find_package-style CMakeLists, but that is a separate UX decision.

* No new public Rust API. The reply enum stays compatible. No semver-
  affecting type changes.

* No CLI surface changes. `dora new --kind node --lang c/cxx` still
  emits the same template.

* Phil's `cmake-templates.txt` duplication concern from #1763:
  inspected and confirmed not duplicative — the CLI template
  scaffolds into user projects (per-project CMakeLists), while
  `dora-api-config.cmake.in` is the package config consumed by
  find_package. Different layers.

Verification
============

  cargo fmt --all -- --check
  cargo clippy --all --exclude dora-{node-api,operator-api,ros2-bridge}-python -- -D warnings
  cargo test -p xtask -p dora-node-api-c -p dora-operator-api-c \
                     -p dora-node-api-cxx -p dora-operator-api-cxx
  cargo check --examples

End-to-end find_package round-trip exercised locally on macos-aarch64:

  cargo build --release -p dora-node-api-c
  cargo run -p xtask -- stage dora-node-api-c target/release dora-c-prefix
  cmake -S tests/cmake-find-package-c -B build-c \
        -DCMAKE_PREFIX_PATH=$(pwd)/dora-c-prefix
  cmake --build build-c                # built smoke executable; exit 0

  cargo build --release -p dora-node-api-cxx --target aarch64-apple-darwin
  cargo run -p xtask -- stage dora-node-api-cxx \
                                 target/aarch64-apple-darwin/release \
                                 dora-cpp-prefix
  cmake -S tests/cmake-find-package-cxx -B build-cxx \
        -DCMAKE_PREFIX_PATH=$(pwd)/dora-cpp-prefix
  cmake --build build-cxx              # built smoke executable; exit 0

CI test workflow drives the same recipe on linux/macos/windows.

Co-authored-by: Ts-sound <Ts-sound@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (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.

3 participants