find_path()

2025-05-16

以下に主な機能と使い方を説明します。

目的

find_path()の主な目的は、指定されたファイル名を持つディレクトリを検索し、そのディレクトリのパスを変数に格納することです。例えば、#include <some_library/some_header.h>のようなヘッダーファイルを探す場合に便利です。

基本的な構文

find_path(<VAR> name1 [name2 ...]
          [HINTS path1 [path2 ...]]
          [PATHS path1 [path2 ...]]
          [PATH_SUFFIXES suffix1 [suffix2 ...]]
          [NO_DEFAULT_PATH]
          [NO_CMAKE_PATH]
          [NO_CMAKE_ENVIRONMENT_PATH]
          [NO_SYSTEM_ENVIRONMENT_PATH]
          [NO_CMAKE_SYSTEM_PATH]
          [NO_CMAKE_FIND_ROOT_PATH]
          [ONLY_CMAKE_PATH | ONLY_CMAKE_ENVIRONMENT_PATH | ONLY_SYSTEM_ENVIRONMENT_PATH | ONLY_CMAKE_SYSTEM_PATH | ONLY_CMAKE_FIND_ROOT_PATH]
          [REQUIRED]
          [QUIET]
          [NO_CACHE]
          [DOC "cache documentation string"]
         )

主要な引数

  • NO_CACHE: 結果をCMakeのキャッシュに保存しません。通常は、find_path()の結果はキャッシュされ、次回以降のCMake実行時に同じ検索が繰り返されないようにします。
  • QUIET: ファイルが見つからなかった場合に、メッセージを表示しません。
  • REQUIRED: ファイルが見つからなかった場合、エラーを発生させます。
  • NO_DEFAULT_PATH: CMakeがデフォルトで検索するパス(システムパス、CMakeの内部パスなど)を検索から除外します。これにより、指定したパスのみを検索できます。
  • PATH_SUFFIXES suffix1 [suffix2 ...]: 指定されたパスの後に付加されるサフィックス(例: include, include/my_lib)。これにより、/usr/local/include/opt/my_lib/includeのようなパスを効率的に検索できます。
  • PATHS path1 [path2 ...]: ファイルを検索する追加のパスを指定します。
  • HINTS path1 [path2 ...]: ファイルが見つかる可能性が高いパスのヒントを提供します。これらのパスは、通常の検索パスよりも優先して検索されます。
  • name1 [name2 ...]: 探したいファイルの名前。複数の名前を指定できます。
  • <VAR>: 見つかったパスが格納される変数名。ファイルが見つからなかった場合、この変数にはVAR-NOTFOUNDが設定されます。

検索順序(デフォルトの場合)

find_path()は、通常以下の順序でパスを検索します(NO_*オプションが指定されていない場合):

  1. HINTS引数で指定されたパス
  2. CMake関連のキャッシュ変数
    • CMAKE_PREFIX_PATH内の<prefix>/include/<arch><prefix>/include
    • CMAKE_INCLUDE_PATH
    • CMAKE_FRAMEWORK_PATH
  3. PATHS引数で指定されたパス
  4. 環境変数
    • INCLUDE環境変数
  5. システムの標準的なインクルードディレクトリ

使用例

SDL2ライブラリのヘッダーファイル(SDL.h)のパスを見つける例:

find_path(SDL2_INCLUDE_DIR NAMES SDL.h
          HINTS ENV SDL2DIR
          PATHS /usr/local/include /opt/local/include
          PATH_SUFFIXES SDL2
          REQUIRED
         )

if(SDL2_INCLUDE_DIR)
  message(STATUS "SDL2 include directory found: ${SDL2_INCLUDE_DIR}")
  include_directories(${SDL2_INCLUDE_DIR})
else()
  message(FATAL_ERROR "SDL2 include directory not found!")
endif()

この例では、

  1. SDL.hという名前のファイルを検索します。
  2. まずSDL2DIRという環境変数で指定されたパスをヒントとして検索します。
  3. 次に/usr/local/include/opt/local/includeを検索します。
  4. これらのパスの後にSDL2というサフィックス(例: /usr/local/include/SDL2/SDL.h)も試します。
  5. REQUIREDが指定されているため、見つからなかった場合はビルドエラーとなります。
  6. 見つかった場合、そのパスがSDL2_INCLUDE_DIRに格納され、include_directories()でインクルードパスに追加されます。


