Skip to content

Commit ac92ccc

Browse files
authored
Add example with conan workspaces (#33)
* Add example with conan workspaces * wip * Update examples/conan_workspace/readme.md
1 parent fa00e4d commit ac92ccc

11 files changed

Lines changed: 247 additions & 1 deletion

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![Conan create ros-kilted](https://github.com/conan-io/ros-conan/actions/workflows/conan-create-ros-kilted.yml/badge.svg)](https://github.com/conan-io/ros-conan/actions/workflows/conan-create-ros-kilted.yml)
44
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
55

6-
Conan recipes for building [ROS 2](https://docs.ros.org/) from source. Pull ROS 2 and all its dependencies with a single `conan install`, no `rosdep`, `apt`, `brew` or `choco` required. Works with plain CMake projects and `colcon` workspaces, on Windows, macOS and Linux.
6+
Conan recipes for building [ROS 2](https://docs.ros.org/) from source. Pull ROS 2 and all its dependencies with a single `conan install`, no `rosdep`, `apt`, `brew` or `choco` required. Works with plain CMake projects, `colcon` workspaces, and [Conan workspaces](https://docs.conan.io/2/tutorial/developing_packages/workspaces.html), on Windows, macOS and Linux.
77

88
---
99

@@ -89,6 +89,7 @@ pinned to. The Conan version `YYYY.MM.DD` maps 1:1 to the rosdistro tag
8989
| ------------------------------------------------------------- | --------------------------------------------------------------------------- |
9090
| [`consumer_cmake`](examples/consumer_cmake/readme.md) | Pure-CMake consumer using `rclcpp` via `CMakeDeps`, no `colcon` needed. |
9191
| [`consumer_colcon`](examples/consumer_colcon/readme.md) | `colcon` workspace consuming the ROS runtime from Conan via `ROSEnv`. |
92+
| [`conan_workspace`](examples/conan_workspace/readme.md) | Same `src/` layout as `consumer_colcon`, orchestrated with a Conan workspace (CMake only). |
9293
| [`pose_estimation`](examples/pose_estimation/readme.md) | `ros-kilted` + `opencv` + `tensorflow-lite` publishing a skeleton overlay. |
9394
| [`consumer_desktop`](examples/consumer_desktop/readme.md) | Installs the `desktop` variant and runs its GUI tooling (`rviz2`, `rqt`). |
9495

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import platform
2+
3+
from test.examples_tools import VARIANT_ARGS, run
4+
5+
run(
6+
f"conan workspace build --profile ../../profiles/ros {VARIANT_ARGS} --build=missing"
7+
)
8+
9+
if platform.system() == "Windows":
10+
run(
11+
r"call .\src\consumer_node\build\generators\conanrun.bat && "
12+
r".\src\consumer_node\build\Release\consumer_node.exe"
13+
)
14+
else:
15+
run(
16+
". ./src/consumer_node/build/Release/generators/conanrun.sh && "
17+
"./src/consumer_node/build/Release/consumer_node"
18+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: conan_workspace
2+
packages:
3+
- path: src/dummy_lib
4+
ref: dummy_lib/0.1
5+
- path: src/consumer_node
6+
ref: consumer_node/0.1

examples/conan_workspace/readme.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# conan_workspace
2+
3+
Same layout as [`consumer_colcon`](../consumer_colcon/readme.md) — two packages under
4+
`src/` — but orchestrated with a [Conan workspace](https://docs.conan.io/2/tutorial/developing_packages/workspaces.html)
5+
instead of `colcon`. There is no `package.xml`, no `ament_cmake`, and no `colcon`:
6+
each package is a plain CMake project with its own `conanfile.py`. Workspace packages
7+
are resolved as [editables](https://docs.conan.io/2/tutorial/developing_packages/editable_packages.html)
8+
and built in dependency order.
9+
10+
- `dummy_lib` — a static library with no ROS dependencies, used to demonstrate
11+
cross-package linking inside the workspace.
12+
- `consumer_node` — depends on `dummy_lib` and `ros-kilted` (`rclcpp`), prints a
13+
message and starts a short-lived node.
14+
15+
[← back to main README](../../README.md)
16+
17+
## Layout
18+
19+
```text
20+
conan_workspace/
21+
├── conanws.yml # workspace root + package inventory
22+
└── src/
23+
├── dummy_lib/
24+
│ ├── conanfile.py # replaces package.xml
25+
│ ├── CMakeLists.txt
26+
│ ├── include/
27+
│ └── src/
28+
└── consumer_node/
29+
├── conanfile.py
30+
├── CMakeLists.txt
31+
└── src/
32+
```
33+
34+
Conan walks up from the current directory until it finds `conanws.yml` and/or
35+
`conanws.py`; that folder is the workspace root. Paths in `conanws.yml` are
36+
relative to it.
37+
38+
This example only needs `conanws.yml`. A `conanws.py` is optional Python
39+
customization (`root_conanfile()` for a monolithic super-build, custom
40+
`add`/`clean`/`build_order`, or `get_ref()` when name/version come from
41+
`python_requires`). Orchestrated `conan workspace build` does not use any of
42+
that.
43+
44+
This example uses the **orchestrated** workspace flow (`conan workspace build`),
45+
which is the closest analogue to `colcon build`: each package is configured and
46+
built on its own, in topological order, with workspace members consumed as
47+
editables.
48+
49+
Conan workspaces are experimental and subject to breaking changes — see the
50+
[Conan stability notes](https://docs.conan.io/2/introduction.html#stability).
51+
52+
## How it is wired
53+
54+
| Piece | Role |
55+
| ----- | ---- |
56+
| [`conanws.yml`](conanws.yml) | Lists `src/dummy_lib` and `src/consumer_node` as workspace packages. |
57+
| [`src/dummy_lib/conanfile.py`](src/dummy_lib/conanfile.py) | CMake library recipe: `CMakeToolchain` + `CMakeDeps`, `cmake_layout()`. |
58+
| [`src/consumer_node/conanfile.py`](src/consumer_node/conanfile.py) | Requires `dummy_lib/0.1` (editable, from this workspace) and `ros-kilted/2026.06.17`. |
59+
| CMake | Plain `find_package(dummy_lib)` / `find_package(rclcpp)` — configs come from Conan, not from sourcing a ROS `setup` script. |
60+
61+
`conan workspace build` is `conan build` for every workspace package, in the
62+
right order. External dependencies (`ros-kilted` and its graph) are installed
63+
from the cache/remotes; `dummy_lib` is never looked up as a binary because it
64+
is in the workspace.
65+
66+
## Prerequisites
67+
68+
- A C++17 compiler.
69+
- CMake ≥ 3.22 (the [profile](../../profiles/ros) tool-requires `cmake/3.29.3`).
70+
- Conan 2.31+ (For `conan workspace build`, as the feature has received recent improvements).
71+
- A Conan remote that exposes `ros-kilted` — see the
72+
[main README](../../README.md#quick-start).
73+
74+
## Build & run
75+
76+
From this directory:
77+
78+
```bash
79+
conan workspace build --profile ../../profiles/ros --build=missing
80+
```
81+
82+
Then activate the consumer's Conan run environment and execute the node:
83+
84+
**Windows (cmd):**
85+
86+
```bat
87+
call src\consumer_node\build\generators\conanrun.bat
88+
src\consumer_node\build\Release\consumer_node.exe
89+
```
90+
91+
**macOS / Linux (bash/zsh):**
92+
93+
```bash
94+
. ./src/consumer_node/build/Release/generators/conanrun.sh
95+
./src/consumer_node/build/Release/consumer_node
96+
```
97+
98+
Expected output (truncated):
99+
100+
```text
101+
[dummy_lib] hello from library
102+
[INFO] [...] [workspace_consumer_node]: Conan workspace consumer_node: rclcpp linked and node started.
103+
```
104+
105+
The exact CI invocation lives in [`ci_test_example.py`](ci_test_example.py).
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
cmake_minimum_required(VERSION 3.22)
2+
project(consumer_node CXX)
3+
4+
find_package(dummy_lib REQUIRED)
5+
find_package(rclcpp REQUIRED)
6+
7+
add_executable(consumer_node src/main.cpp)
8+
target_link_libraries(consumer_node PRIVATE dummy_lib::dummy_lib rclcpp::rclcpp)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from conan import ConanFile
2+
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
3+
4+
5+
class ConsumerNodeConan(ConanFile):
6+
name = "consumer_node"
7+
version = "0.1"
8+
package_type = "application"
9+
settings = "os", "compiler", "build_type", "arch"
10+
exports_sources = "CMakeLists.txt", "src/*"
11+
12+
def layout(self):
13+
cmake_layout(self)
14+
15+
def requirements(self):
16+
self.requires("dummy_lib/0.1")
17+
self.requires("ros-kilted/2026.06.17")
18+
19+
def generate(self):
20+
CMakeDeps(self).generate()
21+
CMakeToolchain(self).generate()
22+
23+
def build(self):
24+
cmake = CMake(self)
25+
cmake.configure()
26+
cmake.build()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <memory>
2+
3+
#include <dummy_lib/print.hpp>
4+
#include <rclcpp/rclcpp.hpp>
5+
6+
int main(int argc, char ** argv)
7+
{
8+
dummy_lib::print_hello();
9+
10+
rclcpp::init(argc, argv);
11+
auto node = std::make_shared<rclcpp::Node>("workspace_consumer_node");
12+
RCLCPP_INFO(node->get_logger(), "Conan workspace consumer_node: rclcpp linked and node started.");
13+
rclcpp::shutdown();
14+
return 0;
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
cmake_minimum_required(VERSION 3.22)
2+
project(dummy_lib CXX)
3+
4+
add_library(dummy_lib STATIC src/print.cpp)
5+
target_include_directories(dummy_lib PUBLIC
6+
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
7+
"$<INSTALL_INTERFACE:include>")
8+
9+
install(TARGETS dummy_lib
10+
ARCHIVE DESTINATION lib
11+
LIBRARY DESTINATION lib
12+
RUNTIME DESTINATION bin)
13+
install(DIRECTORY include/ DESTINATION include)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from conan import ConanFile
2+
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
3+
4+
5+
class DummyLibConan(ConanFile):
6+
name = "dummy_lib"
7+
version = "0.1"
8+
package_type = "static-library"
9+
settings = "os", "compiler", "build_type", "arch"
10+
exports_sources = "CMakeLists.txt", "src/*", "include/*"
11+
12+
def layout(self):
13+
cmake_layout(self)
14+
15+
def generate(self):
16+
CMakeDeps(self).generate()
17+
CMakeToolchain(self).generate()
18+
19+
def build(self):
20+
cmake = CMake(self)
21+
cmake.configure()
22+
cmake.build()
23+
24+
def package(self):
25+
cmake = CMake(self)
26+
cmake.install()
27+
28+
def package_info(self):
29+
self.cpp_info.libs = ["dummy_lib"]
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef DUMMY_LIB__PRINT_HPP_
2+
#define DUMMY_LIB__PRINT_HPP_
3+
4+
namespace dummy_lib
5+
{
6+
7+
/** Writes a fixed dummy message to stdout (example library API). */
8+
void print_hello();
9+
10+
} // namespace dummy_lib
11+
12+
#endif // DUMMY_LIB__PRINT_HPP_

0 commit comments

Comments
 (0)