Jak skopiować pliki DLL do tego samego folderu co plik wykonywalny za pomocą CMake?

98

Używamy CMake do generowania plików Visual Studio naszych źródeł w naszym SVN. Teraz moje narzędzie wymaga, aby niektóre pliki DLL znajdowały się w tym samym folderze co plik wykonywalny. Pliki DLL znajdują się w folderze obok źródła.

Jak mogę zmienić moje CMakeLists.txttak, aby wygenerowany projekt Visual Studio albo miał już określone pliki DLL w folderach Release / Debug, albo skopiował je po kompilacji?

Mata
źródło

Odpowiedzi:

122

Chciałbym add_custom_commandto osiągnąć razem z cmake -E copy_if_different.... Aby uzyskać pełne informacje uruchom

cmake --help-command add_custom_command
cmake -E


Więc w twoim przypadku, jeśli masz następującą strukturę katalogów:

/CMakeLists.txt
/src
/libs/test.dll

a cel CMake, do którego odnosi się polecenie MyTest, to możesz dodać do pliku CMakeLists.txt następujące polecenie:

add_custom_command(TARGET MyTest POST_BUILD        # Adds a post-build event to MyTest
    COMMAND ${CMAKE_COMMAND} -E copy_if_different  # which executes "cmake - E copy_if_different..."
        "${PROJECT_SOURCE_DIR}/libs/test.dll"      # <--this is in-file
        $<TARGET_FILE_DIR:MyTest>)                 # <--this is out-file path


Jeśli chcesz tylko /libs/skopiować całą zawartość katalogu, użyj cmake -E copy_directory:

add_custom_command(TARGET MyTest POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
        "${PROJECT_SOURCE_DIR}/libs"
        $<TARGET_FILE_DIR:MyTest>)


Jeśli chcesz skopiować różne biblioteki DLL w zależności od konfiguracji (np. Release, Debug), możesz mieć je w podkatalogach o nazwach z odpowiednią konfiguracją: /libs/Releasei /libs/Debug. Następnie musisz wstrzyknąć typ konfiguracji do ścieżki do biblioteki dll w add_custom_commandwywołaniu, na przykład:

add_custom_command(TARGET MyTest POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
        "${PROJECT_SOURCE_DIR}/libs/$<CONFIGURATION>"
        $<TARGET_FILE_DIR:MyTest>)
Fraser
źródło
3
Krótka notatka na temat tego, co zadziałało w moim przypadku na wypadek, gdyby w przyszłości pomogło to komuś innemu: mam projekt biblioteki statycznej, z którym opcjonalnie łączy się główny plik wykonywalny, a biblioteka ta wymaga skopiowania biblioteki DLL, jeśli zostanie dodana. Więc w pliku CMakeLists.txt tej biblioteki użyłem ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>jako miejsca docelowego. W przeciwnym razie skopiowałby go do ścieżki budowania biblioteki, co było bezużyteczne.
AberrantWolf
czy nie jest celem install()dyrektyw cmake składanie plików binarnych? A może LIBRARYrzeczy cmake ? Naprawdę nie znam łańcucha narzędzi.
Sandburg
1
$<TARGET_FILE_DIR:MyTest>- co to jest? Jak wydrukować informacje, co dokładnie oznacza
ilw
Chyba że coś przeoczyłem podczas integrowania polecenia, ta metoda nie działa dla bibliotek dodanych z IMPORTED_LIBRARIES. Narzeka, że ​​nie jest w stanie uruchomić polecenia po kompilacji, gdy nic nie zostało zbudowane.
Tzalumen
2
Po przetestowaniu i uruchomieniu mojego cmake: Jeśli używasz IMPORTEDbibliotek i chcesz przenieść biblioteki DLL, musisz użyć polecenia wariantu. Jeśli dodałeś swoją IMPORTEDbibliotekę tak, MyImportedLibjakbyś używał COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:MyImportedLib> $<TARGET_FILE_DIR:MyTest>Zauważ, że aby uruchomić wiele poleceń po kompilacji, potrzebujesz ich wszystkich powiązanych w jednym niestandardowym poleceniu, np.add_custom_command(TARGET MyTest POST_BUILD COMMAND #your first command# COMMAND #Your second command#)
Tzalumen
24

Umieściłem te wiersze w moim pliku CMakeLists.txt najwyższego poziomu. Wszystkie biblioteki i pliki wykonywalne skompilowane przez CMake zostaną umieszczone na najwyższym poziomie katalogu kompilacji, aby pliki wykonywalne mogły znaleźć biblioteki i łatwo było je uruchomić.

set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})