find_path()における一般的なエラーとトラブルシューティング

VAR-NOTFOUND エラー / パスが見つからない

最も一般的な問題は、find_path()が目的のパスを見つけられず、結果として変数が${VAR}-NOTFOUNDとなるケースです。

原因

  • キャッシュの問題
    以前の実行で誤った情報がキャッシュされている。
  • 大文字・小文字の区別
    ファイルシステムによっては大文字・小文字を区別するため、ファイル名やディレクトリ名が正確でない。
  • デフォルトパスの除外
    NO_DEFAULT_PATHなどのオプションを使用しているため、CMakeが通常検索するパスが除外されている。
  • 検索パスが不十分
    HINTSPATHSで指定したパスが、実際にファイルが存在する場所を含んでいない。
  • ファイル名の間違い
    NAMESで指定したファイル名が実際と異なる。

トラブルシューティング

    • NAMESに指定したファイル名(例: SDL.h)が正確であることを確認してください。
    • 複数の候補がある場合は、すべて指定してみてください。
  1. 検索パスの拡充

    • ファイルがどこにインストールされているかを知っている場合は、HINTSPATHSを使ってその場所を明示的に指定します。
      • 例: find_path(MYLIB_INCLUDE_DIR NAMES mylib.h HINTS /usr/local/mylib/include /opt/mylib/include)
    • 環境変数にパスが設定されている場合は、ENV VAR_NAMEを使って参照できます。
      • 例: find_path(MYLIB_INCLUDE_DIR NAMES mylib.h HINTS ENV MYLIB_ROOT)
  2. PATH_SUFFIXESの利用

    • 多くの場合、ライブラリのヘッダーファイルはincludeディレクトリの下にさらにライブラリ名と同じディレクトリが存在します(例: /usr/local/include/SDL2/SDL.h)。この場合、PATH_SUFFIXESを使います。
      • 例: find_path(SDL2_INCLUDE_DIR NAMES SDL.h PATH_SUFFIXES SDL2)
  3. デバッグメッセージの活用

    • message(STATUS "Searching for SDL.h...")のように検索前にメッセージを出したり、message(STATUS "SDL2_INCLUDE_DIR: ${SDL2_INCLUDE_DIR}")のように検索結果を表示して、どこまで処理が進んでいるかを確認します。
    • find_pathの実行後にif(NOT VAR)NOTFOUNDの場合の処理を追加し、エラーメッセージを出すことも有効です。
      find_path(MYLIB_INCLUDE_DIR NAMES mylib.h REQUIRED) # REQUIREDを使うと見つからない場合にエラーになる
      if(NOT MYLIB_INCLUDE_DIR)
        message(FATAL_ERROR "Could not find mylib.h! Please set MYLIB_INCLUDE_DIR.")
      endif()
      
  4. CMakeキャッシュのクリア

    • CMakeCache.txtファイルやビルドディレクトリ全体を削除してから、CMakeを再実行してみてください。特に、以前の実行で誤ったパスがキャッシュされた場合や、ライブラリのインストール場所を変更した場合に有効です。
  5. NO_DEFAULT_PATHなどのオプションの確認

    • これらのオプションを使用している場合、意図的に検索範囲を狭めていることになります。本当にそのオプションが必要か再確認してください。

意図しないパスが見つかる / 誤ったバージョンが見つかる

目的のパスとは異なるパスが見つかってしまうことがあります。

原因

  • 検索順序の問題
    find_path()の検索順序により、意図しないパスが先に評価されてしまう。
  • 複数のインストール
    同じライブラリがシステムの異なる場所にインストールされている(例: /usr/include/usr/local/include、またはHomebrewとシステムデフォルト)。

トラブルシューティング

  1. HINTSとPATHSの適切な利用

    • 最も優先して探したいパスをHINTSに、次に探したいパスをPATHSに指定します。HINTSPATHSよりも優先されます。
    • 特定のパスを最優先で検索させたい場合、そのパスをHINTSの最初に指定します。
  2. NO_CMAKE_PATH / NO_SYSTEM_ENVIRONMENT_PATHなどの利用

    • システムのデフォルトパスや環境変数を検索させたくない場合は、これらのオプションを使用して検索範囲を制限します。これにより、自分で指定したパスのみを検索するようにできます。
      • 例: find_path(MYLIB_INCLUDE_DIR NAMES mylib.h PATHS /my/custom/path NO_DEFAULT_PATH)
  3. 環境変数の確認

    • CMAKE_PREFIX_PATH, CMAKE_INCLUDE_PATH, INCLUDEなどの環境変数が、意図しないパスを含んでいないか確認してください。これらの変数が設定されていると、find_path()の検索に影響を与えます。

