ament_cmake packageからpure cmake packageを呼び出す。~vendor packageの作り方~

こんばんは、ROS/ROS2 advent calender初日に投稿させていただきます。
今回のネタはROS2からはcolconというメタビルドシステムを使うからcmakeのパッケージをインクルードして使うの楽になったよと風のうわさで聞いたのでホンマかと思って試してみたらいろいろハマったので備忘録を残しておきます。
結論から言うとROS2でpure cmakeのパッケージはかんたんに使えますがちょっと準備が必要というような感じです。
それではROS2のビルドシステムを紹介しつつcmakeのpackageの呼び出し方法を説明させてください。

# ROS2のメタビルドシステム colcon

ROS2では「メタ」ビルドシステムcolconを使用します。
なぜ「メタ」とつくかというとcolconは[ament_cmake](https://github.com/ament/ament_cmake)や[cmake](https://cmake.org/),[bazel](https://github.com/colcon/colcon-bazel)といった様々なビルドシステムのPythonラッパーになっています。
なのでcolcon自体はビルドの作業は何もしておらず実務はラップしたビルドシステムがこなしているというような感じです。
これによってROS2開発者はworkspaceに全部ぶち込んでcolcon buildしたら様々な言語で構成されたROS2アプリケーション群が使えるようになるよ、というのがcolconの設計思想です。
なのでament_cmakeのパッケージもpure cmakeのパッケージもおなじワークスペースに放り込んでビルドすることができます。
ただ、この「実務は個別のビルドシステムがやっている」というのが曲者で現状のcolconでは同じワークスペースに入っているpure cmakeとament_cmakeのパッケージに依存関係を見つけて適切に取り込むという機能はないです。
ament_cmakeにもどうもその機能はないっぽいです。pure cmakeのパッケージをワークスペースの外でビルド、インストールすればできないことはないのですができればワークスペース内で一撃で全部ビルドしたいですよね。。。
それをやるのが今回紹介するvendor packageという手法です。

# vendor package
vendor packageはros2公式等がバイナリ配布がないようなパッケージを配布する際に使用している手法です。
https://github.com/ros2/darknet_vendor/blob/master/CMakeLists.txt
vendor packageはament_cmakeにcolcon buildしたときに特定のリポジトリからコードを持ってきてament_packageと紐付けるという方式で上記の一撃ビルドを実現しています。
vendor packageの作り方は複数あるようなのですが、自分の環境で使っているものを紹介します。

# vendor packageの作り方
こちらが自分の作ったvendor packageになります。
https://github.com/OUXT-Polaris/peglib_vendor
これはpeglibというパーサジェネレータのライブラリをROS2の世界で使用するためのvendor packageです。
では詳細をCMakeLists.txtを追って見ていきましょう。

```cmake
cmake_minimum_required(VERSION 3.5)
project(peglib_vendor)

# Default to C99
if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)

macro(build_peglib) # ビルド用のマクロを定義
  set(cmake_commands)
  set(cmake_configure_args -Wno-dev)
  set(cmake_configure_args -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_install)

  include(ExternalProject) # external project機能を使って外部からソースコードを持ってくる
  externalproject_add(cpp-peglib-ext # 持ってきたソースコードにproject名をつける
    GIT_REPOSITORY https://github.com/yhirose/cpp-peglib.git # githubのレポジトリを指定
    GIT_TAG 11ed83e46fb0268c22005a6b8a9f094f8ea7208c # コミットハッシュを指定
    TIMEOUT 6000 # 接続のタイムアウトを追加
    ${cmake_commands} # ビルドの際のコマンド
    CMAKE_ARGS
      ${cmake_configure_args}
  )
　# 持ってきたプロジェクトをインストール
  # The external project will install to the build folder, but we'll install that on make install.
  install(
    DIRECTORY
      ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_install/
    DESTINATION
      ${CMAKE_INSTALL_PREFIX}
  )
endmacro()

# 定義したマクロを実行
build_peglib()
ament_export_include_directories(include)

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # uncomment the line when a copyright and license is not present in all source files
  #set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # uncomment the line when this package is not in a git repo
  #set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

# ament_packageとして登録
ament_package()
```

この実装ではcmakeのexternal project機能を使用しています。(https://qiita.com/trairia/items/d20860d61f0e1eb2fb72)
この機能があればgithubから特定のコミットを指定してソースコードを取ってきたりできるようです。めちゃくちゃ便利ですね。

# vendor packageを作ったあと
ROS2のワークスペースに追加してcolcon buildしたらビルド時に外部ソースコードがなければ自動的にcloneしてビルドしてくれます。
超便利！！
他のパッケージから参照したいときはvendor_packageの依存をpackage.xmlに追記したあと以下のような内容を他のROS2 packageと同じようにCMakeLists.txtに書くだけです。
超かんたん！

```cmake

find_package(peglib_vendor REQUIRED)
ament_target_dependencies(target
  peglib_vendor)
```

# まとめ
今回はROS2 packageからpure cmakeを簡単に扱う方法に関して紹介させていただきました。
この記事がROS2と他のソフトウェアをインテグレーションするときの助けになれば幸いです。