CMakeでシェルコマンドを実行したい?add_custom_commandの使い方を分かりやすく解説

2024-04-02

CMakeでシェルコマンドを実行する: add_custom_command() 完全ガイド

add_custom_command() は CMake における強力なツールであり、シェルコマンドをビルドプロセスにシームレスに統合できます。このガイドでは、add_custom_command() の詳細な使い方を、豊富な例と分かりやすい解説を通して説明します。

基本構文

add_custom_command(
  TARGET ターゲット名
  [POST_BUILD | PRE_BUILD]
  COMMAND コマンド [引数] ...
  [WORKING_DIRECTORY ディレクトリ]
  [COMMENT コメント]
  [DEPENDS 依存関係]
  [BYPRODUCTS 生成ファイル]
)

主要なオプション

  • TARGET: コマンドを実行するターゲットを指定します。
  • POST_BUILD/PRE_BUILD: コマンドを実行するタイミングを指定します。
  • COMMAND: 実行するシェルコマンドとその引数を指定します。
  • WORKING_DIRECTORY: コマンド実行時の作業ディレクトリを指定します。
  • COMMENT: コマンドの説明を記述します。
  • DEPENDS: コマンド実行前に必要な依存関係を指定します。
  • BYPRODUCTS: コマンド実行によって生成されるファイルを指定します。

実践的な例

1 ファイル生成

add_custom_command(
  OUTPUT 生成ファイル
  COMMAND コマンド [引数] ...
  WORKING_DIRECTORY ディレクトリ
)

この例では、コマンドを実行して生成されたファイルを CMake のビルドシステムに登録します。

例: シェルスクリプトを使用して .h ファイルから .cpp ファイルを生成

add_custom_command(
  OUTPUT main.cpp
  COMMAND ./generate_cpp.sh main.h
  WORKING_DIRECTORY src
)

add_executable(my_app main.cpp)

2 外部ツールとの連携

add_custom_command(
  COMMAND 外部ツール名 [引数] ...
  DEPENDS 依存関係
)

この例では、CMake のビルドプロセスに外部ツールを統合できます。

例: Google Test を使用した単体テストの実行

add_custom_command(
  COMMAND gtest_discover_tests
  WORKING_DIRECTORY test
  DEPENDS my_app)

add_custom_target(
  TEST
  COMMAND gtest_run_tests
  WORKING_DIRECTORY test
  DEPENDS my_app_test)

高度な機能

  • 複数のターゲットへのコマンド実行
  • 条件付きコマンド実行
  • カスタム出力ファイルの扱い
  • 並列処理

まとめ

add_custom_command() は CMake における柔軟かつ強力なツールです。このガイドを参考に、シェルコマンドを効果的に活用し、ビルドプロセスを効率化しましょう。



CMake add_custom_command サンプルコード集

ファイル生成

1 シェルスクリプトで .h から .cpp 生成

add_custom_command(
  OUTPUT main.cpp
  COMMAND ./generate_cpp.sh main.h
  WORKING_DIRECTORY src
)

add_executable(my_app main.cpp)

2 テンプレートファイルからソースファイル生成

add_custom_command(
  OUTPUT main.cpp
  COMMAND cmake -E copy_if_different
    ${CMAKE_CURRENT_SOURCE_DIR}/main.tmpl.cpp
    ${CMAKE_CURRENT_BINARY_DIR}/main.cpp
)

add_executable(my_app main.cpp)

外部ツール連携

1 Google Test 単体テスト

add_custom_command(
  COMMAND gtest_discover_tests
  WORKING_DIRECTORY test
  DEPENDS my_app)

add_custom_target(
  TEST
  COMMAND gtest_run_tests
  WORKING_DIRECTORY test
  DEPENDS my_app_test)

2 Python スクリプト実行

add_custom_command(
  COMMAND python3 my_script.py
  DEPENDS input.txt
)

その他

1 ビルドログ出力

add_custom_command(
  COMMAND echo "Building my_app..."
)

add_executable(my_app main.cpp)

2 ソースコードフォーマット

add_custom_command(
  TARGET my_app
  POST_BUILD
  COMMAND clang-format -i main.cpp
)

add_executable(my_app main.cpp)

3 カスタマイズされた依存関係

set(CUSTOM_DEPENDS "dep1 dep2")

add_custom_command(
  COMMAND my_command
  DEPENDS ${CUSTOM_DEPENDS}
)


CMakeでシェルコマンドを実行するその他の方法

execute_process() は、より柔軟で強力なコマンド実行機能を提供します。add_custom_command() と比べて、以下の点が優れています。

  • 出力ストリームのキャプチャ
  • 戻り値の取得
  • ワーキングディレクトリの変更
  • 環境変数の設定

例: シェルスクリプトを実行して出力を変数に格納

string(APPEND output "")

execute_process(
  COMMAND ./my_script.sh
  WORKING_DIRECTORY src
  OUTPUT_VARIABLE output
)

message(STATUS "Output: ${output}")