Zauważ, że nie rozwiązuje to problemu OP, jakim jest kopiowanie prekompilowanych plików binarnych z katalogu źródłowego projektu.

David Grayson
źródło
7

Miałem ten problem dzisiaj, kiedy próbowałem stworzyć wersję mojego programu dla systemu Windows. Skończyło się na tym, że sam przeprowadziłem badania, ponieważ wszystkie te odpowiedzi mnie nie satysfakcjonowały. Były trzy główne problemy:

  • Chciałem, aby kompilacje debugowania były powiązane z wersjami debugowania bibliotek, a kompilacje wydań były odpowiednio powiązane z kompilacjami wydań bibliotek.

  • Oprócz tego chciałem, aby prawidłowe wersje plików DLL (Debug / Release) były kopiowane do katalogów wyjściowych.

  • Chciałem to wszystko osiągnąć bez pisania skomplikowanych i delikatnych skryptów.

Po przejrzeniu kilku podręczników CMake i kilku wieloplatformowych projektów na github znalazłem takie rozwiązanie:

Zadeklaruj swoją bibliotekę jako docelową za pomocą atrybutu „IMPORTOWANE”, odnieś się do jej debugowania i zwolnij pliki .lib i .dll.

add_library(sdl2 SHARED IMPORTED GLOBAL)
set_property(TARGET sdl2 PROPERTY IMPORTED_IMPLIB_RELEASE "${SDL_ROOT_PATH}/lib/SDL2.lib")
set_property(TARGET sdl2 PROPERTY IMPORTED_LOCATION_RELEASE "${SDL_ROOT_PATH}/bin/SDL2.dll")
set_property(TARGET sdl2 PROPERTY IMPORTED_IMPLIB_DEBUG "${SDL_ROOT_PATH}/lib/SDL2d.lib")
set_property(TARGET sdl2 PROPERTY IMPORTED_LOCATION_DEBUG "${SDL_ROOT_PATH}/bin/SDL2d.dll")

Jak zwykle połącz ten cel ze swoim projektem

target_link_libraries(YourProg sdl2 ...)

Wykonaj niestandardowy krok kompilacji, aby skopiować plik dll do miejsca docelowego, jeśli został w jakiś sposób zmieniony od czasu poprzedniej kompilacji

add_custom_command ( TARGET YourProg POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_if_different
    $<TARGET_FILE:sdl2> $<TARGET_FILE_DIR:YourProg>
)
Niewielkie zagrożenie
źródło
5

Aneks do zaakceptowanej odpowiedzi, dodany jako osobna odpowiedź, dzięki czemu otrzymuję formatowanie kodu:

Jeśli budujesz swoje biblioteki DLL w tym samym projekcie, zwykle będą one znajdować się w katalogach Release, Debug itp. Będziesz musiał użyć zmiennych środowiskowych programu Visual Studio, aby poprawnie je skopiować. na przykład:

"${PROJECT_BINARY_DIR}/your_library/\$\(Configuration\)/your_library.dll"

dla źródła i

"${CMAKE_CURRENT_BINARY_DIR}/\$\(Configuration\)/your_library.dll"

do miejsca docelowego. Zwróć uwagę na ucieczkę!

Nie można użyć zmiennej CMake CMAKE_BUILD_TYPE do konfiguracji, ponieważ jest ona rozwiązana w czasie generowania projektu VS i zawsze będzie taka, jaka jest domyślna.

