1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TARGET_RUNTIME_DLLSで依存するDLLを簡単に配置する

Last updated at Posted at 2023-07-14

※ Windowsでのみ有効な解決策であるため注意。

背景

CMakeプロジェクトにおいて、所望のターゲットに別環境でビルド済みのライブラリを動的リンクしたい場合、以下のように書いていた。

CMakeLists.txt
add_executable(mytarget ${MY_PROJECT_SOURCES})
add_library(extLib SHARED IMPORTED)
set_target_properties(
  extLib
  PROPERTIES IMPORTED_LOCATION ${DYLIB_FILE_PATH}
             IMPORTED_IMPLIB ${STLIB_FILE_PATH}
             INTERFACE_INCLUDE_DIRECTORIES ${INCLUDE_DIR_PATH})
target_link_libraries(mytarget extLib)

これでビルドは上手く動作する。
しかしリンクした動的リンクライブラリがビルドディレクトリに配置されていないため、生成された実行ファイルをただ(PATHを追加せずに)実行しても動かない。
configure_filecmake -e copy などのコマンドを使用し、依存するライブラリをコピーすることで回避できるが、依存ライブラリが増えるとコピー対象のファイルが増え記述が大変になることは目に見えている。

解決策

Generator Expression (ジェネレータ式?) の1つ、$<TARGET_RUNTIME_DLLS:tgt> を使用すると、あるターゲットが依存する全ての動的リンクライブラリのパスをリストとして取得できる。
※ 対象(依存するライブラリ)が1つもない場合空文字に変換され、以下のコマンドはConfigure時にエラーとなるので注意。
※ TARGET_RUNTIME_DLLS は CMake3.21以降で導入されている。

これを用いて、ビルドしたターゲット実行ファイルが存在するディレクトリに、そのターゲットが依存する全ての動的リンクライブラリファイルを配置するCMakeスクリプトは以下のように書ける。

add_executable(mytarget ${MY_PROJECT_SOURCES})
#...
function(copy_dlls_to_target TARGET_NAME)
  add_custom_command(
    TARGET ${TARGET_NAME}
    POST_BUILD
    COMMAND
      ${CMAKE_COMMAND} -E copy_if_different
      $<TARGET_RUNTIME_DLLS:${TARGET_NAME}> $<TARGET_FILE_DIR:${TARGET_NAME}>
    COMMAND_EXPAND_LISTS) # COMMAND_EXPAND_LISTSを設定することでパスのリストが正しく展開される
endfunction()
copy_dlls_to_target(mytarget)

インストールについても、同様に $<TARGET_RUNTIME_DLLS:tgt> を用いて以下のように書ける。

install(FILES $<TARGET_RUNTIME_DLLS:mytarget> TYPE BIN)

参考文献

  • TARGET_RUNTIME_DLLSの存在は、以下のstackoverflowの回答を見て知った。

  • TARGET_RUNTIME_DLLS の詳細については以下を参照した。

追記

前述したCMake関数copy_dlls_to_targetについて、以下の要件を追加したくなった。

  • TARGET_RUNTIME_DLLSが使用可能か事前チェックする
  • 対象のDLLファイルが存在しないと cmake -e copy_if_different が構文エラーとなるため、そうしたケースでもConfigureが失敗しないようにする。

これらを踏まえて修正した関数の実装が以下のようになる。

copy_dlls_to_target.qmake
# targetが依存するDLLをコピーする関数
# Windowsでのみ使用可能
# 対象DLLが存在しない場合を考慮し、dummyファイルを作成する
function(copy_dlls_to_target TARGET_NAME)
  if(WIN32 AND (CMAKE_SHARED_LIBRARY_SUFFIX STREQUAL ".dll"))
    message(STATUS "copy_dlls_to_target: ${TARGET_NAME}")
    set(dummy ${PROJECT_BINARY_DIR}/.dummy)
    file(TOUCH ${dummy})
    add_custom_command(
      TARGET ${TARGET_NAME}
      POST_BUILD
      COMMAND
        ${CMAKE_COMMAND} -E copy_if_different
        $<IF:$<BOOL:$<TARGET_RUNTIME_DLLS:${TARGET_NAME}>>,$<TARGET_RUNTIME_DLLS:${TARGET_NAME}>,${dummy}>
        $<TARGET_FILE_DIR:${TARGET_NAME}>
      COMMAND_EXPAND_LISTS)
    cmake_path(GET dummy FILENAME dummy_filename)
    add_custom_command(
      TARGET ${TARGET_NAME}
      POST_BUILD
      COMMAND ${CMAKE_COMMAND} -E remove ${dummy}
              $<TARGET_FILE_DIR:${TARGET_NAME}>/${dummy_filename})
  else()
    # Linuxの場合TARGET_RUNTIME_DLLSは使用できない reference:
    # https://stackoverflow.com/questions/10671916/how-to-copy-dll-files-into-the-same-folder-as-the-executable-using-cmake
    message(
      WARNING "TARGET_RUNTIME_DLLS is not defined. DLLs will not be copied.")
  endif()
endfunction()
  • if文を使用している箇所は、CMake3.27以降であればジェネレータ式の $<LIST:APPEND,list,item,...> を用いて短縮可能と思われる。
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?