誰にも見えないブログ

雑なメモ。まとまってない文章等

cmake tutorial その7(最終回):EXPORT,find_package()など

最後まで終わった!よく頑張った!このチュートリアルのおかげでCMakeにしていた重大な勘違いに幾つも気付く事ができた。

俺がチュートリアル勉強しながら、テディーベアデバッグ的に適当に書きなぐったことなので、読んでもあまり参考にならないのでその点よろしく。

終盤のステップは説明を端折り過ぎてて何を書いてるのかよくわからない箇所が多かった。

ググッて得られる解説はabcのaやbが抜けていきなりcから解説されたものが多く、初心者にはきつかったからです。特にcmake.orgのチュートリアルはまさにその典型で、チュートリアルのStep 1からして長過ぎて、本当に何が必要なのかわかりにくい・・・orz

https://qiita.com/shohirose/items/45fb49c6b429e8b204ac

まじでこれ。

Export

  • Adding Export Configuration (Step 11)の内容

  • 自分で作ったCMakeプロジェクトを外部のプロジェクトが利用できるようにする

  • 様々な形で他のプロジェクトから利用できるようにする

  • ライブラリ( MathFunctions/CMakeLists.txt )のinstallコマンドにEXPORTの追加

install(TARGETS MathFunctions tutorial_compiler_flags
        DESTINATION lib
        EXPORT MathFunctionsTargets) #👈これ!
install(FILES MathFunctions.h DESTINATION include)
  • これでMathFunctionsTargets.cmakeが作られるっぽい。
install(EXPORT MathFunctionsTargets
  FILE MathFunctionsTargets.cmake
  DESTINATION lib/cmake/MathFunctions
)
  • before
├── bin
│   └── Tutorial
├── include
│   ├── MathFunctions.h
│   └── TutorialConfig.h
└── lib
    └── libMathFunctions.so

  • after
├── bin
│   └── Tutorial
├── include
│   ├── MathFunctions.h
│   └── TutorialConfig.h
└── lib
    ├── cmake
    │   └── MathFunctions
    │       ├── MathFunctionsTargets-noconfig.cmake
    │       └── MathFunctionsTargets.cmake
    └── libMathFunctions.so

  • config.cmake.in
    • なんのためのファイル?
@PACKAGE_INIT@

include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )
  • top-levelのCMakeに以下を加筆
install(EXPORT MathFunctionsTargets
  FILE MathFunctionsTargets.cmake
  DESTINATION lib/cmake/MathFunctions
)

include(CMakePackageConfigHelpers)
# generate the config file that is includes the exports
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
  INSTALL_DESTINATION "lib/cmake/example"
  NO_SET_AND_CHECK_MACRO
  NO_CHECK_REQUIRED_COMPONENTS_MACRO
  )
# generate the version file for the config file
write_basic_package_version_file(
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
  VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}"
  COMPATIBILITY AnyNewerVersion
)

# install the configuration file
install(FILES
  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
  DESTINATION lib/cmake/MathFunctions
  )
  • インストール後、パッケージング後に再利用可能なCMake構成(xxxxxConfig.cmakeのこと?)が生成できるらしい*1
export(EXPORT MathFunctionsTargets
  FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
)
  • この辺は何を書いているのかよくわからない。
  • 以下の記述をCMakeLists.txtに追記することでインストールせずに、ビルドだけで他プロジェクトからも利用できるようになる(ビルドディレクトリ経由で外部のプロジェクトが利用できるようにする)
export(EXPORT MathFunctionsTargets
  FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
)
  • 上記記述の有無でビルド・インストールの違いを検証してみた
    • 11_build:上記記述なし
      • 11_install:11_buildのinstall先
    • 11_build_without_install:上記記述あり
$ tree 11 install
11 [error opening dir]
install
├── bin
│   └── Tutorial
├── include
│   ├── MathFunctions.h
│   └── TutorialConfig.h
└── lib
    ├── cmake
    │   └── MathFunctions
    │       ├── MathFunctionsTargets-noconfig.cmake
    │       └── MathFunctionsTargets.cmake
    └── libMathFunctions.so

$ diff -rq 11_build 11_build_wituhout_install/ | grep -E ".*Only in .*"
Only in 11_build_wituhout_install/: MathFunctionsTargets.cmake
  • MathFunctionsTargets.cmakeをexportしている方にのみ、ビルドディレクトリにMathFunctionsTargets.cmakeが存在していることがわかります。

Concumer

function(find_external_dependency name)
  set(${name}_ROOT ""  CACHE PATH "Root directory to find ${name}")
  mark_as_advanced(${name}_DIR)
  find_package(${name} PATHS ${${name}_ROOT} REQUIRED)
endfunction()


project(Consumer)

