MoriKen's Journal

MoriKen's Journal

アラサー社会人博士による徒然日記。技術についてつらつら。だけだとコンテンツが貧弱なので、会社公認で大学院博士課程に進学した経緯や、独学でTOEICを475→910にしたノウハウを共有します。

GitLab CI を使って GitHub の Private Repository を無料で CI ①背景編

はじめに

GitHub ユーザは Travis CI を使っていることが多い

GitHub のリポジトリを CI するサービスは様々ありますが、多くの方は Travis CI を利用されているのではないでしょうか。簡単に連携できますし。

Public Repository を利用する限りは、無料版の Travis CI で制限なく CI ができるので、特に問題はありません。ある Repository を CI する際に、依存する Repository が存在したとしても、それも Public であればこれまた問題はありません。

f:id:MoriKen254:20190601153831p:plain


Private Repository だとお金がかかる…

ところが、訳あって秘匿にしなければならなくなった Private Repository を CI したくなったときが、困りものです。Travis CI を有償プランにアップデートしないと、Private Repository が CI できないというのですから、これは大変です!

f:id:MoriKen254:20190601154043p:plain


そんな!せっかく GitHub で Private Repository を作りやすくなったっていうのに、Travis CI の方がお金かかっちゃうだなんて。いくら社会人とは言え、訳あって何度も休職を余儀なくされるため薄給であり、かつ曲がりなりにも学生でもあるゆえに何かと出費も嵩む私の身では、あまりに痛い仕打ちではあーりませんか(Repository よりも、無駄に事情の方が Private 過ぎる笑)。

救世主、GitLab CI の登場

そんな悩める苦社会人に差し伸べられて救いの手…。それが GitLab CI です!

f:id:MoriKen254:20190601154613p:plain


  • GitLab では Private Repository を無料で作成できる
  • GitLab が提供する GitLab CI では Private Repository を無料で CI できる
    • 1ユーザ or 1グループにつき月2000分が上限
  • GitLab CI では、依存する Private Repository のクローンもできる
    • SSH の設定が必要

性能もお墨付き

このように、うまく設定して使いこないさえすれば、無料で CI できてしまう優れもの!しかも、第三者機関による性能評価も上々というのだからアッパレです。


難点が

素敵過ぎる!そう思ったでしょ。

でもね、でもね…。手順が複雑なんです!!!涙

Travis CI みたいに、軽くポチればいいんじゃなくて、色々と準備をしないといけません。Private Repository クローンしてくるあたりとか、もうなんど諦めかけたことか(オイ)!

まぁ、無料で使えるだけありがたいので、そこはエンジニアとしてハンドリングすればよいのですが、にしたって手順が複雑です。

Private Repository を無料で CI したいと画策したものの、おとなしく Travis CI に泣く泣くお金を払う運びとなったエンジニア仲間がどこかにいるはず!(妄想だったりして…)

というわけで、その複雑な手順を図を交えてチュートリアル形式に記事にしていきます!私と同じような状況に至った悩めるエンジニアにとって、少しでも助けになればと思います。

構成

具体的な手順は次回以降、2回の記事に分けて記述します。

少し補足

GitLab CI 自体は汎用的なサービスですが、私は自分の趣向の関係で、ROS のプロジェクトに CI をかけることが多いです。

そのため、変数定義やユースケースが、ROS の CI サービス用 Repository である industrial_ci に(こっそり)準拠させている場合があります。

github.com

特に動作に支障はないと思いますが、念の為補足です。

それでは、また次回!

次回: ② ミラーリング編

www.moriken254.com

【ROS 2】2 つの別々の docker コンテナで 2 つのノードを実行 (公式文書和訳)

ROS 2 公式文書(英語) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/11 現在のものです。

ターミナルを開きます。対話モードでコンテナ内のイメージを実行し、ros2 run を使用してトピックパブリッシャ(パッケージ demo_nodes_cpp からの実行可能トーカ)を起動します。

docker run -it --rm osrf/ros2:ardent-basic ros2 run demo_nodes_cpp talker

2 番目のターミナルを開きます。対話モードでコンテナ内のイメージを実行し、ros2 run を使用してトピック購読者(パッケージ demo_nodes_cpp からの実行可能リスナ)を起動します。

docker run -it --rm osrf/ros2:ardent-basic ros2 run demo_nodes_cpp listener

コマンドライン呼び出しの代わりに、以下の(最小限の)内容でdocker-compose.yml ファイル(ここではバージョン 2)を作成することができます。

version: '2'

services:
  talker:
    image: osrf/ros2:ardent-basic
    command: ros2 run demo_nodes_cpp talker
  listener:
    image: osrf/ros2:ardent-basic
    command: ros2 run demo_nodes_cpp listener
    depends_on:
      - talker

コンテナを実行するには、docker-compose up を同じディレクトリで呼び出します。 Ctrl+C でコンテナを閉じることができます。

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【ROS 2】単一の docker コンテナ内で2つのノードを実行(公式文書和訳)

ROS 2 公式文書(英語) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/11 現在のものです。

Docker イメージの pull

タグ「ardent-basic」を使用して ROS 2 docker イメージを pull します。

docker pull osrf/ros2:ardent-basic

Docker イメージの実行

インタラクティブモードでコンテナ内のイメージを実行します。

$ docker run -it osrf/ros2:ardent-basic
root@<container-id>:/#

Docker イメージ内での ROS コマンドの実行

例 1:ros2 コマンドラインヘルプの実行

root@<container-id>:/# ros2 --help

例 2:インストールされているすべてのパッケージのリスト

root@<container-id>:/# ros2 pkg list
(you will see a list of packages)

例 3:すべての実行ファイルの一覧表示

root@<container-id>:/# ros2 pkg executables
(you will see a list of <package> <executable>)

例 4:パブリッシャとサブスクライバの起動

このコンテナの demo_nodes_cpp パッケージから、2 つの C++ ノード(1 つのトピック購読者リスナー、1 つのトピックパブリッシャートーカー)の最小限の例を実行します。

ros2 run demo_nodes_cpp listener &
ros2 run demo_nodes_cpp talker

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【ROS 2】RQt プラグインを Windows に移植する(公式文書和訳)