Windows環境でのパスの問題(スペース、バックスラッシュなど)

Windowsでは、パスにスペースが含まれていたり、バックスラッシュ(\)が使用されることが問題になることがあります。

原因

  • パス区切り文字
    CMakeは通常スラッシュ(/)をパス区切り文字として推奨していますが、Windowsのバックスラッシュ(\)が混在すると問題を引き起こすことがあります。
  • パスのスペース
    パスにスペースが含まれている場合、正しく引用符で囲まれていないと、CMakeがパスを正しく解釈できないことがあります。

トラブルシューティング

  1. パスの引用符

    • PATHSHINTSに指定するパスは、常に二重引用符で囲むようにしてください。
      • 例: find_path(... PATHS "C:/Program Files/My Library/include")
      • 環境変数から取得したパスも同様に引用符で囲むことを検討してください。
  2. スラッシュの使用

    • CMakeスクリプト内でパスを記述する際は、バックスラッシュではなくスラッシュを使用することを推奨します。CMakeは内部的にこれを処理します。
      • 例: set(MY_PATH "C:/Program Files/My Library/include")

クロスコンパイル環境での問題

クロスコンパイルでは、ホストシステム(ビルドを実行するマシン)のヘッダーファイルと、ターゲットシステム(最終的に実行されるマシン)のヘッダーファイルを区別する必要があります。

原因

  • find_path()が、ホストシステムにインストールされているヘッダーファイルを誤って見つけてしまう。

トラブルシューティング

  1. CMAKE_FIND_ROOT_PATHの利用

    • ターゲットシステムのルートディレクトリをCMAKE_FIND_ROOT_PATHに設定することで、CMakeは主にそのディレクトリ内で検索を行います。
      • 例: set(CMAKE_FIND_ROOT_PATH /path/to/target/sysroot)
    • 関連する変数として、CMAKE_FIND_ROOT_PATH_MODE_INCLUDEONLY, NEVER, BOTH)を設定することで、インクルードパスの検索動作を細かく制御できます。
  2. NO_CMAKE_SYSTEM_PATH / NO_CMAKE_ENVIRONMENT_PATH

    • ホストシステムのパスを検索から除外するために、これらのオプションを併用することがよくあります。
  • CMake GUI (cmake-gui)
    CMake GUIを使用すると、キャッシュ変数を視覚的に確認・編集できます。find_path()が見つけたパスがキャッシュにどのように保存されているかを確認するのに便利です。
  • CMakeのトレースオプション
    CMakeを実行する際に-DCMAKE_VERBOSE_MAKEFILE=ON--traceオプションを使用すると、CMakeが実行している内部コマンドや変数の展開を詳しく見ることができます。
    • cmake -S . -B build --trace
    • cmake -S . -B build -DCMAKE_VERBOSE_MAKEFILE=ON
  • message(STATUS "...")
    最も基本的なデバッグツールです。変数の値や検索パスを確認するために頻繁に使用してください。
    • 例: message(STATUS "SDL2_INCLUDE_DIR is: ${SDL2_INCLUDE_DIR}")


例1: 最も基本的な使い方 (SDL2のヘッダーファイルを検索)

この例では、SDL.hという名前のヘッダーファイルを含むディレクトリを検索します。

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(MySDL2App C)

# SDL.h が含まれるディレクトリを検索
# 変数 SDL2_INCLUDE_DIR に結果が格納される
# REQUIRED を指定すると、見つからない場合にエラーで停止する
find_path(SDL2_INCLUDE_DIR
          NAMES SDL.h
          REQUIRED
          DOC "Path to SDL2 include directory" # キャッシュ変数に対する説明
         )

# SDL2_INCLUDE_DIR が見つかったかどうかを確認
if(SDL2_INCLUDE_DIR)
  message(STATUS "SDL2 include directory found: ${SDL2_INCLUDE_DIR}")
  # 見つかったパスをインクルードディレクトリに追加
  include_directories(${SDL2_INCLUDE_DIR})
