LoginSignup
30
28

More than 5 years have passed since last update.

[CMake] ライブラリを自動的に探すFind<package>.cmakeのテンプレート

Last updated at Posted at 2018-11-27

はじめに

修正:<package>-config.cmakeが得られる場合はそちらを使うようにという点を追記(@yumetodo さんありがとうございます。)

CMakeには自身のプロジェクトに属していないライブラリを自動的に検索してくれる便利なコマンドfind_packageがあります。
例えばBoostライブラリを自作プログラムで使っている場合、

cmake_minimum_required(VERSION 3.8.2)
project(find_package_example CXX)
find_package(Boost REQUIRED)
add_executable(foo foo.cpp)
target_link_libraries(foo
  Boost::boost
  )

とすればfooをコンパイルする際にBoostライブラリのヘッダーファイルがインクルードされます。

非常に便利なのですが、全てのライブラリに対してfind_packageを使えるわけではありません。
このコマンドを使うには、目的のライブラリを検索するロジックを示すFind<package>.cmakeまたは<package>Config.cmake/<lower-case-package>-config.cmakeがなければいけません。

参考記事:find_packageの動作

基本的にC/C++ライブラリの作成側がこれらのスクリプトを提供するべきです。
CMakeが<package>-config.cmakeを自動作成する機能を提供していますので、そちらを使いましょう。

参考記事:お手軽な xxx-config.cmake の作成方法

ただ提供されていない場合は自作する必要があります。
ここに必要最小限の要素に絞ったFind<package>.cmakeのテンプレートを載せておきますので、参考にしてください。
より細かく設定したい場合(プラットフォームやバージョンを考慮するなど)は、CMake自身が提供するスクリプト(例えばFindPNG.cmake)を参考にしてください。

参考:C++Now 2017: Daniel Pfeifer “Effective CMake"

Find<package>.cmakeを作成したら、CMAKE_MODULE_PATHにそのスクリプトを置いてあるディレクトリを追加します。

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} <path-to-dir>)

これだけでfind_package(<package>)が使えるようになります。
例えば下の例のスクリプトを使うと

# プロジェクトのルートディレクトリ下にあるcmakeというディレクトリに
# FindGMP.cmakeを置き、そのディレクトリを登録
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake)

# ライブラリを検索
find_package(GMP REQUIRED)

add_executable(myapp main.cpp)
# myappにGMPをリンク
target_link_libraries(myapp GMP::GMP)

とできるようになります。

自作したFind<Package>.cmakegithubにアップしていますので、自由に利用してください。現在利用可能なパッケージは以下の通りです。

  • CGAL(The Computational Geometry Algorithms Library)
  • GMP(The GNU Multiple Precision Arithmetic Library)
  • MPFR(The GNU MPFR Library)
  • Microsoft GSL
  • Spdlog

テンプレート

必要最低限のコマンドからなるFind<package>.cmakeは以下のとおりです。

find_path(<package>_INCLUDE_DIR ...)
find_library(<package>_LIBRARY ...) # ヘッダーのみのライブラリの場合は不要
mark_as_advanced(
  <package>_INCLUDE_DIR
  <package>_LIBRARY     # ヘッダーのみのライブラリの場合は不要
  )

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(<package>
  REQUIRED_VARS
    <package>_INCLUDE_DIR
    <package>_LIBRARY      # ヘッダーのみのライブラリの場合は不要
  )

if(<package>_FOUND AND NOT TARGET <package>::<package>)
  add_library(<package>::<package> UNKNOWN IMPORTED)
  set_target_properties(<package>::<package> PROPERTIES
    IMPORTED_LINK_INTERFACE_LANGUAGES ["C"|"CXX"]  # ヘッダーのみのライブラリの場合は不要
    IMPORTED_LOCATION "${<package>_LIBRARY}"       # ヘッダーのみのライブラリの場合は不要
    INTERFACE_INCLUDE_DIRECTORIES "${<package>_INCLUDE_DIR}"
    )
endif()

以下の例ではUNIXのみ考慮しています。

コンパイル済みライブラリ

例えばGMPライブラリを探すスクリプトは以下の通りになります。

FindGMP.cmake
# インクルードディレクトリのパスをgmp.hを頼りに検索する
find_path(GMP_INCLUDE_DIR gmp.h
  # 検索するパス
  PATHS
    # 環境変数GMP_ROOTまたはGMP_INCLUDE_DIRが存在したらそこを検索
    ENV GMP_ROOT
    ENV GMP_INCLUDE_DIR
    # CMakeの変数としてGMP_ROOTが定義されていたらそこを検索
    ${GMP_ROOT}
    /usr
    /usr/local
  PATH_SUFFIXES
    include
  )
# ライブラリへのパスをライブラリ名を元に検索する
find_library(GMP_LIBRARY
  NAMES
    gmp
  PATHS
    ENV GMP_ROOT
    ENV GMP_LIB_DIR
    ${GMP_ROOT}
    /usr
    /usr/local
  PATH_SUFFIXES
    lib
  )
# advancedモードでない限り変数の存在を隠す
mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARY)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GMP
  REQUIRED_VARS
    GMP_INCLUDE_DIR
    GMP_LIBRARY
  )

# GMPが見つかり、かつGMP::GMPが定義されていない場合
if(GMP_FOUND AND NOT TARGET GMP::GMP)
  # GMP::GMPというターゲット名でGMPライブラリを定義
  # UNKNOWN = STATIC/SHAREDかはまだ不明
  # IMPORTED = このプロジェクトに属さないターゲット
  add_library(GMP::GMP UNKNOWN IMPORTED)
  set_target_properties(GMP::GMP PROPERTIES
    # C言語、C++なら"CXX"とする
    IMPORTED_LINK_INTERFACE_LANGUAGES "C"
    IMPORTED_LOCATION "${GMP_LIBRARY}"
    INTERFACE_INCLUDE_DIRECTORIES "${GMP_INCLUDE_DIR}"
    )
endif()

ヘッダーのみのライブラリ

例えばEigenライブラリを探すスクリプトは以下のとおりです。
(CMakeを使ってEigenライブラリをインストールすると、Eigen3Config.cmakeが自動作成されるので、本当は作る必要はありません。)

FindEigen.cmake
find_path(EIGEN_INCLUDE_DIR Eigen/Core
  PATHS
    ENV EIGEN_ROOT
    ENV EIGEN_INCLUDE_DIR
    ${EIGEN_ROOT}
    /usr
    /usr/local
  PATH_SUFFIXES
    include
  )
mark_as_advanced(EIGEN_INCLUDE_DIR)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Eigen
  REQUIRED_VARS EIGEN_INCLUDE_DIR
  )

if(Eigen_FOUND AND NOT TARGET Eigen::Eigen)
  # EigenはヘッダーのみのライブラリなのでINTERFACEキーワードを指定する
  add_library(Eigen::Eigen INTERFACE IMPORTED)
  set_target_properties(Eigen::Eigen PROPERTIES
    INTERFACE_INCLUDE_DIRECTORIES "${EIGEN_INCLUDE_DIR}"
    )
  # set_target_propertiesのかわりにtarget_include_directoriesを使ってもOK
  # target_include_directories(Eigen::Eigen INTERFACE ${EIGEN_INCLUDE_DIR})
endif()
30
28
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
28