ROS 2 公式文書(英語)](https://index.ros.org/doc/ros2/) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/11 現在のものです。

RQt は歴史的には Windows 上でサポートされていませんが、互換性は持つようになってきています(ゆっくりとではありますが)。

RQt 移植の例

Microsoft は ROS の大部分を Windows に移植するよう努力しました。彼らのリポジトリは必要な変更を知るための良いリソースとなっています。そのリソースは init-windows と呼ばれるブランチを持つリポジトリとして、 ms-iot オーガニゼーションに格納してあります。例えば、こちらです。

こちらが qt_gui_core の ROS 2 ポートです。

こちらが python_qt_binding の ROS 2 ポートです。

Windows 10 に関する考慮事項

TinyXML バージョン 1 に関する問題

TinyXML バージョン 1 ではうまく動作しませんでした。ですので、必要に応じて TinyXML-2 にアップグレードしました。それはかなり簡単です。

TinyXML-2 への移植の例についてはこの PR をチェックしてください。

__cplusplus を使用するコードと pluginlib を必要とするコード

何箇所か、特に pluginlib の ROS 2 移植版では、__cplusplusフラグの使用があります。残念ながら Windows では、Visual Studio は実際に使用されている C++ 標準に関係なく、このフラグを正しく設定できません。詳しくはこちらのページをご覧ください。

設定するには、コンパイルオプション /Zc:__cplusplus を追加する必要があります。

たとえば、CMake では、次のようにすることができます。

target_compile_options(${PROJECT_NAME} PUBLIC "/Zc:__cplusplus")

ビルド生成物の格納場所(インストール前)

これは qt_gui_cpp をビルドしている間にだけ起こりました。そのパッケージでは、カスタムコマンドはパッケージの他の部分のターゲットライブラリに依存します。ただし、そのライブラリはビルドが完了するまでインストールされません。 Windowsは ${configuration} ディレクトリに構築されています。例えば:

  • Linuxでは、qt_gui_cpp.a<ros2_ws>/build/qt_gui_cpp/src/qt_gui_cpp / にビルドされます。
  • しかし Windows では qt_gui_cpp.lib<ros2_ws>/build/qt_gui_cpp/src/qt_gui_cpp/Release にビルドされています。

この状況でのプラットフォーム間の互換性のためには、CMake 生成式を使用してください。ただし、リンクするライブラリが必要な場合は、必ず $<TARGET_FILE:_target> ではなく $<TARGET_LINKER_FILE:_target> を使用してください。後者は .dll ファイルを検出できるでしょう(ただしそれは Windows 上でリンクされることができません)。こちらの例をご覧ください。

コンパイラとリンカのフラグ

一般に、Windows に移植するとき、多くのパッケージは追加のコンパイラフラグを利用するかもしれません。 Windows のコンパイラフラグは Microsoft のドキュメントにあります。 C++ コンパイラは cl.exe と呼ばれます。

リンカフラグについては、Microsoft のドキュメントを参照してください。リンカプログラムは link.exe と呼ばれます。

しかし、CMake は実際には変数でこれらのオプションの多くを提供しています。この StackOverflow ページには、スクリプトで使用可能なすべての CMake 変数を確認する方法の良い例が含まれています。

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【ROS 2】ament と catkin の混合(→ catment)について(公式文書和訳)

ROS 2 公式文書(英語) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/11 現在のものです。

背景

かつて rosbuild というビルドシステムがありました。それから catkin と呼ばれるものもできました。最近導入されたものは ament と呼ばれるもので、いつか catkin に取って代わるかもしれません。

これら 3 つのツールはすべて「メタビルドシステム」と見なすことができます。他のビルドシステム(CMake、Python setuptools など)の上に配置され、特に複数のパッケージにまたがる依存関係を管理するときや単一のワークスペースに複数のパッケージをビルドするときに、これらのビルドシステムを使いやすくするための追加機能を提供してくれます。

これらの各メタビルドシステムは、2 つのことを行います。

  1. 一般的なタスクを単純化するために使用できるAPI(例:CMake)を基盤となるビルドシステムに追加します(たとえば、実行可能ファイルをビルドするときに依存パッケージによってエクスポートされるすべてのフラグを指定する)。通常、コアのメタビルドシステムの外部のパッケージによる追加のAPIのインジェクションを可能にするためのフックがあります。
    • rosbuildmk/cmake.mkrosbuild_init()rosbuild_add_executable()など
    • catkincatkin_package()catkin_install_python() など
    • amentament_target_dependencies()ament_export_dependencies()ament_package() など
  2. パッケージでいっぱいのワークスペースで依存関係の順序で反復し、それぞれをビルドしてインストールするために使用できるツールを提供します。
    • rosbuildrosmake
    • catkincatkin buildcatkin_makecatkin_make_isolated など
    • amentament build

これらのシステムすべてを結び付ける共通のスレッドは、コードをパッケージに分割することです。各パッケージには、マニフェストファイル(manifest.xml または package.xml)が含まれています。このマニフェストは、メタビルドシステムの API とビルドツールの両者が機能するために(いくつかの例外を除いて)必要となります。

仮定

  1. 私たちは通常、メタビルドシステムの 2 つの特長(API とビルドツール)を組み合わせることを考えていますが、そうである必要はありません。
    • パッケージ内で使用される API とパッケージを反復処理するツールは、パッケージマニフェストがそれらの間のインタフェースを形成しているため、ほぼ独立していると見なすことができます。rosmake が(cmakemake に渡される適切なフラグを使って)、 catkin パッケージが入っているワークスペースを反復処理し、依存関係の順序でそれらにステップインして、それぞれのルーチン(mkdir build; cd build; cmake ..; make install)を実行するように変更できないのは、原則として理由はありません。
  2. あるメタビルドシステムから別のビルドシステムへ移行するのに必要な労力は最小限にすべきです。

    • rosbuild からcatkin` への大量移行は困難で、多くのユーザにとって依然として悩ましい点です。新機能にアクセスできるようにする、開発者に変更を加えるよう依頼することは合理的なのですが、その変更のため新しいシステムの有効性が損なわれるような影響は可能な限り最小化すべきです。これは、古いシステムが広く使用されている場合に特に当てはまります。
    • 導かれる知見:新しいメタビルドシステムへの移行は、よっぽど正当な理由がない限り、要求されるべきではありません。
      • 開発者が新しいシステムによって提供される機能を特に欲さないのであれば、古いシステムでは解決不能なレベルの問題がない限り(例えば rosbuild のインソースビルドパターンや「インストール」ステップがない等)、そのパッケージを無理に新しいシステムに移行すべきではありません。
  3. 相互運用性は良いことです。

    • 可能であれば(すべての組み合わせが実用的ではない)、開発者は、異なる側面を混在させることを含めて、メタビルドシステムを混在させることができます(つまり、あるシステムの構築ツールと別のシステムのAPIを使用)。このようなミキシングおよびマッチングは、開発者が1つのメタビルドシステム(ROSとcatkinなど)を使用する大規模な既存のコードベースを、別のメタビルドシステムを使用するコードベースによって提供される新しいライブラリとツール(ROS2など)と組み合わせる場合に特に重要です。 amentと)理想的には、そのような組み合わせは、どちらのコードベースでも使用されるAPIへの変更を必要とせずに、またどのビルダーツールを使用するかを開発者に指示することなく実行できます。

    • 導かれる知見:ワークスペースは均一である必要はありません。

      • catkinament の両パッケージをワークスペース内に入っていて、しかも依存関係は両方向にあるような状況だとしても、使用するビルダツールがそれらを両方をビルドできる仕様であれば、それができない理由はありません(要は、両方入っていてもビルドできるでしょってこと)。パッケージ(少なくとも CMake 管理パッケージ)間の主要なインターフェースはそれらの CMake 設定ファイルです。その設定ファイルが標準プロトコル(foo_LIBRARIESの設定など)に従っている限り、誰がファイルを書いたかは関係ありません。 catkinament によって自動生成されたりすることもありますし、自作パッケージでプレーンな CMake を使いたい開発者が手で直接書いた設定ファイルであることもあります。しかも、後者の自作設定ファイル内で、更にcatkinament に依存されているパッケージを記述している場合もあるくらいです。

実験的な実装によるユースケース

ROS 2 ワークスペースに ROS パッケージを追加して ament build でビルドする

既存の ROS パッケージを ROS 2 ワークスペースに追加し、その ROS パッケージを catkin から ament に(またはその逆に)移行したくない状況を仮定としましょう。これを可能にする 2 つのパッチがあります。

  • ament_package:フォーマット 2 を要求する代わりに、フォーマット 1 のパッケージマニフェストのサポートを追加します。この変更は、catkinamentに厳密には関係しません。というのも、フォーマット 2 はしばらく前から存在し、catkin がそれをサポートしているので、開発者が既にcatkin側のマニフェストを更新できる状況でうす。しかし、フォーマット1を使用する大量の ROS コードがありますので、それをサポートする必要があります。この実装は、たとえば、さまざまな種類の依存タグ、およびフォーマット 1 と 2 の間でタグがどのように異なるかを推論することによって改善できます。
  • ament_tools:新しい catkin ビルドタイプを ament に追加します。この実装は catkin パッケージを普通の cmake パッケージと同じように扱うだけで、うまくいきます。今後もっと洗練されたものになるでしょう。

  • 使用例

    1. 上記のブランチを使用して、通常どおり ROS 2 コードを入手してください。
    2. ワークスペースに catkin ROS パッケージを追加して、それらのすべての依存関係が満たされていることを確認します(ワークスペースに存在するか、適切なセットアップシェルファイルを使用して他の場所にインストールされます)。
    3. 通常どおりにビルドします(例:./src/ament/ament_tools/scripts/ament.by build)。

ジャジャ~ン!:新しいビルダーツールが使用されているからといって、既存のコードが突然壊れることはありません。

ケース 1:ament build を使って ROS パッケージをビルドする

新しい ament ツールがお気に入りになり、catkin を内部的に使用する既存の ROS パッケージをビルドするのに ament を使いたくなったとしましょう。これを実行する方法の例を次に示します。ament を最小インストールしてから、それを使用して ROS catkin パッケージの入ったワークスペースを構築します。

mkdir -p ~/ament_ws/src
cd ~/ament_ws/src
git clone https://github.com/osrf/osrf_pycommon.git
git clone https://github.com/ament/ament_package.git
cd ament_package
git checkout catkin
cd ..
git clone https://github.com/ament/ament_tools.git
cd ament_tools
git checkout catkin
cd ../..
 ./src/ament_tools/scripts/ament.py build

それではROSパッケージをビルドしてください。

. $HOME/ament_ws/install/setup.bash
cd ~/ros_catkin_ws
ament build

ジャジャ~ン:catkin パッケージを移行することなく、ament build ツールを使って catkin パッケージを構築しました。

ケース 2:ROS 2 パッケージで catkin API を使う

ここでは、まず ROS 2 上ででビルドしていて(ament API を内部的に使用している)、その後 catkin API を使用して新しいパッケージを追加したい状況を考えます。

これを機能させるには、catkin の Python3 のインストールが必要です(バイナリ Debian は Python2.7 を使用します)。これを行う例を次に示します。$HOME/catkin にインストールします。

# install catkin_pkg
git clone https://github.com/ros-infrastructure/catkin_pkg.git
cd catkin_pkg
git checkout ament
python3 setup.py install --prefix $HOME/catkin --single-version-externally-managed --record foo --install-layout deb
# install catkin
git clone https://github.com/ros/catkin.git
cd catkin
git checkout ament
mkdir build
cd build
PYTHONPATH=$HOME/catkin/lib/python3/dist-packages/ cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/catkin -DPYTHON_EXECUTABLE=/usr/bin/python3
make install

そのバージョンの catkin を使用するには、$HOME/catkin/setup.bash ファイルを入手するだけです。

通常の ROS 2 ワークスペースが ~/ros2_ws にあり、ament_packageament_toolscatkin ブランチにいるとしましょう。そのワークスペースに https://github.com/gerkey/catment から image_tools_catkin パッケージを追加してください。これは ROS 2 の image_tools パッケージを単純に移植したもので、ament API から catkin API に変更されました。それをビルドするには:

cd ~/ros2_ws
. $HOME/catkin/setup.bash
./src/ament/ament_tools/scripts/ament.py build

ジャジャ~ン:ROS 2 の上に新しいパッケージを追加するとき、自作パッケージに対して、どの CMake API を好むかを自由に選ぶことができます。

  • 警告catkin_package() の中で CATKIN_DEPENDS を使用することをコメントアウトしなければなりませんでした。なぜなら、rclcpp 等(ROS 2 で追加されたパッケージ)が catkin パッケージではないことで怒られたからです。その制約はなんとかして外してやる必要があります。
  • TODO:同じデモですが、catkin パッケージに依存する ament パッケージがあります(これは簡単に解決できます)。
  • TODO:同じデモですが、amentcatkin も使用しないきれいな CMakeLists.txtを持ち、ROS の外部でも普通に使えるように適切なものをエクスポートする、手入力で生成された fooConfig.cmake ファイルを提供するパッケージがあります。

catkin_make_isolated による ROS 2 パッケージの構築

すでに ROS と catkin に精通しており、ROS 2 を試すことに興奮しているそこのあなた。ament について学ぶ気なんて起きないかも知れませんね。そんなあなたに、catkin_make_isolated を使ってすべてをビルドできる機能があれば、良いと思いませんか?これを可能にするパッチがあるのです。

  • catkin:ビルドタイプが ament_* であることを宣言しているパッケージのサポートを追加しました。この実装はそのような各パッケージをビルドするために ament を要求します。 ament_cmake パッケージはプレーンな cmake パッケージとして扱うことができますが(amentcatkin サポートを追加したときのように)、ament_python パッケージは普通の Python 呼び出しを必要とする場合があります。そのロジックを catkin で再現するのではなく、単に ament に処理させる方が簡単です。またこのパッチでは、 buildtool_export_depend パッケージをビルド時に考慮されるセットに追加します。
  • catkin_pkg:このパッチでも、トポロジ順序を計算するときに考慮されるbuildtool_export_dependパッケージをセットに追加します。

ここでは ament build を呼び出すことになるので、前の例で行ったように、最低限の ament のインストールも必要になります。

mkdir -p ~/ament_ws/src
cd ~/ament_ws/src
git clone https://github.com/osrf/osrf_pycommon.git
git clone https://github.com/ament/ament_package.git
cd ament_package
git checkout catkin
cd ..
git clone https://github.com/ament/ament_tools.git
cd ament_tools
git checkout catkin
cd ../..
 ./src/ament_tools/scripts/ament.py build

それから、ament 対応版の catkin をどこかにインストールする必要があります。

# install catkin_pkg
git clone https://github.com/ros-infrastructure/catkin_pkg.git
cd catkin_pkg
git checkout ament
python3 setup.py install --prefix $HOME/catkin --single-version-externally-managed --record foo --install-layout deb
# install catkin
git clone https://github.com/ros/catkin.git
cd catkin
git checkout ament
mkdir build
cd build
PYTHONPATH=$HOME/catkin/lib/python3/dist-packages/ cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/catkin -DPYTHON_EXECUTABLE=/usr/bin/python3
make install

それでは ROS 2 パッケージをビルドしてください。

. $HOME/catkin/setup.bash
. $HOME/ament_ws/install/setup.bash
cd ~/ros2_ws
touch src/eProsima/AMENT_IGNORE
PYTHONPATH=$PYTHONPATH:/home/gerkey/ros2_ws_catkin/install_isolated/lib/python3.5/site-packages
catkin_make_isolated --install

ジャジャ~ン:ROS 2 はお馴染みのツールでビルドできました。

  • 警告package.xml ファイルがないため、ワークスペース内の eProsima パッケージは無視されます。つまり、catkin はそれらを見ることができません。ament はそのようなパッケージを扱うための経験則を持っています。
    • オプション:この経験則を catkin に戻します。ワークスペースの外側で package.xml を含まないパッケージのインストールに切り替えます。または、単にそれらの各パッケージに package.xml を追加します(たとえば、fork して編集する等)。

ROS と ROS 2 のすべてを一つのワークスペースにまとめて構築する(TODO)

このステップでは、少なくとも以下を含むいくつかのことを整理する必要があります。

  • パッケージ名の競合:現在、ROS メッセージパッケージのROS 2 バージョンがあります。geometry2 にも同様のものが含まれてしまっています。この機能を両方のシステムをサポートできる 1 つのパッケージにマージするか、新しいバージョンでは異なる名前とする必要があります。
  • メッセージ生成:ROS と ROS 2 には異なるメッセージ生成手順があり、その出力は競合する場合としない場合があります。単一のメッセージパッケージから正しい実体をすべて生成できるようにするには、何らかのうまい方法を実行する必要があります(または、上記のように、新しいメッセージパッケージには別の名前が必要です)。

bloom を使って ament パッケージをリリースする(TODO)¶

ament CMake API を使用するパッケージを bloom がリリースできるようになり、その結果リリースされるものはファーム上に構築できるようになるはずです。このユースケースを有効にするために必要に応じて bloomros_buildfarm に変更を加えることができます。

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【ROS 2】ament_cmake ユーザーズマニュアル(公式文書和訳)

ROS 2 公式文書(英語) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/09 現在のものです。

ament_cmake は ROS 2 の CMake ベースのパッケージビルドシステムです(全部とは言わないまでも、ほとんどの C/C++ プロジェクトで使用されています)。これは CMake を拡張したもので、パッケージ作成時の便利機能が追加されています。 CMake の基本を知っておくと理解が深まります。公式のチュートリアルも参考になります。

基本

基本的な CMake のアウトラインは、コマンドラインでros2 pkg create <パッケージ名>で作成されます。その後、基本的なビルド情報はpackage.xmlCMakeLists.txtの2つのファイルに集約されます。 colcon がパッケージの正しいビルドの順番を検出したり、必要な依存関係を CI にインストールしたり、さらにリリースに関する情報をbloomで提供できるようにするには、package.xmlにすべての依存関係とメタデータを含めておく必要があります。 CMakeLists.txtには、実行可能ファイルとライブラリをビルドおよびパッケージ化するためのコマンドが含まれています。このページでは主としてそのコマンドについて説明します。

ament チュートリアルも参照してください。←リンク切れ

標準プロジェクト概要

ament パッケージのCMakeLists.txtの基本的な概要は以下の通りです。

cmake_minimum_required(VERSION 3.5)
project(my_project)

ament_package()

projectの引数はパッケージ名になり、package.xmlのパッケージ名と同じでなければなりません。

プロジェクトの設定はament_package()によって行われます。この呼び出しはパッケージごとに1回だけ行う必要があります。 ament_package()は、package.xmlをインストールし、そのパッケージをamentインデックスに登録し、そして CMake のための config(そしておそらくtarget)ファイルをインストールします。これにより、find_package で他のパッケージから検出できるようになります。 ament_package()CMakeLists.txt内の全ての情報を集約して処理をする関数なので、"CMakeLists.txt"の最終行に記述する必要があります。ファイルとディレクトリをコピーするinstall関数によってament_package()を再現することもできますが、ament_package()を最後の呼び出すほう楽です。

ament_packageには追加の引数を与えることができます。

  • CONFIG_EXTRAS:パッケージのクライアントに利用できるようにする追加の CMake ファイル(configure_file() によって展開された.cmakeまたは.cmake.inテンプレート)のリスト。これらの引数を使用する場合の例については、リソースの追加を参照してください。テンプレートファイルの使用方法の詳細については、Cmake の公式文書を参照してください。

  • CONFIG_EXTRAS_POSTCONFIG_EXTRASと同じですが、ファイルが追加される順序が異なります。 CONFIG_EXTRASから生成されたファイルがament_export_*用に生成されたファイルの「前」に含まれるのに対して、CONFIG_EXTRAS_POSTから生成されたファイルはその「後」に含まれます。

ament_packageに追加する代わりに、変数${PROJECT_NAME}_CONFIG_EXTRASおよび${PROJECT_NAME}_CONFIG_EXTRAS_POSTに同じ効果を追加することもできます。唯一の違いは、ファイルが追加される順序が次の通りであることです。

  • CONFIG_EXTRASによって追加されたファイル
  • ${PROJECT_NAME} _CONFIG_EXTRASにアペンドして追加されたファイル
  • ${PROJECT_NAME} _CONFIG_EXTRAS_POSTにアペンドして追加されたファイル
  • CONFIG_EXTRAS_POSTによって追加されたファイル

ファイルとヘッダの追加

ビルドする主なターゲットは2つあります。それぞれadd_libraryadd_executableによってビルドされる、ライブラリと実行可能ファイルです。

ヘッダーファイルの分離と C/C++ での実装では、add_library/add_executableの引数として、必ずしも両ファイルを追加する必要はありません。

以下のベストプラクティスが提案されています。

  • ライブラリをビルドする場合は、クライアントに解放する全ヘッダーをパッケージ名に相当する名前のフォルダ配下のincludeサブディレクトリに格納しておきます。一方、エクスポートしてはいけないファイル(.c / .cppとヘッダファイル)は srcフォルダ内にあります。
  • add_libraryまたはadd_executableの呼び出しでは、cpp ファイルのみを明示的に参照する。
  • 下記のコマンドでヘッダを検出させる:
target_include_directories(my_target
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>)

これにより、ビルド時に${CMAKE_CURRENT_SOURCE_DIR}/includeフォルダ内の全てのファイルがパブリックインターフェースに追加され、インストール時に include フォルダ内の全てのファイル(${CMAKE_INSTALL_DIR}からの相対パス)が追加されます。

原則として、両フォルダともincludeフォルダだった場合や、${CMAKE_CURRENT_SOURCE_DIR}および${CMAKE_INSTALL_DIR}直下のフォルダの場合には、ジェネレータ式を使用する必要はありません。ただし、そのような場合でもジェネレータ式を記述しておくのが一般的です。

リソースの追加

パッケージを新しい依存関係に対してリンクするには2つの方法があります。

最初の、そして推奨される方法は ament マクロ であるament_target_dependenciesを使うことです。例として、my_targetを線形代数ライブラリ Eigen3 に対してリンクさせたいとします。

find_package(Eigen3 REQUIRED)
ament_target_dependencies(my_target Eigen3)

これにより、必要なヘッダとライブラリおよびそれらの依存関係を、プロジェクトが検出できるようになります。また、オーバーレイワークスペースを使用するときに、すべての依存関係のインクルードディレクトリが正しく順序付けされていることも保証されます。

2番目の方法はtarget_link_librariesを使うことです。

現代の CMake で推奨される方法は、ターゲットのみを使用し、それらをエクスポートしてリンクすることです。 CMake ターゲットは C++ と同様に名前空間があります。たとえば、Eigen3の場合は、ターゲットとしてEigen3 :: Eigenをを指定します。

少なくともCrystal Clemmysより前までは、ターゲット名がament_target_dependenciesマクロでサポートされていません。場合によっては、target_link_libaries CMake 関数を使う必要があります。 Eigen3 の例では、呼び出しは次のようになります。

find_package(Eigen3 REQUIRED)
target_link_libraries(my_target Eigen3::Eigen)

これには必要なヘッダ、ライブラリ、およびそれらの依存関係も含まれますが、ament_target_dependenciesとは対照的に、オーバーレイワークスペースを使用するときに依存関係を正しく順序付けできない可能性があります。

注意

明示的に必要とされないライブラリをfind_packageすることは不要ですが、別の依存関係(依存A)を持つ依存関係(依存B)を記述する場合には、依存Aを明示的に記述しなければなりません。 その場合、該当するパッケージに対してバグを報告してください。

ライブラリのビルド

再利用可能なライブラリをビルドするとき、それを利用しやすくするために下流のパッケージ用にいくつかの情報をエクスポートする必要があります。

ament_export_interfaces(export_my_library HAS_LIBRARY_TARGET)
ament_export_dependencies(some_dependency)

install(
  DIRECTORY include/
  DESTINATION include
)

install(my_library
  TARGETS my_library
  EXPORT export_my_library
  LIBRARY DESTINATION lib
  ARCHIVE DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES DESTINATION include
)

ここでは、includeフォルダーにエクスポートする必要があるヘッダーが含まれているとします。すべてのヘッダを別々のフォルダに入れる必要はないことに注意してください。クライアントが含めるべきヘッダだけを入れてください。

上記のスニペットで起こっていることは以下の通り:

  • ament_export_interfacesマクロは CMake のターゲットをエクスポートします。これは上記ライブラリのクライアントがtarget_link_libraries(client my_library::my_library)構文を使えるようにするために必要です。

  • ament_export_interfacesは、install コマンドにおけるEXPORTという名前の任意のターゲットリストと、環境変数に潜在的な依存ライブラリを登録する追加オプションHAS_LIBRARY_TARGETを取ることができます。

  • ament_export_dependenciesは依存関係を下流のパッケージにエクスポートします。これは、ライブラリのユーザがそれらの依存関係に対してもfind_packageを呼び出さなくてもいいようにするためです。

  • 最初のinstallコマンドは、クライアントに公開するヘッダファイルをインストールするためのものです。

  • 最後の大きなinstallコマンドはライブラリをインストールします。アーカイブとライブラリファイルはlibフォルダにエクスポートされ、ランタイムバイナリはbinフォルダにインストールされ、インストールされたヘッダへのパスもシステムに登録されます。

    • 注意:Windows の dll は(ライブラリではなく)ランタイムとして扱われ、RUNTIME DESTINATIONフォルダにインストールされます。したがって、UNIX ベースのシステムでライブラリを開発する場合でも、RUNTIMEインストールを除外しないことをお勧めします。
  • include directoryに関しては、install コマンドは CMake に情報を追加するだけで、実際には includes フォルダをインストールしません。これは上述のように、install(DIRECTORY <dir> DESTINATION <dest>)によってヘッダをコピーすることによって行われます。

  • install 呼び出しのEXPORT表記にはさらに注意が必要です。これは、my_library ターゲット用の CMake ファイルをインストールします。これはament_export_interfacesの引数とまったく同じ名前で、ライブラリのように名前を付けることもできます。しかし、これよって出力されたライブラリは、 ament_target_dependenciesでインクルードできなくなってしまいます。それとは完全に切り離せるよう、専用のexport_<target>のようなものをエクスポート先に追加することをお勧めします。

  • すべてのインストールパスは、colcon/amentによってすでに正しく設定されているCMAKE_INSTALL_PREFIXからの相対パスです。

使用できる2つの追加機能がありますが、ターゲットベースのインストールには不要です。

ament_export_include_directories(include)
ament_export_libraries(my_library)

最初のマクロは、エクスポートされたインクルードディレクトリをマークします(これは、ターゲットinstallINCLUDES DESTINATIONによって実現されます)。 2番目のマクロは、インストールされているライブラリの場所を示します(これはament_export_interfaceへの呼び出しのHAS_LIBRARY_TARGET引数によって行われます)。

いくつかのマクロは非ターゲットエクスポートに対して異なるタイプの引数を取ることができますが、最近のMakeのための推奨される方法はターゲットを使うことであるので、ここでは カバー しません。これらのオプションのドキュメントはソースコード内に記載してあります。

コンパイラとリンカのオプション

ROS 2 は、少なくともCrystal Clemmysまでは、C++14 および C99 規格に準拠しているコンパイラを対象としています。より新しいバージョンは将来的には見据えていて、こちらで言及されています。したがって、対応する規格に応じた CMake フラグを設定するのが慣例です。

if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

コードをきれいに保つために、コンパイラは不正が疑われるコードに対してワーニングを出すべきであり、修正されるべきです。

少なくとも次の警告レベルをカバーすることをお勧めします。

  • Visual Studioの場合:デフォルトのW1警告は保持されます。
  • GCC と Clang の場合:-Wall -Wextra -Wpedantic が必要で、-Wshadow -Werrorが推奨されます(後者はワーニングをエラーと解釈します)。

現代の CMake はターゲットベースでコンパイラのフラグを追加することをお勧めしますが(例:call)、

target_compile_options(my_target PRIVATE -Wall)

現時点では、ディレクトリレベルの関数add_compile_options(-Wall)を使用して、すべての実行可能ファイルおよびテスト用のターゲットベースのコンパイルオプションで行数が増えてしまい、コードが乱れないようにすることをお勧めします。

Windows におけるライブラリのビルド

Linux、Mac、Windows はすべて公式にサポートされているプラ​​ットフォームなので、効果を最大限得るためには、どのパッケージも Windows 上でビルドできるようにする必要があります。 Windows ライブラリフォーマットはシンボルの可視性を強化します。クライアントから使用されるべきすべてのシンボルはライブラリによって明示的にエクスポートされる必要があります(そしてデータシンボルは暗黙的にインポートされる必要があります)。

Clang と GCC ビルドとの互換性を保つために、GCC ウィキ内に記載されているロジックを使用することをお勧めします。 my_libraryというパッケージに使用するには

  • リンク内のロジックをvisibility_control.hppというヘッダーファイルにコピーします。
  • DLLMY_LIBRARYに置き換えます(例については、rviz_rendering の可視性制御を参照してください)。
  • エクスポートする必要があるすべてのシンボル(つまり、クラスまたは関数)に対してマクロ「MY_LIBRARY_PUBLIC」を使用します。
  • プロジェクトCMakeLists.txtで、以下のコマンドを使用します。
target_compile_definitions(my_library PRIVATE "MY_LIBRARY_BUILDING_LIBRARY")

テストと構文チェック

colcon を使ってライブラリをビルドをすることととテストを分離するために、構文チェックとテストへのすべての呼び出しを条件付きでラップします。

if(BUILD_TESTING)
  find_package(ament_gtest)
  ament_add_gtest(<tests>)
endif()

構文チェック

ament によって提供されるすべての構文チェッカは別々に追加することができますが、それらを組み合わせたコマンドを使用することをお勧めします。

find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()

これはpackage.xmlで定義されているように構文チェッカを実行します。 ament_lint_commonパッケージで定義されている一連の構文チェッカを使用することをお勧めします。これにより、以下のすべての構文チェッカ実行されます。

  • 著作権文とライセンスヘッダの存在とその正当性を確認する著作権構文チェッカ
  • cppcheck、論理テストも見つけることができるC++ 構文チェッカ
  • cpplint、C++ スタイルの構文チェッカ(例えばコメントスタイル)
  • C++ スタイル構文チェッカ
  • cmake チェッカ
  • XML uncrustify
  • flake8、python ファイル用のスタイルチェッカ
  • pep257、python docstrings のスタイルチェッカ

ament_uncrustifyには、コマンドラインツールが付属しています。これを呼び出すと、スタイルガイドに従ってコードを自動的に再フォーマットできます。

ament_uncrustify --reformat <path_to_source_folders>

テスト

Ament には GTests の設定を簡単にするための CMake マクロが含まれています。下記のように GTest を追加します。

find_package(ament_gtest)
ament_add_gtest(some_test <test_sources>)

これは他のライブラリ(プロジェクトライブラリなど)に対してリンクできる通常のターゲットです。マクロには追加のパラメータがあります。

  • APPEND_ENV:環境変数を追加します。たとえば、次のように呼び出して ament プレフィックスパスに追加できます。
find_package(ament_gtest REQUIRED)
ament_add_gtest(some_test <test_sources>
  APPEND_ENV PATH=some/addtional/path/for/testing/resources)
  • APPEND_LIBRARY_DIRS:実行時にリンカが検出できるようにライブラリを追加します。Windows ではPATH、Linux ではLD_LIBRARY_PATHのような環境変数を設定することでも実現できますが、この場合は呼び出しプラットフォームごとに異なる設定をしなければなりません。
  • ENV:環境変数を設定します(APPEND_ENVと同じ構文)。
  • TIMEOUT:テストタイムアウトを秒単位で設定します。 GTestsのデフォルトは60秒です。
  • SKIP_TEST:このテストをスキップします(コンソール出力に "passed" と表示されます)。
  • SKIP_LINKING_MAIN_LIBRARIES:GTestにリンクしません。
  • WORKING_DIRECTORY:テスト用の作業ディレクトリを設定します。

それ以外のデフォルトの作業ディレクトリはCMAKE_SOURCE_DIRで、これは最上位のCMakeLists.txtのディレクトリとなります。

同様に、GMock を含む GTest を設定するための CMakeマクロがあります。

find_package(ament_gmock REQUIRED)
ament_add_gmock(some_test <test_sources>)

ament_add_gtestと同様に追加パラメータがあります。

ament の拡張

追加のマクロ/関数を ament_cmake に登録し、それを拡張することができます。

ament への関数/マクロの追加

ament を拡張することはしばしば他のパッケージが使えるようにするための機能を追加したいという意図があると思われます。マクロをクライアントパッケージに提供する最善の方法は、それを ament に登録することです。

これは、${PROJECT_NAME}_CONFIG_EXTRAS変数を追加することによって実行できます。これは、次の関数を呼び出した後、ament_package()を呼ぶことで実現できます。

list(APPEND ${PROJECT_NAME}_CONFIG_EXTRAS
  path/to/file.cmake"
  other/pathto/file.cmake"
)

あるいは、ファイルをament_package()で直接追加することもできます。

ament_package(CONFIG_EXTRAS
  path/to/file.cmake
  other/pathto/file.cmake
)

拡張ポイントへの追加

他のパッケージで使用できる機能を追加する単純な設定ファイル以外にも、ament 自体への拡張も可能です。その拡張機能は拡張ポイントを定義する関数で実行されるスクリプトです。 ament 拡張の最も一般的なユースケースは、おそらく rosidl メッセージジェネレータを登録することです。ジェネレータを書くとき、メッセージ/サービス定義パッケージのためのコードを修正することなしにジェネレータですべてのメッセージとサービスを通常生成するのが望ましいです。これは、ジェネレータをrosidl_generate_interfacesの拡張として登録することで可能です。

例として、下記があります。

ament_register_extension(
  "rosidl_generate_interfaces"
  "rosidl_generator_cpp"
  "rosidl_generator_cpp_generate_interfaces.cmake"

これは、パッケージrosidl_generator_cppのマクロrosidl_generator_cpp_generate_interfaces.cmakeを、拡張ポイントrosidl_generate_interfacesに登録するものです。拡張ポイントが実行されると、ここでスクリプトrosidl_generator_cpp_generate_interfaces.cmakeの実行がトリガーされます。もっと正確に言うと、これは関数rosidl_generate_interfacesが実行されるたびにジェネレータを呼び出します。

ジェネレータ用のrosidl_generate_interfaces以外の最も重要な拡張ポイントはament_packageです。これは単にament_package()呼び出しでスクリプトを実行するだけです。この拡張ポイントは、リソースを登録するときに役立ちます(下記参照)。

ament_register_extensionは、必ず3つの引数をとる関数です。

  • extension_point:拡張ポイントの名前(ほとんどの場合、これはament_packageまたはrosidl_generate_interfacesのいずれかになります)
  • package_name:CMake ファイルを含むパッケージの名前(すなわち、ファイルが書き込まれているプロジェクトのプロジェクト名)
  • cmake_filename:拡張ポイントが実行されたときに実行されるcmakeファイル

注意:ament_packagerosidl_generate_interfacesと同じようにカスタム拡張ポイントを定義することは可能ですが、これはほとんど必要ないでしょう。

拡張ポイントの追加

ごくまれに、ament への新しい拡張ポイントを定義したくなることもあるでしょう。

対応するマクロが呼び出されたときにすべての拡張が実行されるように、拡張ポイントをマクロ内に登録できます。以下のように行います。

  • 登録したい拡張の名前(例えばmy_extension_point)を定義して、ファイルとして保存してください。これは拡張ポイントを使うときにament_register_extensionマクロに渡される名前です。
  • 拡張機能を実行するマクロ/関数では、次のように呼び出します。
ament_execute_extensions(my_extension_point)

ament 拡張は、拡張ポイントの名前を含む変数を定義し、それに実行するマクロを入れることによって機能します。 ament_execute_extensionsを呼び出すと、変数に定義されたスクリプトが次々に実行されます。

リソースを追加する

特にプラグインやプラグインを利用するパッケージを開発するときは、ある ROS パッケージから別の ROS パッケージにリソースを追加することが不可欠です(例:プラグイン)。例としては、pluginlib を使ったツール用のプラグインがあります。

これは、ament インデックス(「リソースインデックス」とも呼ばれる)を使用して実現できます。

ament インデックスの説明

設計の詳細については、こちらを参照してください。

原則として、ament インデックスはパッケージの install/share 内のフォルダに含まれています。さまざまな種類のリソースにちなんだ名称のサブフォルダが含まれています。サブフォルダ内では、上記リソースを提供する各パッケージは「マーカファイル」という名で参照されます。ファイルには、リソースを取得するために必要なあらゆるコンテンツが含まれます。(リソースのインストールディレクトリへの相対パスが例。それは単に空の場合もありますが。)

例として、RViz 用の表示プラグインを提供することを検討してください。pluginlib によって読み込まれるmy_rviz_displaysという名前のプロジェクトで RViz プラグインを提供するときは、pluginlib によってインストールおよび使用されるplugin_description.xmlファイルを提供します。プラグインこれを実現するために、plugin_description.xml は次のようにして resource_index にリソースとして登録されます。

pluginlib_export_plugin_description_file(rviz_common plugins_description.xml)

colcon buildを実行すると、my_rviz_displaysファイルを resource_index のサブフォルダrviz_common__pluginlib__pluginにインストールします。 rviz_common 内の Pluginlib モジュールは、プラグインをエクスポートするパッケージについて、rviz_common__pluginlib__pluginという名称の全フォルダから情報を収集することを認識します。 pluginlib モジュール用のマーカーファイルには、plugins_description.xmlファイルへのインストールフォルダへの相対パス(およびマーカファイル名としてのライブラリの名前)が含まれています。この情報を使って、pluginlib はライブラリをロードし、どのプラグインをplugin_description.xmlファイルからロードするかを知ることができます。

2つ目の例として、RViz プラグインがあなた自身のカスタムメッシュを使うようにする可能性を考えてください。メッシュは起動時にロードされるため、プラグインの所有者はそれを処理する必要はありませんが、これは RViz がメッシュについて知っている必要があることを意味します。これを達成するための関数を RViz は備えています。

register_rviz_ogre_media_exports(DIRECTORIES <my_dirs>)

これは、ディレクトリを agre_media リソースとして ament インデックスに登録します。つまり、関数を呼び出すプロジェクト名のファイルをrviz_ogre_media_exportsというサブフォルダーにインストールします。このファイルには、マクロにリストされているディレクトリへのインストールフォルダへの相対パスが含まれています。RViz の起動時、rviz_ogre_media_exportsという名前のすべてのフォルダを検索し、指定された全フォルダにリソースをロードできるようになりました。これらの検索はament_index_cpp(または Python パッケージの場合はament_index_py)を使って行われます。

以下のセクションでは、カスタマイズしたリソースを ament index に追加し、そうするためのベストプラクティスを提供する方法を探ります。

ament インデックスへの問い合わせ

必要ならば、CMake を介して ament インデックスに対してリソースの問い合わせが可能です。これについては、3 つの機能があります。

  • ament_index_has_resource:以下のパラメータで存在する場合、リソースへのプレフィックスパスを取得します。
  • var:出力パラメータ:リソースが存在しない場合はこの変数にFALSEを、存在する場合はリソースへのプレフィックスパスを設定します。
    • resource_type:リソースの種類(例:rviz_common__pluginlib__plugin
  • resource_name:通常resource_type 型のリソースを追加したパッケージの名前に相当するリソースの名前(例:rviz_default_plugins

  • ament_index_get_resource:特定のリソースの内容、すなわち ament インデックス内のマーカファイルの内容を取得する。

  • var:出力パラメータ:リソースマーカファイルが存在する場合はその内容が代入されます。
    • resource_type:リソースの種類(例:rviz_common__pluginlib__plugin
  • resource_name:通常resource_type型のリソースを追加したパッケージの名前に相当するリソースの名前(例:rviz_default_plugins
  • PREFIX_PATH:検索するプレフィックスパス(通常、デフォルトのament_index_get_prefix_path()で十分です)。

リソースが存在しない場合、ament_index_get_resourceはエラーをスローするので、ament_index_has_resourceを使用して確認する必要があるかもしれません。

  • ament_index_get_resources:インデックスから特定の種類のリソースを登録したすべてのパッケージを取得します。
  • var:出力パラメータ:resource_typeのリソースを登録した全てのパッケージ名のリストが代入されます。
    • resource_type:リソースの種類(例:rviz_common__pluginlib__plugin
  • PREFIX_PATH:検索するプレフィックスパス(通常、デフォルトの`ament_index_get_prefix_path() で十分です)。

