I want to build my project using libwebsockets with mbedtls. I have used FetchContent_Declare() and FetchContent_GetProperties() along with add_subdirectory() to pull in the repos and now I'm stuck in the configuration phase with the following errors:
CMake Error in build/_deps/libwebsockets-src/CMakeLists.txt:
export called with target "websockets" which requires target "mbedtls" that
is not in any export set.
CMake Error in build/_deps/libwebsockets-src/CMakeLists.txt:
export called with target "websockets" which requires target "mbedcrypto"
that is not in any export set.
CMake Error in build/_deps/libwebsockets-src/CMakeLists.txt:
export called with target "websockets" which requires target "mbedx509"
that is not in any export set.
I've had a look at the mbedtls code and confirmed that they in fact ARE in the MbedTLSTargets export set:
set(target_libraries ${mbedcrypto_target} ${mbedx509_target} ${mbedtls_target})
# ...
foreach(target IN LISTS target_libraries)
# ...
install(
TARGETS ${target}
EXPORT MbedTLSTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
endforeach(target)
I don't understand how those export informations are not propagated to the upstream consumer (libwebsockets). Do I specifically have to redeclare the export rules for those targets in my main project?
Related
I'm new to CMake and have trouble understanding how targets are exposed in the install interface. I have a CMakeLists.txt where I define two targets (PROJECT_NAME is "realtime"):
...
add_library(${PROJECT_NAME} realtime_client.cpp)
...
configure_file(version/realtime_version.cpp.in realtime_version.cpp #ONLY)
add_library(realtime_version STATIC ${CMAKE_CURRENT_BINARY_DIR}/realtime_version.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE realtime_version)
...
You can see that the realtime_version target is privately linked to the realtime target. For the install interface I just want the realtime target to be exposed to the outside world whereas the version target should vanish. So I would typically do this:
include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME} EXPORT "${PROJECT_NAME}_Exports"
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT ${PROJECT_NAME}_Runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT ${PROJECT_NAME}_Runtime
NAMELINK_COMPONENT ${PROJECT_NAME}_Development
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT ${PROJECT_NAME}_Development
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
set(REALTIME_TARGET_EXPORT_NAME "${PROJECT_NAME}_Exports")
install(EXPORT ${REALTIME_TARGET_EXPORT_NAME}
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/realtime-${PROJECT_VERSION}
NAMESPACE supabase::
FILE ${PROJECT_NAME}_Development.cmake
COMPONENT ${PROJECT_NAME}_Development
)
But then CMake complains that the realtime_version target has no install rules:
[cmake] CMake Error: install(EXPORT "realtime_Exports" ...) includes target "realtime" which requires target "realtime_version" that is not in any export set.
What I really want is for this library to just be compiled into the binary of the realtime target. I thought this would happen automatically, but it seems I'm mistaken. I might have missed a few essential things. So how can I hide the realtime_version target from export_sets and instead just compile it into the binary?
Take the following example as a start:
...
add_library(Foo ...)
install(TARGETS Foo EXPORT FooTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
...
)
install(EXPORT FooTargets
FILE lib/cmake/Foo
...
)
Running this with
$ mkdir build; cd build
$ cmake -DCMAKE_BUILD_TYPE=Release .. (or in Debug)
$ cmake --build .
$ cmake --install . --prefix my/custom/prefix
This will create the files:
my/custom/prefix/lib/cmake/Foo/FooTargets.cmake
my/custom/prefix/lib/cmake/Foo/FooTargets-release.cmake (Or debug, respectively)
my/custom/prefix/lib/libFoo.a
And from what I managed to understand from the FooTargets.cmake file, it globs for all FooTargets-*.cmake files and includes() them all.
In turn, the FooTargets-release.cmake file is the one that references the libFoo.a file.
In the docs about the install command, it says that you can add the CONFIGURATIONS option to the install TARGETS command, so that if we change the above:
install(TARGETS Foo EXPORT FooTargets
CONFIGURATIONS Debug
LIBRARY DESTINATION lib/Debug
ARCHIVE DESTINATION lib/Debug
This will install the libFoo.a file in my/custom/path/lib/Debug/libFoo.a. Now let's say I want the Release library to be installed in lib/Release and the Debug library be installed in lib/Debug, and that when the downstream project will consume my package, it will have the right library depending on its configuration - i.e. - debug build will link against the Debug library, same for release.
What I thought I can do is:
install(TARGETS Foo EXPORT FooTargets
CONFIGURATIONS Debug
LIBRARY DESTINATION lib/Debug
ARCHIVE DESTINATION lib/Debug
)
install(TARGETS Foo EXPORT FooTargets
CONFIGURATIONS Release
LIBRARY DESTINATION lib/Release
ARCHIVE DESTINATION lib/Release
)
install(EXPORT FooTargets
DESTINATION lib/cmake/Foo
...
)
And what will happen is that when building in Debug, the FooTargets-debug.cmake will be generated, pointing to the lib/Debug/libFoo.a, and when building in Release, the FooTargets-release.cmake will be generated, pointing to the lib/Release/libFoo.a. The FooTargets.cmake will then check what configuration is the consuming project is building with, and include the right configuration.
When I try doing the above, I get:
-- Configuring done
CMake Error: install(EXPORT "FooTargets" ...) includes target "Foo" more than once in the export set.
-- Generating done
CMake Generate step failed. Build files cannot be regenerated correctly.
How is this should be done??
EDIT
I found out, in a not-very-straightforward way, that when I build the consuming project as such:
$ cmake -DCMAKE_BUILD_TYPE=Debug ..
and like this:
$ cmake -DCMAKE_BUILD_TYPE=Release ..
I am linking against only the relevant library. Basically how this works is that the FooTargets.cmake file is included by the FooConfig.cmake file, which is called by find_package. The FooTargets.cmake file is the one that defines the imported target:
add_library(Foo IMPORTED)
This file then calls all the FooTargets-*.cmake, which adds the relevant library to some list called _IMPORT_CHECK_FILES_FOR_FOO.
What this file also does, is:
set_property(TARGET Foo::Foo APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
Apparently there is some property IMPORTED_CONFIGURATIONS that holds the imported configurations.
I suppose that somewhere down the road, find_package takes this list and filters it according to the CMAKE_BUILD_TYPE variable, and this way links only the relevant library.
What I still don't understand, is how to make it so that upon Release builds (of Foo), the FooTargets-release.cmake will be created, pointing to lib/Release/Foo.a, and the same for debug builds.
It seems that CMake went half-way with this, unless I'm seriously missing something.
It seems per-CONFIGURATIONS installs are not easily fit to EXPORT semantic.
However, in simple cases per-configuration's specific can be achieved by using generator expressions in DESTINATION:
install(TARGETS Foo EXPORT FooTargets
LIBRARY DESTINATION lib/$<CONFIG>
ARCHIVE DESTINATION lib/$<CONFIG>
)
install(EXPORT FooTargets
DESTINATION lib/cmake/Foo
...
)
The code above will install libraries into lib/Debug for Debug configuration, and into lib/Release for Release configuration.
I am trying to export a library from a CMake project. Internally, I've broken this library up into multiple sub-targets. I would now like to export just the full public library, without needing my private library binaries. The following doesn't work.
cmake_minimum_required(VERSION 3.2)
project(export-mwe)
add_library(priv priv.cpp)
add_library(exp-lib exp-lib.cpp)
target_link_libraries(exp-lib PRIVATE priv)
install(TARGETS exp-lib EXPORT export-mwe DESTINATION lib)
install(EXPORT export-mwe DESTINATION .)
When I try generating this project I get an error.
CMake Error: install(EXPORT "export-mwe" ...) includes target "exp-lib" which requires target "priv" that is not in the export set.
How can I export only exp-lib in this example, without having to export priv with it?
If you link (even privately) your library exp-lib with another shared library priv, CMake needs to be aware of that linking when link other executable with your main library. So, information of private linking is stored in the export file:
... to tell the importing CMake that it needs to ensure the linker can
find A when the application links to B even though A will not appear
on the link line.
As for
How can I export only exp-lib in this example, without having to export priv with it?
Make priv library STATIC. Information about private linking with static library isn't stored in the export file.
The docs for CMake 3.2.2 state, that it is possible to use generator expressions for the install(<FILES|PROGRAMS> ...) signature. I was trying to use generator expressions in other signatures of install(), but apparently it is not working. I would like to do something like this:
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/$<CONFIG>"
LIBRARY DESTINATION "Lib/$<CONFIG>"
ARCHIVE DESTINATION "Lib/$<CONFIG>"
I also tried calling install() multiple times like this:
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/Debug" CONFIGURATIONS Debug
LIBRARY DESTINATION "Lib/Debug" CONFIGURATIONS Debug
ARCHIVE DESTINATION "Lib/Debug" CONFIGURATIONS Debug
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/Release" CONFIGURATIONS Release
LIBRARY DESTINATION "Lib/Release" CONFIGURATIONS Release
ARCHIVE DESTINATION "Lib/Release" CONFIGURATIONS Release
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/MinSizeRel" CONFIGURATIONS MinSizeRel
LIBRARY DESTINATION "Lib/MinSizeRel" CONFIGURATIONS MinSizeRel
ARCHIVE DESTINATION "Lib/MinSizeRel" CONFIGURATIONS MinSizeRel
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/RelWithDebInfo" CONFIGURATIONS RelWithDebInfo
LIBRARY DESTINATION "Lib/RelWithDebInfo" CONFIGURATIONS RelWithDebInfo
ARCHIVE DESTINATION "Lib/RelWithDebInfo" CONFIGURATIONS RelWithDebInfo
This causes CMake to emit an error along the lines of Target 'foo' exported more than once in 'fooConfig'.
I cannot use CMAKE_BUILD_TYPE here either without updating the CMake cache and re-running the build. Rather than that, I want to use the Visual Studio batch-build feature, which builds multiple configurations for me.
There was a little hack that I tried too. I noticed that CMake's generated INSTALL project is simply invoking a CMake script ${CMAKE_BINARY_DIR}/cmake_install.cmake with the argument -DBUILD_TYPE=$(Configuration). So I tried the following:
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/\${BUILD_TYPE}"
LIBRARY DESTINATION "Lib/\${BUILD_TYPE}"
ARCHIVE DESTINATION "Lib/\${BUILD_TYPE}"
This actually worked well for the installation. However, the installed export script, i.e. the output of install(EXPORT fooConfig DESTINATION .) now also tries to use ${BUILD_TYPE}, which is not set at the time the user includes that script...
If anyone knows another way to accomplish my goals, please let me know.
Unluckily the install command only supports generator expressions for the list of files to be installed, but not for the destination directory.
I would recommend on sticking with your little hack, but use CMAKE_INSTALL_CONFIG_NAME instead of CMAKE_BUILD_TYPE, i.e.:
install(TARGETS foo EXPORT fooConfig
RUNTIME DESTINATION "Bin/\${CMAKE_INSTALL_CONFIG_NAME}"
LIBRARY DESTINATION "Lib/\${CMAKE_INSTALL_CONFIG_NAME}"
ARCHIVE DESTINATION "Lib/\${CMAKE_INSTALL_CONFIG_NAME}"
CMAKE_INSTALL_CONFIG_NAME is set to the actual build configuration used for installing in the generated cmake_install.cmake script.
The generated export scripts (e.g., fooConfig-debug.cmake) can be automatically fixed by adding a patch script to the installation process. Generate a file patch_export_files.cmake with the following contents in the source directory:
file(GLOB_RECURSE _configFiles "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/fooConfig-*.cmake")
foreach(_configFile ${_configFiles})
file (READ "${_configFile}" _contents)
string (REGEX MATCH "configuration \"[A-Za-z]+\"" _configName "${_contents}")
if (_configName MATCHES "\"([A-Za-z]+)\"")
message(STATUS "Patching: ${_configFile}")
string (REPLACE "\${CMAKE_INSTALL_CONFIG_NAME}" "${CMAKE_MATCH_1}" _patchedContents "${_contents}")
file (WRITE "${_configFile}" "${_patchedContents}")
endif()
endforeach()
The patch script need to be run at installation time by adding an install(SCRIPT ... call after the install(EXPORT ...:
install(EXPORT fooConfig DESTINATION .)
...
install(SCRIPT patch_export_files.cmake)
The patch script first parses the configuration that the generated export script is valid for from its header comment and then replaces each use of ${CMAKE_INSTALL_CONFIG_NAME} with the configuration name.
I am working on a project which requires builds with different configurations. The way I have achieved this is using "ExternalProject".
I have created a sample repository to show you the idea:
https://github.com/mpaluru/cmake_multiple_build_configs_example
(Linux is the environment I use mostly and don't have access to Visual Studio.)
If you pass on the -G flag in the top level CMakeLists.txt, you should be able to generate your VS files. I have tested this on Linux and "make -j" works fine. Both the Debug and Release configurations build in parallel.
Summary:
You create a new superbuild project which calls your project using ExternalProject_Add with different CMAKE_BUILD_TYPE.
And based on the build type, you pass different definitions, or do install differently.
When building an opensource project with CMake (in my case, it was the lemon graph library), I got this error when I tried to build shared libaries via -DBUILD_SHARED_LIBS=1:
TARGETS given no LIBRARY DESTINATION for shared library target
Where does this error come from and how do I fix it?
In my CMakeLists.txt, my INSTALL command had no LIBRARY parameter.
Changing from this:
INSTALL(
TARGETS lemon
ARCHIVE DESTINATION lib
COMPONENT library
)
to this:
INSTALL(
TARGETS lemon
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib # <-- Add this line
COMPONENT library
)
fixed my problem.
I got this... Another reason this happens is when you create a shared library
add_library(${NAME} SHARED sources )
then when Cmake reaches the install command on Windows platform, it complains of these error, solution is to use RUNTIME instead of LIBRARY, like
if(WIN32)
install(TARGETS ${NAME}
RUNTIME DESTINATION path)
else()
install(TARGETS ${NAME}
LIBRARY DESTINATION path)
endif()
After DESTINATION, it should have bin, lib, include.
install lib or bin
install(TARGETS snappy
EXPORT SnappyTargets
# RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # DESTINATION error
RUNTIME DESTINATION bin ${CMAKE_INSTALL_BINDIR} # should add bin or other dir
LIBRARY DESTINATION lib ${CMAKE_INSTALL_LIBDIR}
# ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR # DESTINATION error
ARCHIVE DESTINATION lib ${CMAKE_INSTALL_LIBDIR} # should add lib
)
For example, install .h file:
install(
FILES
"${PROJECT_SOURCE_DIR}/test_hard1.h"
"${PROJECT_BINARY_DIR}/config.h"
# DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # error install FILES given no DESTINATION!
# add include after DESTINATION, then it works
DESTINATION include ${CMAKE_INSTALL_INCLUDEDIR}
)
see https://cmake.org/cmake/help/v3.0/command/install.html for more detail:
install(TARGETS myExe mySharedLib myStaticLib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
install(TARGETS mySharedLib DESTINATION /some/full/path)
I just faced a similar problem. As #Fernando said in his answer, you can go with that solution. Another and similar solution is to just include(GNUInstallDirs)
include(GNUInstallDirs)
install(TARGETS ${PROJECT_NAME}
EXPORT "${PROJECT_NAME}Config"
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
I had a similar, but not identical error message:
CMake Error at xxx/CMakeLists.txt:123 (install):
install Library TARGETS given no DESTINATION!
The fix in this case was to simply update CMake; from version 3.13.3 to 3.25.1 or really any newer version.
Note: I had originally posted my own question at CMake Error: install Library TARGETS given no DESTINATION but it was closed as a duplicate (although the error message was different), so I posted my own answer here.
I do not know if this would fix the TARGETS given no LIBRARY DESTINATION for shared library target error.
The other answers all talk about modifying the CMake configuration files (i.e. CMakeLists.txt), so this answer is new - and easier.
Our fix was just to update, and leave the configuration alone. As the error was thrown from another open source project, this was by far the better fix, for us.
Since CMake 3.14 the option DESTINATION is no longer required for executables, shared/static libraries and some other artifacts: CMake provides reasonable defaults for their installation directories. Alternatively, one could include module GNUInstallDirs, and set corresponding CMAKE_INSTALL_<dir> variable.
For regular executables, static libraries and shared libraries, the DESTINATION argument is not required. For these target types, when DESTINATION is omitted, a default destination will be taken from the appropriate variable from GNUInstallDirs, or set to a built-in default value if that variable is not defined. The same is true for the public and private headers associated with the installed targets through the PUBLIC_HEADER and PRIVATE_HEADER target properties.
https://cmake.org/cmake/help/v3.14/command/install.html#targets