【CMake】PROJECT_IS_TOP_LEVELを使いこなす!トップレベルかどうかを判定して処理を制御
PROJECT_IS_TOP_LEVEL
は CMake における組み込み変数であり、現在処理されている CMakeLists.txt ファイルがプロジェクトのトップレベルのものかどうかを真偽値で示します。これは、CMake 3.21 以降で導入された比較的新しい変数です。
用途
この変数は、主に以下の用途に役立ちます。
- サブディレクトリの処理を制御する:
add_subdirectory()
コマンドでサブディレクトリを追加する場合、そのサブディレクトリ内にproject()
コマンドが存在するかどうかを判定するのに役立ちます。 - 外部プロジェクトの依存関係を管理する:
FetchContent
モジュールを使用して外部プロジェクトを追加する場合、PROJECT_IS_TOP_LEVEL
を用いてトップレベルプロジェクトのみで依存関係を処理することができます。 - プロジェクト固有のビルド設定を適用する: トップレベルの CMakeLists.txt ファイルでのみ実行される処理を定義したい場合に便利です。
動作
PROJECT_IS_TOP_LEVEL
の値は、以下のいずれかの場合に TRUE
になります。
add_subdirectory()
で追加されたサブディレクトリ (ただし、そのサブディレクトリ内にproject()
コマンドが存在しない場合):add_subdirectory()
コマンドでサブディレクトリを追加した場合、そのサブディレクトリ内の CMakeLists.txt ファイルが処理されている場合。ただし、そのサブディレクトリ内にproject()
コマンドが存在する場合はFALSE
になります。ExternalProject
で追加された外部プロジェクトのトップレベル:FetchContent
モジュールを使用して追加された外部プロジェクトのルートディレクトリにある CMakeLists.txt ファイルが処理されている場合。- トップレベルの CMakeLists.txt ファイル: プロジェクトのルートディレクトリにある CMakeLists.txt ファイルが処理されている場合。
- サブディレクトリ内に複数の CMakeLists.txt ファイルが存在する場合、
PROJECT_IS_TOP_LEVEL
は そのサブディレクトリ内で最初に処理された CMakeLists.txt ファイルに基づいて値が決定されます。 PROJECT_IS_TOP_LEVEL
は、現在のディレクトリスコープまたはそれより上位における 最も最近呼び出されたproject()
コマンド に基づいて値が決定されます。
例
以下の例は、PROJECT_IS_TOP_LEVEL
を使用してプロジェクト固有のビルド設定を適用する方法を示しています。
cmake_minimum_required(VERSION 3.21)
project(MyProject)
if(PROJECT_IS_TOP_LEVEL)
# トップレベルプロジェクトの場合のみ実行される処理
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_FLAGS "-Wall -O0")
endif()
add_executable(my_app main.cpp)
この例では、CMAKE_BUILD_TYPE
と CMAKE_CXX_FLAGS
はトップレベルプロジェクトの場合のみ設定されます。
cmake_minimum_required(VERSION 3.21)
project(MyProject)
if(PROJECT_IS_TOP_LEVEL)
# トップレベルプロジェクトの場合のみ実行される処理
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_FLAGS "-Wall -O0")
endif()
add_executable(my_app main.cpp)
説明
この例では、PROJECT_IS_TOP_LEVEL
を使用して、トップレベルプロジェクトの場合のみ CMAKE_BUILD_TYPE
と CMAKE_CXX_FLAGS
を設定しています。
例 2: 外部プロジェクトの依存関係を管理する
cmake_minimum_required(VERSION 3.21)
project(MyProject)
if(PROJECT_IS_TOP_LEVEL)
# トップレベルプロジェクトの場合のみ実行される処理
fetch_content_make_available(MyExternalProject)
endif()
add_executable(my_app main.cpp)
target_link_libraries(my_app MyExternalProject)
説明
この例では、PROJECT_IS_TOP_LEVEL
を使用して、FetchContent
モジュールで追加された外部プロジェクトの依存関係をトップレベルプロジェクトのみで処理しています。
例 3: サブディレクトリの処理を制御する
cmake_minimum_required(VERSION 3.21)
project(MyProject)
add_subdirectory(lib1)
add_subdirectory(lib2)
if(PROJECT_IS_TOP_LEVEL)
# トップレベルプロジェクトの場合のみ実行される処理
add_executable(my_app main.cpp lib1/lib1.a lib2/lib2.a)
endif()
説明
この例では、PROJECT_IS_TOP_LEVEL
を使用して、add_subdirectory()
で追加されたサブディレクトリを処理するかどうかを制御しています。lib1
サブディレクトリ内に project()
コマンドが存在するため、lib1
はサブディレクトリとして処理され、トップレベルプロジェクトにはなりません。一方、lib2
サブディレクトリ内に project()
コマンドが存在しないため、lib2
はトップレベルプロジェクトとして処理されます。
しかし、状況によっては PROJECT_IS_TOP_LEVEL
の代替方法が必要となる場合があります。以下に、いくつかの代替方法をご紹介します。
CMAKE_ROOT を使用する
CMAKE_ROOT
は、CMake の現在の作業ディレクトリを表す組み込み変数です。この変数を使用して、トップレベルディレクトリかどうかを判定することができます。
cmake_minimum_required(VERSION 3.0)
project(MyProject)
if(CMAKE_ROOT STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
# トップレベルプロジェクトの場合のみ実行される処理
message(STATUS "This is the top-level project directory.")
else()
# サブディレクトリの場合
message(STATUS "This is a subdirectory.")
endif()
親ディレクトリを確認する
現在のディレクトリの親ディレクトリを確認することで、トップレベルディレクトリかどうかを判定することができます。
cmake_minimum_required(VERSION 3.0)
project(MyProject)
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt")
# トップレベルプロジェクトの場合
message(STATUS "This is the top-level project directory.")
else()
# サブディレクトリの場合
message(STATUS "This is a subdirectory.")
endif()
カスタム変数を使用する
プロジェクトのトップレベルかどうかを判定するカスタム変数を作成することができます。
cmake_minimum_required(VERSION 3.0)
project(MyProject)
set(PROJECT_TOP_LEVEL FALSE)
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt")
set(PROJECT_TOP_LEVEL TRUE)
endif()
if(PROJECT_TOP_LEVEL)
# トップレベルプロジェクトの場合のみ実行される処理
message(STATUS "This is the top-level project directory.")
else()
# サブディレクトリの場合
message(STATUS "This is a subdirectory.")
endif()
CMAKE_COMMAND を使用する
CMAKE_COMMAND
変数は、現在の CMake 実行コマンドを表す組み込み変数です。この変数を使用して、コマンドライン引数からトップレベルディレクトリかどうかを判定することができます。
cmake_minimum_required(VERSION 3.0)
project(MyProject)
string(FIND "${CMAKE_COMMAND}" "-DCMAKE_BUILD_ROOT=" BUILD_ROOT_DIR)
if(BUILD_ROOT_DIR STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
# トップレベルプロジェクトの場合
message(STATUS "This is the top-level project directory.")
else()
# サブディレクトリの場合
message(STATUS "This is a subdirectory.")
endif()
最適な代替方法の選択
上記の代替方法はそれぞれ長所と短所があります。状況に応じて最適な方法を選択する必要があります。
CMAKE_COMMAND
を使用する: 高度な方法ですが、複雑なコマンドライン引数を使用している場合は、誤動作する可能性があります。- カスタム変数を使用する: 柔軟性が高い方法ですが、コードが煩雑になる可能性があります。
- 親ディレクトリを確認する: どのバージョンの CMake でも使用できますが、
CMakeLists.txt
ファイルが存在しないサブディレクトリをトップレベルと誤認する可能性があります。 CMAKE_ROOT
を使用する: 最もシンプルで分かりやすい方法ですが、CMake 3.0 以降でのみ使用可能です。
- 具体的な方法は、プロジェクトの要件や環境に合わせて調整する必要があります。
- 上記の代替方法はあくまでも例であり、状況に応じて様々な方法が考えられます。