見出し画像

CMakeLists.txtで任意のファイルを任意のディレクトリにコピーする(タイトル長い)


気に入らないので一部修正しました

テスト用の実行ファイル、例えば a.out なんかは buildディレクトリに作成させることが多い

例えば 何かのファイルを読み込むコードを考えるとテストとしては'何かのファイル'もbuildディレクトリにコピーしてくれると楽になる

これは地味に面倒くさい

$ ./a.out ../testfile/test01.json

これを

$ ./a.out test01.json

にしたい。
そのためには、上記の例では ../testfileにある *.jsonをbuildにコピーさせたい

そういう話なので、CMakeLists.txt用の関数を作った

function(copy_files PJ_NAME S_DIR FILE_EXT TAG_DIR)
    file(GLOB TARGET_FILES "${S_DIR}/*.${FILE_EXT}")
    if(NOT TARGET_FILES)
        return()
    endif()

    # Generate a unique target name using UUID
    string(RANDOM LENGTH 8 ALPHABET "0123456789ABCDEF" RANDOM_SUFFIX)
    string(REPLACE "/" "_" CLEAN_S_DIR "${S_DIR}")
    string(REPLACE "@" "_" CLEAN_S_DIR "${CLEAN_S_DIR}")
    string(REPLACE "/" "_" CLEAN_PJ_NAME "${PJ_NAME}")
    string(REPLACE "@" "_" CLEAN_PJ_NAME "${CLEAN_PJ_NAME}")

    set(UNIQUE_TARGET_NAME "${CLEAN_PJ_NAME}_${CLEAN_S_DIR}_${FILE_EXT}_${RANDOM_SUFFIX}_copy")
    string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" UNIQUE_TARGET_NAME "${UNIQUE_TARGET_NAME}")

    # Ensure target length remains manageable
    string(SUBSTRING "${UNIQUE_TARGET_NAME}" 0 50 UNIQUE_TARGET_NAME)

    set(COPIED_TARGET_FILES "")

    foreach(TARGET_FILE ${TARGET_FILES})
        get_filename_component(TARGET_FILENAME ${TARGET_FILE} NAME)
        set(OUTPUT_FILE "${TAG_DIR}/${TARGET_FILENAME}")

        # Add a custom command to copy each file
        add_custom_command(
            OUTPUT ${OUTPUT_FILE}
            COMMAND ${CMAKE_COMMAND} -E copy_if_different ${TARGET_FILE} ${OUTPUT_FILE}
            DEPENDS ${TARGET_FILE}
            COMMENT "Copying updated ${TARGET_FILENAME} to ${TAG_DIR}"
        )
        list(APPEND COPIED_TARGET_FILES ${OUTPUT_FILE})
    endforeach()

    if(COPIED_TARGET_FILES)
        # Check if the target already exists
        if(NOT TARGET ${UNIQUE_TARGET_NAME})
            add_custom_target(${UNIQUE_TARGET_NAME} ALL
                DEPENDS ${COPIED_TARGET_FILES}
                COMMENT "Ensuring all .${FILE_EXT} files are copied to ${TAG_DIR}"
            )

            # Make the main project depend on this custom target to ensure it runs
            add_dependencies(${PJ_NAME} ${UNIQUE_TARGET_NAME})
        endif()
    else()
        message(WARNING "No target files found to copy in ${S_DIR}")
    endif()
endfunction()

使い方は簡単で上記のコードをCMakeLists.txtの一番後ろにでも貼り付けて、その後に下のように呼び出すだけ
呼び出しは複数行記述しても問題ない

# Usage example
copy_files(${PROJECT_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/json" "json" "${CMAKE_BINARY_DIR}")

テスト用の拡張子を持つファイルを編集した場合には、makeを実行するとちゃんと更新される(一応簡単に試した)
ので、安心

念のために、OpenAIのChatGPT-4oに添削をお願いしたところ
コードは修正がなかったけど英文のコメントのスペルミスを指摘された
ちぇっ

いいなと思ったら応援しよう!