賢くROS2でパッケージづくりをしよう

# パッケージをどう作ってますか？

```sh
$ ros2 pkg create hoge
```

だけで済ませてませんか？

# 引数を設定しよう

ここでどんな引数が使えるか見てみましょう

```sh
$ ros2 pkg create --help
```

と打つと以下のように出てきますね

<details><summary>出力</summary>

```markdown
usage: ros2 pkg create [-h] [--package-format {2,3}]
                       [--description DESCRIPTION] [--license LICENSE]
                       [--destination-directory DESTINATION_DIRECTORY]
                       [--build-type {cmake,ament_cmake,ament_python}]
                       [--dependencies DEPENDENCIES [DEPENDENCIES ...]]
                       [--maintainer-email MAINTAINER_EMAIL]
                       [--maintainer-name MAINTAINER_NAME]
                       [--node-name NODE_NAME] [--library-name LIBRARY_NAME]
                       package_name

Create a new ROS2 package

positional arguments:
  package_name          The package name

optional arguments:
  -h, --help            show this help message and exit
  --package-format {2,3}, --package_format {2,3}
                        The package.xml format.
  --description DESCRIPTION
                        The description given in the package.xml
  --license LICENSE     The license attached to this package
  --destination-directory DESTINATION_DIRECTORY
                        Directory where to create the package directory
  --build-type {cmake,ament_cmake,ament_python}
                        The build type to process the package with
  --dependencies DEPENDENCIES [DEPENDENCIES ...]
                        list of dependencies
  --maintainer-email MAINTAINER_EMAIL
                        email address of the maintainer of this package
  --maintainer-name MAINTAINER_NAME
                        name of the maintainer of this package
  --node-name NODE_NAME
                        name of the empty executable
  --library-name LIBRARY_NAME
                        name of the empty library
```
</details>

よく使うものからピックアップしていきましょう

一応参考程度に引数なしで出力したものを書いておきます

おそらくデフォルトではament_cmakeが設定されているようです

<details><summary>引数なしver</summary>

```md:file-tree
├── CMakeLists.txt
├── include
│   └── hoge
├── package.xml
└── src
```

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(hoge)

# 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)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)

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()

```

```xml:package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>hoge</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="your-mail">your-name</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

```

</details>

## --build-type

これは使っている人は多いのではないでしょうか
これは文字どおりビルドの方式を変えてくれるものです
使用例としては以下のような感じです

パッケージ名は`hoge`とします

```sh
# cmake
$ ros2 pkg create hoge --build-type ament_cmake

# python 
$ ros2 pkg create hoge --build-type ament-python
```

以下に出力されるファイルを書きます
長いので畳んでいます

<details><summary>ament_cmake</summary>

引数なしのところで書いたものと同じですが書きます

```md: file-tree
├── CMakeLists.txt
├── include
│   └── hoge
├── package.xml
└── src

```

```cmake: CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(hoge)

# 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)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)

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()
```

```xml:package.xml
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>hoge</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="your-email">your-name</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
```

</details>

<details><summary>ament_python</summary>

```md:file-tree
├── hoge
│   └── __init__.py
├── package.xml
├── resource
│   └── hoge
├── setup.cfg
├── setup.py
└── test
    ├── test_copyright.py
    ├── test_flake8.py
    └── test_pep257.py
```

```cfg:setup.cfg
script-dir=$base/lib/hoge
[install]
install-scripts=$base/lib/hoge
```

```py:setup.py
from setuptools import setup

package_name = 'hoge'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='your-name',
    maintainer_email='your-email',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
        ],
    },
)
```

```xml:package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>hoge</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="your-mail">your-name</maintainer>
  <license>TODO: License declaration</license>

  <test_depend>ament_copyright</test_depend>
  <test_depend>ament_flake8</test_depend>
  <test_depend>ament_pep257</test_depend>
  <test_depend>python3-pytest</test_depend>

  <export>
    <build_type>ament_python</build_type>
  </export>