ament インデックスへの追加

リソースを定義するには、2 ビットの情報が必要です。

  • ユニークなリソース名
  • マーカファイルのレイアウト。これは何でもかまいませんし、空にすることもできます(例えば ROS 2 パッケージをマークする“package”リソースに当てはまります)。

RVizメッシュリソースの場合、対応する選択肢は次のとおりです。

  • リソースの名前として rviz_ogre_media_exports
  • リソースを含むすべてのフォルダへの相対パスをインストールします。これにより、パッケージ内の対応するリソースを使用するためのロジックを記述できるようになります。

パッケージのリソース登録を容易にするには、さらに、pluginlib関数やrviz_ogre_media_exportsなどのマクロや関数を用意する必要があります。

リソースを登録するには、ament関数ament_index_register_resourceを使用します。これによりマーカファイルが作成され、resource_indexにインストールされます。例として、rviz_ogre_media_exportsの対応する呼び出しは以下のとおりです。

ament_index_register_resource(rviz_ogre_media_exports CONTENT ${OGRE_MEDIA_RESOURCE_FILE})

これにより、${PROJECT_NAME}のような名前のファイルが、変数${OGRE_MEDIA_RESOURCE_FILE}で指定された内容で resource_index の中にあるrviz_ogre_media_exportsフォルダにインストールされます。マクロには色々な便利パラメータが用意されています。

  • 最初の(名前なし)パラメータはリソースの名前です。これは、resource_index 内のフォルダの名前になります。
  • CONTENT:文字列としてのマーカファイルの内容。これは相対パスのリストなどです。CONTENTCONTENT_FILEと一緒に使用することはできません。
  • CONTENT_FILE:マーカファイル作成のために使用されるファイルへのパス。ファイルはプレーンファイルでも、configure_file()で展開されたテンプレートファイルでも、どちらでも構いません。CONTENT_FILECONTENTと一緒に使用することはできません。
  • PACKAGE_NAME:リソースをエクスポートしているパッケージ/ライブラリの名前。マーカファイルの名前になります。デフォルトは${PROJECT_NAME}です。
  • AMENT_INDEX_BINARY_DIR:生成された ament インデックスの基底パス。本当に必要な場合以外は、常にデフォルトの${CMAKE_BINARY_DIR}/ament_cmake_indexを使用してください。
  • SKIP_INSTALL:マーカファイルのインストールをスキップします。