find_external_dependency(MathFunctions)

  • find_packageを関数でwrapしています
  • wrap内で何やらキャッシュ関連の制御をしているようです。
    • set(${name}_ROOT "" CACHE PATH "Root directory to find ${name}")
      • find_packageの検索先を指定する変数をキャッシュしている
    • mark_as_advanced(${name}_DIR)
      • これもなにやらキャッシュ関係
    • ↑の2行は消しても特に動きは変わらんかったので解説はなし。

find_package

find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
             [REQUIRED] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [NO_POLICY_SCOPE])
  • unix環境なら標準で以下のパスに依存先の<packageName>Config.cmakeを検索しにいく
    • 第一引数はtarget名ではなく、パッケージ名なので注意。
<prefix>/(lib/<arch>|lib*|share)/cmake/<name>*/
<prefix>/(lib/<arch>|lib*|share)/<name>*/
<prefix>/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/
<prefix>/<name>*/(lib/<arch>|lib*|share)/cmake/<name>*/
<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/
<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/
  • CMAKE_INSTALL_PREFIXCMAKE_PREFIX_PATH<prefix>に追加することもできる(検索先の追加)

  • チュートリアルで何の説明もなしにいきなり応用的な使い方(Full Signature and Config Mode記載内容)が出てくるあたりCMakeは厳しいw

    • 補足:Full Signature~のdocは、この先はプロジェクト管理者向けと書かれている
    • Project maintainers wishing to provide a package to be found by this command are encouraged to read on.
  • tutorialに颯爽登場👉`find_package(${name} PATHS ${${name}_ROOT} REQUIRED)
    • 解説
      • ドキュメントによると、PATHSを指定した場合は上記で説明したprefixに加え、PAHTで指定した箇所も検索されるらしい。
      • が今回のPATHのオペランド(${MathFunctions_ROOT})はmessageで標準出力に表示したが空だった。いみあるのか?この記述
      • とりあえず分からないものを全部削除したが、自分の知っている範囲で想定内の動きをした。
    • おそらくfind_packageの一般的な使い方で依存先を追加するだけだと以下のCMakeLists.txtで可能。
cmake_minimum_required(VERSION 3.10)

if(NOT DEFINED CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 11)
  set(CMAKE_CXX_STANDARD_REQUIRED True)
endif()


project(Consumer)

# Wrapする必要はない
find_package(MathFunctions REQUIRED)

add_library(consumer consumer.cxx)
target_link_libraries(consumer PUBLIC MathFunctions)

# install the consumer library
install(TARGETS consumer DESTINATION bin EXPORT ConsumerTargets)

# install the configuration targets
install(EXPORT ConsumerTargets
  FILE ConsumerTargets.cmake
  DESTINATION lib/cmake/Consumer
)

include(CMakePackageConfigHelpers)
# generate the config file that is includes the exports
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
  "${CMAKE_CURRENT_BINARY_DIR}/ConsumerConfig.cmake"
  INSTALL_DESTINATION "lib/cmake/example"
  NO_SET_AND_CHECK_MACRO
  NO_CHECK_REQUIRED_COMPONENTS_MACRO
  )

# install the configuration file
install(FILES
  ${CMAKE_CURRENT_BINARY_DIR}/ConsumerConfig.cmake
  DESTINATION lib/cmake/Consumer
  )

# generate the export targets for the build tree
# needs to be after the install(TARGETS ) command
export(EXPORT ConsumerTargets
  FILE "${CMAKE_CURRENT_BINARY_DIR}/ConsumerTargets.cmake"
)
  • いきなり応用的なことをやって説明を放棄するのやめてくれ~
  • 最終章はcpackの使い方なのでここで終了とします。
    • 一応動かしたが、debug buildとrelease buildを同時に配布パッケージに含めるcpackの使い方がかかれていた。

わからんことメモ

cmake経由で下位ビルドシステムを実行するときのコマンドがよくわからない

  • installはcmake --install .で良いと思ったら失敗する
  • 業務で使ってるのに近い形cmake --build . --target installだとうまく行った。
    • なぜこれでうまく行くのかは不明
    • 下位ビルドシステムをそのまま叩くのもうまく行った
      • make install
      • 弊社内だとビルドフェイズは下位のビルドシステムをそのまま叩くのが一般的らしいが。。。

memo

  • 普通のdiffにはgit diff --name-only相当のオプションがない
    • パイプ使ってこんな感じで対処。
      • $ diff -rqs 11_build 11_build_wituhout_install/ | grep -E ".*Only in .*"
      • 小さいツールを組み合わせるunixらしい?やりかたなんだろうか。

おつかれ🍻

*1:At this point, we have generated a relocatable CMake Configuration for our project that can be used after the project has been installed or packaged.