CMakeプログラミングのデバッグ術:message()コマンドの活用法とよくあるエラー
message()
コマンドは、CMakeのスクリプト実行中にユーザーに対して情報を出力するために使用されます。デバッグ、進捗状況の表示、エラーや警告の通知など、さまざまな目的に利用できます。
基本的な使い方
message()
コマンドの最も基本的な使い方は、引数に文字列を渡すことです。
message("これは単なるメッセージです。")
このコマンドが実行されると、CMakeのコンソールに出力されます。
メッセージの種類(ログレベル)
message()
コマンドは、出力するメッセージの種類(ログレベル)を指定することができます。これにより、メッセージの重要度に応じてCMakeの動作を変えたり、特定のログレベルのメッセージだけを表示したりすることが可能になります。主なログレベルは以下の通りです。
-
DEBUG (CMake 3.15以降)
VERBOSE
よりもさらに詳細なデバッグ情報に使用されます。message(DEBUG "内部処理: 関数Aが呼び出されました。")
-
VERBOSE (CMake 3.15以降)
詳細なデバッグ情報など、通常は表示されないが、デバッグ時に役立つメッセージです。CMAKE_MESSAGE_LOG_LEVEL
変数を設定することで表示を制御できます。message(VERBOSE "デバッグ情報: 変数Xの値は ${VARIABLE_X} です。")
-
NOTICE (CMake 3.15以降)
STATUS
よりも少し重要度の高い情報メッセージです。message(NOTICE "最適化オプションが有効になっています。")
-
FATAL_ERROR
: 致命的なエラーが発生し、CMakeの実行を即座に中断させたい場合に使用します。SEND_ERROR
と同様にCMakeは中断されますが、より深刻な状況を示します。message(FATAL_ERROR "設定ファイルが見つかりません。")
-
SEND_ERROR
: 致命的ではないが、ビルドを続行できないエラーを通知します。CMakeの実行は中断されます。message(SEND_ERROR "必要なライブラリが見つかりませんでした。")
-
AUTHOR_WARNING
: 開発者(AUTHOR
)がCMakeLists.txtの記述に問題があることを通知したい場合に使用します。通常、エンドユーザーには表示されません。message(AUTHOR_WARNING "この機能は非推奨です。更新してください。")
-
WARNING
: ビルドプロセスには影響しないが、ユーザーに注意を促したい場合に利用します。message(WARNING "古いコンパイラバージョンが検出されました。")
-
STATUS
: プロジェクトの進捗状況を示すメッセージによく使われます。ユーザーに現在の処理段階を伝えるのに適しています。message(STATUS "プロジェクトのビルドを開始します。")
変数の出力
message()
コマンドは、CMakeの変数の内容を出力するのにも非常に便利です。変数を$変数名
の形式で文字列内に含めることで、その値が出力されます。
set(MY_VARIABLE "Hello CMake")
message(STATUS "MY_VARIABLEの値: ${MY_VARIABLE}")
デバッグ用途での利用
message()
は、CMakeスクリプトが意図した通りに動作しているかを確認するためのデバッグツールとして非常に強力です。例えば、特定の条件分岐に入ったかどうか、変数の値が期待通りになっているかなどを確認するために使用できます。
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Linuxシステムで実行中...")
else()
message(STATUS "Linux以外のシステムで実行中: ${CMAKE_SYSTEM_NAME}")
endif()
注意点
SEND_ERROR
やFATAL_ERROR
を使用すると、CMakeの実行が中断されるため、エラーハンドリングに役立ちます。message()
は、ビルドのたびに必ず出力されます。製品ビルドなどでは不要なメッセージは削除するか、条件付きで出力するように考慮すると良いでしょう。
message()
コマンド自体は比較的シンプルですが、その使用方法やコンテキストによっては予期せぬ挙動やエラーにつながることがあります。
メッセージが出力されない、または期待通りに出力されない
考えられる原因とトラブルシューティング
-
キャッシュされた変数の影響
- 原因
以前のCMake実行で設定された変数のキャッシュが原因で、現在のビルド設定が意図通りになっていない可能性があります。 - トラブルシューティング
- ビルドディレクトリを削除し、一からビルドプロセスをやり直してみてください(クリーンビルド)。
- または、
cmake-gui
やccmake
を使用してキャッシュ変数の値を確認し、必要に応じてリセットします。
- 原因
-
条件分岐の中にある
- 原因
message()
がif()
文やforeach()
ループなどの条件分岐の中にあり、その条件が満たされていないため、メッセージが出力されないことがあります。 - トラブルシューティング
message()
を一時的に条件分岐の外に出して、そもそもそのmessage()
コマンド自体が実行されるかを確認します。- 条件分岐の条件式で使われている変数の値が期待通りになっているかを、別の
message()
で出力して確認します。
- 原因
-
- 原因
CMake 3.15以降で導入されたNOTICE
,VERBOSE
,DEBUG
などのログレベルを使用している場合、CMAKE_MESSAGE_LOG_LEVEL
変数の設定によってはこれらのメッセージが表示されません。デフォルトのログレベルでは表示されないことがあります。 - トラブルシューティング
- CMakeを実行する際に、コマンドラインで
-DCMAKE_MESSAGE_LOG_LEVEL=DEBUG
のように指定して、より詳細なログレベルを有効にしてみてください。 - または、
CMakeLists.txt
の先頭でset(CMAKE_MESSAGE_LOG_LEVEL DEBUG)
のように設定することもできます(ただし、これはユーザーの設定を上書きする可能性があるため、注意が必要です)。 - また、
STATUS
や通常のメッセージなど、常に表示されるログレベルを使用しているか確認してください。
- CMakeを実行する際に、コマンドラインで
- 原因
FATAL_ERRORやSEND_ERRORが意図せずビルドを停止する
考えられる原因とトラブルシューティング
- トラブルシューティング
- エラーメッセージの内容を注意深く読み、何が原因でエラーがトリガーされたのかを特定します。
- エラーを発生させている条件分岐(例:
if(NOT SOME_REQUIRED_VAR)
)を確認し、その条件がなぜ満たされていないのか(例:SOME_REQUIRED_VAR
が設定されていないのはなぜか)を調べます。 - 必要なファイルやライブラリが適切にインストールされているか、パスが正しく設定されているかを確認します。
- もしデバッグのために一時的に
FATAL_ERROR
を使っていたのであれば、デバッグが完了したらSTATUS
やWARNING
などのログレベルに戻すのを忘れないようにしてください。
- 原因
message(FATAL_ERROR "...")
やmessage(SEND_ERROR "...")
は、CMakeの実行を中断させるように設計されています。意図せずにこれらがトリガーされると、ビルドが停止します。これは、設定ファイルのパスの誤りや、必須コンポーネントの不足など、本来はエラーであるべき状況で発生します。
変数が展開されない、または間違った値が出力される
考えられる原因とトラブルシューティング
- トラブルシューティング
- 変数の参照
CMakeでは変数を参照する際に${VAR_NAME}
の形式を使用します。$VAR_NAME
形式は一部の文脈(例えば正規表現)で異なる意味を持つ可能性があるため、一貫して${VAR_NAME}
を使用することが推奨されます。# 良い例 message("変数の値: ${MY_VAR}") # 悪い例 (意図しない結果になる可能性) # message("変数の値: $MY_VAR")
- 変数の定義順
message()
で変数を出力する前に、その変数がset()
コマンドなどで適切に定義されていることを確認してください。 - スコープ
CMakeの変数はスコープを持ちます。function()
やmacro()
内で定義された変数は、デフォルトではそのブロック内でのみ有効です(PARENT_SCOPE
やCACHE
修飾子を使わない限り)。変数を定義した場所とmessage()
で参照した場所のスコープ関係を確認してください。 - キャッシュ変数
set(MY_VAR "value" CACHE STRING "description")
のようにキャッシュ変数として定義された変数は、一度設定されるとCMake実行間でその値が保持されます。意図しない古い値が出力される場合は、ビルドディレクトリをクリーンアップするか、cmake-gui
/ccmake
でキャッシュをリセットしてください。
- 変数の参照
- 原因
- 変数の参照方法が誤っている(例:
$VARIABLE
ではなく${VARIABLE}
を使うべき)。 - 変数が定義される前に
message()
で参照されている。 - スコープの問題(変数が現在のスコープで利用できない)。
- 変数がキャッシュされている。
- 変数の参照方法が誤っている(例:
文字列中の特殊文字やクォーテーションの問題
考えられる原因とトラブルシューティング
- トラブルシューティング
- クォーテーション
message()
の引数として渡す文字列は、必ずダブルクォーテーションで囲んでください。message("Hello, World!")
- エスケープシーケンス
バックスラッシュ(\
)をエスケープしたい場合は、二重のバックスラッシュ(\\
)を使用します。改行文字は通常通り\n
で機能します。message("パス: C:\\Program Files\\My App") message("一行目\n二行目")
- 変数の内容
変数の内容が特殊文字を含んでいる場合、それが原因でmessage()
が予期せぬ挙動をすることがあります。変数の内容を単独で出力してみて、問題がないか確認してください。
- クォーテーション
- 原因
メッセージ文字列内で特殊文字(例:\n
、\
、"
など)を使用したり、文字列全体を正しくクォーテーションで囲まなかったりすると、構文エラーや意図しない出力につながることがあります。
コンパイラエラーと混同される
考えられる原因とトラブルシューティング
- トラブルシューティング
- CMakeの設定段階で出力される
message()
によるエラーや警告は、通常、コンパイラが起動する前(Configureステップ)に表示されます。問題の発生タイミング(Configure時かGenerate時かBuild時か)を把握することで、CMakeスクリプトの問題なのか、コンパイル/リンクの問題なのかを切り分けられます。 - CMakeが生成するログファイル(
CMakeFiles/CMakeOutput.log
やCMakeFiles/CMakeError.log
など)を確認することで、より詳細な情報が得られる場合があります。
- CMakeの設定段階で出力される
- 原因
message()
で出力されたエラーや警告が、その後のコンパイルエラーやリンクエラーと混同され、問題の切り分けが難しくなることがあります。
message()
コマンドのデバッグでは、以下の点に注目することが重要です。
- メッセージが出力されるべきタイミングで出力されているか? (条件分岐、ログレベル)
- 出力されているメッセージの内容は期待通りか? (変数の展開、文字列のエスケープ)
- エラーメッセージの場合、何が原因で停止したのか? (パス、依存関係、キャッシュ)
message()
コマンドは、デバッグ、情報通知、エラー処理など、さまざまな場面で活用できます。以下にいくつかの典型的な例を示します。
基本的な情報メッセージの出力
最もシンプルに、ユーザーに進捗状況や一般的な情報を伝える場合です。
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MyExampleProject CXX)
message("--- プロジェクト設定を開始します ---")
# 何らかの処理...
# 例えば、変数の設定
set(BUILD_TYPE "Debug")
message("現在のビルドタイプは '${BUILD_TYPE}' です。")
message("プロジェクトのビルド準備中...")
# ...
message("--- プロジェクト設定が完了しました ---")
実行結果の例
--- プロジェクト設定を開始します ---
現在のビルドタイプは 'Debug' です。
プロジェクトのビルド準備中...
--- プロジェクト設定が完了しました ---
ログレベルを指定したメッセージ
CMake 3.15以降で導入されたログレベル(STATUS
, WARNING
, NOTICE
, VERBOSE
, DEBUG
など)を使って、メッセージの重要度を分類する例です。
# CMakeLists.txt
cmake_minimum_required(VERSION 3.15) # VERBOSE, DEBUGなどを使う場合は3.15以上
project(LogLevelExample CXX)
# 通常、ユーザーに表示される情報
message(STATUS "プロジェクト設定を開始中...")
# 注意を促す警告
message(WARNING "最適化フラグが設定されていません。リリースビルドでは検討してください。")
# より詳細な情報。通常は表示されないが、デバッグ時に役立つ
message(VERBOSE "内部デバッグ情報: コンパイラIDは '${CMAKE_CXX_COMPILER_ID}' です。")
message(DEBUG "さらに詳細なデバッグ情報: ターゲットディレクトリは '${CMAKE_BINARY_DIR}' です。")
# 新しい通知レベル (CMake 3.15以降)
message(NOTICE "このCMakeバージョンは3.15以上です。")
# ...
実行方法と結果の例
-
DEBUGメッセージも表示させる場合
cmake -S . -B build -DCMAKE_MESSAGE_LOG_LEVEL=DEBUG
-- プロジェクト設定を開始中... CMake Warning (dev) at CMakeLists.txt:10 (message): 最適化フラグが設定されていません。リリースビルドでは検討してください。 This warning is for project developers. Use -Wno-dev to suppress it. -- 内部デバッグ情報: コンパイラIDは 'GNU' です。 -- さらに詳細なデバッグ情報: ターゲットディレクトリは '/path/to/project/build' です。 -- このCMakeバージョンは3.15以上です。 -- Configuring done -- Generating done -- Build files have been written to: /path/to/project/build
-
VERBOSEメッセージも表示させる場合
cmake -S . -B build -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE
-- プロジェクト設定を開始中... CMake Warning (dev) at CMakeLists.txt:10 (message): 最適化フラグが設定されていません。リリースビルドでは検討してください。 This warning is for project developers. Use -Wno-dev to suppress it. -- 内部デバッグ情報: コンパイラIDは 'GNU' です。 -- このCMakeバージョンは3.15以上です。 -- Configuring done -- Generating done -- Build files have been written to: /path/to/project/build
-
cmake -S . -B build
-- プロジェクト設定を開始中... CMake Warning (dev) at CMakeLists.txt:10 (message): 最適化フラグが設定されていません。リリースビルドでは検討してください。 This warning is for project developers. Use -Wno-dev to suppress it. -- このCMakeバージョンは3.15以上です。 -- Configuring done -- Generating done -- Build files have been written to: /path/to/project/build
条件付きメッセージとエラー処理
特定の条件が満たされた場合のみメッセージを出力したり、致命的なエラーが発生した場合にビルドを停止させたりする例です。
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(ConditionalMessageExample CXX)
# 必須のライブラリのパスを定義(デモンストレーションのため手動で設定)
set(REQUIRED_LIB_PATH "/usr/local/mylib")
# set(REQUIRED_LIB_PATH "") # パスがない場合をシミュレートするならコメントアウト
if(NOT EXISTS "${REQUIRED_LIB_PATH}/libmylib.so")
# 致命的なエラー: このライブラリがないとビルドできない
message(FATAL_ERROR "エラー: 必要なライブラリ 'libmylib.so' が '${REQUIRED_LIB_PATH}' に見つかりません。ビルドを停止します。")
else()
# 成功メッセージ
message(STATUS "ライブラリ 'libmylib.so' が '${REQUIRED_LIB_PATH}' で見つかりました。")
# ここでライブラリをリンクする処理などを記述
# 例えば add_library や target_link_libraries など
endif()
# ユーザーが特定のコンパイラを使用しているか確認し、警告を出す
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
message(WARNING "MSVCコンパイラを使用しています。一部の機能は非推奨です。")
else()
message(STATUS "現在のコンパイラは ${CMAKE_CXX_COMPILER_ID} です。")
endif()
実行結果の例
-
ライブラリが見つかり、MSVC以外のコンパイラの場合
(set(REQUIRED_LIB_PATH "/usr/local/mylib")
が有効で、libmylib.so
が存在する場合)-- ライブラリ 'libmylib.so' が '/usr/local/mylib' で見つかりました。 -- 現在のコンパイラは GNU です。 -- Configuring done -- Generating done -- Build files have been written to: /path/to/project/build
-
ライブラリが見つからない場合
(set(REQUIRED_LIB_PATH "")
を有効にして実行した場合)CMake Error at CMakeLists.txt:10 (message): エラー: 必要なライブラリ 'libmylib.so' が '' に見つかりません。ビルドを停止します。 -- Configuring incomplete, errors occurred!
この場合、CMakeの処理は即座に中断されます。
変数の値のデバッグ出力
デバッグ時によく使うパターンで、変数の現在の値を確認します。
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(DebugVariableExample)
set(SOURCE_FILES "main.cpp" "utility.cpp" "header.h")
set(INSTALL_PREFIX "/opt/my_app")
set(DEBUG_MODE FALSE)
# DEBUG_MODEがONの場合にのみ詳細な情報を出力
if(DEBUG_MODE)
message(STATUS "--- デバッグモードが有効です ---")
message(STATUS "ソースファイルリスト: ${SOURCE_FILES}")
message(STATUS "インストールプレフィックス: ${INSTALL_PREFIX}")
else()
message(STATUS "デバッグモードは無効です。")
endif()
# LIST_APPENDを使ってリストに項目を追加した後も確認
list(APPEND SOURCE_FILES "new_feature.cpp")
message(STATUS "更新後のソースファイルリスト: ${SOURCE_FILES}")
実行結果の例
-- デバッグモードは無効です。
-- 更新後のソースファイルリスト: main.cpp;utility.cpp;header.h;new_feature.cpp
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/project/build
set(DEBUG_MODE TRUE)
に変更して実行すると、より詳細な情報が表示されます。
message()
コマンドはCMakeスクリプトのデバッグや情報出力に非常に便利ですが、より構造化された出力や特定の目的のためには、他のコマンドやCMakeの機能を利用する代替手法があります。
cmake_print_variables() (CMake 3.15以降)
# CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(PrintVariablesExample)
set(MY_VAR1 "Value A")
set(MY_VAR2 "Value B")
set(MY_LIST "Item1" "Item2" "Item3")
# 複数の変数をまとめて出力
cmake_print_variables(MY_VAR1 MY_VAR2 MY_LIST CMAKE_SOURCE_DIR)
# 結果の例:
# MY_VAR1="Value A"
# MY_VAR2="Value B"
# MY_LIST="Item1;Item2;Item3"
# CMAKE_SOURCE_DIR="/path/to/source"
利点
message()
のように個別に文字列を組み立てる必要がない。- 複数の変数を一度に指定できる。
- 変数の名前と値が自動的に整形されて出力されるため、デバッグがしやすい。
欠点
- CMake 3.15より前のバージョンでは利用できない。
- 固定の出力形式しか選択できない。
string(CONCAT ...) または string(JOIN ...) と message() の組み合わせ
複雑なメッセージ文字列を構築し、それをmessage()
で出力する場合に、string()
コマンドのサブコマンドが役立ちます。これにより、message()
の引数部分が簡潔になります。
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(StringConcatExample)
set(VAR_A "Hello")
set(VAR_B "World")
set(VAR_C "!")
# CONCATで複数の文字列と変数を結合
string(CONCAT MY_MESSAGE "--- " ${VAR_A} " " ${VAR_B} ${VAR_C} " ---")
message(STATUS "${MY_MESSAGE}")
# 結果: --- Hello World! ---
set(MY_LIST "Apple" "Banana" "Cherry")
# JOINでリストの要素を区切り文字で結合
string(JOIN ", " JOINED_LIST "${MY_LIST}")
message(STATUS "果物リスト: ${JOINED_LIST}")
# 結果: 果物リスト: Apple, Banana, Cherry
利点
message()
のログレベルや他の機能と組み合わせて利用できる。- 特にリストの要素を整形して表示したい場合に便利。
- 複雑な文字列を事前に構築でき、
message()
の引数をシンプルに保てる。
file(WRITE ...) / file(APPEND ...) によるログファイルへの出力
コンソール出力だけでなく、永続的なログファイルに情報を書き込みたい場合に利用します。ビルドプロセス中に発生した詳細な情報を後で確認するのに役立ちます。
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(FileLogExample)
set(LOG_FILE "${CMAKE_BINARY_DIR}/my_build_log.txt")
# ファイルを新規作成して書き込む
file(WRITE "${LOG_FILE}" "--- ビルドログ開始 (${CMAKE_BUILD_TYPE}) ---\n")
message(STATUS "ログを '${LOG_FILE}' に出力します。")
# 情報をログファイルに追記
file(APPEND "${LOG_FILE}" "プロジェクト名: ${PROJECT_NAME}\n")
file(APPEND "${LOG_FILE}" "ソースディレクトリ: ${CMAKE_SOURCE_DIR}\n")
# 条件付きでさらに情報を追記
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
file(APPEND "${LOG_FILE}" "システム: Linux\n")
else()
file(APPEND "${LOG_FILE}" "システム: ${CMAKE_SYSTEM_NAME}\n")
endif()
file(APPEND "${LOG_FILE}" "--- ビルドログ終了 ---\n")
利点
- CI/CD環境などで自動的にログを収集するのに適している。
- コンソール出力が流れてしまっても、後から詳細な情報を確認できる。
欠点
- エラーメッセージのようにビルドプロセスを中断させる機能はない(別途
message(FATAL_ERROR)
と組み合わせる必要がある)。 - 即座にコンソールで情報を確認できない。
find_package() の出力
これは直接的な代替というよりは、外部のライブラリやモジュールの検索結果を表示する際に、message()
を補完するものです。find_package()
自体がデフォルトでSTATUS
メッセージを出力しますが、QUIET
やREQUIRED
オプションでその挙動を制御できます。
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(FindPackageExample)
# 通常の出力(STATUSメッセージで成功/失敗を表示)
find_package(Boost COMPONENTS system filesystem)
# 静かに検索し、成功した場合のみ表示
find_package(ZLIB QUIET)
if(ZLIB_FOUND)
message(STATUS "ZLIBが見つかりました: ${ZLIB_LIBRARIES}")
else()
message(WARNING "ZLIBが見つかりません。")
endif()
# 必須のパッケージで、見つからない場合はFATAL_ERRORに相当
# find_package(NonExistentLib REQUIRED)
# 上記は「見つかりません」というエラーメッセージを出力し、CMakeを停止させます。
利点
REQUIRED
オプションで依存関係の不足を自動的にFATAL_ERROR
として扱える。QUIET
オプションで冗長な出力を抑制できる。- パッケージ検索に関する標準的な情報出力として機能する。
欠点
- 一般的な情報出力ではなく、パッケージ検索に特化している。
add_custom_command() や add_custom_target() を利用した外部スクリプトの実行
CMakeの範囲を超えて、PythonやBashなどの外部スクリプトに処理を委譲し、そのスクリプト内で情報出力やログ記録を行うことも可能です。
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(ExternalScriptExample)
# Pythonスクリプトを作成(デモンストレーションのためCMakeLists.txt内に記述)
file(WRITE "${CMAKE_BINARY_DIR}/my_script.py" [[
import sys
print("Pythonスクリプトから: ビルド前処理を開始します。")
print(f"Pythonスクリプトから: 第一引数 = {sys.argv[1]}")
print(f"Pythonスクリプトから: 第二引数 = {sys.argv[2]}")
# デバッグ情報をファイルに書き込むことも可能
with open("python_log.txt", "w") as f:
f.write("Pythonスクリプトによる詳細ログ\n")
]])
# カスタムコマンドとしてPythonスクリプトを実行
add_custom_command(
OUTPUT "${CMAKE_BINARY_DIR}/prebuild_done.txt"
COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_BINARY_DIR}/prebuild_done.txt" # ダミーのOUTPUT
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_BINARY_DIR}/my_script.py" "Arg1" "Arg2"
COMMENT "Pythonスクリプトを実行中..."
)
# カスタムターゲットとしてPythonスクリプトを実行
add_custom_target(run_my_python_script
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_BINARY_DIR}/my_script.py" "TargetArg1" "TargetArg2"
COMMENT "カスタムターゲット経由でPythonスクリプトを実行中..."
)
# 例として、カスタムコマンドを何らかのターゲットのpre-buildイベントに紐づける
add_executable(MyApp main.cpp)
add_dependencies(MyApp ${CMAKE_BINARY_DIR}/prebuild_done.txt) # 常に実行されるようにする
# ユーザーは `cmake --build . --target run_my_python_script` で明示的に実行
利点
- CMakeの機能を補完する柔軟性がある。
- より複雑なロギングロジックや外部システムとの連携が可能になる。
- CMakeのログとは別の方法で出力を管理する必要がある。
- 外部スクリプトの依存関係(Pythonインタープリタなど)が発生する。