add_script() は、短いスクリプトを直接 CMakeLists.txt ファイルに記述できる簡易な方法です。ただし、複雑なコマンドや外部ツールの利用には向いていません。

例: テキストファイルの内容を出力する

add_script(
  "cat input.txt"
  RESULT result
)

message(STATUS "Result: ${result}")

ExternalProject は、外部プロジェクトを CMake プロジェクトに統合するためのモジュールです。複雑な依存関係を持つ外部プロジェクトを管理する場合に役立ちます。

例: Git リポジトリから取得したライブラリをビルド

add_external_project(
  NAME my_library
  URL https://github.com/example/my_library.git
  SOURCE_DIR ${CMAKE_BINARY_DIR}/my_library
  BUILD_COMMAND make
  INSTALL_COMMAND make install
)

target_link_libraries(my_app my_library)

その他

上記以外にも、cmake_policy()configure_file() などの方法も存在します。それぞれの方法にはメリットとデメリットがあり、状況に応じて使い分ける必要があります。

適切な方法は、実行したいコマンドの複雑さ、必要な機能、プロジェクトの規模などによって異なります。以下は、それぞれの方法の適用例です。

  • 単純なコマンド: add_custom_command() または add_script()
  • 複雑なコマンド: execute_process()
  • 外部プロジェクト: ExternalProject
  • 条件付き実行: if() ステートメント
  • ループ処理: foreach() ステートメント

どの方法を選択する場合でも、CMake のドキュメントをよく読んで、使用方法を理解することが重要です。




CMakeコマンド「ctest_submit()」でテスト結果をCDashサーバーに送信

ctest_submit()は、CMakeの「Commands」カテゴリに属するコマンドで、テスト結果をCDashなどのダッシュボードサーバーに送信するために使用されます。テスト実行後の結果を可視化、共有したい場合に役立ちます。基本構文オプション解説



CMake find_libraryコマンドとfind_packageモジュールの比較

find_library() コマンドは、CMake で外部ライブラリを見つけるために使用されます。これは、プロジェクトに必要なライブラリを自動的に検出して、ビルドプロセスを簡略化するのに役立ちます。コマンドフォーマット<VAR>: 検索結果を格納する変数名


CMakeのCommandsにおけるuse_mangled_mesa()

use_mangled_mesa() は CMake の Commands における関数で、Mesa ライブラリの mangled シンボル名を解決するために使用されます。Mesa は OpenGL の実装であり、古いバージョンの Mesa ではシンボル名が mangled されるため、use_mangled_mesa() を使用してこれらのシンボル名を解決する必要があります。


cmake_policy()コマンドを使いこなしてCMakeプロジェクトをマスター

cmake_policy()コマンドの基本的な構文は以下の通りです。<policy-id>: ポリシーの識別子。CMP<NNNN>形式で指定されます。<behavior>: ポリシーの動作。OLDまたはNEWを指定します。OLDとNEWの動作の違い


CMake include() で効率的なビルドを実現

include() は CMake の重要なコマンドの一つで、他の CMake ファイルやモジュールを読み込むために使用されます。 これにより、コードを分割し、再利用性と保守性を向上させることができます。機能他の CMake ファイルを読み込んで、その中のコマンドを実行する



CMakeでMSVC_IDEを使ってVisual Studioプロジェクトを構築

CMAKE_GENERATOR: 使用する Visual Studio ジェネレータを指定します。主な値は以下の通りです。 "Visual Studio 16 2019": Visual Studio 2019"Visual Studio 16 2019": Visual Studio 2019


CMAKE 変数 CMAKE_INSTALL_NAME_DIR の詳細解説:macOS におけるライブラリインストールの極意

用途macOS における共有ライブラリや実行ファイルのインストールパスを制御します。アプリケーションバンドル内のライブラリのロード順序を制御します。@rpath を使用して、ランタイム検索パスを設定します。設定方法上記のように、CMAKE_INSTALL_NAME_DIR 変数にインストールディレクトリパスを設定できます。


CMake の CMAKE_GENERATOR_TOOLSET 変数に関する FAQ

CMAKE_GENERATOR_TOOLSET は CMake の変数であり、特定のジェネレータが使用するネイティブビルドシステムのツールセットを指定します。これは、特定のコンパイラやツールチェーンを選択したり、ビルドプロセスをカスタマイズしたりするために使用できます。


C++11/14/17/20/23をCMakeで使う!各標準のサンプルコード付き

CMakeのCMAKE_CXX_STANDARD変数は、プロジェクトで使用されるC++言語の標準規格を指定するために使用されます。これは、コンパイラにどの言語機能が利用可能であるかを伝える重要な役割を果たします。設定方法CMAKE_CXX_STANDARD変数は、以下の方法で設定できます。


CMakeにおける「CMAKE_LIBRARY_PATH」変数の詳細解説

デフォルト値CMAKE_LIBRARY_PATH はデフォルトで空の文字列に設定されています。つまり、CMakeはライブラリを検索する際に、現在のディレクトリのみを調べます。設定方法CMAKE_LIBRARY_PATH は、CMakeLists