</package>
```

</details>

## `--dependencies`

>ここからは`ament_cmake`ベースでお話します

これは実に便利なやつなんです

例えば`--build-type`を`ament_cmake`にしたとして依存関係を記述したくなった時、
いつも依存関係をどのように記述していますか？
例えば`rclcpp`を使いたくなり、`cmake`に直接

```cmake
find_package(rclcpp REQUIRED)
```

とか書いてませんか？
わざわざ`cmake`をこれを書くために編集するなんてめんどくさいと思いませんか？

ここで`--dependencies`を使います

```sh
$ ros2 pkg create hoge --build-type ament_cmake --dependencies rclcpp
```

と書くだけでなんと`CMakeLists.txt`と`package.xml`に記述されるのです

実際に確認してみると

```cmake: CMakeLists.txt
# find dependencies
find_package(rclcpp REQUIRED)
```

```xml:package.xml
<depend>rclcpp</depend>
```

と記述されていますね

他にもよく使うものとして`std_msgs`, `sensor_msgs`, pythonとc++を両立したいときには`ament_cmake_python`, `rclpy`, 独自にメッセージ型を記述したいときは`rosidl_default_generators`などでしょうか

全部依存関係に書いてしまいましょう

```sh
$ ros2 pkg create hoge --build-type ament_cmake --dependencies rclcpp rclpy ament_cmake_python std_msgs sensor_msgs rosidl_default_generators
```

~~クッソなげぇですわ〜~~

でもこうすると

```cmake:CMakeLists.txt
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclpy REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(std_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)
```

```xml:package.xml
<depend>rclcpp</depend>
<depend>rclpy</depend>
<depend>ament_cmake_python</depend>
<depend>std_msgs</depend>
<depend>sensor_msgs</depend>
<depend>rosidl_default_generators</depend>
```

と勝手に記述してくれるので楽ですね

## `--node-name`

これを書くだけで`cmake`の記述がしなくて良くなります

どこまで書いてくれるのか確かめて見ましょう

次のコマンドを打ってプロジェクトを作成します

```sh
$ ros2 pkg create hoge --build-type ament_cmake --node-name my_node
```

ここで`CMakeLists.txt`はどうでしょうか

```cmake:CMakeLists.txt
add_executable(my_node src/my_node.cpp)
target_include_directories(my_node PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)

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

と記述されていますね

また、生成されたファイルを見てみると

```:file-tree
.
├── CMakeLists.txt
├── include
│   └── hoge
├── package.xml
└── src
    └── my_node.cpp
```

となっていて、`src/my_node.cpp`を見てみると、

```cpp
#include <cstdio>

int main(int argc, char ** argv)
{
  (void) argc;
  (void) argv;

  printf("hello world hoge package\n");
  return 0;
}
```

となっています
~~いやもうちょっとがんばってよ~~

あとは`ament_target_dependencies`を記述するくらいでしょうか

### + `--dependencies`

ではここでさっきやった`--dependencies`を記述しましょう
そうするとどうなるのでしょうか

```sh
$ ros2 pkg create huga --build-type ament_cmake --node-name my_node --dependencies rclcpp
```

ここで`CMakeLists.txt`を確認してみるとどうなっているかというと

```cmake:CMakeLists.txt
ament_target_dependencies(
  my_node
  "rclcpp"
)
```

という行が追加されています

つまり`ament_target_dependencies`も書かなくてもいいということです

これで書く文量がかなり減ったのではないでしょうか

### 2つ書いてみた

```sh
ros2 pkg create hoge --build-type ament_cmake --node-name my_node1 --node-name my_node2
```

としたらどうなるか気になりませんか？

実行して`CMakeLists.txt`を確認してみると

```cmake:CMakeLists.txt
add_executable(my_node2 src/my_node2.cpp)
target_include_directories(my_node2 PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)
```

としか実行ファイルの生成が指示されていません

つまりあとに書いたノードしか生成されないということみたいです

ちなみに

```:file-tree
.
├── CMakeLists.txt
├── include
│   └── hoge
├── package.xml
└── src
    └── my_node2.cpp
```

からも本当に後ろに書いたノードしか生成してくれないようです


## `--library-name`

ライブラリ作って便利にコーディングしたいという方のためにros2はライブラリの生成も対応してくれています

次のコマンドを打ってプロジェクトを作成しましょう

```sh
$ ros2 pkg create hoge --build-type ament_cmake --library-name my_lib
```