else()
  # REQUIRED を使っているので、通常ここには来ないが、念のため
  message(FATAL_ERROR "SDL2 include directory not found!")
endif()

# サンプル実行可能ファイルの追加
add_executable(my_sdl2_app main.c)

# SDL2ライブラリへのリンクは find_package(SDL2) などで行うのが一般的だが、
# この例では find_path() に焦点を当てるため省略。
# 仮に手動でリンクパスを設定するなら:
# find_library(SDL2_LIBRARY NAMES SDL2 REQUIRED)
# target_link_libraries(my_sdl2_app ${SDL2_LIBRARY})

main.c
(簡単なテスト用)

#include <SDL.h> // find_path() が見つけたディレクトリのおかげでインクルード可能

int main(int argc, char* argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        // エラー処理
    }
    SDL_Quit();
    return 0;
}

説明

  • DOC "...": SDL2_INCLUDE_DIR変数がCMakeキャッシュに表示される際の説明文を設定します。
  • REQUIRED: このファイルが見つからなかった場合、CMakeの構成段階でエラーを発生させ、ビルドを中止します。これは、必須の依存関係が見つからない場合に便利です。
  • NAMES SDL.h: 検索するヘッダーファイル名を指定します。

例2: 特定のパスヒントとサフィックスの利用

この例では、カスタムライブラリMyLibのヘッダーファイルmylib.hを検索します。MyLibは、ユーザーが特定のディレクトリにインストールしている可能性があり、またその下にmylibというサブディレクトリがあるかもしれません(例: /path/to/MyLib/include/mylib/mylib.h)。

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(MyLibApp CXX)

# MyLibのルートディレクトリが環境変数 MYLIB_ROOT に設定されていると仮定
# PATHS と PATH_SUFFIXES を使用して、より柔軟な検索を行う

find_path(MYLIB_INCLUDE_DIR
          NAMES mylib.h
          HINTS ENV MYLIB_ROOT # 環境変数 MYLIB_ROOT をヒントとして使う
          PATHS /usr/local /opt /usr # 一般的なインストールパスも指定
          PATH_SUFFIXES include include/mylib # `include` または `include/mylib` サブディレクトリを探す
          DOC "Path to MyLib include directory"
         )

if(MYLIB_INCLUDE_DIR)
  message(STATUS "MyLib include directory found: ${MYLIB_INCLUDE_DIR}")
  include_directories(${MYLIB_INCLUDE_DIR})
else()
  message(FATAL_ERROR "MyLib include directory (mylib.h) not found! "
                      "Please set MYLIB_ROOT environment variable or check installation paths.")
endif()

add_executable(my_lib_app main.cpp)

main.cpp

#include <iostream>
#include <mylib/mylib.h> // PATH_SUFFIXES の効果で可能になるインクルード

int main() {
    MyLib::greet(); // mylib.h で定義された関数を呼び出す
    std::cout << "MyLib version: " << MyLib::getVersion() << std::endl;
    return 0;
}

mylib/mylib.h (架空)

#ifndef MYLIB_MYLIB_H
#define MYLIB_MYLIB_H

#include <string>

namespace MyLib {
    void greet();
    std::string getVersion();
}

#endif // MYLIB_MYLIB_H

説明

  • PATH_SUFFIXES include include/mylib: find_path()は、上記の各パスにincludeまたはinclude/mylibというサフィックスを追加して検索を試みます。これにより、/usr/local/include/mylib/mylib.h/opt/mylib/include/mylib.hのようなパスを効率的に見つけることができます。
  • PATHS /usr/local /opt /usr: 一般的なシステムワイドなインストールパスも検索対象に含めます。
  • HINTS ENV MYLIB_ROOT: MYLIB_ROOTという環境変数が設定されている場合、そのパスを最優先で検索します。ユーザーが手動でライブラリをインストールした場合などに便利です。

例3: デフォルトパスの除外と詳細な制御

この例では、システムのデフォルトパスを検索せず、特定のパスのみを検索するシナリオを示します。これは、特定のバージョンのライブラリを探している場合や、クロスコンパイル環境でホストシステムのパスを誤って参照するのを防ぎたい場合に役立ちます。

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(CustomPathApp C)

# 特定の場所にあるカスタムヘッダーファイルのみを検索
# NO_DEFAULT_PATH を使用して、システムパスなどのデフォルト検索を無効にする
find_path(CUSTOM_HEADER_DIR
          NAMES custom_header.h
          PATHS /path/to/my/custom_libs/v2.0/include # 特定のバージョン2.0のパスのみを指定
                /usr/local/custom_libs/include # 別のカスタムパス
          NO_DEFAULT_PATH # これが重要: デフォルトの検索パス(システムパスなど)を除外する
          REQUIRED
          DOC "Path to custom_header.h for version 2.0"
         )

if(CUSTOM_HEADER_DIR)
  message(STATUS "Custom header directory found: ${CUSTOM_HEADER_DIR}")
  include_directories(${CUSTOM_HEADER_DIR})
else()
  message(FATAL_ERROR "custom_header.h (version 2.0) not found! "
                      "Please ensure it's in /path/to/my/custom_libs/v2.0/include or similar.")
endif()

add_executable(my_custom_app custom_app.c)

custom_app.c

#include <stdio.h>
#include "custom_header.h" // ローカルなヘッダーとしてインクルードされる

int main() {
    printf("Custom message: %s\n", CUSTOM_MESSAGE);
    return 0;
}

/path/to/my/custom_libs/v2.0/include/custom_header.h (架空)

#ifndef CUSTOM_HEADER_H
#define CUSTOM_HEADER_H

#define CUSTOM_MESSAGE "Hello from custom_header.h version 2.0!"

#endif // CUSTOM_HEADER_H

説明

  • NO_DEFAULT_PATH: これにより、find_path()PATHSで明示的に指定されたパスのみを検索し、システム標準のインクルードパスやCMakeの内部パスなどは無視します。これにより、予期せぬ場所にある古いバージョンのヘッダーファイルを拾ってしまうのを防ぎます。

例4: キャッシュの利用と再構成時の動作

find_path()の結果は、デフォルトでCMakeのキャッシュに保存されます。これにより、2回目以降のCMake実行時に同じ検索を繰り返す必要がなくなり、高速化されます。

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(CacheExample C)

# find_path() は結果をキャッシュする
find_path(CACHED_INCLUDE_DIR
          NAMES some_cached_header.h
          PATHS /opt/my_cached_lib/include # 一度見つかればこのパスがキャッシュされる
          DOC "A header path that gets cached"
         )

if(CACHED_INCLUDE_DIR)
  message(STATUS "CACHED_INCLUDE_DIR: ${CACHED_INCLUDE_DIR}")
  include_directories(${CACHED_INCLUDE_DIR})
else()
  message(STATUS "CACHED_INCLUDE_DIR not found on first attempt, or after clearing cache.")
  # 見つからなかった場合の代替処理やエラー
endif()

add_executable(cache_app cache_app.c)

some_cached_header.h/opt/my_cached_lib/includeに配置し、中身は適当で構いません。

動作の確認

  1. 初回実行
    buildディレクトリを作成し、cmake -S . -B buildを実行します。 CACHED_INCLUDE_DIR: /opt/my_cached_lib/includeのようなメッセージが表示されるはずです。 build/CMakeCache.txtを開くと、CACHED_INCLUDE_DIR:FILEPATH=/opt/my_cached_lib/includeのようなエントリが見つかります。

  2. 2回目以降の実行 (変更なし)
    もう一度cmake -S . -B buildを実行します。 find_path()はキャッシュされた値を使用するため、検索は実際には行われず、すぐにCACHED_INCLUDE_DIR: /opt/my_cached_lib/includeというメッセージが表示されます。

  3. キャッシュのクリアと再実行
    build/CMakeCache.txtファイルを削除するか、buildディレクトリ全体を削除します。 再度cmake -S . -B buildを実行すると、find_path()は再びパスを検索し直します。

説明

  • キャッシュされた値をユーザーが変更したい場合は、CMake GUIを使用するか、cmake -DCACHED_INCLUDE_DIR=/new/pathのようにコマンドラインから直接設定できます。
  • find_path()の結果はCMakeCache.txtに保存され、その後の設定フェーズで再利用されます。これにより、ビルドシステムの再設定が高速化されます。


以下に、find_path()の代替となる一般的なプログラミング手法を説明します。

find_package() の利用 (推奨される最も一般的な方法)

最も推奨される方法は、find_package()コマンドを使用することです。これは、特定のライブラリやパッケージ全体を見つけるための包括的なコマンドであり、通常はヘッダーファイルのパスだけでなく、ライブラリのパス、依存関係、バージョン情報なども自動的に検出してくれます。

利点

  • ターゲットの提供
    多くのfind_package()モジュールは、target_link_libraries()で直接使用できる「Imported Targets」を提供します(例: target_link_libraries(my_app SDL2::SDL2))。これにより、パスやライブラリ名を個別に管理する手間が省けます。
  • 依存関係の自動解決
    パッケージが他のパッケージに依存している場合、find_package()はそれらの依存関係も処理するように設計されていることがあります。
  • 標準化
    多くの一般的なライブラリには、CMakeによって提供されるFind<PackageName>.cmakeモジュール(モジュールモード)や、ライブラリ自体が提供するConfig.cmakeファイル(コンフィグモード)があります。これにより、検索が標準化され、信頼性が高まります。
  • 包括的
    ヘッダー、ライブラリ、実行可能ファイルなど、パッケージのすべてのコンポーネントを一度に検索できます。


cmake_minimum_required(VERSION 3.10)
project(MySDL2App C)

# SDL2パッケージを検索
# COMPONENTS を指定することで、特定のサブモジュールを要求できる
# REQUIRED を指定すると、見つからない場合にエラーで停止する
find_package(SDL2 REQUIRED)

# SDL2パッケージが見つかった場合
if(SDL2_FOUND)
  message(STATUS "SDL2 package found.")
  # SDL2によって提供されるインクルードディレクトリを追加
  # これは通常、SDL2_INCLUDE_DIR または SDL2::SDL2 のプロパティとして提供される
  # find_package によって自動的に設定されることが多い
  include_directories(${SDL2_INCLUDE_DIR}) # 古い方法
  # または、現代的なCMakeの方法でターゲットを使う
  # target_link_libraries(my_sdl2_app SDL2::SDL2) # これはリンク時にインクルードパスも設定する

else()
  message(FATAL_ERROR "SDL2 package not found! Please ensure it's installed and findable by CMake.")
endif()

add_executable(my_sdl2_app main.c)

# SDL2のライブラリにリンク (通常 find_package が Imported Target を提供する)
target_link_libraries(my_sdl2_app SDL2::SDL2) # 最新のCMakeの推奨方法

いつ使うべきか

  • 単にヘッダーファイルだけでなく、対応するライブラリも一緒に見つけたい場合。
  • パッケージが独自のConfig.cmakeファイルを提供している場合。
  • 使用したいライブラリやフレームワークが、CMakeのfind_package()に対応するFindモジュールを提供している場合。

find_file() の利用

find_path()がディレクトリを検索するのに対し、find_file()特定のファイルそのものを検索します。結果として返されるのは、そのファイルへのフルパスです。

find_path()との違い

  • find_file(): 検索したファイル自身のフルパスを返す。
  • find_path(): ファイルが存在するディレクトリのパスを返す。

利点

  • 特定の実行可能ファイル、設定ファイル、データファイルなど、ディレクトリではなくファイルそのもののパスが必要な場合に適しています。


cmake_minimum_required(VERSION 3.10)
project(FindFileExample C)

# 特定のデータファイル (例: my_data.json) を検索
find_file(MY_DATA_FILE
          NAMES my_data.json
          PATHS "${CMAKE_CURRENT_SOURCE_DIR}/data" # ソースディレクトリの 'data' サブディレクトリ
                /usr/local/share/my_app
          DOC "Path to my_data.json"
         )

if(MY_DATA_FILE)
  message(STATUS "my_data.json found at: ${MY_DATA_FILE}")
  # アプリケーションで使用するために定義 (例: マクロとして渡すなど)
  add_definitions("-DMY_DATA_JSON_PATH=\"${MY_DATA_FILE}\"")
else()
  message(FATAL_ERROR "my_data.json not found!")
endif()

add_executable(my_app main.c)

main.c

#include <stdio.h>

int main() {
    printf("Loading data from: %s\n", MY_DATA_JSON_PATH);
    // ここで MY_DATA_JSON_PATH を使用してファイルを開くなど
    return 0;
}

いつ使うべきか

  • ヘッダーディレクトリを見つける必要がなく、そのファイル自体が目的の場合。
  • 単一の特定のファイルの場所を見つけたい場合。

file(GLOB ...) / file(GLOB_RECURSE ...) の利用

これらのコマンドは、パターンに一致するファイルやディレクトリを検索するために使用されます。特定の種類のファイルを一括で集める場合に便利です。

