ROS2 package.xmlとCMakeLists.txtのチートシート

【[ROS2関係トップページへ](https://qiita.com/NeK/items/7ac0f4ec10d51dbca084)】

ケースごとの代表例の紹介．基本的に関係部分のみ抜粋．
Foxy Fitzroyからの変更が多いので，[ROS2における変更点: from Eloquent Elusor to Foxy Fitzroy](https://qiita.com/NeK/items/c1d731964f0d324aad58)を参照のこと．
[Humble Hawksbill以降はこちらを参照](https://qiita.com/NeK/items/eb4777936d95e2c25956)のこと．

基本方針として一つのパッケージにすべて(ターゲット・ライブラリ・独自メッセージ)を入れるのではなく，なるべく分割する．
関連するものは，まずワークスペースをつくり，その中に「独自メッセージのパッケージ」「ライブラリとなるnode」「ターゲット」と分ける．
それぞれのパッケージ名を以下のようにすることで分かりやすくする．

- 独自メッセージのパッケージ名
    - ***_msgs
- ライブラリとなるパッケージ名
    - ***_node
- ターゲットとなるパッケージ名
    - ***
    - ***_target
        - どっちでも

例えば強化学習用のワークスペースだと

- reinforcement_learning_msgs
- reinforcement_learning_node
- reinforcement_learningやreinforcement_learning_target
    - 実行形式だけど，テスト用とかなら以下もあり
        - reinforcement_learning_test

もちろん独自ルール．

# ターゲットの作成
rclcppは通常使用するパッケージ．
それ以外のパッケージを使う場合，例としてパッケージ名:packとして書いておく．packとしては，std_msgsなどROS2が用意しているものもあれば自分で作成した独自メッセージのパッケージやライブラリのパッケージなども考えられる．特になければpackに関係する部分はいらない．

## package.xml

```xml:package.xml
<package format="3">
  <depend>rclcpp</depend>
  <depend>pack</depend>
</package>
```

<details><summary>dependについて</summary>
<div>
以前は以下のようにビルド用・実行用・テスト用に分かれていた気がするしtutorialにもそう書いてあった気がするが，現在はdependにまとめられている．

```xml:package.xml
<package format="3">
  <build_depend>rclcpp</build_depend>
  <test_depend>rclcpp</test_depend>
  <exec_depend>rclcpp</exec_depend>

  <build_depend>pack</build_depend>
  <test_depend>pack</test_depend>
  <exec_depend>pack</exec_depend>
</package>
```

こちらの方が便利であるが，[独自メッセージの作成](#独自メッセージの作成)のようにbuildとexecで異なるパッケージが必要な場合(rosidl_default_generatorsとrosidl_default_runtime)はもちろん別々に記載する．
</div></details>

## CMakeLists.txt
二つのターゲット(ここではtarget_name_1とtarget_name_2)を作成する場合．一つの場合や三つ以上の場合は，例のようにadd_executableとament_target_dependenciesおよびinstallの中を追加していく．

### 自作の共有ライブラリを使わない場合．

```CmakeLists.txt
find_package(rclcpp REQUIRED)
find_package(pack REQUIRED)

add_executable(target_name_1
  src/main.cpp
#  ... すべてのcppファイルを記述 
)
ament_target_dependencies(target_name_1
  rclcpp
  pack
)

add_executable(target_name_2
  src/main.cpp
#  ... すべてのcppファイルを記述 
)
ament_target_dependencies(target_name_2
  rclcpp
  pack
)

install(TARGETS
  target_name_1
  target_name_2
  DESTINATION lib/${PROJECT_NAME}
)
```


### 自作の共有ライブラリ(異なるパッケージ)を使う場合．
自作ライブラリのパッケージ名:packとする．
自作ライブラリのパッケージがexportするincludeファイルを読み込むために以下に関する部分を追加．

- target_include_directories

```CmakeLists.txt
find_package(rclcpp REQUIRED)
find_package(pack REQUIRED)

add_executable(target_name_1
  src/main.cpp
#  ... すべてのcppファイルを記述 
)
ament_target_dependencies(target_name_1
  rclcpp
  pack
)

target_include_directories(target_name_1
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

add_executable(target_name_2
  src/main.cpp
#  ... すべてのcppファイルを記述 
)
ament_target_dependencies(target_name_2
  rclcpp
  pack
)

target_include_directories(target_name_2
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

install(TARGETS
  target_name_1
  target_name_2
  DESTINATION lib/${PROJECT_NAME}
)
```

# 自作の共有ライブラリの作成

## package.xml
~~ライブラリのパッケージを作るには，rclcpp_componentsが必要なのでそれを追加．~~
rclcpp_componentsはコンポーネンツを作成するときに必要で共有ライブラリの作成時にはいらない．

```xml:package.xml
<package format="3">
  <depend>rclcpp</depend>
```

## CMakeLists.txt
ライブラリ名:my_libを作成．そのために必要なソースファイルの例をlib1.cppとlib2.cppとする．
ament_export_targetsで指定しているexport_my_libはinstallのEXPORTで名前つけしているもの．EXPORTでどんなものでも大丈夫なようだけど，通常パッケージ名の頭にexport_を付けるみたい．

```CMakeLists.txt
find_package(rclcpp REQUIRED)

add_library(my_lib SHARED
  src/my_lib1.cpp
  src/my_lib2.cpp
)
target_compile_definitions(my_lib
  PRIVATE "MY_LIBRARY_BUILDING_LIBRARY"
)
target_compile_options(my_lib PUBLIC -Wall)
ament_target_dependencies(my_lib
  rclcpp
  rclcpp_components
)

target_include_directories(my_lib
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

if(NOT WIN32)
  ament_environment_hooks(
    "${ament_cmake_package_templates_ENVIRONMENT_HOOK_LIBRARY_PATH}"
  )
endif()

ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET)

install(
  DIRECTORY include/
  DESTINATION include
)
install(TARGETS
  my_lib
  EXPORT export_${PROJECT_NAME}
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES DESTINATION include
)
```

<details><summary>Eloquent Elusorまでの場合</summary><div>
Eloquent Elusorでは，ament_export_{interface,dependencies,libraries,include_directories}の4つが必要だったが，Foxy Fitzroyからはament_export_targets一つとなった．使い方はament_export_interfacesのように使う．
詳しくは[Foxy Fitzroy](https://qiita.com/NeK/items/48c55bd473091d41cc75)と[Eloquent Elusor](https://qiita.com/NeK/items/fb5dac0158497c1ff77f)のページを見比べること．
</div></details>

# 同一パッケージでターゲットと共有ライブラリを作成
異なるパッケージの共有ライブラリを使用してターゲットを作成する場合には，共有ライブラリのパッケージをfind_packageで読み込み，target_include_directoriesで指定した．
同一パッケージの場合，add_libraryで名付けたライブラリ名をtarget_include_directoriesで指定するだけ．

## package.xml
~~ライブラリのパッケージを作るには，rclcpp_componentsが必要なのでそれを追加．~~
rclcpp_componentsはコンポーネンツを作成するときに必要で共有ライブラリの作成時にはいらない．

```xml:package.xml
<package format="3">
  <depend>rclcpp</depend>
```

## CMakeLists.txt


```CmakeLists.txt
find_package(rclcpp REQUIRED)

add_library(my_lib SHARED
  src/my_lib1.cpp
  src/my_lib2.cpp
)
target_compile_definitions(my_lib
  PRIVATE "MY_LIBRARY_BUILDING_LIBRARY"
)
target_compile_options(my_lib PUBLIC -Wall)
ament_target_dependencies(my_lib
  rclcpp
  rclcpp_components
)

target_include_directories(my_lib
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

if(NOT WIN32)
  ament_environment_hooks(
    "${ament_cmake_package_templates_ENVIRONMENT_HOOK_LIBRARY_PATH}"
  )
endif()

ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET)

add_executable(target_name_1
  src/main.cpp
#  ... すべてのcppファイルを記述 
)
ament_target_dependencies(target_name_1
  rclcpp
  my_lib
)

target_include_directories(target_name_1
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

install(
  DIRECTORY include/
  DESTINATION include
)
install(TARGETS
  my_lib
  EXPORT export_${PROJECT_NAME}
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES DESTINATION include
)

install(TARGETS
  target_name_1
  DESTINATION lib/${PROJECT_NAME}
)
```

# 独自メッセージの作成
Foxy Fitzroyから以下の点が変わった？

* rosidl_default_generatorsについて
    * タグがbuildtool_dependからbuild_dependに変更
* ament_export_dependencies(rosidl_default_runtime)が不必要
    * 自動判別してくれるようになった？
    * あってもOK

詳しくは[ROS2独自メッセージの作成](https://qiita.com/NeK/items/26751b8ed12976ff977d)参照のこと．

## package.xml

```xml:package.xml
<package format="3">
  <build_depend>rosidl_default_generators</build_depend>
  <exec_depend>rosidl_default_runtime</exec_depend>
  <member_of_group>rosidl_interface_packages</member_of_group>
```

## CMakeLists.txt
メッセージファイルとして以下のものを例として表示

- msg/TwoInts.msg
- srv/CalcTwoFloats.srv

setは中身(複数行可)をラベル付けし${ラベル名}でアクセスしやすくするもの．
setを使わず直接ファイル名をrosidl_generate_interfacesの中に書いてもよい．

```CMakeLists.txt
find_package(rosidl_default_generators REQUIRED)

set(msg_files
  "msg/TwoInts.msg"
)
set(srv_files
  "srv/CalcTwoFloats.srv"
)
rosidl_generate_interfaces(${PROJECT_NAME}
  ${msg_files}
  ${srv_files}
)
# ament_export_dependencies(rosidl_default_runtime)
```

### 同じパッケージでターゲットも作成する場合
例:ターゲット名:my_target
add_executableなどは省略

以上の上，上記のCMakeLists.txtに下記を追加．

```CMakeLists.txt
get_default_rmw_implementation(rmw_implementation)
find_package("${rmw_implementation}" REQUIRED)
get_rmw_typesupport(typesupport_impls "${rmw_implementation}" LANGUAGE "cpp")

foreach(typesupport_impl ${typesupport_impls})
  rosidl_target_interfaces(my_target
    ${PROJECT_NAME} ${typesupport_impl}
  )
endforeach()
```

### 同じパッケージでライブラリも作成する場合
同じパッケージでターゲットを作成する場合と同様に．．．とやらない．
現在エラーを解決できない状態．


# 参考

- [ament_cmake User Documentation](https://index.ros.org/doc/ros2/Tutorials/Ament-CMake-Documentation/)