Help us understand the problem. What is going on with this article?

お手軽な xxx-config.cmake の作成方法

More than 5 years have passed since last update.

モチベーション

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) するだけで、自作のライブラリをリンクできる。

おおまかな手順

  1. 自作のライブラリを install(TARGETS) する際に EXPORT <export-name> オプションでエクスポート名を付与する
  2. install(EXPORT <export-name>) コマンドで、インストールツリー向けの foo-config.cmake ファイルを生成して、インストールする
  3. (オプション)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 では、以下が行われます。
1. ターゲット foo を IMPORTED ライブラリとして定義する。
2. ターゲット foo の配置場所(IMPORTED_LOCATION) にインストール先のライブラリファイルを指定する。
3. ターゲット 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 プロジェクトをインストールせずとも利用することができます。

詳細

  1. CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE を有効にする。
    このCMake変数を ON に指定すると、ターゲットの INTERFACE_INCLUDE_DIRECTORIES にソースディレクトリと、ビルドディレクトリが自動的に追加されます。

  2. ライブラリのビルド指定です。通常の指定と同じで構いません。

  3. インストールヘッダの指定

  4. ライブラリリンクの指定

  5. ターゲットのインストール指定
    普段と同様に、fooターゲットのインストール先を指定しますが、その際にいくつかの追加のオプションを指定します。
    なお指定するパスが相対パスの場合は、 CMAKE_INSTALL_PREFIX からの相対パスになります。

    a. 後続の install(EXPORT) で利用する エクスポート名を指定します。ここでは foo-export という名前を指定しました。

    c. 生成される foo-config.cmake で foo ターゲットに指定する INTERFACE_INCLUDE_DIRECTORIESプロパティの設定値を指定します。その際に5.d で指定するヘッダのインストール先と一貫性を持たせます。

  6. 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 のサンプルを記載する。

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.cmakefind_package(foo) を記述する必要があるようです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした