Skopiuj plik z katalogu źródłowego do katalogu binarnego przy użyciu CMake

108

Próbuję stworzyć prosty projekt na CLion. Używa CMake (jestem tutaj nowy) do generowania plików Makefile do budowania projektu (lub czegoś w tym rodzaju)

Wszystko, czego potrzebuję, to przenieść jakiś plik niebędący projektem (jakiś rodzaj pliku zasobów) do katalogu binarnego za każdym razem, gdy uruchamiam mój kod.

Ten plik zawiera dane testowe i aplikacja otwiera go, aby je odczytać. Próbowałem to zrobić na kilka sposobów:

  • Przez file(COPY ...

    file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
            DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/input.txt
    

    Wygląda dobrze, ale działa tylko raz i nie kopiuje ponownie pliku po następnym uruchomieniu.

  • Przez add_custom_command

    • OUTPUT wersja

      add_custom_command(
              OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/input.txt
              COMMAND ${CMAKE_COMMAND} -E copy
                      ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
                      ${CMAKE_CURRENT_BINARY_DIR}/input.txt)
      
    • TARGET wersja

      add_custom_target(foo)
      add_custom_command(
              TARGET foo
              COMMAND ${CMAKE_COMMAND} copy
                      ${CMAKE_CURRENT_BINARY_DIR}/test/input.txt
                      ${CMAKE_SOURCE_DIR})
      

    Ale nikt z tego nie działa.

Co ja robię źle?

mongolrgata
źródło

Odpowiedzi:

137

Możesz rozważyć użycie configure_file z COPYONLYopcją:

configure_file(<input> <output> COPYONLY)

w odróżnieniu file(COPY ...) tego tworzy zależność na poziomie pliku między danymi wejściowymi i wyjściowymi, to znaczy:

Jeśli plik wejściowy zostanie zmodyfikowany, system kompilacji ponownie uruchomi CMake, aby ponownie skonfigurować plik i ponownie wygenerować system kompilacji.

tamas.kenez
źródło
13
Zwróć uwagę, że configure_filenie będzie działać z podkatalogami, nawet jeśli używasz GLOB do tworzenia listy plików.
Tarantula
67

obie opcje są prawidłowe i dotyczą dwóch różnych etapów kompilacji:

  1. file(COPY ...kopiuje plik w kroku konfiguracji i tylko w tym kroku. Kiedy odbudujesz swój projekt bez zmiany konfiguracji cmake, to polecenie nie zostanie wykonane.
  2. add_custom_command jest preferowanym wyborem, gdy chcesz kopiować plik na każdym kroku kompilacji.

Odpowiednia wersja do Twojego zadania to:

add_custom_command(
        TARGET foo POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy
                ${CMAKE_SOURCE_DIR}/test/input.txt
                ${CMAKE_CURRENT_BINARY_DIR}/input.txt)

można wybierać pomiędzy PRE_BUILD, PRE_LINK, POST_BUILD najlepiej jest zapoznać się z dokumentacją add_custom_command

przykład użycia pierwszej wersji można znaleźć tutaj: Użyj CMake add_custom_command, aby wygenerować źródło dla innego celu

jenseb
źródło
1
Czy to CMAKE_SOURCE_DIR czy CMAKE_CURRENT_SOURCE_DIR?
Syaiful Nizam Yahya
2
@SyaifulNizamYahya użyj, CMAKE_CURRENT_SOURCE_DIRjeśli test/input.txtplik jest względny w stosunku do bieżącego CMakeLists.txtpliku. Jeśli jest względny w stosunku do katalogu głównego CMakeLists.txt, użyj CMAKE_SOURCE_DIR.
Mark Ingram
Czy istnieje sposób, aby uzyskać nazwę pliku docelowego w cmake? bez zakodowania na stałe nazwy pliku? np. buduję plik lib, czy cmake ma zmienną do pobrania nazwy pliku docelowego? jak libSomething.so?
Rika
16

Pierwsza wypróbowana opcja nie działa z dwóch powodów.

Po pierwsze, zapomniałeś zamknąć nawias.

Po drugie, DESTINATIONpowinien to być katalog, a nie nazwa pliku. Zakładając, że zamknąłeś nawias, plik trafiłby do folderu o nazwie input.txt.

Aby to działało, po prostu zmień to na

file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
     DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
Niemiecki Capuano
źródło
4

Sugerowałbym, TARGET_FILE_DIRjeśli chcesz, aby plik został skopiowany do tego samego folderu, co plik .exe.

$ Katalog głównego pliku (.exe, .so.1.2, .a).

add_custom_command(
  TARGET ${PROJECT_NAME} POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy 
    ${CMAKE_CURRENT_SOURCE_DIR}/input.txt 
    $<TARGET_FILE_DIR:${PROJECT_NAME}>)

W VS ten skrypt cmake skopiuje input.txt do tego samego pliku, co końcowy plik exe, bez względu na to, czy jest debugowany, czy wydany.

Felix Xu
źródło
3

To jest to, czego użyłem do skopiowania niektórych plików zasobów: pliki kopii są pustym celem do ignorowania błędów

 add_custom_target(copy-files ALL
    COMMAND ${CMAKE_COMMAND} -E copy_directory
    ${CMAKE_BINARY_DIR}/SOURCEDIRECTORY
    ${CMAKE_BINARY_DIR}/DESTINATIONDIRECTORY
    )
Cory Lewis
źródło
Dodałbym również, add_dependencies(MainTarget copy-files)aby uruchamiał się automatycznie, gdy buduję MainTarget
Herrgott
Wydaje się, że jest to najlepsza odpowiedź (+ komentarz Herrgotta), ponieważ w rzeczywistości zapewnia, że ​​aktualna wersja źródła jest zawsze w kompilacji docelowej. W przypadku małych kopii działa to dobrze, dziękuję. Umieszczenie pliku add_dependencies(MainTarget copy-files)głównego CMakeLists.txtoznacza, że ​​można go używać w całym projekcie.
satnhak
2

jeśli chcesz skopiować folder z katalogu porzeczki do folderu binarnego (folder kompilacji)

file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/yourFolder/ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/yourFolder/)

to syntexe to:

file(COPY pathSource DESTINATION pathDistination)
Amirouche Zeggagh
źródło
0

Sugerowany plik configure_file jest prawdopodobnie najłatwiejszym rozwiązaniem. Jednak polecenie kopiowania nie zostanie ponownie uruchomione, jeśli ręcznie usuniesz plik z katalogu kompilacji. Aby obsłużyć również ten przypadek, działa dla mnie następujące rozwiązanie:

add_custom_target(copy-test-makefile ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/input.txt)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/input.txt
                   COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
                                                    ${CMAKE_CURRENT_BINARY_DIR}/input.txt
                   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/input.txt)
ar31
źródło