CMake - リンク
概要
CMakeには、プロジェクトのターゲット間の依存関係を管理して、正しくリンクするためのいくつかのリンカ関連コマンドがある。
主要なコマンドには、ターゲットにリンクするライブラリを指定するtarget_link_libraries
コマンド、ライブラリターゲットを追加するadd_library
コマンド、
ライブラリディレクトリを追加するlink_directories
コマンド等がある。
これらのコマンドを使用することにより、ライブラリや実行ファイルが必要な依存関係とともに正しくビルドされるようになる。
target_link_librariesコマンド
target_link_libraries
コマンドは、CMakeでターゲットがリンクするライブラリを指定するために使用する。
このコマンドにより、ターゲットが他のライブラリとどのように関係するかを定義することができる。
target_link_libraries
コマンドは、ターゲットがどのライブラリとリンクするかを定義して、ターゲット間の依存関係とビルド設定を管理するための重要なコマンドである。
修飾子を使用することで、ライブラリの可視性と依存関係の伝播を制御できる。
# 基本構文
target_link_libraries(<ターゲット名> <修飾子 (PRIVATE|PUBLIC|INTERFACE)>
<ライブラリ名 1>
<ライブラリ名 2>
...
)
- ターゲット名
- プロジェクトのターゲット名
- 修飾子 (PRIVATE|PUBLIC|INTERFACE)
- ライブラリのリンクの可視性を制御する修飾子。
- ライブラリ名
- リンクするライブラリ名
ターゲットのリンク
target_link_libraries
コマンドを呼び出す時は、常に、PRIVATE
、PUBLIC
、INTERFACE
オプションを指定する。
例えば、ライブラリAがライブラリBを必要とする時、ライブラリ間に存在し得る依存関係には、いくつかの異なる種類がある。
- PRIVATE
- プライベートな依存関係とは、ライブラリAが自身の内部実装でライブラリBを使用することを指定する。
- ライブラリAにリンクする他のものは、ライブラリBについて知る必要はない。なぜなら、ライブラリBはライブラリAの内部実装だからである。
- 指定したライブラリは、ターゲット自身のみに適用され、他のターゲットには適用されない。
- PUBLIC
- パブリックな依存関係とは、ライブラリAが内部でライブラリBを使用するだけでなく、そのインターフェイスでもライブラリBを使用することを指定する。
- 例えば、ライブラリAで定義された関数が、ライブラリBで定義・実装された型のパラメータを少なくとも1つ持つ場合、
- ライブラリBの型を持つパラメータを提供しなければ、ライブラリAからその関数を呼び出すことができない。
- 指定したライブラリは、ターゲット自身と、そのターゲットをリンクする他のターゲットの両方に適用される。
- INTERFACE
- インターフェースな依存関係とは、ライブラリAを使用するために、ライブラリBの一部も使用しなければならないことを指定する。
- これは、ライブラリAが内部的にライブラリBを必要とせず、そのインターフェイスでライブラリBを使用するだけであるという点で、
PUBLIC
とは異なる。 - 指定したライブラリは、ターゲットをリンクする他のターゲットにのみ適用され、ターゲット自身には適用されない。
add_library
コマンドのINTERFACE形式を使用して定義されたライブラリターゲットを使用する場合に便利である。- 例えば、ヘッダファイルのみのライブラリの依存関係を表すためにターゲットを使用する場合等が挙げられる。
target_link_libraries
コマンドを使用して、リンクだけではなく、依存関係を捉えることができる。
target_link_libraries(
<ターゲット名>
<PRIVATE または PUBLIC または INTERFACE>
item 1
item 2
# ...略
[<PRIVATE または PUBLIC または INTERFACE>]
item 3
item 4
# ...略
)
これにより、あるライブラリが他のライブラリにどのように依存するかをプロジェクトで定義することができる。
CMakeは、この方法でリンクされたライブラリの連鎖を通して依存関係を管理する。
以下の例では、uiライブラリはPUBLIC
としてcollectorライブラリとリンクされているため、myAppが直接collectorにリンクしているだけでも、そのPUBLIC
関係からmyAppはuiにもリンクされる。
一方、algoライブラリとengineライブラリは、PRIVATE
としてcollectorにリンクされているため、myAppはそれらに直接リンクされるわけではない。
これらのコマンドにより、ターゲット間でtarget_link_libraries
コマンドで接続された場合、コンパイラやリンカのフラグやヘッダ検索パスもターゲット間で伝達されるようになる。
add_library(collector src1.cpp)
add_library(algo src2.cpp)
add_library(engine src3.cpp)
add_library(ui src4.cpp)
add_executable(myApp main.cpp)
target_link_libraries(
collector
PUBLIC ui
PRIVATE algo engine
)
target_link_libraries(myApp PRIVATE collector)
このような場合、target_link_libraries
コマンドで使用するターゲット名は、
target_link_libraries
コマンドが呼び出されている同じディレクトリにおいて、add_executable
コマンドまたはadd_library
コマンドにより定義されている必要がある。
非ターゲットのリンク
target_link_libraries
コマンドは、CMakeのターゲット以外にも、以下に示すものを指定することが可能である。
- ライブラリファイルへのフルパス
- CMakeはそのライブラリファイルをリンカコマンドに追加する。
- ライブラリファイルが変更された場合、CMakeはその変更を検出して、ターゲットを再リンクする。
- CMake 3.3以降では、リンカコマンドは指定されたフルパスを使用するが、CMake 3.2以前では、CMakeが代わりにライブラリを検索するようリンカに要求する場合がある。
- (例. /usr/lib/libfoo.soファイルを-lfooに置換する等)
- 単純なライブラリ名
- パスなしでライブラリ名だけを指定する場合、リンカコマンドはそのライブラリを検索する。
- (例えば、プラットフォームによって、fooは-lfoo、または、foo.libになる)
- リンクフラグ
- 特殊なケースとして、-lや-framework以外のハイフンで始まる項目は、リンカコマンドに追加されるフラグとして扱われる。
- ただし、これらは
PRIVATE
項目にのみ使用するように警告している。 PUBLIC
やINTERFACE
として定義する場合は、他のターゲットに引き継がれてしまい、必ずしも安全とは言えないからである。
上記に加え、項目の前にdebug
、optimized
、general
のいずれかのオプションを付加することができる。
これらのオプションは、ビルドがデバッグビルドとして設定されているかどうかに基づいて、それに続く項目が含まれるべき時をさらに絞り込むことである。
ただし、これらのオプションは、同様の機能をより明確で柔軟かつ堅牢な方法が別に存在するため、現在は使用しない方がよい。
- debugオプション
- 項目の前にdebugオプションを付加する場合、そのビルドがデバッグビルドである場合にのみ追加される。
- optimizedオプション
- 項目の前にoptimizedオプションを付加する場合、ビルドがデバッグビルドでない場合のみ追加される。
- generalオプション
- 項目の前にgeneralオプションを付加する場合、全てのビルド構成に対して項目を追加するように指定する。
ライブラリの検索
find_libraryコマンドを使用して、特定の名前のライブラリを検索することができる。
- NAMESオプション
- ライブラリ名を指定することにより、libプレフィックス無しでライブラリを検索する。
- PATHSオプション
- 任意
- ライブラリを検索する場所を指定する。
- 必要に応じて、パスを調整する。
- NO_DEFAULT_PATHオプション
- 任意
- 指定したパスのみを検索する。
以下の例では、動的ライブラリであるlibsmbclient.soファイルを検索して使用している。
もし、ライブラリの検索に失敗した場合、エラーメッセージを表示してビルドを停止する。
ターゲット (実行可能ファイルまたはライブラリ) を定義する。
target_link_librariesコマンドを使用して、検索で発見したライブラリをターゲットにリンクする。
# ライブラリの検索
find_library(SMBCLIENT_LIBRARY
NAMES sambclient
PATHS /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib # PATHオプションは任意
NO_DEFAULT_PATH # NO_DEFAULT_PATHオプションは任意
)
# ライブラリの検索に成功したかどうかを確認
if(NOT SMBCLIENT_LIBRARY)
message(FATAL_ERROR "libsmbclient.so library not found")
endif()
# ターゲット (実行可能ファイルまたはライブラリ) を定義
# Qtの場合は、qt_add_executableコマンドを使用する
add_executable(<ターゲット名>
<ソースファイル 1>
<ソースファイル 2>
# ...略
)
# ターゲットにライブラリをリンク
target_link_libraries(<ターゲット名>
${SMBCLIENT_LIBRARY}
# ...略
)
ライブラリパスの指定 (target_link_directoriesコマンド)
target_link_directories
コマンドは、特定のターゲット (実行可能ファイルやライブラリ) に対して、リンカが追加のライブラリを検索する場所を指定する。
target_link_directoriesコマンドの特徴を以下に示す。
- スコープの明確化
target_link_directories
コマンドは、特定のターゲットに対してのみリンクディレクトリを設定するため、影響範囲が明確である。
- より細かい制御
- ターゲット単位で設定できるため、プロジェクト全体ではなく必要なターゲットにのみ適用できる。
- ジェネレータ式の対応
target_link_directories
コマンドはジェネレータ式をサポートしており、より柔軟な設定が可能である。- ${CMAKE_CURRENT_SOURCE_DIR}のようなCMake変数や、$<...>のようなジェネレータ式が使用できる。
- 順序
- 複数のディレクトリを指定する場合、最初に指定したディレクトリから検索する。
基本的な構文を以下に示す。
target_link_directories(<ターゲット名>
<INTERFACE|PUBLIC|PRIVATE> [items1...] # INTERFACE, PUBLIC, PRIVATE: リンクディレクトリの適用スコープ
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...] # INTERFACE, PUBLIC, PRIVATE: リンクディレクトリの適用スコープ
)
スコープの意味を以下に示す。
- PRIVATE
- 指定したディレクトリは、このターゲットのみに適用される。
- このターゲットに依存する他のターゲットには伝播しない。
- INTERFACE
- このターゲットには適用されず、このターゲットに依存する他のターゲットに伝播する。
- 主にインターフェースライブラリで使用する。
- PUBLIC
- PRIVATEとINTERFACEの組み合わせである。
- このターゲットとそれに依存する他のターゲットの両方に適用される。
以下の例では、ターゲット1 (mylib) は、/path/to/private/libsおよび/path/to/public/libsからライブラリを検索する。
ターゲット2 (myapp) は、/path/to/public/libsおよび/path/to/interface/libsからライブラリを検索する。 (mylibに依存しているため)
add_library(mylib STATIC
source1.cpp
source2.cpp
)
target_link_directories(mylib
PRIVATE /path/to/private/libs
PUBLIC /path/to/public/libs
INTERFACE /path/to/interface/libs
)
add_executable(myapp
main.cpp
)
target_link_libraries(myapp PRIVATE
mylib
)
ライブラリパスの指定 (link_directoriesコマンド)
link_directories
コマンドの使用は非推奨である。
代わりに、target_link_directories
コマンドを使用することが推奨されている。
ライブラリパスを指定する場合、link_directories
コマンドを使用する。
link_directories(/path/to/lib)
link_directories
コマンドを複数使用する場合、デフォルトでは、指定したパスは最後尾となる。
ただし、link_directories
コマンドにBEFORE
オプションを付加した場合は最前となる。
以下の例では、"-I/path1/to/lib -I/path2/to/lib"となる。
link_directories(/path1/to/lib)
link_directories(/path2/to/lib)
以下の例では、"-I/path2/to/lib -I/path1/to/lib" となる。
link_directories(/path1/to/lib)
link_directories(BEFORE /path2/to/lib)