CMakeLists.txt trong package ROS2 — đọc hiểu từng dòng
Atlasbot · 17/8/2026 · 11 phút đọc
Mở CMakeLists.txt của một package ament_cmake bất kỳ lần đầu thường thấy rối vì nhiều dòng lệnh lạ. Thực tế phần lớn package chỉ lặp lại 5-6 khối lệnh theo đúng thứ tự cố định — nắm được khuôn này là đọc hiểu (và sửa) được hầu hết CMakeLists.txt trong hệ sinh thái ROS2.
Khái niệm chính
cmake_minimum_required(VERSION 3.8)
project(my_cpp_pkg)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
add_executable(hello_node src/hello_node.cpp)
ament_target_dependencies(hello_node rclcpp std_msgs)
install(TARGETS hello_node
DESTINATION lib/${PROJECT_NAME})
ament_package()
cmake_minimum_required/project()— khai tên project, bắt buộc với mọi file CMake chuẩn, không riêng ROS2find_package(... REQUIRED)— tìm và nạp từng dependency đã khai ởpackage.xml; thiếu dòng này cho dependency nào thì build sẽ báo lỗi "package not found" ngay cả khi package đó đã cài trên máyadd_executable()— khai một file.cppsẽ được biên dịch thành binary, đặt tên executable (tên dùng vớiros2 run)ament_target_dependencies()— tự động link đúng thư viện + include path cho executable vừa khai, dựa theo cácfind_package()ở trên — thay thế chotarget_link_libraries()/target_include_directories()thuần CMake mà bạn phải tự viết nếu không dùngament_cmakeinstall()— khai nơi copy file kết quả vào thư mụcinstall/của workspace, đểros2 run/ros2 launchtìm thấy đượcament_package()— luôn là dòng cuối cùng, đăng ký package vào hệ thống ament (sinh các file cần thiết để package khácfind_package()được nó)
Tóm lại: Thứ tự cố định là find_package (khai cần gì) → add_executable (khai biên dịch gì) → ament_target_dependencies (khai link gì) → install (khai đặt ở đâu) → ament_package (chốt lại). Sai thứ tự — ví dụ install() một executable chưa được add_executable() — sẽ báo lỗi CMake ngay khi build.
Nguyên lý hoạt động
Một package có nhiều node chỉ cần lặp lại cặp add_executable + ament_target_dependencies + liệt kê thêm trong install(TARGETS ...):
add_executable(node_a src/node_a.cpp)
ament_target_dependencies(node_a rclcpp)
add_executable(node_b src/node_b.cpp)
ament_target_dependencies(node_b rclcpp std_msgs)
install(TARGETS node_a node_b
DESTINATION lib/${PROJECT_NAME})
Nếu có code dùng chung giữa nhiều node (ví dụ một class helper), tách thành thư viện riêng bằng add_library() rồi cho từng executable link vào, tránh biên dịch trùng lặp cùng một đoạn code nhiều lần:
add_library(my_helper_lib src/helper.cpp)
ament_target_dependencies(my_helper_lib rclcpp)
add_executable(node_a src/node_a.cpp)
target_link_libraries(node_a my_helper_lib)
Kiến thức kỹ thuật
Thông số
Một số macro ament_cmake khác ít gặp hơn nhưng quan trọng khi package của bạn được package khác dùng lại:
| Macro | Dùng khi nào |
|---|---|
cmake_minimum_required(VERSION 3.8) | Phiên bản CMake tối thiểu ROS2 hiện hành yêu cầu |
ament_export_targets() | Cho phép package khác find_package() và dùng thư viện của bạn |
ament_export_dependencies() | Tự động kéo theo dependency của bạn khi package khác dùng thư viện của bạn |
ament_export_include_directories() | Cho package khác biết header của bạn nằm ở đâu |
Ứng dụng thực tế
Khi một package chứa thư viện dùng chung cho nhiều package khác trong hệ thống robot (VD: 1 package robot_utils chứa các hàm hình học/toán học dùng lại ở cả node điều khiển lẫn node xử lý cảm biến), cần thêm bộ ament_export_* để "xuất khẩu" đúng cách — thiếu bước này, package khác dù find_package(robot_utils REQUIRED) thành công vẫn không link được vào thư viện bên trong, vì ament không biết phải export những gì. Đây là khác biệt giữa 1 package "chỉ tự dùng nội bộ" và 1 package "được thiết kế để tái sử dụng" trong toàn hệ thống.
Với package lớn có nhiều file .cpp, thay vì liệt kê từng file trong add_executable(), thường dùng biến CMake để gom danh sách file nguồn gọn hơn:
file(GLOB SOURCES "src/*.cpp")
add_executable(main_node ${SOURCES})
Cách này tiện khi package đang phát triển nhanh (thêm file .cpp mới thường xuyên), nhưng cần lưu ý file(GLOB ...) chỉ quét danh sách file tại thời điểm chạy cmake, không tự phát hiện file mới thêm vào giữa các lần build — nếu thêm file .cpp mới mà build không nhận, cần chạy lại cmake từ đầu (rm -rf build && colcon build) thay vì chỉ colcon build thông thường.
Ví dụ thực tế
$ colcon build --packages-select my_cpp_pkg
Output khi thiếu find_package(std_msgs REQUIRED) dù code có #include <std_msgs/...>:
CMake Error at CMakeLists.txt:9 (ament_target_dependencies):
Could not find a package configuration file provided by "std_msgs"
-- Configuring incomplete, errors occurred!
Thông báo lỗi chỉ thẳng đúng dòng và đúng package thiếu — đọc kỹ dòng CMake Error at CMakeLists.txt:X luôn cho biết chính xác vị trí cần sửa, thay vì phải đoán mò trong toàn bộ file.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân |
|---|---|
Could not find a package configuration file for "X" | Thiếu find_package(X REQUIRED), hoặc package X chưa cài/chưa source đúng workspace |
undefined reference to ... khi link | Thiếu tên thư viện trong ament_target_dependencies() dù đã find_package() |
ros2 run báo không thấy executable dù build "thành công" | Thiếu executable đó trong install(TARGETS ...) |
Package khác không link được thư viện của bạn dù find_package() thành công | Thiếu ament_export_targets()/ament_export_dependencies() |
Kết luận
CMakeLists.txt trong ament_cmake tuân theo 1 khuôn cố định — hiểu đúng thứ tự find_package → add_executable/add_library → ament_target_dependencies → install → ament_package là đọc/sửa được hầu hết package C++ trong hệ sinh thái ROS2, kể cả những package chưa từng thấy trước đó. Bài tiếp theo trong chuyên mục nói về package.xml — file khai dependency song hành với CMakeLists.txt, quyết định thứ tự build của cả workspace.