パッケージごとに存在するマーカファイルは1つだけなので、cmake 関数/マクロが同じプロジェクトによって 2 回呼び出されると、通常は問題になります。ただし、大規模プロジェクトでは、リソースを登録する呼び出しを分割するのが最善の方法です。

したがって、register_rviz_ogre_media_exports.cmakeなどのリソースを登録するマクロにいくつかの変数のみを設定させることがベストプラクティスとなります。 ament_index_register_resourceの実際の呼び出しは、ament_packageへのament 拡張内で追加できます。プロジェクトごとにament_packageを呼び出すのは 1 回限りでなければならないため、リソースが登録される場所は常に1つのみとなります。 rviz_ogre_media_exportsの場合、これは次のような方針となります。

  • マクロregister_rviz_ogre_media_exportsはフォルダのリストを受け取り、それらをOGRE_MEDIA_RESOURCE_FILEという変数に追加します。

${OGRE_MEDIA_RESOURCE_FILE}が空でない場合、register_rviz_ogre_media_exports_hookという別のマクロがament_index_register_resourceを呼び出します。

register_rviz_ogre_media_exports_hook.cmake は、呼び出しによって3番目のファイル register_rviz_ogre_media_exports_hook-extras.cmakeament 拡張として登録されます。

ament_register_extension("ament_package" "rviz_rendering"
  "register_rviz_ogre_media_exports_hook.cmake")