利点

  • 特定のディレクトリ内のすべてのソースファイルやヘッダーファイルを収集するのに便利。
  • ワイルドカードパターンを使用して、複数のファイルを一度に検索できる。

注意点

  • パスの検索目的にはあまり直接的ではありませんが、特定のディレクトリ内にあるヘッダーファイル群を収集する場合には利用できます。
  • GLOB系のコマンドは、CMakeの構成段階でファイルシステムをスキャンします。後で新しいファイルが追加されても、CMakeを再構成しない限り検出されません。これはビルドシステムのインクリメンタルビルドに問題を引き起こす可能性があるため、一般的にはソースファイルの収集には推奨されません(add_executable()add_library()に直接ファイルリストを渡す方が良い)。


cmake_minimum_required(VERSION 3.10)
project(GlobExample CXX)

# 特定のディレクトリ内のすべてのヘッダーファイルを収集
file(GLOB_RECURSE MY_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/my_module/*.h"
                                            "${CMAKE_CURRENT_SOURCE_DIR}/my_module/*.hpp")

message(STATUS "Found headers: ${MY_HEADERS}")

# これらのヘッダーが含まれるディレクトリをインクルードパスに追加したい場合
# (find_path() の方が適していることが多いが、一応の例)
# list(GET MY_HEADERS 0 first_header) # 最初のヘッダーファイルを取得
# if(first_header)
#   get_filename_component(HEADER_DIR "${first_header}" DIRECTORY)
#   message(STATUS "Inferred header dir: ${HEADER_DIR}")
#   include_directories(${HEADER_DIR})
# endif()

add_executable(my_glob_app main.cpp)

いつ使うべきか

  • 注意
    パスを「見つける」というよりは、ファイル群を「収集する」用途です。ヘッダーディレクトリのパスを特定するのには、find_path()find_package()の方が直接的で信頼性が高いです。
  • 特定のディレクトリ内の、あるパターンに一致するすべてのファイル(特にソースファイルやアセットファイル)を収集したい場合。

環境変数やユーザー設定からの直接パス指定

最も原始的な方法ですが、ユーザーがビルド時に特定の環境変数やCMakeキャッシュ変数を介して、直接パスを指定することを期待する方法です。

利点

  • ユーザーが依存関係の場所を正確に知っている場合に便利。
  • CMakeの複雑な検索ロジックに依存しないため、非常にシンプル。


cmake_minimum_required(VERSION 3.10)
project(ManualPathExample C)

# ユーザーが MY_LIB_ROOT 環境変数または CMake オプションでパスを指定することを期待
# MY_LIB_ROOT が設定されていない場合、NOTFOUND になる
set(MY_LIB_ROOT "$ENV{MY_LIB_ROOT}" CACHE PATH "Root directory of MyLib library")

if(NOT MY_LIB_ROOT)
  message(FATAL_ERROR "MY_LIB_ROOT environment variable or CMake option is not set. "
                      "Please specify the root directory of MyLib.")
endif()

# ヘッダーディレクトリは MY_LIB_ROOT/include であると仮定
set(MY_LIB_INCLUDE_DIR "${MY_LIB_ROOT}/include")

# ディレクトリが存在するか検証
if(NOT EXISTS "${MY_LIB_INCLUDE_DIR}")
  message(FATAL_ERROR "MyLib include directory not found: ${MY_LIB_INCLUDE_DIR}. "
                      "Please ensure MY_LIB_ROOT points to a valid installation.")
endif()

message(STATUS "MyLib include directory: ${MY_LIB_INCLUDE_DIR}")
include_directories(${MY_LIB_INCLUDE_DIR})

add_executable(my_manual_app main.c)

いつ使うべきか

  • find_package()モジュールが存在しない、または作成する手間を避けたい非常に小規模な内部プロジェクト。
  • 開発者が依存関係を手動で管理することが一般的である場合。
  • 特定のライブラリが非常に特殊なインストールパスを持つ場合。

ほとんどの場合、外部ライブラリの依存関係を解決するには**find_package()**が最も堅牢で推奨される方法です。これはCMakeの設計思想に沿ったものであり、将来的なメンテナンスも容易になります。

find_path()は、find_package()では対応できないような特定のヘッダーディレクトリの検索に特化した際に使用します。find_file()は特定のファイルそのものが必要な場合に利用します。