CMakeを使ってビルドする(3)

C++,CMake

前々回の記事(CMakeを使ってビルドする(1))では、プロジェクト直下にのみソースファイルが存在するケースを用いて、基本的なCMakeLists.txtの書き方をまとめました。

プロジェクトが大きくなると多数のサブディレクトリが作られ、その中にソースファイルが配置されることも多くなってきます。今回は、そのようなサブディレクトリが存在するケースについて、CMakeLists.txtの書き方をまとめてみました。

単純なケースでのCMakeLists.txtの書き方はこちら。

Generator-Expressionsについてはこちら。

サブディレクトリを含むときのCMakeLists.txtの書き方

想定しているディレクトリ構成

今回は以下のようなプロジェクトのディレクトリ直下にソースファイルとサブディレクトリfunc1が置かれているケースを考え、main.cppfunc1/func1.cppからtest1をビルドするとします。

project1/
  |- main.h
  |- main.cpp
  |- CMakeLists.txt
  |- func1/
      |- func1.h
      |- func1.cpp
      |- CMakeLists.txt
  |- build/ (ビルド用のディレクトリ)

CMakeLists.txtはディレクトリに1つ必要なので、ルートディレクトリの他にfunc1の中にも作成しておきます。

サブディレクトリのファイルからライブラリを作成する場合

  • CMakeLists.txt

    cmake_minimum_required(VERSION 3.8)
    project(test1 CXX)
    add_subdirectory(func1)
    add_executable(test1 main.cpp)
    target_link_libraries(test1 PRIVATE func1)
  • func1/CMakeLists.txt

    cmake_minimum_required(VERSION 3.8)
    add_library(func1 STATIC func1.cpp)

CMakeLists.txt3行目のadd_subdirectory()

ここでfunc1以下をビルドの対象に加えます。次のような書式で記述します。対象に加えたディレクトリにもCMakeLists.txtが必要です。

add_subdirectory(ディレクトリ [EXCLUDE_FROM_ALL])

EXCLUDE_FROM_ALLはデフォルトターゲット(all)から除外するときに指定します。

add_executable()で作成した実行ファイルにライブラリをリンクします。次のような書式で記述します。

target_link_libraries(ターゲット名 [PRIVATE|PUBLIC|INTERFACE] ライブラリ ...)

PRIVATE, PUBLIC, INTERFACEのキーワードはオプションの有効な範囲を示すために使われます。ターゲット自身に有効な設定の場合にはPRIVATEPUBLICを、ターゲットに依存する他のターゲットに有効なものはINTERFACEPUBLICを指定します。

キーワードターゲット自身のビルドに必要ターゲットに依存するターゲットのビルドに必要
PRIVATE×
PUBLIC
INTERFACE×

詳細は公式ドキュメントを参照してください。

func1/CMakeLists.txt2行目のadd_library()

ライブラリファイルを作成します。次のような書式で記述します。

add_library(ライブラリ名 [STATIC|SHARED|MODULE] 依存ファイル ...)

作成するライブラリの種類によってSTATIC,SHARED,MODULEのキーワードを切り替えます。このコマンドの便利な点は静的ライブラリ(*.a, *.libなど)と動的ライブラリ(*.so, *.dllなど)を簡単に切り替えられる点です。各キーワードの違いについて以下にまとめました。

キーワード生成されるライブラリの種類
STATIC静的ライブラリ
SHAREDプログラム実行時にロードされる動的ライブラリ
MODULE必要な時にロードする動的ライブラリ

詳細は公式ドキュメントを参照してください。

実際のビルド例(Windows + GCC + Make)

>cd build
>cmake -G "MinGW Makefiles" ..
(略)
>make all
[ 25%] Building CXX object func1/CMakeFiles/func1.dir/func1.cpp.obj
[ 50%] Linking CXX static library libfunc1.a
[ 50%] Built target func1
[ 75%] Building CXX object CMakeFiles/test1.dir/main.cpp.obj
[100%] Linking CXX executable test1.exe
[100%] Built target test1

このようにライブラリ(libfunc1.a)が生成された上で、test1.exeがビルドされています。

サブディレクトリのファイルからライブラリを作成しない場合

上記の方法で困ることは少ないと思いますが、ライブラリが必要ないのにもかかわらず、いちいちライブラリを作成するのがムダと思うこともあります。通常このような場合は、オブジェクトファイルを直接リンクするはずです。では、CMakeでこれを実現するにはどうすればよいのでしょうか。

オブジェクトライブラリ

CMakeには「オブジェクトライブラリ」というものがあり、オブジェクトファイルを直接リンクする場合には、これを使用します。ライブラリと名前がついていますが、ライブラリファイルは生成せず、CMake上でオブジェクトファイルのリストをライブラリのように扱えるようにする機能です。

実際に作成したCMakeLists.txtfunc1/CMakeLists.txtを見てみます。

  • CMakeLists.txt

    cmake_minimum_required(VERSION 3.8)
    project(test1 CXX)
    add_subdirectory(func1)
    add_executable(test1 main.cpp $<TARGET_OBJECTS:func1>)
  • func1/CMakeLists.txt

    cmake_minimum_required(VERSION 3.8)
    add_library(func1 OBJECT func1.cpp)

CMakeLists.txt4行目のadd_executable()

ここでGenerator-Expressionsがでてきました。$<TARGET_OBJECTS:オブジェクトライブラリ名>とすることで、オブジェクトライブラリに設定されたオブジェクトファイルのリストを得ることができます。

func1/CMakeLists.txt2行目のadd_library()

add_library()OBJECTをつけることで、指定されたソースから生成されるオブジェクトファイルのリストをオブジェクトライブラリとして扱えるようになります。詳細は公式ドキュメントを参照してください。

実際のビルド例(Windows + GCC + Make)

>cd build
>cmake -G "MinGW Makefiles" ..
(略)
>make all
[ 33%] Built target func1
Scanning dependencies of target test1
[100%] Built target test1

確かにライブラリが作成されていません。出力もスッキリしています。

まとめ

今回は、プロジェクトのディレクトリ内にさらにサブディレクトリが存在するケースに対応するCMakeLists.txtの書き方についてまとめると以下のようになります。

  • 各ディレクトリにCMakeLists.txtを作成する
  • サブディレクトリのCMakeLists.txtではadd_library()を記述する
  • プロジェクト直下のCMakeLists.txtでは
    • サブディレクトリを追加するためにadd_subdirectory()を記述する
    • ライブラリをリンクする場合はtarget_link_libraries()を記述する
    • オブジェクトファイルをリンクする場合は、add_executable()$<TARGET_OBJECTS:...>を追加する

C++,CMake

Posted by izadori