Como copiar arquivos DLL para a mesma pasta que o executável usando CMake?

98

Usamos CMake para gerar os arquivos do Visual Studio de nossas fontes em nosso SVN. Agora minha ferramenta requer que alguns arquivos DLL estejam na mesma pasta que o executável. Os arquivos DLL estão em uma pasta ao lado da fonte.

Como posso alterar o meu de CMakeLists.txtmodo que o projeto do Visual Studio gerado já tenha os arquivos DLL específicos nas pastas de liberação / depuração ou os copie durante a compilação?

Esteira
fonte

Respostas:

122

Eu usaria add_custom_commandpara conseguir isso junto com cmake -E copy_if_different.... Para informações completas, corra

cmake --help-command add_custom_command
cmake -E


Portanto, no seu caso, se você tiver a seguinte estrutura de diretório:

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

e seu destino CMake ao qual o comando se aplica é MyTest, então você pode adicionar o seguinte ao seu CMakeLists.txt:

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


Se você deseja apenas todo o conteúdo do /libs/diretório copiado, use 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>)


Se você precisar copiar dlls diferentes dependendo da configuração (Release, Debug, por exemplo), você pode tê-los em subdiretórios nomeados com a configuração correspondente:, /libs/Releasee /libs/Debug. Em seguida, você precisa injetar o tipo de configuração no caminho para a dll na add_custom_commandchamada, assim:

add_custom_command(TARGET MyTest POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
        "${PROJECT_SOURCE_DIR}/libs/$<CONFIGURATION>"
        $<TARGET_FILE_DIR:MyTest>)
Fraser
fonte
3
Nota rápida sobre o que funcionou no meu caso, caso isso ajude alguém no futuro: Eu tenho um projeto de biblioteca estática ao qual o executável principal se vincula opcionalmente e essa biblioteca requer que uma DLL seja copiada, se adicionada. Portanto, no arquivo CMakeLists.txt daquela biblioteca, usei ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>como destino. Caso contrário, ele iria copiá-lo para o caminho de construção da biblioteca, o que era inútil.
AberrantWolf de
não é o objetivo das install()diretivas cmake montar os binários? Ou talvez cmake LIBRARYcoisas? Eu realmente não conheço o conjunto de ferramentas.
Sandburg
1
$<TARGET_FILE_DIR:MyTest>- O que é isso? Como imprimir informações, o que exatamente significa
ilw
A menos que eu tenha esquecido algo quando estava integrando o comando, esse método não funciona para bibliotecas adicionadas com IMPORTED_LIBRARIES. Ele reclama por não ser capaz de executar um comando pós-construção quando nada foi construído.
Tzalumen de
2
Após testar e estimular meu cmake: Se você estiver usando IMPORTEDbibliotecas e precisar realocar as DLLs, será necessário usar um comando variante. Se você adicionou sua IMPORTEDbiblioteca como MyImportedLibusaria COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:MyImportedLib> $<TARGET_FILE_DIR:MyTest>Observe que para executar vários comandos pós-compilação, você precisa de todos eles vinculados a um comando personalizado, por exemploadd_custom_command(TARGET MyTest POST_BUILD COMMAND #your first command# COMMAND #Your second command#)
Tzalumen
24

Coloquei essas linhas em meu arquivo CMakeLists.txt de nível superior. Todas as bibliotecas e executáveis ​​compilados pelo CMake serão colocados no nível superior do diretório de compilação para que os executáveis ​​possam encontrar as bibliotecas e seja fácil executar tudo.

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

Observe que isso não resolve o problema do OP de copiar binários pré-compilados do diretório de origem do projeto.

David Grayson
fonte
7

Tive esse problema hoje quando tentei fazer uma compilação do meu programa para Windows. E acabei fazendo algumas pesquisas sozinho, uma vez que todas essas respostas não me satisfizeram. Havia três problemas principais:

  • Queria que compilações de depuração fossem vinculadas a versões de depuração de bibliotecas e compilações de lançamento fossem vinculadas a compilações de lançamento de bibliotecas, respectivamente.

  • Além disso, eu queria que as versões corretas dos arquivos DLL (Debug / Release) fossem copiadas para os diretórios de saída.

  • E eu queria conseguir tudo isso sem escrever scripts complexos e frágeis.

Depois de navegar alguns manuais do CMake e alguns projetos multiplataforma no github, encontrei esta solução:

Declare sua biblioteca como um destino com o atributo "IMPORTED", faça referência a sua depuração e libere os arquivos .lib e .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")

Vincule este destino ao seu projeto como de costume

target_link_libraries(YourProg sdl2 ...)

Faça uma etapa de compilação personalizada para copiar o arquivo dll para seu destino se ele tiver sido alterado de alguma forma desde a compilação anterior

add_custom_command ( TARGET YourProg POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_if_different
    $<TARGET_FILE:sdl2> $<TARGET_FILE_DIR:YourProg>
)
Ameaça Menor
fonte
5

Um adendo à resposta aceita, adicionado como uma resposta separada para obter a formatação do código:

Se você estiver construindo suas dlls no mesmo projeto, elas geralmente estarão nos diretórios Release, Debug, etc. Você terá que usar as variáveis ​​de ambiente do Visual Studio para copiá-las corretamente. por exemplo:

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

para a fonte e

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

para o destino. Observe o escape!

Você não pode usar a variável CMake CMAKE_BUILD_TYPE para a configuração, pois ela é resolvida no momento da geração do projeto VS e sempre será o padrão.

Dana Robinson
fonte
8
A última parte da resposta aceita aborda isso da maneira mais limpa CMake usando$<CONFIGURATION>
Chuck Claunch
4

Você também pode usar o comando find_library:

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

Com um EXECUTABLE_PATH definido, por exemplo:

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

você pode mover os arquivos .dll de que seu executável precisa, com

file(COPY ${<some_var>}
    DESTINATION ${EXECUTABLE_OUTPUT_PATH})
Felipe espinho
fonte
2

Isso é útil para um deles

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
fonte
1

Você provavelmente precisará adicionar um destino personalizado e torná-lo dependente de um de seus destinos executáveis.

Para copiar o arquivo usando a função acima, use:

COMMAND ${CMAKE_PROGRAM} -E copy_if_different ${CMAKE_BINARY_DIR}/path/to/file.dll ${CMAKE_BINARY_DIR}/where/to/put/file.dll`
arrowd
fonte
0

Sou um iniciante em CMake, mas ainda assim queria compartilhar minha experiência. No meu caso, eu precisava de uma cópia pós-instalação para que todos os meus binários estivessem dentro. No caso de binários de terceiros que podem ser importados dentro do CMake, o seguinte funciona para mim:

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()

Obviamente, IMPORTED_LOCATION_RELEASE pode ter variantes, dependendo de como a biblioteca compartilhada foi construída / instalada. Pode ser IMPORTED_LOCATION_DEBUG.

Talvez haja uma maneira melhor de obter esse nome de propriedade, não sei.

Norman Pellet
fonte
0

Movendo arquivos durante a construção usando install

Tive esse problema ao tentar seguir o tutorial oficial do CMake na Etapa 9 . Este era o local do arquivo que eu queria mover:

src
 |_build
    |_Debug
       - `MathFunctions.dll`

Este era o local onde eu queria que o arquivo estivesse:

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

Uma vez que esta DLL foi gerado como uma biblioteca compartilhada, tudo o que fiz foi a de incluir esta linha no CMakeLists.txtno subdiretório que continha o código fonte para a bibliotecasrc/Mathfunctions/CMakeLists.txt

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

Graças às suas respostas, pude pensar sobre este. É apenas uma linha, então acho que está tudo bem. O $<CONFIG>pode ter dois valores Debug ou Release Dependendo de como o projeto é construído, conforme a pergunta original exigia.

David Martinez C.
fonte