toge's diary

コンピュータ関連の趣味をつらつらと。

cmakeでHeader Onlyライブラリをお手軽に使う

FetchContentを使う動機

cmakeでHeader Onlyライブラリをちょっと使いたい場合ありますよね。
git submoduleでいいのですが、ちょっと管理が煩雑だなぁと思っています。

普段はなるべくConanパッケージを作る→使うのですが、お試しでマイナーなライブラリ使うときには仰々しいかなぁと思ったりします。

そこでCMake 3.11から導入されたFetchContent機能を利用してみました。 cmake.org

FetchContentはかなり柔軟に書くことができて、gitで指定ブランチもtar.gz/zipの取得・展開もできます。
私は「なるべくリリースされたものを使いたい」という考え方なので、リリースされたものを利用することにします。

今回はちょうど最近リリースされたCTML 2.0.0を触ってみたかったのでそれを例にしてみます。
※CTMLはHeader OnlyのHTML生成ライブラリです。

CMakeLists.txtの書き方

FetchContent機能を使ったCMakeLists.txtの例です。

project(ctml CXX)
cmake_minimum_required(VERSION 3.11.0)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")

include(FetchContent)
FetchContent_Populate(
  ctml
  URL        https://github.com/tinfoilboy/CTML/archive/2.0.0.tar.gz
  URL_HASH   MD5=936e08167384fee9a4445fed343e83dc
)

include_directories(${ctml_SOURCE_DIR}/include)

add_executable(test test.cpp)

本当はFetchContent_DeclareFetchContent_MakeAvailable を組み合わせるべきなんでしょうが、簡単にするため FetchContent_Populate だけですませています。

CMakeの実行と結果の確認

CMakeLists.txtの存在するディレクトリでcmakeを実行してみます。

% cmake -B build --verbose -S .
-- The CXX compiler identification is AppleClang 12.0.0.12000032
-- Check for working CXX compiler: /Library/Developer/CommandLineTools/usr/bin/c++
-- Check for working CXX compiler: /Library/Developer/CommandLineTools/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Populating ctml
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/toge/src/ctml/build/ctml-subbuild
Scanning dependencies of target ctml-populate
[ 11%] Creating directories for 'ctml-populate'
[ 22%] Performing download step (download, verify and extract) for 'ctml-populate'
-- Downloading...
   dst='/Users/toge/src/ctml/build/ctml-subbuild/ctml-populate-prefix/src/2.0.0.tar.gz'
   timeout='none'
-- Using src='https://github.com/tinfoilboy/CTML/archive/2.0.0.tar.gz'
* Closing connection 0
-- verifying file...
       file='/Users/toge/src/ctml/build/ctml-subbuild/ctml-populate-prefix/src/2.0.0.tar.gz'
* Closing connection 1
-- Downloading... done
-- extracting...
     src='/Users/toge/src/ctml/build/ctml-subbuild/ctml-populate-prefix/src/2.0.0.tar.gz'
     dst='/Users/toge/src/ctml/build/ctml-src'
-- extracting... [tar xfz]
-- extracting... [analysis]
-- extracting... [rename]
-- extracting... [clean up]
-- extracting... done
[ 33%] No patch step for 'ctml-populate'
[ 44%] No update step for 'ctml-populate'
[ 55%] No configure step for 'ctml-populate'
[ 66%] No build step for 'ctml-populate'
[ 77%] No install step for 'ctml-populate'
[ 88%] No test step for 'ctml-populate'
[100%] Completed 'ctml-populate'
[100%] Built target ctml-populate
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/toge/src/ctml/build

こうすると、buildディレクトリの下が次のようになります。

% find build -type d 
build
build/ctml-src
build/ctml-src/include
build/ctml-src/tests
build/ctml-src/.github
build/CMakeFiles
build/CMakeFiles/3.15.3
build/CMakeFiles/CMakeTmp
build/CMakeFiles/test01.dir
build/ctml-subbuild
build/ctml-subbuild/CMakeFiles
build/ctml-subbuild/CMakeFiles/ctml-populate.dir
build/ctml-subbuild/CMakeFiles/3.15.3
build/ctml-subbuild/ctml-populate-prefix
build/ctml-subbuild/ctml-populate-prefix/tmp
build/ctml-subbuild/ctml-populate-prefix/src
build/ctml-subbuild/ctml-populate-prefix/src/ctml-populate-stamp
build/ctml-build

ちゃんとctml-srcの下にCTML-2.0.0.tar.gzを展開した結果が配置されてます。

ソースコードでの参照方法

build/ctml-src というパスは <name>_SOURCE_DIR というCMakeの変数で参照可能になっています。
nameはFetchContent_Populateの最初の引数で指定しているので、今回の場合はctmlですね。
このため、今回は次のようにinclude_directoryを指定しています。

include_directories(${ctml_SOURCE_DIR}/include)

これでソースコードの中でCTMLのヘッダをインクルードできます。

#include <iostream>

#include "ctml.hpp"

int main(int argc, char* argv[]) {
  ...

まとめ

CMakeのFetchContentを利用してHeader Onlyライブラリを利用する方法を説明しました。

FetchContentではconfigure→build→testの処理も定義できるみたいなのですが、今回はHeader Onlyライブラリなので何も指定していませんでした。
機会があればビルド必須なライブラリをFetchContentで利用してみようと思います。