How to get the output path of a target defined by ExternalProject? - c++

I'm building Google's FlatBuffers as a dependency for my own project and I need to compile a schema at build-time. I don't want to use BuildFlatBuffers.cmake or FindFlatBuffers.cmake because I'm using a specific version and I can't rely on it being locally installed.
This is a simplified version of my CMakeLists.txt:
ExternalProject_Add (
flatbuf
URL "https://github.com/google/flatbuffers/archive/v1.8.0.tar.gz"
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
)
add_custom_target (
flatbuf_schema
PREFIX ${FLATBUF_PREFIX}
DEPENDS flatbuf
COMMAND ${FLATBUF_PREFIX}/src/flatbuf-build/flatc --cpp ${FLATBUF_SCHEMA}
)
It works fine for Make and Ninja but fails in Xcode, which builds flatc in the Debug directory.
I thought about these possible solutions:
use add_subdirectory instead of ExternalProject_Add so that I can use ${FLATBUFFERS_FLATC_EXECUTABLE} or $<TARGET_FILE:flatc>;
manually assign a RUNTIME_OUTPUT_DIRECTORY for flatbuf;
search for flatc in multiple paths (not portable; I also don't know how to make it happen at build-time).
I tried (2) and (3) but without success. As for (1), I'm not sure it's a good idea. How can I build schemas in a portable manner?

You can use ExternalProject_Get_Property, something like this...
note: I suppose you don't even need to install flatbuf, just build it and use it.
ExternalProject_Add (
flatbuf_project
URL "https://github.com/google/flatbuffers/archive/v1.8.0.tar.gz"
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(flatbuf_project source_dir)
ExternalProject_Get_Property(flatbuf_project binary_dir)
# Export flatbuf executable to consume schema file during build
add_executable(flatbuf::flatbuf IMPORTED)
set_target_properties(flatbuf::flatbuf PROPERTIES IMPORTED_LOCATION
"${binary_dir}/flatc")
add_dependencies(flatbuf::flatbuf flatbuf_project)
add_custom_target(flatbuf_schema
PREFIX ${FLATBUF_PREFIX}
COMMAND flatbuf::flatbuff --cpp ${FLATBUF_SCHEMA}
)
note2:
If COMMAND specifies an executable target name (created by the add_executable() command) it will automatically be replaced by the location of the executable created at build time. If set, the CROSSCOMPILING_EMULATOR executable target property will also be prepended to the command to allow the executable to run on the host. Additionally a target-level dependency will be added so that the executable target will be built before this custom target.
note3:
target ALIAS are not working on IMPORTED target unfortunately...

Apparently CMake provides a variable to solve this exact problem, i.e. CMAKE_CFG_INTDIR (docs). The path to the flatc executable should then be
ExternalProject_Get_Property(flatbuf BINARY_DIR)
set (FLATC "${BINARY_DIR}/${CMAKE_CFG_INTDIR}/flatc")

Related

How can i link a custom library with Cmake? [duplicate]

We use CMake for generating the Visual Studio files of our sources in our SVN. Now my tool requires some DLL files to be in the same folder as the executable. The DLL files are in a folder alongside the source.
How can I change my CMakeLists.txt such that the generated Visual Studio project will either have already the particular DLL files in the release/debug folders or will copy them upon compilation?
I'd use add_custom_command to achieve this along with cmake -E copy_if_different.... For full info run
cmake --help-command add_custom_command
cmake -E
So in your case, if you have the following directory structure:
/CMakeLists.txt
/src
/libs/test.dll
and your CMake target to which the command applies is MyTest, then you could add the following to your 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
If you just want the entire contents of the /libs/ directory copied, 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>)
If you need to copy different dlls depending upon the configuration (Release, Debug, eg) then you could have these in subdirectories named with the corresponding configuration: /libs/Release, and /libs/Debug. You then need to inject the configuration type into the path to the dll in the add_custom_command call, like this:
add_custom_command(TARGET MyTest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PROJECT_SOURCE_DIR}/libs/$<CONFIGURATION>"
$<TARGET_FILE_DIR:MyTest>)
I put these lines in my top-level CMakeLists.txt file. All the libraries and executables compiled by CMake will be placed in the top level of the build directory so that the executables can find the libraries and it is easy to run everything.
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
Note that this doesn't solve the OP's problem of copying precompiled binaries from the project's source directory.
I've had this problem today when tried to make a Windows build of my program. And I ended up doing some research myself since all these answers didn't satisfy me. There were three main issues:
I wanted debug builds to be linked with debug versions of libraries
and release builds to be linked with release builds of libraries,
respectively.
In addition to that, I wanted correct versions of DLL files
(Debug/Release) to be copied to output directories.
And I wanted to achieve all this without writing complex and fragile scripts.
After browsing some CMake manuals and some multiplatform projects at github I've found this solution:
Declare your library as a target with "IMPORTED" attribute, reference its debug and release .lib and .dll files.
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")
Link this target with your project as usual
target_link_libraries(YourProg sdl2 ...)
Make custom build step to copy dll file to its destination if it has been altered somehow since previous build
add_custom_command ( TARGET YourProg POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:sdl2> $<TARGET_FILE_DIR:YourProg>
)
For Windows users, there is a new generator expression $<TARGET_RUNTIME_DLLS:tgt> in CMake 3.21+ and you could use this official snippet for copying all of the DLLs that a target depends on.
find_package(foo REQUIRED)
add_executable(exe main.c)
target_link_libraries(exe PRIVATE foo::foo foo::bar)
add_custom_command(TARGET exe POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:exe> $<TARGET_FILE_DIR:exe>
COMMAND_EXPAND_LISTS
)
Moving files during build using install
I had this issue trying to follow the CMake official tutorial on Step 9. This was the location of the file I wanted to move:
src
|_build
|_Debug
- `MathFunctions.dll`
This was the location I wanted the file to be in:
src
|_build
|_install
|_bin
- `MathFunctions.dll`
Since this DLL was generated as a shared library, all I did was to include this line in the CMakeLists.txt in the subdirectory that contained the source code for the library src/Mathfunctions/CMakeLists.txt
install(FILES ${PROJECT_BINARY_DIR}/$<CONFIG>/MathFunctions.dll
DESTINATION bin)
Thanks to your answers I could think on this one. Is just one line, so I think is ok. The $<CONFIG> can have two values Debug or Release Depending on how the project is built, as the original question required.
You can also use the command find_library:
find_library(<some_var> NAMES <name_of_lib> PATHS "<path/to/lib>")
With a defined EXECUTABLE_PATH, for instance:
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
you could move the .dll files that your executable need, with
file(COPY ${<some_var>}
DESTINATION ${EXECUTABLE_OUTPUT_PATH})
An addendum to the accepted answer, added as a separate answer so I get code formatting:
If you are building your dlls in the same project, they will usually be in Release, Debug, etc. directories. You'll have to use the Visual Studio environment variables to correctly copy them. e.g.:
"${PROJECT_BINARY_DIR}/your_library/\$\(Configuration\)/your_library.dll"
for the source and
"${CMAKE_CURRENT_BINARY_DIR}/\$\(Configuration\)/your_library.dll"
for the destination. Note the escaping!
You can't use the CMake CMAKE_BUILD_TYPE variable for the configuration since it's resolved at VS project generation time and will always be whatever the default is.
This is useful for one of them
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)
1. The most correct way: TARGET_RUNTIME_DLLS (CMake >= 3.21)
install(FILES $<TARGET_RUNTIME_DLLS:your_exe_here> TYPE BIN)
For this to work, your dependencies' CMake modules have to be well-written. In other words, they use CMake 3 targets with all their target properties set up correctly. If they set up everything right, all transitively-linked DLLs will be automagically gathered up and installed alongside your exe.
A big difference between this and the straight-up copy-the-files approach is that, because it goes through install(), CMake will actually know about them as files to-be-installed. CPack will know about your DLLs and include them in any installer you generate with it. CMake will automatically adjust what type of DLL (release vs debug) to match your target exe.
This is where CMake will be headed more in the future, and the way you should prefer if you have a choice.
2. The second most correct way: RUNTIME_DEPENDENCIES (CMake >= 3.21)
install(TARGETS your_exe_here
RUNTIME ARCHIVE LIBRARY RUNTIME FRAMEWORK BUNDLE PUBLIC_HEADER RESOURCE)
install(TARGETS your_exe_here
COMPONENT your_exe_here
RUNTIME_DEPENDENCIES
PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-"
POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
DIRECTORIES $<TARGET_FILE_DIR:your_exe_here>)
The key here is RUNTIME_DEPENDENCIES.
Internally, RUNTIME_DEPENDENCIES calls file(GET_RUNTIME_DEPENDENCIES), which scans your executable binary, tries very hard to exactly replicate what actual dependency resolution would look like, and write down all the DLLs mentioned along the way. These are passed back up to install().
What this means is that this doesn't depend on your dependencies' CMake modules having their target properties set up correctly. Your actual executable binary is scanned. Everything will get picked up.
3. The third most correct way: install(DIRECTORY)
install(
DIRECTORY "${DIR_CONTAINING_YOUR_DLLS}"
TYPE BIN
FILES_MATCHING REGEX "[^\\\\/.]\\.[dD][lL][lL]$"
)
To use, put the DLLs appropriate for your build in $DIR_CONTAINING_YOUR_DLLS.
The trick here is that, unlike install(FILES), install(DIRECTORY) doesn't care what specific files are in the directory until install time. That means now we have all of configure time and compile time to get a list of your DLLs and stuff them in $DIR_CONTAINING_YOUR_DLLS. As long as the DLL files are in $DIR_CONTAINING_YOUR_DLLS by install time, install(DIRECTORY) will pick them up.
If you choose this method, it's becomes your responsibility to match DLLs to your build config. (Consider: static vs dynamic, debug vs release, import lib version vs DLL version, libs with optional multithreading, forgetting to remove DLLs you don't need anymore.)
If you choose this method, you might want to automate DLL finding and matching using something like what vcpkg's applocal.ps1 does.
Hint for vcpkg
If you use vpckg with VCPKG_APPLOCAL_DEPS enabled, vcpkg will locate and copy your DLLs into your $CMAKE_RUNTIME_OUTPUT_DIRECTORY for you, but without going through install(). You need to use the install(DIRECTORY) trick to get CMake to pick them up.
(Internally, vcpkg uses dumpbin, llvm-objdump, and objdump to scan your executable binary to get these filenames.)
You probably need to add custom target and make it depend on one of your executable targets.
To copy file using above function use:
COMMAND ${CMAKE_PROGRAM} -E copy_if_different ${CMAKE_BINARY_DIR}/path/to/file.dll ${CMAKE_BINARY_DIR}/where/to/put/file.dll`
The following command from the currently top rated answer depends on the output being put in /libs/.
add_custom_command(TARGET MyTest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PROJECT_SOURCE_DIR}/libs/$<CONFIGURATION>"
$<TARGET_FILE_DIR:MyTest>)
I use the below command to do the copy, which works for me everywhere. Note that I'm only copying the output dll here, not the entire directory. Also note that I'm hard coding a destination /bin/ directory, which is specific to this project. But I wanted to share the $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}> syntax, which I think is neat:
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}>
${CMAKE_CURRENT_SOURCE_DIR}/bin/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}>)
I'm a CMake beginner, but still I wanted to shared my experience. In my case I needed a post-install copy so that all my binaries are in.
In the case of third-party binary that can be imported within CMake, the following works for me:
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()
Obviously IMPORTED_LOCATION_RELEASE can have variants depending on how the shared library was built / installed. Could be IMPORTED_LOCATION_DEBUG.
Maybe there's a better way to get that property name, I don't know.

missing dlls in a project build with vcpkg and CMake [duplicate]

We use CMake for generating the Visual Studio files of our sources in our SVN. Now my tool requires some DLL files to be in the same folder as the executable. The DLL files are in a folder alongside the source.
How can I change my CMakeLists.txt such that the generated Visual Studio project will either have already the particular DLL files in the release/debug folders or will copy them upon compilation?
I'd use add_custom_command to achieve this along with cmake -E copy_if_different.... For full info run
cmake --help-command add_custom_command
cmake -E
So in your case, if you have the following directory structure:
/CMakeLists.txt
/src
/libs/test.dll
and your CMake target to which the command applies is MyTest, then you could add the following to your 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
If you just want the entire contents of the /libs/ directory copied, 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>)
If you need to copy different dlls depending upon the configuration (Release, Debug, eg) then you could have these in subdirectories named with the corresponding configuration: /libs/Release, and /libs/Debug. You then need to inject the configuration type into the path to the dll in the add_custom_command call, like this:
add_custom_command(TARGET MyTest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PROJECT_SOURCE_DIR}/libs/$<CONFIGURATION>"
$<TARGET_FILE_DIR:MyTest>)
I put these lines in my top-level CMakeLists.txt file. All the libraries and executables compiled by CMake will be placed in the top level of the build directory so that the executables can find the libraries and it is easy to run everything.
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
Note that this doesn't solve the OP's problem of copying precompiled binaries from the project's source directory.
I've had this problem today when tried to make a Windows build of my program. And I ended up doing some research myself since all these answers didn't satisfy me. There were three main issues:
I wanted debug builds to be linked with debug versions of libraries
and release builds to be linked with release builds of libraries,
respectively.
In addition to that, I wanted correct versions of DLL files
(Debug/Release) to be copied to output directories.
And I wanted to achieve all this without writing complex and fragile scripts.
After browsing some CMake manuals and some multiplatform projects at github I've found this solution:
Declare your library as a target with "IMPORTED" attribute, reference its debug and release .lib and .dll files.
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")
Link this target with your project as usual
target_link_libraries(YourProg sdl2 ...)
Make custom build step to copy dll file to its destination if it has been altered somehow since previous build
add_custom_command ( TARGET YourProg POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:sdl2> $<TARGET_FILE_DIR:YourProg>
)
For Windows users, there is a new generator expression $<TARGET_RUNTIME_DLLS:tgt> in CMake 3.21+ and you could use this official snippet for copying all of the DLLs that a target depends on.
find_package(foo REQUIRED)
add_executable(exe main.c)
target_link_libraries(exe PRIVATE foo::foo foo::bar)
add_custom_command(TARGET exe POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:exe> $<TARGET_FILE_DIR:exe>
COMMAND_EXPAND_LISTS
)
Moving files during build using install
I had this issue trying to follow the CMake official tutorial on Step 9. This was the location of the file I wanted to move:
src
|_build
|_Debug
- `MathFunctions.dll`
This was the location I wanted the file to be in:
src
|_build
|_install
|_bin
- `MathFunctions.dll`
Since this DLL was generated as a shared library, all I did was to include this line in the CMakeLists.txt in the subdirectory that contained the source code for the library src/Mathfunctions/CMakeLists.txt
install(FILES ${PROJECT_BINARY_DIR}/$<CONFIG>/MathFunctions.dll
DESTINATION bin)
Thanks to your answers I could think on this one. Is just one line, so I think is ok. The $<CONFIG> can have two values Debug or Release Depending on how the project is built, as the original question required.
You can also use the command find_library:
find_library(<some_var> NAMES <name_of_lib> PATHS "<path/to/lib>")
With a defined EXECUTABLE_PATH, for instance:
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
you could move the .dll files that your executable need, with
file(COPY ${<some_var>}
DESTINATION ${EXECUTABLE_OUTPUT_PATH})
An addendum to the accepted answer, added as a separate answer so I get code formatting:
If you are building your dlls in the same project, they will usually be in Release, Debug, etc. directories. You'll have to use the Visual Studio environment variables to correctly copy them. e.g.:
"${PROJECT_BINARY_DIR}/your_library/\$\(Configuration\)/your_library.dll"
for the source and
"${CMAKE_CURRENT_BINARY_DIR}/\$\(Configuration\)/your_library.dll"
for the destination. Note the escaping!
You can't use the CMake CMAKE_BUILD_TYPE variable for the configuration since it's resolved at VS project generation time and will always be whatever the default is.
This is useful for one of them
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)
1. The most correct way: TARGET_RUNTIME_DLLS (CMake >= 3.21)
install(FILES $<TARGET_RUNTIME_DLLS:your_exe_here> TYPE BIN)
For this to work, your dependencies' CMake modules have to be well-written. In other words, they use CMake 3 targets with all their target properties set up correctly. If they set up everything right, all transitively-linked DLLs will be automagically gathered up and installed alongside your exe.
A big difference between this and the straight-up copy-the-files approach is that, because it goes through install(), CMake will actually know about them as files to-be-installed. CPack will know about your DLLs and include them in any installer you generate with it. CMake will automatically adjust what type of DLL (release vs debug) to match your target exe.
This is where CMake will be headed more in the future, and the way you should prefer if you have a choice.
2. The second most correct way: RUNTIME_DEPENDENCIES (CMake >= 3.21)
install(TARGETS your_exe_here
RUNTIME ARCHIVE LIBRARY RUNTIME FRAMEWORK BUNDLE PUBLIC_HEADER RESOURCE)
install(TARGETS your_exe_here
COMPONENT your_exe_here
RUNTIME_DEPENDENCIES
PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-"
POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
DIRECTORIES $<TARGET_FILE_DIR:your_exe_here>)
The key here is RUNTIME_DEPENDENCIES.
Internally, RUNTIME_DEPENDENCIES calls file(GET_RUNTIME_DEPENDENCIES), which scans your executable binary, tries very hard to exactly replicate what actual dependency resolution would look like, and write down all the DLLs mentioned along the way. These are passed back up to install().
What this means is that this doesn't depend on your dependencies' CMake modules having their target properties set up correctly. Your actual executable binary is scanned. Everything will get picked up.
3. The third most correct way: install(DIRECTORY)
install(
DIRECTORY "${DIR_CONTAINING_YOUR_DLLS}"
TYPE BIN
FILES_MATCHING REGEX "[^\\\\/.]\\.[dD][lL][lL]$"
)
To use, put the DLLs appropriate for your build in $DIR_CONTAINING_YOUR_DLLS.
The trick here is that, unlike install(FILES), install(DIRECTORY) doesn't care what specific files are in the directory until install time. That means now we have all of configure time and compile time to get a list of your DLLs and stuff them in $DIR_CONTAINING_YOUR_DLLS. As long as the DLL files are in $DIR_CONTAINING_YOUR_DLLS by install time, install(DIRECTORY) will pick them up.
If you choose this method, it's becomes your responsibility to match DLLs to your build config. (Consider: static vs dynamic, debug vs release, import lib version vs DLL version, libs with optional multithreading, forgetting to remove DLLs you don't need anymore.)
If you choose this method, you might want to automate DLL finding and matching using something like what vcpkg's applocal.ps1 does.
Hint for vcpkg
If you use vpckg with VCPKG_APPLOCAL_DEPS enabled, vcpkg will locate and copy your DLLs into your $CMAKE_RUNTIME_OUTPUT_DIRECTORY for you, but without going through install(). You need to use the install(DIRECTORY) trick to get CMake to pick them up.
(Internally, vcpkg uses dumpbin, llvm-objdump, and objdump to scan your executable binary to get these filenames.)
You probably need to add custom target and make it depend on one of your executable targets.
To copy file using above function use:
COMMAND ${CMAKE_PROGRAM} -E copy_if_different ${CMAKE_BINARY_DIR}/path/to/file.dll ${CMAKE_BINARY_DIR}/where/to/put/file.dll`
The following command from the currently top rated answer depends on the output being put in /libs/.
add_custom_command(TARGET MyTest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PROJECT_SOURCE_DIR}/libs/$<CONFIGURATION>"
$<TARGET_FILE_DIR:MyTest>)
I use the below command to do the copy, which works for me everywhere. Note that I'm only copying the output dll here, not the entire directory. Also note that I'm hard coding a destination /bin/ directory, which is specific to this project. But I wanted to share the $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}> syntax, which I think is neat:
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}>
${CMAKE_CURRENT_SOURCE_DIR}/bin/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}>)
I'm a CMake beginner, but still I wanted to shared my experience. In my case I needed a post-install copy so that all my binaries are in.
In the case of third-party binary that can be imported within CMake, the following works for me:
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()
Obviously IMPORTED_LOCATION_RELEASE can have variants depending on how the shared library was built / installed. Could be IMPORTED_LOCATION_DEBUG.
Maybe there's a better way to get that property name, I don't know.

