CMake - How propage multiple Dll copy with TARGET_RUNTIME_DLLS when using INTERFACE library to group that Dll - cmake

I have a library INTERFACE which reference multiples IMPORTED library, one for each Dll/lib.
The INTERFACE library is here to group that Dlls/sublibraries in one lib.
When I'm using TARGET_RUNTIME_DLLS to copy all depending Dlls, the Dlls are not copy if declare through an INTERFACE library.
# OpenSSL eay
add_library(openssl_eay SHARED IMPORTED)
set_property(TARGET openssl_eay PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/Libs/OpenSSL/Bin/libeay32.dll")
set_property(TARGET openssl_eay PROPERTY IMPORTED_IMPLIB "${CMAKE_SOURCE_DIR}/Libs/OpenSSL/Bin/libeay32.lib")
# OpenSSL ssleay
add_library(openssl_ssleay SHARED IMPORTED)
set_property(TARGET openssl_ssleay PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/Libs/OpenSSL/Bin/ssleay32.dll")
set_property(TARGET openssl_ssleay PROPERTY IMPORTED_IMPLIB "${CMAKE_SOURCE_DIR}/Libs/OpenSSL/lib/Bin/ssleay32.lib")
# OpenSSL
add_library(openssl INTERFACE IMPORTED)
target_link_libraries(openssl INTERFACE openssl_eay openssl_ssleay)
# Foo
add_library(Foo SHARED IMPORTED)
set_property(TARGET Foo PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/Libs/Foo/Bin/Foo.dll")
set_property(TARGET Foo PROPERTY IMPORTED_IMPLIB "${CMAKE_SOURCE_DIR}/Libs/Foo/Bin/Foo.lib")
add_executable(MyExecutable Main.cpp)
target_link_libraries(MyExecutable PRIVATE openssl)
add_custom_command(TARGET MyExecutable POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:MyExecutable> $<TARGET_FILE_DIR:MyExecutable>
COMMAND_EXPAND_LISTS
)
With that configuration, CMake only copy Foo.dll next to the executable, but not libeay32.dll and ssleay32.dll.
I try to declare add_library(openssl INTERFACE) without IMPORTED, but same result/problem.
How I can modified openssl library declaration to have the dependant imported Dlls copy?

Related

What does this CMake error mean? And how could I go about resolving it?

I get this error, which I've been trying to understand for some time now but getting nowhere.
add_library cannot create target "Plugin" because another target
with the same name already exists. The existing target is a shared library created
in source directory "D:/CHAI3D/SOFA/src/applications/plugins/plugin".
See documentation for policy CMP0002 for more details.
Below is the CMakelists.txt added for reference. I tried to remove the unnecessary code. So far, I've tried allowing duplicate targets with set(ALLOW_DUPLICATE_CUSTOM_TARGETS TRUE)
But to no avail
cmake_minimum_required(VERSION 3.1)
project(Plugin VERSION 21.06.99)
# Policies
cmake_policy(SET CMP0079 NEW)
set(ALLOW_DUPLICATE_CUSTOM_TARGETS TRUE)
set (PLUGIN_VERSION ${PROJECT_VERSION})
set(HEADER_FILES
src/initPlugin.h
...
)
set(SOURCE_FILES
src/initPlugin.cpp
...
)
file(GLOB_RECURSE RESOURCE_FILES "*.md" "*.psl" "*.py" "*.pyscn" "*.scn" "*.ah")
add_library(${PROJECT_NAME} SHARED ${HEADER_FILES} ${SOURCE_FILES} ${RESOURCE_FILES} )
target_include_directories(${PROJECT_NAME} PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>")
set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "-DSOFA_BUILD_PLUGIN")
target_link_libraries(${PROJECT_NAME} SofaCore SofaConstraint SofaSimpleFem SofaBaseMechanics SofaRigid SofaBaseVisual SofaOpenglVisual)
## Install rules for the library and headers; CMake package configurations files
sofa_create_package_with_targets(
PACKAGE_NAME ${PROJECT_NAME}
PACKAGE_VERSION ${PROJECT_VERSION}
TARGETS ${PROJECT_NAME} AUTO_SET_TARGET_PROPERTIES
INCLUDE_SOURCE_DIR "src"
INCLUDE_INSTALL_DIR ${PROJECT_NAME}
RELOCATABLE "plugins"
)
SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON)
SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY FOLDER "plugins")

CMake: link order of imported targets incorrect

I have the following scenario:
I import two prebuilt libraries into my project (libA, libB)
libB has a dependency on libA
The executable depends on both libA and libB
However, the relative linking order in my link.txt is incorrect
/usr/bin/c++ CMakeFiles/bin.dir/main.cpp.o -o bin ../libA.a ../libB.a
I would expect libA.a to be listed after libB.a.
The CMakeLists.txt looks something along the following lines
cmake_minimum_required(VERSION 3.13)
project(cmake_test)
set(lib_dir ${CMAKE_CURRENT_SOURCE_DIR})
add_library(MY::libA IMPORTED INTERFACE)
set_target_properties(MY::libA PROPERTIES INTERFACE_LINK_LIBRARIES "${lib_dir}/libA.a")
add_library(MY::libB IMPORTED INTERFACE)
set_target_properties(MY::libB PROPERTIES INTERFACE_LINK_LIBRARIES "MY::libA;${lib_dir}/libB.a")
add_executable(bin ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(bin PUBLIC MY::libB MY::libA)
Below a description of my attempts to solve the problem. Some without success and some with sucecss but using modifications that render the code usless for the production environment.
Successful attempts:
Remove the depedency of bin on libA (i.e. replace the last line by target_link_libraries(bin PUBLIC MY::libB). This works but I cannot remove the dependency in real code.
Replace the target type IMPORTED INTERFACE by IMPORTED STATIC. Use IMPORTED_LOCATION instead of INTERFACE_LINK_LIBRARIES and use target_link_libraries to express the dependency of libB on libA. In this case the link.txt yields: [...] -o bin ../libA.a ../libB.a ../libA.a. As soon as I revert the target type for libB the link order breaks down again. In the production environment, however, one of the targets is created by conan as IMPORTED INTERFACE.
Attempts without success (same behaviour as described):
Create a separate IMPORTED target (use IMPORTED_LOCATION) for every lib and group them inside an INTERFACE target
Sprinkle the code with ADD_DEPENDENCIES
Remove libA from the INTERFACE_LINK_LIBRARIES in line 9 and use target_link_libraries(MY::libB INTERFACE MY::libA) instead. Same result.
Example code that shows the same failure using INTERFACES as a building block
cmake_minimum_required(VERSION 3.13)
project(cmake_test)
set(lib_dir ${CMAKE_CURRENT_SOURCE_DIR})
# libA
add_library(MY::libA_file1 IMPORTED STATIC)
set_target_properties(MY::libA_file1 PROPERTIES IMPORTED_LOCATION "${lib_dir}/libA.a")
add_library(libA INTERFACE)
target_link_libraries(libA INTERFACE MY::libA_file1)
# libB
add_library(MY::libB_file1 IMPORTED STATIC)
set_target_properties(MY::libB_file1 PROPERTIES IMPORTED_LOCATION "${lib_dir}/libB.a")
add_library(libB INTERFACE)
target_link_libraries(libB INTERFACE MY::libB_file1 libA)
add_executable(bin ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(bin PUBLIC libA libB)
You incorrectly think about INTERFACE_LINK_LIBRARIES property as a "content" of the library's target, which is ordered by target_link_libraries call.
Using
target_link_libraries(MY::libB INTERFACE MY::libA)
you setup link dependency between library targets MY::libB and MY::libA. That is, "content" of MY::libB target should come before "content" of MY::libA target in the linking command line.
But INTERFACE_LINK_LIBRARIES property is NOT a "content" of the library target! It is just an additional link dependency.
As opposite, IMPORTED_LOCATION (for non-INTERFACE IMPORTED target) is a "content" of the library, and target_link_libraries affects on its ordering.
It seems that you cannot add link dependency for a library, using INTERFACE library target. You should use IMPORTED library target for that purpose:
# Collect libraries related to 'libA'
file(GLOB libs_A "${lib_dir}/libA*.a")
# For each library create IMPORTED target with IMPORTED_LOCATION property.
set(libs_A_targets)
foreach(lib_A ${libs_A})
# Form a unique name for the IMPORTED target: subtarget_A_*
string(REGEX REPLACE "^${lib_dir}/libA([^.]*).a$" "subtarget_A_\\1" lib_A_target ${lib_A})
# Create a target with this name
add_library(${lib_A_target} STATIC IMPORTED)
set_target_properties(${lib_A_target} PROPERTIES IMPORTED_LOCATION ${lib_A})
# And add the target into the list
list(APPEND libs_A_targets ${lib_A_target})
endforeach()
# In a similar way collect libraries for libB.
set(lib_B_targets ...)
# Now link each libB* library with each libA*.
foreach(lib_B_target ${libs_B_targets})
target_link_libraries(${lib_B_target} INTERFACE ${libs_A_targets})
endforeach()
# Now interface libraries, which combine libA* and libB*, can be created
add_library(libA INTERFACE)
target_link_libraries(libA INTERFACE ${libs_A_targets})
add_library(libB INTERFACE)
target_link_libraries(libB INTERFACE ${libs_B_targets})
# Now these INTERFACE libraries can be linked into an executable in any order
add_executable(bin ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(bin PUBLIC libA libB)

How to instruct CMake to use external pre-built libraries

I'm struggling how to find a way to say to CMake to include external prebuilt libraries... It's driving me insane.
It's understatement that I'm new to CMake. I just want that MinGW linker add two external .lib files to link list... I'm pulling my hair at this point.
This is my CMakeLists.txt file:
cmake_minimum_required(VERSION 3.9)
project(opengltest C CXX)
set(CMAKE_CXX_STANDARD 11)
file(GLOB
TestSRC
"src/*.h"
"src/*.cpp"
"src/*.c"
)
add_executable(opengltest ${TestSRC})
include_directories(
3rdparty/glew/include
3rdparty/glfw/include
)
link_directories(
${PROJECT_SOURCE_DIR}/3rdparty/glew/lib/Release/x64/
${PROJECT_SOURCE_DIR}/3rdparty/glfw/build/src/Debug/
)
target_link_libraries(${PROJECT_NAME} glew32s glfw3)
Linker says it cannot find glew32s and glfw3.
EDIT: I think I've found the solution:
...
add_library(glew32s STATIC IMPORTED)
set_property(TARGET glew32s PROPERTY IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/glew32s.lib)
add_library(glfw3 STATIC IMPORTED)
set_property(TARGET glfw3 PROPERTY IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/glfw3.lib)
target_link_libraries(${PROJECT_NAME} glew32s glfw3)
I've moved all my .lib files to one folder and used add_library to include them.
...
add_library(glew32s STATIC IMPORTED)
set_property(TARGET glew32s PROPERTY IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/glew32s.lib)
add_library(glfw3 STATIC IMPORTED)
set_property(TARGET glfw3 PROPERTY IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/glfw3.lib)
target_link_libraries(${PROJECT_NAME} glew32s glfw3)
After that linker was able to find lib files.

Transitively require other imported library's interface from imported library

CMake 3.5
I have an existing external library, which I have made an IMPORTED library in my CMakeLists.txt:
find_path(
FOO_INCLUDE_DIR NAMES foo.h
PATHS "${FOO_ROOT}"
NO_DEFAULT_PATH
PATH_SUFFIXES include/foo
)
find_library(
FOO_LIBRARY NAMES foo
PATHS "${FOO_ROOT}"
PATH_SUFFIXES lib
)
mark_as_advanced(FOO_INCLUDE_DIR FOO_LIBRARY)
find_package_handle_standard_args(
FOO REQUIRED_VARS
FOO_INCLUDE_DIR
FOO_LIBRARY
)
add_library(Foo::Foo SHARED IMPORTED)
set_property(TARGET Foo::Foo
PROPERTY INTERFACE_INCLUDE_DIRECTORIES
"${FOO_INCLUDE_DIR}" "${BAR_INCLUDE_DIR}"
"${Boost_INCLUDE_DIRS}" "${BAZ_INCLUDE_DIR}"
)
set_property(TARGET Foo::Foo
PROPERTY IMPORTED_LOCATION
"${FOO_LIBRARY}"
)
set_property(TARGET Foo::Foo
PROPERTY INTERFACE_LINK_LIBRARIES
"${Boost_LIBRARIES}" "${BAR_LIBRARY}" "${BAZ_LIBRARIES}"
)
Programs that link with this particular library need thread support. Adding thread support to something is fairly straightforward:
set(THREADS_PREFER_PTHREAD_FLAG on)
include(FindThreads)
...
target_link_libraries(something PUBLIC Threads::Threads)
I would like anything that links against Foo::Foo to automatically include Threads::Threads. But you can't use target_link_libraries() on an IMPORTED library. So how do I transitively require Threads::Threads from Foo::Foo?
I managed to work around this by doing the following, but it depended on me checking to see what properties FindThreads sets on Threads::Threads. Is there a better way?
set_property(TARGET Foo::Foo
PROPERTY INTERFACE_LINK_LIBRARIES
"${Boost_LIBRARIES}" "${BAR_LIBRARY}" "${BAZ_LIBRARIES}"
$<TARGET_PROPERTY:Threads::Threads,INTERFACE_LINK_LIBRARIES>
)
set_property(TARGET Foo::Foo
PROPERTY INTERFACE_COMPILE_OPTIONS
$<TARGET_PROPERTY:Threads::Threads,INTERFACE_COMPILE_OPTIONS>
)
It turns out the answer was quite simple. I simple needed to add Threads::Threads to the INTERFACE_LINK_LIBRARIES property of Foo::Foo. I have no idea why I had determined that would not work the first time around.

Provide pre-compiled library as install(TARGET ...) to other projects in CMake

I want to define a CMakeLists which is able to export a precompiled library to an out of source output folder.
This is how it currently look like:
cmake_minimum_required (VERSION 3.3)
project(duktape)
include_directories("./code" "./code")
file(GLOB allCodeFiles
"./code/*.h"
#"./code/*.cpp"
)
add_library(${PROJECT_NAME} SHARED IMPORTED)
set_property(TARGET ${PROJECT_NAME} PROPERTY IMPORTED_LOCATION ./bin/duktape.dll)
set_property(TARGET ${PROJECT_NAME} PROPERTY IMPORTED_IMPLIB ./lib/duktape.lib)
export(PACKAGE ${PROJECT_NAME})
Message(INSTALL_INCLUDE_DIR=${INSTALL_INCLUDE_DIR})
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
ARCHIVE DESTINATION "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}"
LIBRARY DESTINATION "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
PUBLIC_HEADER DESTINATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}/code"
)
My current understanding of the install(TARGETS ...) is that it moves my libs to my out of soruce output folder and shares the headers to the project (some binary maybe) which depends on the lib.
The problem is that the export than tells:
CMake Error at C:/Test1/duktape-1.4.0.win32/CMakeLists.txt:30 (install):
install TARGETS given target "duktape" which does not exist in this directory.
[Edit]:
I still found no solution to just define a "project" to link a pre-compiled lib for integration purpose as a dependency to other projects?! I saw this kind of question ~10 times on google etc. and still can't belive that i didnt found one clear easy answer for this generic thing...
This is what i now end up with trying:
cmake_minimum_required (VERSION 3.3)
project(duktape)
#include(${CMAKE_BINARY_DIR}/DebugCmakeStuff.wtf)
# https://cmake.org/cmake/help/v3.0/command/add_library.html
add_library(${PROJECT_NAME} SHARED IMPORTED GLOBAL)
set_target_properties(${PROJECT_NAME} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ./include)
set_target_properties(${PROJECT_NAME} PROPERTIES INTERFACE_COMPILE_DEFINITIONS "")
set_target_properties(${PROJECT_NAME} PROPERTIES INTERFACE_COMPILE_OPTIONS "")
set_target_properties(${PROJECT_NAME} PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll)
set_target_properties(${PROJECT_NAME} PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib/duktape.lib)
set_target_properties(${PROJECT_NAME} PROPERTIES IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll)
set_target_properties(${PROJECT_NAME} PROPERTIES IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/lib/duktape.lib)
set_target_properties(${PROJECT_NAME} PROPERTIES IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll)
set_target_properties(${PROJECT_NAME} PROPERTIES IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/lib/duktape.lib)
#target_link_libraries(${PROJECT_NAME} LINK_PUBLIC "duktape.lib")
#add_custom_target(${PROJECT_NAME}_Export MODULE)
#add_dependencies(${PROJECT_NAME} duktape)
#export(PACKAGE ${PROJECT_NAME})
#install(TARGETS ${PROJECT_NAME}
# #EXPORT ${PROJECT_NAME}_Targets
# RUNTIME DESTINATION "${EXECUTABLE_OUTPUT_PATH}"
# ARCHIVE DESTINATION "${LIBRARY_OUTPUT_PATH}"
# LIBRARY DESTINATION "${LIBRARY_OUTPUT_PATH}"
# PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/${PROJECT_NAME}/include")
#install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll DESTINATION "${EXECUTABLE_OUTPUT_PATH}")
#install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/lib/duktape.lib DESTINATION "${LIBRARY_OUTPUT_PATH}")
#add_custom_command(TARGET ${PROJECT_NAME}
# PRE_LINK
# COMMAND copy ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll "${EXECUTABLE_OUTPUT_PATH}"
# COMMAND copy ${CMAKE_CURRENT_SOURCE_DIR}/lib/duktape.lib "${LIBRARY_OUTPUT_PATH}"
# )
#add_custom_command(OUTPUT duktape.dll
# COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll ${EXECUTABLE_OUTPUT_PATH}/duktape.dll
# COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll ${EXECUTABLE_OUTPUT_PATH}/duktape.dll
# COMMAND "copy ${CMAKE_CURRENT_SOURCE_DIR}/lib/duktape.lib ${LIBRARY_OUTPUT_PATH}"
# )
So after 2 days wasting time with CMake and its crashes... i just realized that i'm most propably 90% fast, if i would have write the build files manually...
A solution would be nice
Command flow install(TARGETS) installs only targets built within project. It doesn't install imported targets. See, e.g., this bugreport.
For install imported libraries you may use install(FILES) command flow:
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.dll
DESTINATION <...>)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/bin/duktape.lib
DESTINATION <...>)
(In your code you install into CMAKE_RUNTIME_OUTPUT_DIRECTORY and similar dirs, which refers to build tree. Never install under build tree!)