register_rviz_ogre_media_exports.cmakeおよびregister_rviz_ogre_media_exports_hook-extra.cmakeは、ament_package()CONFIG_EXTRAとして登録されます。

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【ROS 2】クロスコンパイル(公式文書和訳)

ROS 2 公式文書(英語) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/11 現在のものです。

概要

Open Robotics はマルチプラットフォーム用にビルド済みの ROS 2 パッケージを提供していますが、それでも以下のようなさまざまな理由で多くの開発者がクロスコンパイルに頼っている状況です。

  • 開発マシンがターゲットシステムと一致しない。
  • 特定のコアアーキテクチャ向けにビルドを調整する(例:Raspberry Pi 3 用にビルドする場合は -mcpu = cortex-a53 -mfpu = neon-fp-armv8と設定)。
  • Open Robotics がリリースしたビルド済みイメージでサポートされているもの以外の異なるファイルシステムをターゲットにしている。

このドキュメントでは、ROS 2 ソフトウェアスタックをクロスコンパイルする方法の詳細と、Arm コアベースのシステムへのクロスコンパイルの例を紹介します。

  • 注意
    • ビルド中に無効にしなければならない(クロスコンパイルが無効な) ROS 2 パッケージがいくつかあります。 4. ビルドの章を参照してください。