Automatically copy dlls to output directory CPP Using Poco [duplicate]

We use CMake for generating the Visual Studio files of our sources in our SVN. Now my tool requires some DLL files to be in the same folder as the executable. The DLL files are in a folder alongside the source.
How can I change my CMakeLists.txt such that the generated Visual Studio project will either have already the particular DLL files in the release/debug folders or will copy them upon compilation?
I'd use add_custom_command to achieve this along with cmake -E copy_if_different.... For full info run
cmake --help-command add_custom_command
cmake -E
So in your case, if you have the following directory structure:
/CMakeLists.txt
/src
/libs/test.dll
and your CMake target to which the command applies is MyTest, then you could add the following to your 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
If you just want the entire contents of the /libs/ directory copied, 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>)
If you need to copy different dlls depending upon the configuration (Release, Debug, eg) then you could have these in subdirectories named with the corresponding configuration: /libs/Release, and /libs/Debug. You then need to inject the configuration type into the path to the dll in the add_custom_command call, like this:
add_custom_command(TARGET MyTest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PROJECT_SOURCE_DIR}/libs/$<CONFIGURATION>"
$<TARGET_FILE_DIR:MyTest>)
I put these lines in my top-level CMakeLists.txt file. All the libraries and executables compiled by CMake will be placed in the top level of the build directory so that the executables can find the libraries and it is easy to run everything.
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
Note that this doesn't solve the OP's problem of copying precompiled binaries from the project's source directory.
I've had this problem today when tried to make a Windows build of my program. And I ended up doing some research myself since all these answers didn't satisfy me. There were three main issues:
I wanted debug builds to be linked with debug versions of libraries
and release builds to be linked with release builds of libraries,
respectively.
In addition to that, I wanted correct versions of DLL files
(Debug/Release) to be copied to output directories.
And I wanted to achieve all this without writing complex and fragile scripts.
After browsing some CMake manuals and some multiplatform projects at github I've found this solution:
Declare your library as a target with "IMPORTED" attribute, reference its debug and release .lib and .dll files.
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")
Link this target with your project as usual
target_link_libraries(YourProg sdl2 ...)
Make custom build step to copy dll file to its destination if it has been altered somehow since previous build
add_custom_command ( TARGET YourProg POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:sdl2> $<TARGET_FILE_DIR:YourProg>
)
For Windows users, there is a new generator expression $<TARGET_RUNTIME_DLLS:tgt> in CMake 3.21+ and you could use this official snippet for copying all of the DLLs that a target depends on.
find_package(foo REQUIRED)
add_executable(exe main.c)
target_link_libraries(exe PRIVATE foo::foo foo::bar)
add_custom_command(TARGET exe POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:exe> $<TARGET_FILE_DIR:exe>
COMMAND_EXPAND_LISTS
)
Moving files during build using install
I had this issue trying to follow the CMake official tutorial on Step 9. This was the location of the file I wanted to move:
src
|_build
|_Debug
- `MathFunctions.dll`
This was the location I wanted the file to be in:
src
|_build
|_install
|_bin
- `MathFunctions.dll`
Since this DLL was generated as a shared library, all I did was to include this line in the CMakeLists.txt in the subdirectory that contained the source code for the library src/Mathfunctions/CMakeLists.txt
install(FILES ${PROJECT_BINARY_DIR}/$<CONFIG>/MathFunctions.dll
DESTINATION bin)
Thanks to your answers I could think on this one. Is just one line, so I think is ok. The $<CONFIG> can have two values Debug or Release Depending on how the project is built, as the original question required.
You can also use the command find_library:
find_library(<some_var> NAMES <name_of_lib> PATHS "<path/to/lib>")
With a defined EXECUTABLE_PATH, for instance:
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
you could move the .dll files that your executable need, with
file(COPY ${<some_var>}
DESTINATION ${EXECUTABLE_OUTPUT_PATH})
An addendum to the accepted answer, added as a separate answer so I get code formatting:
If you are building your dlls in the same project, they will usually be in Release, Debug, etc. directories. You'll have to use the Visual Studio environment variables to correctly copy them. e.g.:
"${PROJECT_BINARY_DIR}/your_library/\$\(Configuration\)/your_library.dll"
for the source and
"${CMAKE_CURRENT_BINARY_DIR}/\$\(Configuration\)/your_library.dll"
for the destination. Note the escaping!
You can't use the CMake CMAKE_BUILD_TYPE variable for the configuration since it's resolved at VS project generation time and will always be whatever the default is.
This is useful for one of them
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)
1. The most correct way: TARGET_RUNTIME_DLLS (CMake >= 3.21)
install(FILES $<TARGET_RUNTIME_DLLS:your_exe_here> TYPE BIN)
For this to work, your dependencies' CMake modules have to be well-written. In other words, they use CMake 3 targets with all their target properties set up correctly. If they set up everything right, all transitively-linked DLLs will be automagically gathered up and installed alongside your exe.
A big difference between this and the straight-up copy-the-files approach is that, because it goes through install(), CMake will actually know about them as files to-be-installed. CPack will know about your DLLs and include them in any installer you generate with it. CMake will automatically adjust what type of DLL (release vs debug) to match your target exe.
This is where CMake will be headed more in the future, and the way you should prefer if you have a choice.
2. The second most correct way: RUNTIME_DEPENDENCIES (CMake >= 3.21)
install(TARGETS your_exe_here
RUNTIME ARCHIVE LIBRARY RUNTIME FRAMEWORK BUNDLE PUBLIC_HEADER RESOURCE)
install(TARGETS your_exe_here
COMPONENT your_exe_here
RUNTIME_DEPENDENCIES
PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-"
POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
DIRECTORIES $<TARGET_FILE_DIR:your_exe_here>)
The key here is RUNTIME_DEPENDENCIES.
Internally, RUNTIME_DEPENDENCIES calls file(GET_RUNTIME_DEPENDENCIES), which scans your executable binary, tries very hard to exactly replicate what actual dependency resolution would look like, and write down all the DLLs mentioned along the way. These are passed back up to install().
What this means is that this doesn't depend on your dependencies' CMake modules having their target properties set up correctly. Your actual executable binary is scanned. Everything will get picked up.
3. The third most correct way: install(DIRECTORY)
install(
DIRECTORY "${DIR_CONTAINING_YOUR_DLLS}"
TYPE BIN
FILES_MATCHING REGEX "[^\\\\/.]\\.[dD][lL][lL]$"
)
To use, put the DLLs appropriate for your build in $DIR_CONTAINING_YOUR_DLLS.
The trick here is that, unlike install(FILES), install(DIRECTORY) doesn't care what specific files are in the directory until install time. That means now we have all of configure time and compile time to get a list of your DLLs and stuff them in $DIR_CONTAINING_YOUR_DLLS. As long as the DLL files are in $DIR_CONTAINING_YOUR_DLLS by install time, install(DIRECTORY) will pick them up.
If you choose this method, it's becomes your responsibility to match DLLs to your build config. (Consider: static vs dynamic, debug vs release, import lib version vs DLL version, libs with optional multithreading, forgetting to remove DLLs you don't need anymore.)
If you choose this method, you might want to automate DLL finding and matching using something like what vcpkg's applocal.ps1 does.
Hint for vcpkg
If you use vpckg with VCPKG_APPLOCAL_DEPS enabled, vcpkg will locate and copy your DLLs into your $CMAKE_RUNTIME_OUTPUT_DIRECTORY for you, but without going through install(). You need to use the install(DIRECTORY) trick to get CMake to pick them up.
(Internally, vcpkg uses dumpbin, llvm-objdump, and objdump to scan your executable binary to get these filenames.)
You probably need to add custom target and make it depend on one of your executable targets.
To copy file using above function use:
COMMAND ${CMAKE_PROGRAM} -E copy_if_different ${CMAKE_BINARY_DIR}/path/to/file.dll ${CMAKE_BINARY_DIR}/where/to/put/file.dll`
The following command from the currently top rated answer depends on the output being put in /libs/.
add_custom_command(TARGET MyTest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PROJECT_SOURCE_DIR}/libs/$<CONFIGURATION>"
$<TARGET_FILE_DIR:MyTest>)
I use the below command to do the copy, which works for me everywhere. Note that I'm only copying the output dll here, not the entire directory. Also note that I'm hard coding a destination /bin/ directory, which is specific to this project. But I wanted to share the $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}> syntax, which I think is neat:
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}>
${CMAKE_CURRENT_SOURCE_DIR}/bin/$<TARGET_FILE_NAME:${CMAKE_PROJECT_NAME}>)
I'm a CMake beginner, but still I wanted to shared my experience. In my case I needed a post-install copy so that all my binaries are in.
In the case of third-party binary that can be imported within CMake, the following works for me:
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()
Obviously IMPORTED_LOCATION_RELEASE can have variants depending on how the shared library was built / installed. Could be IMPORTED_LOCATION_DEBUG.
Maybe there's a better way to get that property name, I don't know.

CMake declare dependency of function on ExternalProject_Add

TLDR:
My problem is that CMake starts executing this function before
downloading the repository. I would like to declare a dependency for
that function on ExternalProject_Add so that CMake understands that it
should download, build and then run the function.
Context:
I have a cmake module SomeModule.cmake which is supposed to add flatbuffers as an external project from its repository and build it. The build would produce flatbuffers compiler executable which I intend to use in some/directory/CMakeLists.txt file to generate c++ header files from a flatbuffers schema. So in that same CMake module that I use ExternalProject_Add, I have declared a CMake function that generates header files from a given set of schema files and somewhere in some/directory/CMakeLists.txt I call that function.
My problem is that CMake starts executing this function before downloading the repository. I would like to declare a dependency for that function on ExternalProject_Add so that CMake understands that it should download, build and then run the function.
Enough talk. Here's relevant parts of the code:
SomeModule.cmake:
include(ExternalProject)
set(flatbuffers_CMAKE_ARGS
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
"-DFLATBUFFERS_BUILD_TESTS=OFF"
"-DFLATBUFFERS_BUILD_FLATC=ON"
"-DFLATBUFFERS_BUILD_FLATHASH=OFF"
"-DCMAKE_INSTALL_PREFIX=${OTS_DEPENDENCIES}"
)
ExternalProject_Add(
flatbuffers
GIT_REPOSITORY "https://github.com/google/flatbuffers.git"
GIT_TAG "v1.9.0"
SOURCE_DIR "${OTS_DEPDENDENCIES_DIR}/flatbuffers"
BINARY_DIR "${OTS_DEPDENDENCIES_DIR}/flatbuffers"
CMAKE_ARGS "${flatbuffers_CMAKE_ARGS}"
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(flatbuffers SOURCE_DIR)
ExternalProject_Get_Property(flatbuffers BINARY_DIR)
set(flatbuffers_SOURCE_DIR ${SOURCE_DIR})
set(flatbuffers_BINARY_DIR ${BINARY_DIR})
set(flatbuffers_INCLUDE_DIR ${flatbuffers_SOURCE_DIR}/include)
set(flatbuffers_FLATC_EXECUTABLE ${flatbuffers_BINARY_DIR}/flatc)
# please assume that the variables above are all set to appropriate values
function(FlatbuffersGenerateCpp SCHEMA_FILES GENERATED_DIR GENERATED_CXX)
foreach(SCHEMA_FILE ${SCHEMA_FILES})
get_filename_component(NAME ${SCHEMA_FILE} NAME_WE)
set(GENERATED_HEADER_FILE_PATH ${GENERATED_DIR}/${NAME}_generated.h)
message(STATUS "attempting to generate: ${GENERATED_HEADER_FILE_PATH}")
add_custom_command(
DEPENDS ${flatbuffers_FLATC_EXECUTABLE}
OUTPUT ${GENERATED_HEADER_FILE_PATH}
COMMAND ${flatbuffers_FLATC_EXECUTABLE} -o ${GENERATED_DIR} -c ${SCHEMA_FILE}
COMMENT "generating flatbuffers c++ header file: ${GENERATED_HEADER_FILE_PATH}"
)
list(APPEND GENERATED_FILES ${GENERATED_HEADER_FILE_PATH})
endforeach()
message(STATUS "generated c++ header files: ${GENERATED_FILES}")
set(${GENERATED_CXX} ${GENERATED_FILES} PARENT_SCOPE)
endfunction()
And some/directory/CMakeLists.txt:
# cmake module path is properly set so the following works:
include(SomeModule)
set(flatbuffers_GENERATED_INCLUDES_DIR
${CMAKE_BINARY_DIR}/generated/config/flatbuffers
)
FlatbuffersGenerateCpp(
"${flatbuffers_SCHEMAS}"
"${flatbuffers_GENERATED_INCLUDES_DIR}"
flatbuffers_GENERATED_CXX
)
add_library(
my_framework
SHARED
${THE_PUBLIC_HEADER_FILES}
${THE_IMPL_SOURCE_FILES}
${THE_IMPL_HEADER_FILES}
${flatbuffers_GENERATED_CXX}
)
add_dependencies(my_framework flatbuffers ${flatbuffers_GENERATED_CXX})
target_include_directories(my_framework PRIVATE ${flatbuffers_INCLUDE_DIR})
target_include_directories(my_framework PRIVATE ${CMAKE_SOURCE_DIR})
target_include_directories(my_framework PRIVATE ${CMAKE_BINARY_DIR}/generated)
set_source_files_properties(${flatbuffers_GENERATED_CXX} PROPERTIES GENERATED TRUE)
I did start modifying my code based on comment posted by Tsyvarev:
CMake functions are executed at configure stage, so you need to build the external project at configure stage too.
While I trusted that his proposed solution would work, I was slightly uncomfortable and kept thinking that there has to be a more elegant solution. I consulted with a colleague and came up with a better solution which is as simple as the following diff (which removes ${flatbuffers_GENERATED_CXX}).
- add_dependencies(my_framework flatbuffers ${flatbuffers_GENERATED_CXX})
+ add_dependencies(my_framework flatbuffers)
we reviewed that the problem with the code in question is that as is, CMake reads add_dependencies(my_framework flatbuffers ${flatbuffers_GENERATED_CXX}) and understands that it needs ${flatbuffers_GENERATED_CXX} as target to build my_framework so it proceeds with running the function. But there is no way for it to understand that the function depends on the external project. Now if we remove the explicit dependency declaration of ${flatbuffers_GENERATED_CXX}, CMake defers running the function to after resolving other dependencies (flatbuffers target) which will effectively download and build the external project prior to running the project.

Specify RPATH for externally built CMAKE project

How do I specify an RPATH for a project that was built externally in CMake (using 3.0.0) via ExternalProject_Add() macro?
For reference, let's say my ExternalProject is SFML. My external project call looks like:
set(SFML_INSTALL_PATH "${CMAKE_CURRENT_BINARY_DIR}/ext-deps/ext-sfml-build")
set(SFML_RPATH "${SFML_INSTALL_PATH}/src/lib")
set(SFML_CMAKE_ARGS -DCMAKE_BUILD_TYPE:STRING=Release -DUSE_STATIC_LIBS:BOOL=true
-DCMAKE_INSTALL_PREFIX:PATH=${SFML_INSTALL_PATH}
-DCMAKE_INSTALL_RPATH:PATH=${SFML_RPATH})
ExternalProject_Add(ext-sfml
GIT_REPOSITORY "${SFML_REPO}"
GIT_TAG "${SFML_TAG}"
URL SFML_URL
URL_HASH 256=${SFML_SHA256}
CMAKE_ARGS "${SFML_CMAKE_ARGS}"
TMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/${EXT_DEPS_PREFIX}/ext-sfml-tmp"
STAMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/${EXT_DEPS_PREFIX}/ext-sfml-stamp"
INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/${EXT_DEPS_PREFIX}/ext-sfml-build"
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${EXT_DEPS_PREFIX}/ext-sfml"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${EXT_DEPS_PREFIX}/ext-sfml-build"
TEST_COMMAND "")
The error command I get when I run the build step (configuration is fine) is
Make Error at src/cmake_install.cmake:45 (file):
file RPATH_CHANGE could not write new RPATH:
/home/hendrix/repo/project/build/ext_deps/ext_sfml-build/lib
to the file:
/home/hendrix/repo/project/build/ext_deps/ext_sfml-build/bin/exe
Finally, this "/home/hendrix/repo/project/build/ext_deps/ext_sfml-build/lib" is the line I'm trying to modify. It appears that it is the value of my ${CMAKE_INSTALL_RPATH} variable but containing the install prefix of my ext-dep project and not the top-level master project.
I figured it out (thanks to Tsyvarev's comments). The externally built project that I was using set it's own RPATH. So the string that I found in the error message was that RPATH, not any RPATH which I set in my "top-level" project.
It appears that setting the RPATH of an external project can not override that external project's internally set RPATH (if it is using CMAKE).