rclgoのhumble対応(中)

# はじめに

https://twitter.com/fudekunJP/status/1590214251427549185?s=20&t=ufsGxA49O4259W2cOnzjIA

すいません，私もこのネタ（includeパス問題）です．

Go言語(以下，golang)はクラウドネイティブな各モジュールと相性が良い．Webサーバ機能がPy○○onなどと違ってテスト用ではなく商用にも耐えられるモノが標準で採用されてる点が大きい．その他諸々からもREST APIファーストな用途には欠かすことが出来ない言語と言える．

そんなgolangをROS2でも利用したい！と考えるのは最早，必然である(？)

2022年現在，目についたrclgo実装は次の通り．

- [https://github.com/tiiuae/rclgo](https://github.com/tiiuae/rclgo)
- [https://github.com/juaruipav/rclgo](https://github.com/juaruipav/rclgo)
- [https://github.com/PiusNyakoojo/rclgo](https://github.com/PiusNyakoojo/rclgo)

いずれの実装もhumbleには未対応である点は共通している．ただし，更新が積極的に行われており比較的利用者が多いことが想定できる `tiiuae/rclgo` のhumble対応に挑戦してみようと思った次第である．

# tiiuae/rclgoについて

このrclgoは，ROS2コアライブラリ（ros-galactic-ros-coreで入るような，rclcppや各種Pub/Subのメッセージ定義，rmw等）に依存している．golangのスタンダード・ライブラリの一つである[cgo](https://pkg.go.dev/cmd/cgo)を利用することによって，C/C++言語で書かれたコード（rclcpp）を呼び出すgolangラッパーの役割を果たす．

# cgoについて

以下は，[https://pkg.go.dev/cmd/cgo](https://pkg.go.dev/cmd/cgo)　にあるベーシックな例である．

```go
package main

// #include <stdio.h>
// #include <stdlib.h>
//
// static void myprint(char* s) {
//   printf("%s\n", s);
// }
import "C"
import "unsafe"

func main() {
	cs := C.CString("Hello from stdio")
	C.myprint(cs)
	C.free(unsafe.Pointer(cs))
}
```

こんな感じで，golangソースコードにコメントでC実装を書き込み， `import C` を宣言する(プリアンブル)．Go ツールは，1 つ以上の Go ファイルが特別な `import C` を使用していることを確認すると，非 Go ファイルを探し，それらを Go パッケージの一部としてコンパイルする．

また，Cのコンパイラに伝えるオプションCFLAGS，CPPFLAGS，CXXFLAGS，FFLAGS，および LDFLAGS は，コメント内で疑似 #cgo ディレクティブを使用して定義できる．

```go
// #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo
```

その他，環境変数としてもC用のコンパイラオプションの設定が可能．

# tiiuae/rclgoでのcgo利用と直面する諸問題

表題のgolangパッケージでは2つのパッケージエントリがある．

1. gogen
   - メッセージ定義毎の相互変換用パッケージを自動生成するためのツール．（rclcppからrclgoへ）
1. rclgo
   - 今回目的のrcl

rclgoを使うためには，gogenであらかじめパッケージを生成しておく必要がある．これで生成されるメッセージ定義の一例として`std_msgs/msg/string`のcgo利用箇所を抜粋する．

https://github.com/tiiuae/rclgo/blob/main/internal/msgs/std_msgs/msg/String.gen.go#L22
```go
/*
#include <rosidl_runtime_c/message_type_support_struct.h>
#include <std_msgs/msg/string.h>
*/
import "C"
```

https://github.com/tiiuae/rclgo/blob/main/internal/msgs/std_msgs/msg/common.gen.go#L13-L23
```go
/*
#cgo LDFLAGS: "-L/opt/ros/galactic/lib" "-Wl,-rpath=/opt/ros/galactic/lib"
#cgo LDFLAGS: -lrcl -lrosidl_runtime_c -lrosidl_typesupport_c -lrcutils -lrmw_implementation
#cgo LDFLAGS: -lstd_msgs__rosidl_typesupport_c -lstd_msgs__rosidl_generator_c
#cgo LDFLAGS: -lbuiltin_interfaces__rosidl_typesupport_c -lbuiltin_interfaces__rosidl_generator_c
#cgo CFLAGS: "-I/opt/ros/galactic/include"
*/
import "C"
```

もう，お分かりですね．

そう，数々のROS2戦士達が一度は「🤔🤔🤔🤔🤔🤔」となったであろう，[includeパス変更問題](https://answers.ros.org/question/404262/rclcpp-file-not-found-error-for-creating-gui-user-interface-with-ros2-humble-using-qt-creator/)に直面するのである．

`std_msgs/msg/string`の場合は，メッセージ定義上位パッケージで`LDFLAGS`と`CFLAGS: -I/opt/ros/galactic/include`を使用して，依存関係を無事解決！！となっていたわけであるが，，，

## 私はどうしたか？
とりあえず，一刻でも早くhumbleで動くことを確認をしたかったので，環境変数を使った力技で押し切るためのスペシャルな（？？）ラッピングBashスクリプト`commander.bash` を作った．
（多分，パッケージジェネレーターのテンプレートエンジンをいじるのが最も根本的な解決なのであろうが，人のコードは読むのが辛い．．．．時間を優先した結果として許して欲しい．）

```bash
#!/bin/bash

BASE_DIR=/opt/ros/humble
CGO_CFLAGS="-L${BASE_DIR}/lib -Wl,-rpath=$BASE_DIR/lib -lrcl -lrmw -lrosidl_runtime_c -lrosidl_typesupport_c -lrcutils -lrcl_action -lrmw_implementation"
include_list=
for dir in $( ls -d $BASE_DIR/include/* ); do
    if [ -d $dir ]; then
        include_list="$include_list -I$dir"
    fi
done

CGO_CFLAGS="$CGO_CFLAGS $include_list"
echo $CGO_CFLAGS
# Exec
CGO_ENABLED=1 CC=gcc CGO_CFLAGS=$CGO_CFLAGS make $1

exit $?
```

使うときは，`Makefile`のオプションを指定すればよい．

```bash
./commander.bash build
./commander.bash test
```


## 他の問題
この他，humble移行に伴い，いくつかの変更があったことから，rclgoに影響のある範囲に対しての修正が必要である．

1. 各パスで`galactic`になっているの部分を`humble`に置換が必要
1. 構造体のサフィックスが`_t`から`_s`へ（[struct type name suffix changed from _t to _s](https://docs.ros.org/en/rolling/Releases/Release-Humble-Hawksbill.html#struct-type-name-suffix-changed-from-t-to-s)）
   - `sizeof_struct_rcl_service_t` から `sizeof_struct_rcl_service_s`
   - `struct_rmw_service_info_t` から `struct_rmw_service_info_s`
   - `struct_rcl_client_t` から `struct_rcl_client_s`
1. rcl_clock_initのコンストラクタに渡す型が変わった．
   - `uint32` から `rcl_clock_type_t`


# 動作確認結果

Pub/Sub共に動いた模様．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96992/e63e4be7-91e9-dd2c-689a-bca944aacd89.png)


# おわりに

この辺までやったところで，本業やらなんやらが忙しくなったのでテンプレートエンジンをイジるところまで着手できていない．

テンプレートエンジンをいじったら，本家にPRを送ってみようかなと思う（その前に，対応をしてこられそうですが．）