どう動くのか?

単純なソフトウェアのクロスコンパイル(外部ライブラリに依存しないなど)は比較的単純で、ネイティブツールチェーンの代わりにクロスコンパイラツールチェーンを使用するだけで済みます。

このプロセスをより複雑にする要因はいくつかあります。

  • 構築中のソフトウェアは、ターゲットアーキテクチャをサポートしている必要があります。ターゲットアーキテクチャに応じて、アーキテクチャ固有のコードをビルド中に適切に分離して有効にする必要があります。例としてはアセンブリコードがあります。
  • すべての依存関係(ライブラリなど)は、ビルド済みパッケージとして存在するか、またはそれらを使用するターゲットソフトウェアがクロスコンパイルされる前にクロスコンパイルされる必要があります。
  • ビルドツール(colcon など)を使用して(スタンドアロンソフトウェアとは対照的に)ソフトウェアスタックをビルドする場合、開発者がスタック内の各ソフトウェアで使用されている基盤ビルドシステムでクロスコンパイルを有効にできる仕組みが必要です。

ROS 2 のクロスコンパイル

ROS 2 は数多くの依存関係を持つ豊富なソフトウェアスタックですが、主に 2 種類のパッケージを使用しています。

  • クロスコンパイルを必要としない Python ベースのソフトウェア。
  • クロスコンパイルを行うためのメカニズムを提供する CMake ベースのソフトウェア。

