パソコンでプログラミングしよう ウィンドウズC++プログラミング環境の構築
1.6.3.6(15)
Code::Blocksのプレ/ポストビルドステップ

統合開発環境Code::Blocksでビルド前後に外部ツールを自動実行するプレ/ポストビルドステップを説明する。

本サイトの利用する統合開発環境Code::Blocksは[Project|Build options]の[Project build options]ダイアログによりプロジェクトオプションとターゲットオプションの両方において[Pre/post build steps]ページ[Pre-buld steps]および[Post-build steps]複数行テキストボックスにより、実行ファイルのビルド前後に外部ツールを起動することができる。

外部ツールはToolsメニューからも起動できるが、ビルド時の自動処理を目的とする場合はこちらを利用する。

外部ツール起動時の挙動

プレ/ポストビルドステップはアプリケーションを非表示で起動し、標準出力を[Logs & others|Code::Blocks]ウィンドウへリダイレクトし、ツールの終了を待つ。すなわちToolsメニュー外部起動の[Tools]メニューLaunch tool hidden with standard output redirectオプションとほぼ同じ動作で、デスクトップアプリケーションは新たなコマンドプロンプト(cmd /c)を介さないと表示起動できないのも同様である。一方でToolsメニューは単一ステップしか定義できないが、プレ/ポストビルドステップは複数ステップの定義ができる。ただし単一ステップでもcmd /cを介せば&演算子でコマンド連結できるので、これは大きなメリットとは言えない。逆にプレ/ポストビルドステップはワーキングディレクトリがプロジェクトディレクトリに固定されるが、これもcmd /c cd <directory> & <command>とすれば良い。

通常ターゲット(Debug32とする)のプレ/ビルドステップを含むビルド実行順は以下である。

  1. プロジェクトプレビルドステップ
  2. Debug32プレビルドステップ
  3. Debug32ビルド(コンパイルリンク)
  4. Debug32ポストビルドステップ
  5. プロジェクトポストビルドステップ

複数ターゲットのプロジェクトでターゲット依存ビルドステップを定義する場合、組み込み変数$(TARGET_NAME)を用いてプロジェクトプレ/ビルドステップで共用化すればターゲット毎の定義を省略できると思うかもしれないが、これは避けた方が良い。二つのターゲット(Release32とRelease64とする)を持つバーチャルターゲット([Project|Properties]の[Project/targets options]ダイアログ[Build targets|Build targets|Virtual targets])のビルド実行順と$(TARGET_NAME)値は以下となり、プロジェクトプレ/ビルドステップに定義されるターゲット依存ビルドステップはあなたの意図と異なる結果を招く。

  1. プロジェクトプレビルドステップ($(TARGET_NAME)の値はRelease32)
  2. Release32プレビルドステップ($(TARGET_NAME)の値はRelease32)
  3. Release32ビルド
  4. Release32ポストビルドステップ($(TARGET_NAME)の値はRelease32)
  5. Release64プレビルドステップ($(TARGET_NAME)の値はRelease64)
  6. Release64ビルド
  7. Release64ポストビルドステップ($(TARGET_NAME)の値はRelease64)
  8. プロジェクトポストビルドステップ($(TARGET_NAME)の値はRelease64)

ビルド時の自動処理

ターゲットレベルの自動処理はターゲットプレ/ポストビルドステップで行えば良いが、プロジェクトワイドな自動処理は悩ましい。例えば32ビット/64ビットアプリケーションをビルドして両対応インストーラを作成するDeployバーチャルターゲットを考えてみよう。

  1. プロジェクトプレビルドステップ(何もしない)
  2. Release32プレビルドステップ
  3. Release32ビルド(32ビット実行ファイルの作成)
  4. Release32ポストビルドステップ
  5. Release64プレビルドステップ
  6. Release64ビルド(64ビット実行ファイルの作成)
  7. Release64ポストビルドステップ
  8. プロジェクトポストビルドステップ(32ビット/64ビット両対応インストーラの作成)

取り合えずこれで機能するが単一ターゲットビルドもプロジェクトプレ/ポストビルドステップを実行することを忘れている。アプリケーション開発は無数にデバッグビルドを繰り返すがその度にインストーラを作成されてはたまらない。そこでビルドは行わないがプレ/ポストビルドステップだけを行うターゲット(ダミーターゲット)として_DeployBeginと_DeployEndをDeployの最初と最後に追加する。

  1. プロジェクトプレビルドステップ(何もしない)
  2. _DeployBeginプレビルドステップ(何もしない)
  3. _DeployBeginポストビルドステップ(何もしない)
  4. Release32プレビルドステップ
  5. Release32ビルド(32ビット実行ファイルの作成)
  6. Release32ポストビルドステップ
  7. Release64プレビルドステップ
  8. Release64ビルド(64ビット実行ファイルの作成)
  9. Release64ポストビルドステップ
  10. _DeployEndプレビルドステップ(32ビット/64ビット両対応インストーラの作成)
  11. _DeployEndポストビルドステップ(何もしない)
  12. プロジェクトポストビルドステップ(何もしない)

ダミーターゲットは[Project|Properties]の[Project/targets options]ダイアログ[Build targets]ページ[...|Build targets|Add]で作成し[...|Selected build target options|Type]でCommands onlyを選択する。[...|Selected build target options|Output filename]テキストボックスなどビルドに必要なパラメータの入力コントロールのほとんどが無効化される。[...|Selected build target options|Objects output dir]テキストボックスのみ有効のままだがその入力値が用いられることもなく恐らくバグであろう。Commands onlyターゲットに関するドキュメントは十分と言えないが、ビルドを要求しても実行せずプレ/ポストビルドステップのみ実行するターゲットとして使える。

