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

# はじめに

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

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

```cmake
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の動作](https://qiita.com/osamu0329/items/bd3d1e50edf37c277fa9)

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

参考記事：[お手軽な xxx-config.cmake の作成方法](https://qiita.com/osamu0329/items/134de918c0ffa7f0557b)

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

参考：[C++Now 2017: Daniel Pfeifer “Effective CMake"](https://youtu.be/bsXLMQ6WgIk)

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

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

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

```cmake
# プロジェクトのルートディレクトリ下にある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>.cmake`を[github](https://github.com/shohirose/cmake_find_package)にアップしていますので、自由に利用してください。現在利用可能なパッケージは以下の通りです。

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


# テンプレート

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

```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ライブラリ](https://gmplib.org/)を探すスクリプトは以下の通りになります。

```cmake: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ライブラリ](http://eigen.tuxfamily.org/index.php?title=Main_Page)を探すスクリプトは以下のとおりです。
（CMakeを使ってEigenライブラリをインストールすると、Eigen3Config.cmakeが自動作成されるので、本当は作る必要はありません。）

```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()
```