さらに、ROS 2 ソフトウェアスタックは、各パッケージ/ライブラリの個々のビルドに使用される CMake インスタンスにパラメータを転送する仕組みをもつ Colcon でビルドされています。

ROS 2 をネイティブでビルドする場合、(ROS 2 ディストリビューションの一部である)パッケージをコンパイルする前にすべての依存関係(例えば Python と他のライブラリ)をダウンロードする必要があります。クロスコンパイルするときも、同様です。最初にターゲットシステムのファイルシステムにすべての依存関係がインストールされている必要があります。

次の章では、ROS 2を クロスコンパイルするための cmake-toolchainsCMAKE_SYSROOT 機能の使用方法について詳しく説明します。

CMake toolchain-file

CMake toolchain-file は、クロスコンパイル用に CMake を設定するための変数を定義するファイルです。基本的なエントリは以下のとおりです。

  • CMAKE_SYSTEM_NAME:ターゲットプラットフォーム。例: linux
  • CMAKE_SYSTEM_PROCESSOR:ターゲットアーキテクチャ。例: aarch64arm
  • CMAKE_SYSROOT:ターゲットファイルシステムへのパス
  • CMAKE_C_COMPILER:Cクロスコンパイラ。例:aarch64-linux-gnu-gcc
  • CMAKE_CXX_COMPILER:C ++クロスコンパイラ。例:aarch64-linux-gnu-g++
  • CMAKE_FIND_ROOT_PATH:ファイルシステムを見つけるためにfind_*コマンドによって使用される代替パス

ROS 2 をクロスコンパイルするときは、次のオプションを設定する必要があります。

  • CMAKE_FIND_ROOT_PATHfind_*コマンドによって使用される代替パス。これを使用して ROS 2 /install フォルダへのパスを指定します。
  • CMAKE_FIND_ROOT_PATH_MODE_*:プログラム、パッケージ、ライブラリ、およびインクルードの検索方法。通常は、NEVER(host-fs を検索)、ONLY(sysroot を検索)、ONLY(sysroot を検索)、およびONLY(sysrootを検索)
  • PYTHON_SOABI:ROS 2 によって生成された Python ライブラリのインデックス名。例: cpython-36m-aarch64-linux-gnu
  • THREADS_PTHREAD_ARG "0" CACHE STRING "Result from TRY_RUN" FORCE:バイナリはホストシステムで実行できないため、TRY_RUNコマンドの結果を矯正的に 0(成功)にします。

toolchain-file は、-DCMAKE_TOOLCHAIN_FILE=path/to/fileパラメータでパスを指定して CMake に提供されます。これにより、ビルド中のソフトウェアのCMAKE_CROSSCOMPILING変数もtrueに設定されます。

CMAKE_SYSROOT は ROS 2 にとって特に重要です。パッケージには多くの依存関係(python、openssl、opencv、poco、eigen3 など)が必要だからです。すべての依存関係がインストールされているターゲットファイルシステムにCMAKE_SYSROOTを設定すると、CMake はクロスコンパイル中にそれらを見つけることができます。

  • 注意
    • あなたは CMake のページでより多くの情報を見つけることができます。

ROS 2 ソースコードをダウンロードするとき、一般的な toolchain-file はリポジトリ ros2/cross_compile/cmake-toolchains にあり、別々にダウンロードすることができます。それを使用することに関する更なる例は、Arm のクロスコンパイルの章に記してあります。

対象ファイルシステム

前述のように、ROS2はクロスコンパイルするために提供される必要がある異なるライブラリを必要とします。

ファイルシステムを取得する方法はいくつかあります。

  • ビルド済みイメージをダウンロードする。
  • ターゲットへの依存関係のインストールとファイルシステムのエクスポート(sshfs などを使用)をする。
  • qemu + docker(または chroot)を使ってホストマシン上にファイルシステムを生成する。

 

  • 注意
    • Docker + qemuの使い方については、次の Arm のクロスコンパイルの章を参照してください。

ビルドプロセス

ビルドプロセスはネイティブコンパイルと似ています。唯一の違いは、toolchain-file を指定するための Colcon への追加の引数です。

colcon build --merge-install \
    --cmake-force-configure \
    --cmake-args \
        -DCMAKE_TOOLCHAIN_FILE="<path_to_toolchain/toolchainfile.cmake>"

toolchain-fileは、クロスコンパイラターゲットファイルシステムの情報を CMake に提供します。 colcon は ROS 2 のすべてのパッケージで与えられたtoolchain-fileで CMake を呼び出します。

