catkin_makeしたらdoxygenでドキュメントが生成されるようにする。

こんにちは、今回はcatkin経由でdoxgenを叩いてドキュメント生成ができるようにしたのでその方法について備忘録を残しておこうと思います。
実際に作業を行ったリポジトリは[こちら](https://github.com/OUXT-Polaris/robotx_packages)です。
作業結果はこんな感じで出来上がります。
![Screenshot from 2018-06-09 15-51-05.png](https://qiita-image-store.s3.amazonaws.com/0/160346/cb73a505-aa56-2ccf-24aa-ab5fa5eacfc5.png)

![Screenshot from 2018-06-09 15-51-17.png](https://qiita-image-store.s3.amazonaws.com/0/160346/db7bed75-446d-39a3-7351-4bf403f18c92.png)


まずは今回使ったツールの紹介から
# 使用ツール
## catkin
現行のROSで主に使用されているビルドシステム、CMakeをベースにしているためCMakeのモジュールを作れば結構そのままいろいろできたりする。

## doxygen
[OpenCVのドキュメント](https://docs.opencv.org/3.4.1/)とかでよく見るページを自動生成してくれる賢い子
HTML形式いがいにもlatexの形式とかいろんな形式で出力してくれるが今回はとりあえずHTMLを出力した。

# 下準備

過去にdoxygenとCMakeを組み合わせてドキュメントを生成するTipsはあったのでそれをベースに改良してみた。
参考記事その１:https://qiita.com/tmichi/items/9105bc271ee7fd7277fb
参考記事その２:https://qiita.com/yoyomion/items/16d152421ef4f7bfc8ac

今回はROSノードごとにドキュメントをビルドしたかったのでその２の方法をベースにした。

今回のディレクトリ構成は以下のようになっています。
robotx_packages 
┣ Doxygen
┃  ┣ images ━ *.jpg,*.jpeg,*.png
┃  ┣ Doxyfile.in
┃  ┣ doxygen-script.cmake
┃  ┗ doxygen.cmake
┃
┗ ros_packages ━ CMakeLists.txt

Doxyfile.inは[参考記事その２](https://qiita.com/yoyomion/items/16d152421ef4f7bfc8ac)のものをそのまま使っています。
かなり長いのでコピペされたい方は[こちら](https://github.com/OUXT-Polaris/robotx_packages/blob/master/Doxygen/Doxyfile.in)からどうぞ。

doxygen-script.cmakeは[参考記事その２](https://qiita.com/yoyomion/items/16d152421ef4f7bfc8ac)と同じで以下のようになっています。

```cmake:doxygen-script.cmake
configure_file(
    "${DOXYGEN_TEMPLATE}"
    "${DOXY_DOCUMENTATION_OUTPUT_PATH}/${DOXY_PROJECT_NAME}/Doxyfile"
    @ONLY)
```


doxygen.cmakeはほぼ[参考記事その２](https://qiita.com/yoyomion/items/16d152421ef4f7bfc8ac)と同じですがDoxygen/images内部の画像を転送できるようにした、*.hな拡張子のファイルだけでなく*.hhを探せるようにしたので一部異なっています。

```cmake:doxygen.cmake
find_package(Doxygen)

#WITH_DOCUMENT オプションを追加、ONにするとドキュメントがビルドされるようになる。
option(WITH_DOCUMENT OFF)

#add_document関数を追加、targetにビルドターゲットを入れるとDoxygenによりドキュメントがビルドされる。
function(add_document target)
    if(${DOXYGEN_FOUND})
        #各種パスの設定
        set(doxydir ${PROJECT_SOURCE_DIR}/../Doxygen)
        set(outputdir ${PROJECT_BINARY_DIR}/DoxyDoc)
        set(image_source_dir ${PROJECT_SOURCE_DIR}/../Doxygen/images)
        set(image_dir ${PROJECT_BINARY_DIR}/DoxyDoc//${target}/html/images)
        message("build document for ${target}")

        #*.jpg,*.jpeg,*.pngの拡張子のファイルを探索しimage_dirに転送
        # すべてのドキュメントごとにimage_dirが存在するので
        #
        #/**
        # * @mainpage gazebo plugin for twist sensor simulation
        # * @image html images/vmrc.jpg
        # * @author Masaya Kataoka
        # * @date 2018-06-09
        # */
        #
        #　のような書き方をすると画像をドキュメントに入れられます。
        file(GLOB images_png ${image_source_dir}/*.png)
        file(GLOB images_jpg ${image_source_dir}/*.jpg)
        file(GLOB images_jpeg ${image_source_dir}/*.jpeg)
        message("images : ${images_png} ${images_jpg} ${images_jpeg}")
        foreach (img ${images_png})
            file(COPY ${img} DESTINATION ${image_dir})
            set(image_spaces "${image_spaces} ${img}")
            message("transport images : ${img}")
        endforeach ()
        foreach (img ${images_jpg})
            file(COPY ${img} DESTINATION ${image_dir})
            set(image_spaces "${image_spaces} ${img}")
            message("transport images : ${img}")
        endforeach ()
        foreach (img ${images_jpeg})
            file(COPY ${img} DESTINATION ${image_dir})
            set(image_spaces "${image_spaces} ${img}")
            message("transport images : ${img}")
        endforeach ()

        #ソースファイル一覧を取得
        get_property(sourcefiles
            TARGET ${target}
            PROPERTY SOURCES)
        foreach (source ${sourcefiles})
            set(source_spaces "${source_spaces} ${PROJECT_SOURCE_DIR}/${source}")
        endforeach ()
        get_property(includedirs
            TARGET ${target}
            PROPERTY INCLUDE_DIRECTORIES)

        #ヘッダファイル一覧を取得
        file(GLOB headers ${includedirs}/*.h)
        foreach (h ${headers})
            set(header_spaces "${header_spaces} ${h}")
        endforeach ()
        file(GLOB headers_hh ${includedirs}/*.hh)
        foreach (h ${headers_hh})
            set(header_spaces "${header_spaces} ${h}")
        endforeach ()
        foreach (dir ${includedirs})
            set(dir_spaces "${dir_spaces} ${dir}")
        endforeach ()
        get_property(definitions
            DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
            PROPERTY COMPILE_DEFINITIONS)
        foreach (def ${definitions})
            set(predef_spaces "${predef_spaces} ${def}")
        endforeach ()


        message("found source codes : ${source_spaces}")
        message("output directory : ${outputdir}")
        message("doxygen directory : ${doxydir}")
        message("include directory : ${includedirs}")

        #出力ディレクトリを作成
        file(MAKE_DIRECTORY ${outputdir})
        add_custom_command(
            OUTPUT  ${outputdir}/${target}/Doxyfile
            COMMAND ${CMAKE_COMMAND}
                    -D "DOXYGEN_TEMPLATE=${doxydir}/Doxyfile.in"
                    -D "DOXY_PROJECT_INPUT=${source_spaces} ${header_spaces}"
                    -D "DOXY_PROJECT_INCLUDE_DIR=${dir_spaces}"
                    -D "DOXY_PROJECT_PREDEFINED=${predef_spaces}"
                    -D "DOXY_PROJECT_STRIP_FROM_PATH=${PROJECT_SOURCE_DIR}"
                    -D "DOXY_DOCUMENTATION_OUTPUT_PATH=${outputdir}"
                    -D "DOXY_PROJECT_NAME=${target}"
                    -P "${doxydir}/doxygen-script.cmake"
            DEPENDS ${doxydir}/Doxyfile.in
                    ${outputdir}
            WORKING_DIRECTORY
                    ${outputdir}
            COMMENT "Generating Doxyfile for ${target}")
        
        #Doxyfileからドキュメントを生成
        add_custom_command(
            OUTPUT  ${outputdir}/${target}/index.html
            COMMAND ${DOXYGEN_EXECUTABLE}
            DEPENDS ${outputdir}/${target}/Doxyfile
            WORKING_DIRECTORY
                    ${outputdir}/${target}
            COMMENT "Creating HTML documentation for ${target}")

        #WITH_DOCUMENT オプションがONならばデフォルトでドキュメントをビルドする設定に
        if(WITH_DOCUMENT)
            add_custom_target("doxygen-${PROJECT_NAME}-${target}" ALL
                DEPENDS ${outputdir}/${target}/index.html)
        else()
            add_custom_target("doxygen-${PROJECT_NAME}-${target}"
            DEPENDS ${outputdir}/${target}/index.html)
        endif()
    else()
        message("doxygen not found")
    endif()
endfunction()
```
# CMakeLists.txtの設定
以下の例のようにadd_document関数にターゲットを引き渡す。

```cmake:CMakeLists.txt
# usv_twist_sensor_plugin
add_library(usv_twist_sensor_plugin
  src/usv_twist_sensor_plugin.cc
)
target_link_libraries(usv_twist_sensor_plugin
  ${catkin_LIBRARIES}
  ${GAZEBO_LIBRARIES}
  ${Eigen_LIBRARIES}
)
add_document(usv_twist_sensor_plugin)
```

#ビルド
```
catkin_make -DWITH_DOCUMENT=ON
```
以上のやり方でドキュメント作成の自動化が実現できました。