Dana Robinson
źródło
8
Ostatnia część zaakceptowanej odpowiedzi rozwiązuje ten problem w bardziej przejrzysty sposób, używając$<CONFIGURATION>
Chuck Claunch
4

Możesz także użyć polecenia find_library:

find_library(<some_var> NAMES <name_of_lib> PATHS "<path/to/lib>")

Na przykład ze zdefiniowaną ścieżką EXECUTABLE_PATH:

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

możesz przenieść pliki .dll, których potrzebujesz, za pomocą

file(COPY ${<some_var>}
    DESTINATION ${EXECUTABLE_OUTPUT_PATH})
Felipe Thorn
źródło
2

Przydaje się to jednemu z nich

SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY
    ${PROJECT_SOURCE_DIR}/lib CACHE
    PATH "Directory where all the .lib files are dumped." FORCE)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY
    ${PROJECT_SOURCE_DIR}/bin CACHE
    PATH "Directory where .exe and .dll files are dumped." FORCE)
Sai Kiran Nadipilli
źródło
1

Prawdopodobnie musisz dodać cel niestandardowy i uzależnić go od jednego z plików wykonywalnych.

Aby skopiować plik za pomocą powyższej funkcji użyj:

COMMAND ${CMAKE_PROGRAM} -E copy_if_different ${CMAKE_BINARY_DIR}/path/to/file.dll ${CMAKE_BINARY_DIR}/where/to/put/file.dll`
arrowd
źródło
0

Jestem początkującym użytkownikiem CMake, ale nadal chciałem podzielić się swoim doświadczeniem. W moim przypadku potrzebowałem kopii poinstalacyjnej, aby wszystkie moje pliki binarne były w formacie. W przypadku pliku binarnego innej firmy, który można zaimportować w CMake, działa dla mnie następujące rozwiązanie:

find_package( dependency REQUIRED )
if( MSVC ) 
    # If done properly and if the dependency has a correct config file, IMPORTED_LOCATION_RELEASE should be defined
    get_target_property( DEP_SHARED_LIB_PATH dependency IMPORTED_LOCATION_RELEASE )
    # Create a bin directory in the install folder
    add_custom_command(TARGET BGS POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory  ${CMAKE_INSTALL_PREFIX}/bin/)
    # Copy the shared lib file
    add_custom_command(TARGET BGS POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${DEP_SHARED_LIB_PATH} ${CMAKE_INSTALL_PREFIX}/bin/)
endif()

Oczywiście IMPORTED_LOCATION_RELEASE może mieć warianty w zależności od tego, jak została zbudowana / zainstalowana biblioteka współdzielona. Może być IMPORTED_LOCATION_DEBUG.

Może jest lepszy sposób na uzyskanie nazwy tej nieruchomości, nie wiem.

Norman Pellet
źródło
0

Przenoszenie plików podczas kompilacji przy użyciu install

Miałem ten problem, próbując postępować zgodnie z oficjalnym samouczkiem CMake w kroku 9 . To była lokalizacja pliku, który chciałem przenieść:

src
 |_build
    |_Debug
       - `MathFunctions.dll`

To była lokalizacja, w której chciałem, aby znajdował się plik:

src
 |_build
    |_install
         |_bin
             - `MathFunctions.dll`

Od tego DLL została wygenerowana w udostępnionej biblioteki, wszystko, co nie było włączenie tej linii w CMakeLists.txtw podkatalogu , który zawierał kod źródłowy bibliotekisrc/Mathfunctions/CMakeLists.txt

install(FILES ${PROJECT_BINARY_DIR}/$<CONFIG>/MathFunctions.dll
DESTINATION bin)

Dzięki Twoim odpowiedziom mogłem pomyśleć o tym. To tylko jedna linia, więc myślę, że jest w porządku. $<CONFIG>Może mieć dwie wartości Debug lub Release W zależności od tego, jak projekt jest zbudowany zgodnie z wymogami oryginalne pytanie.

David Martinez C.
źródło