Arm のクロスコンパイルの例

ROS 2 のソースコードをダウンロードした後、git clone https://github.com/ros2/cross_compile.git src/ros2/cross_compileでクロスコンパイルアセットをワークスペースに追加できます。これらは、Arm コアのクロスコンパイル方法に関する実用的な例です。

以下のターゲットがサポートされています。

  • Ubuntu-arm64:ARMv8-A ベースのシステムで使用します。
  • Ubuntu-armhf:最新の ARMv7-A ベースのシステムと一緒に使用します。

主な手順は次のとおりです。

  • 開発ツールのインストール
  • ROS2ソースコードのダウンロード
  • ROS2クロスコンパイルアセットのダウンロード
  • sysrootを準備する
  • ROS2ソフトウェアスタックのクロスコンパイル

次のセクションでは、これらの各ステップについて詳しく説明します。簡易設定もありますので、それについては自動クロスコンパイルを見てください。

  • 注意
    • これらのステップは Ubuntu 18.04(Bionic)でテストされました。

1. 開発ツールをインストールする

このステップは、ネイティブに構築する場合と似ていますが、違いもあります。ライブラリとツールの一部は、代わりに sysroot に含まれるため、必要とされないライブラリとツールがあります。以下のパッケージが必要です

sudo apt update && sudo apt install -y \
    cmake \
    git \
    wget \
    python3-pip \
    qemu-user-static \
    g++-aarch64-linux-gnu \
    g++-arm-linux-gnueabihf \
    pkg-config-aarch64-linux-gnu

python3 -m pip install -U \
    vcstool \
    colcon-common-extensions
  • 注意
    • pip を使って vcstool と colcon-common-extensions をインストールできますので、特別な apt リポジトリを追加する必要はありません。

Docker はターゲット環境を構築するために使用されます。インストールについてはDcoker の公式文書に従ってください。

2. ROS 2 のソースコードをダウンロードする

次にワークスペースを作成して ROS 2 ソースコードをダウンロードします。

mkdir -p ~/cc_ws/ros2_ws/src
cd ~/cc_ws/ros2_ws
wget https://raw.githubusercontent.com/ros2/ros2/release-latest/ros2.repos
vcs-import src < ros2.repos
git clone https://github.com/ros2/cross_compile.git src/ros2/cross_compile
cd ..

3. sysroot を準備する

Docker と qemu を使ってすべての ROS 2 依存関係について解決済みの arm Ubuntu イメージをビルドします。qemu-staticバイナリをワークスペースにコピーします。これは、dockerを 使用して ros2 依存関係をターゲットファイルシステムにインストールするために使用されます。

mkdir qemu-user-static
cp /usr/bin/qemu-*-static qemu-user-static

ROS 2 の標準セットアッププロセスが、arm docker 内で実行されます。これは、arm マシンをエミュレートするqemu-staticのおかげで可能となっています。使用されているベースイメージは Docker Hub の Ubuntu Bionic です。

docker build -t arm_ros2:latest -f ros2_ws/src/ros2/cross_compile/sysroot/Dockerfile_ubuntu_arm .
docker run --name arm_sysroot arm_ros2:latest

結果のコンテナをTAR形式にエクスポートして抽出します。

docker container export -o sysroot_docker.tar arm_sysroot
mkdir sysroot_docker
tar -C sysroot_docker -xf sysroot_docker.tar lib usr opt etc
docker rm arm_sysroot

このコンテナは、作成されたファイルシステムを実行してデモコードを実行するための仮想ターゲットとして、後ほど使用できます。

4. ビルド

汎用toolchain-fileで使用される変数を設定します

export TARGET_ARCH=aarch64
export TARGET_TRIPLE=aarch64-linux-gnu
export CC=/usr/bin/$TARGET_TRIPLE-gcc
export CXX=/usr/bin/$TARGET_TRIPLE-g++
export CROSS_COMPILE=/usr/bin/$TARGET_TRIPLE-
export SYSROOT=~/cc_ws/sysroot_docker
export ROS2_INSTALL_PATH=~/cc_ws/ros2_ws/install
export PYTHON_SOABI=cpython-36m-$TARGET_TRIPLE

以下のパッケージは、クロスコンパイル中にエラーを引き起こします(調査中)。今のところ無効にする必要があります。

touch \
    ros2_ws/src/ros2/rviz/COLCON_IGNORE \
    ros2_ws/src/ros-visualization/COLCON_IGNORE

構築済みのPocoには、SYSROOT ではなくホストシステムでlibzlibpcreを検索するという既知の問題があります。現時点での回避策として、両方のライブラリをホストのファイルシステムにリンクしてください。

mkdir -p /usr/lib/$TARGET_TRIPLE
ln -s `pwd`/sysroot_docker/lib/$TARGET_TRIPLE/libz.so.1 /usr/lib/$TARGET_TRIPLE/libz.so
ln -s `pwd`/sysroot_docker/lib/$TARGET_TRIPLE/libpcre.so.3 /usr/lib/$TARGET_TRIPLE/libpcre.so

次に、toolconin-fileを指定して colcon でビルドを開始します。

cd ros2_ws

colcon build --merge-install \
    --cmake-force-configure \
    --cmake-args \
        -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \
        -DCMAKE_TOOLCHAIN_FILE="$(pwd)/src/ros2/cross_compile/cmake-toolchains/generic_linux.cmake" \
        -DSECURITY=ON

完了です!インストールディレクトリとビルドディレクトリには、クロスコンパイルされたファイル(クロスコンパイルアセット)が出力されます。

自動クロスコンパイル

上記のステップはすべて Dockerfile にも含まれており、自動化/CI に使用できます。

まず、dockerfile をダウンロードしてイメージを構築します。

wget https://raw.githubusercontent.com/ros2/cross_compile/master/Dockerfile_cc_for_arm
docker build -t ros2-crosscompiler:latest - < Dockerfile_cc_for_arm

それでは、イメージを実行します。(しばらく時間がかかります。。。)

docker run -it --name ros2_cc \
    -v /var/run/docker.sock:/var/run/docker.sock \
    ros2-crosscompiler:latest
  • メモ
    • -v /var/run/docker.sockにより、DockerをDocker内で使用することができます。

ビルドの結果はros2_wsディレクトリ内になります。このディレクトリは次のコマンドでエクスポートできます。

docker cp ros2_cc:/root/cc_ws/ros2_ws .

ビルド済みの ROS 2 に対するクロスコンパイル

事前に構築された ROS 2 に対して自作パッケージをクロスコンパイルすることも可能です。以下の変更を加えて、ステップは前述の Arm クロスコンパイルの章で示した例と似ています。

ROS 2 スタックをダウンロードする代わりに、自身のワークスペースにパッケージ(この場合は ros2 の例)とクロスコンパイルアセットを追加するだけです。

mkdir -p ~/cc_ws/ros2_ws/src
cd ~/cc_ws/ros2_ws/src
git clone https://github.com/ros2/examples.git
git clone https://github.com/ros2/cross_compile.git
cd ..

3. sysroot を準備するの説明に従って、ファイルシステムを生成してエクスポートします(ただし、提供されている Dockerfile_ubuntu_arm64_prebuilt を使用します)。これらの_prebuilt Dockerfile は、ソースからビルドするのではなく、バイナリパッケージを使用して ROS 2 をインストールします。

インストールディレクトリを指すように環境変数 ROS2_INSTALL_PATH を変更します。

export ROS2_INSTALL_PATH=~/cc_ws/sysroot_docker/opt/ros/crystal

ターゲットファイルシステムのsetup.bashスクリプトを読み込みます。

source $ROS2_INSTALL_PATH/setup.bash

次に、toolconin-fileを指定してColconでビルドを開始します。

colcon build \
    --merge-install \
    --cmake-force-configure \
    --cmake-args \
        -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \
        -DCMAKE_TOOLCHAIN_FILE="$(pwd)/src/cross_compile/cmake-toolchains/generic_linux.cmake"

ターゲットで実行

ターゲットにファイルシステムをコピーするか、以前に作成したdockerイメージを使用します。

docker run -it --rm -v `pwd`/ros2_ws:/ros2_ws arm_ros2:latest

環境を取り込みます。

source /ros2_ws/install/local_setup.bash

C++ または python の例をいくつか実行します。

ros2 run demo_nodes_cpp listener &
ros2 run demo_nodes_py talker

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com