CMakeでビルドした実行プログラムのあるディレクトリにリソースファイルをコピーする

やりたいこと

CMakeを用いてプロジェクトを管理しているときに、ビルドした実行ファイルと同じディレクトリ(またはそこから相対的に定義されるディレクトリ)に画像データ等のリソースデータをコピーしたいという状況を考える。

リソースファイルは、例えば次のように定義されているとする。

file(GLOB RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/*.png)

問題点

ビルド環境ごとにビルドした実行プログラムが配置されるディレクトリは異なる可能性がある。

例えば、Makefileを生成してmakeとした場合には${CMAKE_CURRENT_BINARY_DIR}に実行プログラムが配置されるが、Xcodeを用いてビルドすると、例えばDebugモードでビルドすると${CMAKE_CURRENT_BINARY_DIR}/Debugに配置される。

更に、macOS.appバンドルを生成する場合は、実行ファイルの本体は[...].app/Contents/MacOSに配置される。

したがって、リソースファイルを実行ファイルのあるディレクトリにコピーしたい場合、単にfileコマンドを使って

file(COPY ${RESOURCE_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

としても、ビルド環境によっては意図通りにコピーされないことがある。

解決策:Generator Expression

Generator Expressionを用いれば良い。これは、CMakeのドキュメントに

Generator expressions are evaluated during build system generation to produce information specific to each build configuration.

と説明にあるように、ビルド環境に応じて評価される式ということらしい。

したがって、ビルド環境依存の部分は、generator expressionsを用いることで統一的に記述することができるようになる。

CMakeに定義されているgenerator expressionsのうち、特に

$<TARGET_FILE_DIR:my-program>

という式を用いると、ビルドした実行プログラムが配置されるディレクトリを得ることができる。ただしmy-programの部分にはそれぞれのターゲット名が入る。

リソースファイルをコピーするには、これを用いて

add_custom_command(TARGET my-program POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${resources} $<TARGET_FILE_DIR:my-program>)

などと記述すれば良い。