しかしそのままではCommands onlyターゲットはいくつかの不具合を抱え以下の対応を必要とする。

Commands onlyターゲット

Commands onlyターゲットを[Build|Build]などでビルドすると各ターゲットファイル毎にコンパイルが実行され多くの場合エラー終了する。一方でリンクはスキップされ問題とならない。[Build|Run]などでファイル実行を要求すると[Project|Set programs' arguments]の[Select target]ダイアログ[Host application]のホストアプリケーションを実行しようとする。コンパイルエラーを抑止するにはコンパイラ定義として*No Compiler*を選択するか、あるいは[...|Build target files]のチェックを全て外しターゲットファイルを持たせなければ良い。ただし後者は他のターゲット設定と大きく異なりメンテナンスを複雑化させるため推奨しない。

覚え書き
なぜコンパイルとリンクで扱いが異なっているのか解らない。ファイル実行で本来はDLLデバッグに用いる[Host application]を起動する理由も不明だ。

例として動作確認(1)のDesktop1プロジェクトにCommands onlyターゲット_Dummyを作成し、コンパイラ定義GNU GCC Compiler (32bit)で[Build|Build]する。

i686-w64-mingw32-g++.exe -c C:\Users\user\MinGW\TestDrive\Desktop1\wx_pch.h -o wx_pch.h.gch\_Debug_wx_pch_h_gch
i686-w64-mingw32-g++.exe -c C:\Users\user\MinGW\TestDrive\Desktop1\Desktop1App.cpp -o \Desktop1App.o
C:\Users\user\MinGW\TestDrive\Desktop1\wx_pch.h:14:10: fatal error: wx/wxprec.h: No such file or directory
14 | #include <wx/wxprec.h>
| ^~~~~~~~~~~~~
compilation terminated.
Process terminated with status 1 (0 minute(s), 0 second(s))
1 error(s), 0 warning(s) (0 minute(s), 0 second(s))

コンパイルは実行するが与えられたパラメータが不十分でエラー終了する。コンパイルをスキップさせるため*No Compiler*に変更する必要があるが、残念ながらそのままでは別のエラーが発生する

Project/Target: "Desktop1 - _Debug":
The compiler's setup (*No Compiler*) is invalid, so Code::Blocks cannot find/run the compiler.
Probably the toolchain path within the compiler options is not setup correctly?!
Do you have a compiler installed?
Goto "Settings->Compiler...->Global compiler settings->*No Compiler*->Toolchain executables" and fix the compiler's setup.
Tried to run compiler executable '-- No Compiler --/bin/', but failed!
Skipping...
Project/Target: "Desktop1 - _Debug":
The compiler's setup (*No Compiler*) is invalid, so Code::Blocks cannot find/run the compiler.
Probably the toolchain path within the compiler options is not setup correctly?!
Do you have a compiler installed?
Goto "Settings->Compiler...->Global compiler settings->*No Compiler*->Toolchain executables" and fix the compiler's setup.
Tried to run compiler executable '-- No Compiler --/bin/', but failed!
Skipping...
Nothing to be done (all items are up-to-date).

*No Compiler*の設定も変更する必要がある。

覚え書き
実際は*No Compiler*を選択すればCommands onlyターゲットでなくともダミーターゲットとして機能するが、通常と異なるターゲットである事を明示するためにもCommand onlyターゲットを用いる。

Commands onlyターゲットはポストビルドステップを2回実行する。リンクの代わりにポストビルドステップを実行するためだが恐らくバグで、ダミーターゲットはプレ/ポストビルドステップの区別に意味は無いのでプレビルドステップのみを用いれば済む。他に注意すべき点としてCommands onlyターゲットはターゲット出力を持たず$(TARGET_OUTPUT_FILE)や$(TARGET_OUTPUT_BASENAME)が空文字列となる事などが挙げられる。

*No Compiler*コンパイラ定義

*No Compiler*はデフォルトでインストールディレクトリに- No Compiler -、コンパイラなどの実行ファイル名全てに空文字列が設定されている。ビルド時のコンパイルはそのままでスキップするようになっているものの、ビルド前のコンパイラ定義妥当性チェックに引っかかりエラーとなる。チェックはC言語コンパイラ実行ファイルの存否によるのでこれをあらかじめ設定しておけば回避できる。[Settings|Compiler]の[Compiler settings]ダイアログ[Global compiler settings]ページ[...|Selected compiler]で*No Compiler*を選択し、例えば[...|Toolchaing executables|Compiler's installation directory]に$(#mingw32)、[...|Toolchaing executables|C compiler]にi686-w64-mingw32-gcc.exeを入力する。ファイル存否のみをチェックし実行する事は無いので実際はどんなファイルでも構わない。

これでDesktop1プロジェクト_Dummyターゲットを[Build|Build]すると意図通りターゲット毎にコンパイルがスキップされ、ようやくダミーターゲットとして機能する。

Skipping file (no compiler program set): Desktop1App.cpp
Skipping file (no compiler program set): Desktop1Frame.cpp
Skipping file (no compiler program set): resource.rc
Nothing to be done (all items are up-to-date).
覚え書き
*No Compiler*のコンパイラ定義妥当性チェックは不要でバグと見なすべきだろう。*No Compiler*のコピーを作成し対策を施して使用した方が*No Compiler*デフォルトを変更せず好ましく思えるし、コピーでもダミーターゲットとして機能する事は確認している。しかしソースコードの数箇所に*No Compiler*ID文字列"null"による条件判断が存在し、異なるID文字列を持つコピーで挙動が変わる可能性があるため、*No Compiler*に対策を加えて使用する方が安全と判断する。