生成されるファイルは以下のようになっています

```:file-tree
.
├── CMakeLists.txt
├── include
│   └── hoge
│       ├── my_lib.hpp
│       └── visibility_control.h
├── package.xml
└── src
    └── my_lib.cpp
```

ここで`CMakeLists.txt`を確認すると

```cmake:CMakeLists.txt
find_package(ament_cmake_ros REQUIRED)

# 省略

add_library(my_lib src/my_lib.cpp)
target_include_directories(my_lib PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)
# 省略
target_compile_definitions(my_lib PRIVATE "HOGE_BUILDING_LIBRARY")

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

# 省略
  include
)
ament_export_libraries(
  my_lib
)
ament_export_targets(
  export_${PROJECT_NAME}
)
```

では`package.xml`はどうかというと

```xml:package.xml
  <buildtool_depend>ament_cmake_ros</buildtool_depend>
```

が追加されていますね

では生成されている`include/hoge/my_lib.hpp`と`src/my_lib.cpp`を確認していきましょう

```cpp:include/hoge/my_lib.hpp
#ifndef HOGE__MY_LIB_HPP_
#define HOGE__MY_LIB_HPP_

#include "hoge/visibility_control.h"

namespace hoge
{

class MyLib
{
public:
  MyLib();

  virtual ~MyLib();
};

}  // namespace hoge
```

```cpp:src/my_lib.cpp
#include "hoge/my_lib.hpp"

namespace hoge
{

MyLib::MyLib()
{
}

MyLib::~MyLib()
{
}

}  // namespace hoge
```

クラスの基本のキみたいな感じのものが生成されていますね
基本的にはこれに追加していって書いていく感じです

では一番(？)気になっている`include/hoge/visibility_control.h`を見てみましょう

長いのでたたんでます

<details><summary>include/hoge/visibility_control.h</summary>

```cpp
#ifndef HOGE__VISIBILITY_CONTROL_H_
#define HOGE__VISIBILITY_CONTROL_H_

// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
//     https://gcc.gnu.org/wiki/Visibility

#if defined _WIN32 || defined __CYGWIN__
  #ifdef __GNUC__
    #define HOGE_EXPORT __attribute__ ((dllexport))
    #define HOGE_IMPORT __attribute__ ((dllimport))
  #else
    #define HOGE_EXPORT __declspec(dllexport)
    #define HOGE_IMPORT __declspec(dllimport)
  #endif
  #ifdef HOGE_BUILDING_LIBRARY
    #define HOGE_PUBLIC HOGE_EXPORT
  #else
    #define HOGE_PUBLIC HOGE_IMPORT
  #endif
  #define HOGE_PUBLIC_TYPE HOGE_PUBLIC
  #define HOGE_LOCAL
#else
  #define HOGE_EXPORT __attribute__ ((visibility("default")))
  #define HOGE_IMPORT
  #if __GNUC__ >= 4
    #define HOGE_PUBLIC __attribute__ ((visibility("default")))
    #define HOGE_LOCAL  __attribute__ ((visibility("hidden")))
  #else
    #define HOGE_PUBLIC
    #define HOGE_LOCAL
  #endif
  #define HOGE_PUBLIC_TYPE
#endif

#endif  // HOGE__VISIBILITY_CONTROL_H_
```

</details>


ビルド環境で変わるやつですね
つまり、様々な環境に対応したROS2に対応するためののヘッダファイルだと思われます

もし、これでライブラリを作るときは触るファイルは`include/<project_name>/<lib_name>.hpp`と`src/<lib_name>`.cppだけでいいみたいですね

このライブラリに対しても`--dependencies`が使えるようです
なので、ここでも簡単に依存関係を記述できるみたいです

# 終わりに

これで簡単に書けるようになったのではないでしょうか
例えば今、`rclcpp`と`std_msgs`, `sensor_msgs`を使ったノードを生成してくれと言われたらコマンド一行ですぐ作れますよね？

当然

```sh
$ ros2 pkg create hoge --node-name my_node --dependencies rclcpp std_msgs sensor_msgs
```

ですよね

これにてすべて終了です

では良きROS2ライフをお送りください！

>不明点、間違っている点があればコメントなどで教えてください！