ROS講座97 CMakeList.txtとpackage.xmlの書き方

# 環境
この記事は以下の環境で動いています。

| 項目 | 値 |
|:-:|:-:|
| CPU | Core i5-8250U |
| Ubuntu | 16.04 |
| ROS | Kinetic |

インストールについては[ROS講座02 インストール](https://qiita.com/srs/items/e0e0a9dc3f94c2d3348e)を参照してください。
またこの記事のプログラムはgithubにアップロードされています。[ROS講座11 gitリポジトリ](https://qiita.com/srs/items/25a56a6abda41dcf396e)を参照してください。

# 概要
ROSのパッケージを製作するためにはCMakeList.txtとpackage.xmlを記述する必要があります。しかしこれらの記述についてまとまって書かれている情報が少ないのでここでまとめてみます。

# 2つのファイルの役割
2つのファイルはそれぞれ役割が違います。以下のように2つのファイルは使われるところが違いますが、2つのファイルに書かれる依存の整合性を保つ仕組みはありません（これはROSの欠点の1つといえます）。これらのファイルを正確に記述する必要があります。

## rospack listを実行した場合
$ROS_PACKAGE_PATHにあるディレクトリの中でpackage.xmlが探され、これがあることでROSパッケージだと認識されます。roscdなどで特定のROSパッケージにcdする場合のパスもこれを使って探されます。

## caktin_makeを実行した場合
まずcatkin_ws/src以下のROSパッケージのpackage.xmlに書かれている依存を読んでcatkinマクロを解決して、ビルドする対象と順番を決めます。その後各ROSパッケージのCMakeList.txtがCmakeにかけられます。

# package.xmlの役割
package.xmlは以下の役割があります。

* システム依存の記述系  
あくまでROSシステムとしての依存を扱います。ビルドの依存は以下のCMakeList.txtで扱います。roscdやrospack等のコマンドで参照されるのはこちらです。
* プラグインやパスのエクスポートの記述  
例えばnodeletなどでは「nodelet」パッケージのノードから、「ユーザーが作った」パッケージのプラグインを参照して実行します。この時nodeletがプラグインの存在を通知するのにpackage.xmlのexportの機能を使います。
* その他の記述  
メンテナンスなどの人が参照する部分です。

## 最小構成

```xml:package.xmlの最小構成
<package format="2">
  <name>foo_core</name>
  <version>1.2.4</version>
  <description>
  This package provides foo capability.
  </description>
  <maintainer email="ivana@osrf.org">Ivana Bildbotz</maintainer>
  <license>BSD</license>
</package>
```

[ROSwiki](http://wiki.ros.org/catkin/package.xml)より

* packageタグのフォーマット属性  
package.xmlにはversion1とversion2があり、両者の構文を混ぜるとエラーになります。現在は2が主流なのでそちらで解説します。
* nameタグ  
パッケージ名を決定します。実はパッケージのディレクトリ名はパッケージ名に影響せずに、ここで書いた名前がパッケージの名前となります。
* versionタグ・descripionタグ・maintainerタグ・licenseタグ  
自由に記述できます。  

## 依存の記述

依存タグは以下の7つがあります。

* `<buildtool_depend>`  
ビルドツールの指定をします。通常は`catkin`のみを指定します。
* `<build_depend>`  
ビルド時の依存の指定をします。基本的にCMakeList.txtで指定するものは全てここで指定しておく必要があります。
* `<build_export_depend>`  
ビルド依存のエクスポートの指定をします。
* `<exec_depend>`  
実行時の依存の指定をします。pythonのノードを使うときには要注意です。version1では`<run_depend>`となっていたものです。バージョンを混ぜるとエラーになるので注意です。
* `<depend>`  
これを書くと`<build_depend>`、`<build_export_depend>`、`<exec_depend>`の3つを指定したのと同じ効果になります。
* `<test_depend>`  
テスト実行時の依存を指定します。
* `<doc_depend>`  
doxygen等のドキュメント作成用の依存を指定します。

```xml:依存の記述の例
<package format="2">
　<!-- 省略 -->

  <buildtool_depend>catkin</buildtool_depend>
  <depend>roscpp</depend>
  <depend>std_msgs</depend>
  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>
  <exec_depend>rospy</exec_depend>
</package>
```

依存を正確に記述するためには`<build_depend>`や`<exec_depend>`を使い分ける必要がありますが、実用的には全部を`<depend>`で指定すれば問題ありません。

## 依存しているパッケージへのエクスポート
依存しているパッケージへのプラグインなどのエクスポートをすることが出来ます。
例えば以下の様に記述すると各ROSパッケージで`<export>`タグの中で`<gazebo_ros gazebo_media_path="*******" />`と書いてある内容の一覧を取得できます。

```shell:エクスポートの検索
plugins --attrib=gazebo_model_path gazebo_ros
```

# CMakeList.txtの役割
これはcatkin_makeをするときに使用されるものです。名前の通りcmakeで使われるファイルです。逆に言うとそれ以外では使いません。
cmakeの標準の書式にcatkin特有のマクロが含まれているのに注意です。
このファイルに記述する記述は以下のように分けれらます。

* 依存の記述
* ビルド前に行われる作業
* 依存のエクスポートの記述
* ビルドする対象
* ビルド対象の依存

## 最小構成

```cmake:最小構成
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
```

1行目は最小バージョンンの指定です。rosのversionによって違いますが、kineticでは標準では`2.8.3`と書くようです。
2行目はパッケージ名を書きます。

## 実行ファイルを生成する最小構成

```cmake:最小構成
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
find_package(catkin REQUIRED COMPONENTS
  roscpp
)
include_directories(
  ${catkin_INCLUDE_DIRS}
)
add_executable(main src/main.cpp)
target_link_libraries(main
  ${catkin_LIBRARIES}
)
```

```c++:ソースの例(package_A/src/main.cpp)
#include <stdio.h>
#include <ros/ros.h>
int main(int argc, char **argv){
  ros::init(argc, argv, "talker");
  ros::NodeHandle n;
  return 0;
}
```

実行ファイルをつくり、それをrosのライブラリとリンクする最小の例です。

* `find_package(catkin REQUIRED COMPONENTS <依存package1> <依存package2>)`では依存するROSパッケージ名を記述します。
* `include_directories()`ではインクルードパスを追加します。これが無いと`ros/ros.h`とインクルードすることができません。
* `add_executable(<ターゲット名> <cppファイルのパス>)` ビルドするターゲットを選択します
* `target_link_libraries(<ターゲット名> ${catkin_LIBRARIES})`ではリンクするライブラリのパスを追加します。これが無いと`ros/ros.h`をインクルードするのでAPIには到達しますが、内部の実装を読めずにエラーになります。

## find_packageでのcatkinマクロの効果
以下のようにcatkinのマクロを使用せずにcmake標準の文法だけで同等の記述できます。

```cmake:最小構成(catkinマクロを使わない例)
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
find_package(roscpp)
include_directories(
  ${roscpp_INCLUDE_DIRS}
)
add_executable(main src/main.cpp)
target_link_libraries(main
  ${roscpp_LIBRARIES}
)
```

今回は依存がroscppだけなので上記のように短いですが、依存するパッケージが多くなると記述が長くなります。`find_package(catkin REQUIRED COMPONENTS <依存package>)`とするとパッケージの依存を追加してかつ`${依存package_***}`のパスを`${catkin_***}`に追加してくれます。このために`${catkin_***}`だけを書いていれば足ります。

しては`find_package(roscpp)`だけでroscppへの依存を追加できますが、上記のようにcatkinマクロを使用することで、わざわざインクルードやライブラリの依存で`${roscpp_INCLUDE_DIRS}`や`${roscpp_LIBRARIES}`

## ライブラリを作成する例（使用する側への設定が抜けている）

```cmake:ライブラリを生成する最小構成
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
)
include_directories(
  include
  ${catkin_INCLUDE_DIRS}
)
add_library(TestLib
  src/TestLib.cpp
)
target_link_libraries(TestLib
  ${catkin_LIBRARIES}
)
```

```cpp:include/package_A/TestLib.h
#include <stdio.h>
#include <ros/ros.h>
#include <std_msgs/Int32.h>

std_msgs::Int32 ReturnInt(void);
```

```cpp:src/TestLib.cpp
#include <package_A/TestLib.h>

#include <stdio.h>
#include <ros/ros.h>
#include <std_msgs/Int32.h>

std_msgs::Int32 ReturnInt(void){
    std_msgs::Int32 int_msg;
    int_msg.data = 0;
    return int_msg;
}
```

ライブラリの場合は`add_executable`の代わりに`add_library(<ターゲット名> <cppファイルのパス1> <cppファイルのパス2>)`と書きます。このように記述するとpakckage_A/lib<ターゲット名>.soといオブジェクトファイルが生成されます。複数の関数やクラスを1つのオブジェクトファイルで扱うことができます。
しかしこの記述では使用する側がいちいちincludeファイルのパスを手動で解決したり、このライブラリの依存をわざわざ調べてライブラリを使用する側で依存を書かないといけません。これをcatkinマクロで解決できます。

## ライブラリを作成する例（使用する側への設定込み）

```cmake:ライブラリを生成・使用側への設定をする最小構成
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
)
catkin_package(
  INCLUDE_DIRS include
  LIBRARIES package_A
  CATKIN_DEPENDS roscpp std_msgs
)
include_directories(
  include
  ${catkin_INCLUDE_DIRS}
)
add_library(TestLib
  src/TestLib.cpp
)
target_link_libraries(TestLib
  ${catkin_LIBRARIES}
)
```

`catkin_package()`が加わりました。

* `INCLUDE_DIRS include`は<catkin_INCLUDE_DIRS>にincludeを加えます。これでほかのパッケージからこのパッケージのincludeファイルが見えるようになります。
* `LIBRARIES package_A`は上記と同じようにライブラリのパスが自動で解決できるようになります。
* `CATKIN_DEPENDS roscpp std_msgs`では他のパッケージがこのパッケージに依存をした時に自動的にここで記述したパッケージも依存に加わります。

## msg・srv・actファイルを作成する例（使用する側への依存の解決が抜けている）
この3つはそれぞれROS形式のファイルをhファイルに変換して、各ノードで読み込めるヘッダファイルをを生成します。この3つは同様のコマンドなのでmsgの生成を例に説明します。

```cmake:msgファイルを生成
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
  message_generation
)
add_message_files(
  FILES
  Msg1.msg
)
generate_messages(
  DEPENDENCIES
  std_msgs
)
catkin_package(
  CATKIN_DEPENDS message_runtime std_msgs
)
```

* `find_package`では`message_generation`を追加する必要があります。
* `add_message_files`にmsgファイルのファイル名を指定します。
* `generate_messages`ではmsgファイル内で使用しているmsgの依存を書く必要があります。
* `catkin_package`はこのパッケージを依存に追加するだけで`message_runtime`と`std_msgs`を自動で依存が追加されるようにします。

## msgを使う側

```cmake:msgファイルを使う
cmake_minimum_required(VERSION 2.8.3)
project(package_A)
find_package(catkin REQUIRED COMPONENTS
  roscpp
)
include_directories(
  ${catkin_INCLUDE_DIRS}
)
add_dependencies(main ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_executable(main src/main.cpp)
target_link_libraries(main
  ${catkin_LIBRARIES}
)
```

ここではpackage_Aの中のmsgをmainが使うとします。`add_dependencies(main ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
`が必要です。msgファイルはビルド時に生成されるものですので、ただ`package_A`に依存しても、ファイルを取得できません。`package_A`のビルド後の生成物に依存をする必要があります。

# 参考
[ROSwiki](http://wiki.ros.org/catkin/package.xml)
[依存の解説](http://docs.ros.org/jade/api/catkin/html/howto/format2/catkin_library_dependencies.html)

# 目次ページへのリンク
[ROS講座の目次へのリンク](https://qiita.com/srs/items/5f44440afea0eb616b4a)