モチベーション
CMake でライブラリを作成する際に、<foo>-config.cmake
ファイルを作成しておくと、そのライブラリを利用するCMakeプロジェクトで find_package コマンドを利用して検索できます。
この foo-config.cmake を手で作成することもできますが、これはなかなか骨の折れる作業になります。
そこで、この記事では install(EXPORT) コマンドを使用して、foo-config.cmake を自動生成するCMakeLists.txtの書き方について解説します。
この方法のメリット
- FindFoo.cmake を書く必要がない。
- クライアント側のCMakeLists.txt で
find_package(foo)
した後に、target_link_libraries(foo)
するだけで、自作のライブラリをリンクできる。
おおまかな手順
- 自作のライブラリを
install(TARGETS)
する際にEXPORT <export-name>
オプションでエクスポート名を付与する -
install(EXPORT <export-name>)
コマンドで、インストールツリー向けのfoo-config.cmake
ファイルを生成して、インストールする - (オプション)
export(TARGETS)
コマンドで、ビルドツリー向けのfoo-config.cmake
ファイルが生成される
以下のサンプルでは、usage-requirement を用いて、このライブラリを find_package
して使用するCMakeプロジェクトがtarget_link_link_libraries()
を行うだけで以下が行えるようにしている。
- 自動的にインクルードパスが追加される
- 依存ライブラリがリンク対象に含まれる(以下の例では、
foo
をリンクすると、bar
が自動的にリンクされる)
実際のサンプル(インストールツリー向けのサンプル)
ライブラリ foo を作成して、そのライブラリ向けの foo-config.cmake を自動生成するサンプルを記述します。
CMakeLists.txt は以下のような記載になります。
cmake_minimum_required(VERSION 2.8.12)
project(libfoo C)
# 1. INTERFACE_INCLUDE_DIRECTORIES にソースディレクトリとビルドディレクトリを自動追加する。
set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON)
# 2. ライブラリのビルド指定
add_library(foo SHARED foo.c)
# 3. インストールするヘッダファイルを指定しておく。
# 指定したファイルは、INSTALL(TARGETS) の PUBLIC_HEADER で指定した
# ディレクトリにインストールされる。
set_target_properties(foo PROPERTIES
PUBLIC_HEADER foo/foo.h
)
# 4. foo ライブラリが依存するライブラリを指定する。(オプション)
# PUBLIC指定してリンクすると、依存ライブラリがエクスポートされる(注1)
target_link_libararies(foo PUBLIC bar)
# 5. ターゲットのインストール指定
install(TARGETS foo
EXPORT foo-export # 5.a EXPORT オプションで エクスポート名を指定する
LIBRARY DESTINATION lib # 5.b ライブラリのインストール先
INCLUDES DESTINATION include # 5.c エクスポート時にincludeパスに含めるパスを指定する
PUBLIC_HEADER DESTINATION include/foo) # ヘッダのインストール先
# 6. foo-config.cmake を自動生成してインストールする。
install(EXPORT foo-export # 6.a
FILE foo-config.cmake # 6.b ファイル名を指定する
DESTINATION share/cmake/foo/ # 6.c インストール先
EXPORT_LINK_INTERFACE_LIBRARIES) # 6.d 同時にリンクすべきライブラリをエクスポートする
上記のプロジェクトをビルドしてインストールすると、${CMAKE_INSTALL_PREFIX}/share/cmake/foo/foo-config.cmake
が生成されます。
生成された foo-config.cmake では、以下が行われます。
- ターゲット foo を IMPORTED ライブラリとして定義する。
- ターゲット foo の配置場所(IMPORTED_LOCATION) にインストール先のライブラリファイルを指定する。
- ターゲット foo の INTERFACE_INCLUDE_DIRECTORIES プロパティにインストール先の foo/include を指定する。
1, 2 によって、find_package(foo) したプロジェクトで、ターゲット foo を使用してリンクすることができます。
3 によって、target_link_libraries( foo) することで、 foo/include が自動的にインクルードされます。
上記の foo プロジェクトの CMakeLists.txt に下記のexport(EXPORT) コマンドを追加すると、foo のビルドツリーから、 fooターゲットを利用するための foo-export.cmake
ファイルが、foo プロジェクトのビルドツリー内に生成されます。
このファイルは foo プロジェクトをインストールせずとも利用することができます。
詳細
-
CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE を有効にする。
このCMake変数を ON に指定すると、ターゲットの INTERFACE_INCLUDE_DIRECTORIES にソースディレクトリと、ビルドディレクトリが自動的に追加されます。 -
ライブラリのビルド指定です。通常の指定と同じで構いません。
-
インストールヘッダの指定
-
ライブラリリンクの指定
-
ターゲットのインストール指定
普段と同様に、fooターゲットのインストール先を指定しますが、その際にいくつかの追加のオプションを指定します。
なお指定するパスが相対パスの場合は、 CMAKE_INSTALL_PREFIX からの相対パスになります。a. 後続の install(EXPORT) で利用する エクスポート名を指定します。ここでは foo-export という名前を指定しました。
c. 生成される foo-config.cmake で foo ターゲットに指定する INTERFACE_INCLUDE_DIRECTORIESプロパティの設定値を指定します。その際に5.d で指定するヘッダのインストール先と一貫性を持たせます。
-
install(EXPORT) コマンドで、 foo-config.make の自動生成とインストール設定を行います。
a. foo-export には、5 で指定したエクスポート名を指定します。
b. 自動生成するファイル名を指定します。
c. インストール先を指定します。find_packageが検索するパスを指定すると便利です。
d. foo ライブラリのLINK_INTERFACE_LIBRARIES を指定します。
インストールせずに、fooターゲットを利用したい
ビルドツリーから直接 foo ターゲットを利用するための foo-export.cmake
を生成するためには、以下の export(EXPORT) コマンドを、fooプロジェクトに追加します。
# ビルドツリーからターゲットをIMPORTするための CMakeファイルを生成する(オプション)
export(EXPORT foo-export FILE foo-export.cmake
EXPORT_LINK_INTERFACE_LIBRARIES)
生成された foo-export.cmake
では、ビルドツリー内のライブラリファイルやディレクトリを参照した IMPORTED ターゲットが定義されます。
ライブラリ利用側のプロジェクト
メリットで述べたとおり、find_package(foo)
を実行すると、foo-config.cmake が検索され、その中で foo
というIMPORTEDターゲットが定義される。ライブラリ利用側のプロジェクトでは、target_link_libraries(<target> foo)
とすれば、自動的にinclude_directoryと依存ライブラリのリンクが実施される。
以下に、foo ライブラリを使用するプロジェクトの CMakeLists.txt のサンプルを記載する。
cmake_minimum_required(VERSION 2.8.12)
project(foo-client C)
find_package(foo REQUIRED)
add_executable(foo-client foo-client.c) # fooライブラリを使用する実行ファイルを作成
target_link_libraries(foo-client foo) # foo ライブラリをリンクする。
# 自動的にインクルードディレクトリも設定されるため、
# target_include_directories を記述しないで済む。
注意:多段の依存関係の問題
参照 http://www.itk.org/Bug/print_bug_page.php?bug_id=1101
多段の依存関係が存在すると、ライブラリパスがうまく引き継げないようです。
たとえば以下のような3つのCMakeプロジェクトがあるとします。
- foo: ライブラリ
- bar: fooライブラリに依存するライブラリ
- client: barに依存する実行プログラム
client をビルドするときに、fooライブラリのインストール先が引き継がれずに、リンクエラーになる可能性があります。
これを解消するには、クライアント側で明示的に find_package(foo)
とするか、
もしくは bar-config.cmake
に find_package(foo)
を記述する必要があるようです。