I have a CMake build that sends /IMPLIB to the linker on Windows. This is a problem in my case because the argument to implib is the same path as one of the input files.
It looks to me that CMake will always issue /IMPLIB when building with Visual Studio, and the passed argument cannot be modified.
Is there a way to control this behaviour?
Looking at CMake's source cmComputeLinkInformation.cxx it will only add a valid /implib:... option if CMAKE_IMPORT_LIBRARY_SUFFIX is set:
// Check whether we should use an import library for linking a target.
this->UseImportLibrary =
this->Makefile->IsDefinitionSet("CMAKE_IMPORT_LIBRARY_SUFFIX");
So in the following test the import library was removed from my executable project's options:
cmake_minimum_required(VERSION 3.0)
project(NoImpLib CXX)
unset(CMAKE_IMPORT_LIBRARY_SUFFIX)
file(WRITE main.cpp "int main() { return 0; }")
add_executable(${PROJECT_NAME} main.cpp)
An VS specific alternative - because this option is not set otherwise/per configuration - would be to add the global property IgnoreImportLibrary with:
set_target_properties(${PROJECT_NAME} PROPERTIES VS_GLOBAL_IgnoreImportLibrary "true")
I don't think its possible to prevent CMake from issuing an /IMPLIB option to the linker.
You can however control the name of the generated import library by setting the following properties of a shared library target:
add_library(foo SHARED foo.cpp)
# set base name of generated DLL import library
set_target_properties(foo PROPERTIES ARCHIVE_OUTPUT_NAME "bar")
# set prefix of generated DLL import library
set_target_properties(foo PROPERTIES IMPORT_PREFIX "")
# set suffix of generated DLL import library
set_target_properties(foo PROPERTIES IMPORT_SUFFIX ".lib")
The name of the generated shared library can be adjusted by setting the following target properties:
# set base name of generated DLL shared library
set_target_properties(foo PROPERTIES RUNTIME_OUTPUT_NAME "bar")
# set prefix of generated DLL shared library
set_target_properties(foo PROPERTIES PREFIX "")
# set suffix of generated DLL shared library
set_target_properties(foo PROPERTIES SUFFIX ".dll")
This answer suggests adding the /noimplib option to target_link_options() should prevent the generation of an import library, but in my experience, it still gets generated.
This e-mail has the answer that worked for me; simply avoid specifying ARCHIVE DESTINATION and LIBRARY DESTINATION in your install() command.
Related
I have a C++ project that links to a external library
The library was provided by a vendor which only contains a directory of .h headers and a shared object file "libabc.so".
in the CMakeLists of my project I have a obj(which I built it to bar.so) that uses the external lib.
when building the final exeuctable, I have tried mutilple ways to do it.
add_library(bar STATIC /some/source/file/bar.cpp)
add_library(abc_lib SHARED IMPORTED)
set_property(TARGET abc_lib PROPERTY IMPORTED_LOCATION /path/to/external/lib/libabc.so)
add_executable(foo /some/file/to/main.cpp)
target_link_libraries(foo bar abc_lib)
this builds ok and links ok, however when I do
ldd foo, the abc_lib does not appear in the form of
abc.so => /path/to/external/lib/libabc.so
instead it shows up in a standalone form
/path/to/external/lib/libabc.so, which indicates the library was not directly linked against according to some post I read recently.
But when I do chrpath -d foo or patchelf --remove-rpath foothe executable still contains the path and wont use the one I provided in LD_LIBRARY_PATH
so I tried the other way around
add_library(bar STATIC /some/source/file/bar.cpp)
add_library(abc_lib SHARED IMPORTED)
set_property(TARGE abd_lib PROPERTY IMPORTED_SONAME abc)
link_libraries(/path/to/external/lib)
add_executable(foo /some/file/to/main.cpp)
target_link_libraries(foo bar abc_lib)
however this time, it complains abc_lib-NOTFOUND
To sum up my question, I would like to have a project built link against a local shared object and at the same time I should be able to clean up the rpath using chrpath or patchelf so that I can copy the executable to a server with similiar environment but possible different path to the external lib, I would like to overwrite the path using LD_LIBRARY_PATH.
The key you are looking for is IMPORTED_NO_SONAME.
You can add an extra property to the shared imported library target:
set_property(TARGET abc_lib PROPERTY IMPORTED_NO_SONAME TRUE)
or better in one cmake command:
set_target_properties(abc_lib PROPERTIES
IMPORTED_LOCATION /path/to/external/lib/libabc.so
IMPORTED_NO_SONAME TRUE
)
CMake will use /path/to/external/lib/libabc.so instead of -L/path/to/external/lib -llibabc.so as link options if libabc.so has no soname in its ELF header.
You can verify this with:
readelf -d /path/to/external/lib/libabc.so
The offcial cmake doc:Imported Libraries explains more details.
I have a project A that depends on spdlog. Here is the structure:
|--- dir A
...|---src
......|---CMakeLists.txt
...|---include
...|---CMakeLists.txt
|---external/3rd_party/spdlog
I am trying to access spdlog in project A by adding a subdirectory. Here is how my A/CMakeLists.txt looks like:
cmake_minimum_required(VERSION 3.9.3 FATAL_ERROR)
project(GLOBAL CXX)
add_subdirectory(../external/3rd_party/spdlog ${CMAKE_BINARY_DIR}/spdlog EXCLUDE_FROM_ALL)
add_subdirectory(src)
Here is how my A/src/CMakeLists.txt looks like:
cmake_minimum_required(VERSION 3.9.3 FATAL_ERROR)
project(Alib CXX)
if(NOT TARGET spdlog)
# Stand-alone build
find_package(spdlog_header_only REQUIRED)
endif()
add_librray(A A.cpp)
target_link_libraries(A PUBLIC spdlog_header_only)
install(TARGETS A
EXPORT ATargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(EXPORT ATargets
NAMESPACE A::
FILE ATargets.cmake
DESTINATION ${INSTALL_CONFIGDIR})
install(FILES AConfig.cmake DESTINATION ${INSTALL_CONFIGDIR})
When I try to build this, I get the following error:
CMake Error: install(EXPORT "ATargets" ...) includes target "A" which requires target "spdlog_header_only" that is not in the export set.
Please can you suggest me how to fix it?
For some reason I need to maintain the same directory structure I have shown above.
Here is a related question but does not have an answer : here
Meaining of the error
Since you use PUBLIC keyword when link your library (A) with spdlog_header_only, CMake expects that this linking is also needed for users of your library. So, when you create config file for your library (with install(EXPORT)), CMake adds linking with spdlog_header_only target into the config file too. Like
# [ATargets.cmake]
target_link_libraries(A::A PUBLIC spdlog_header_only)
Linking with a target implies existence of this target, but you do not install spdlog_header_only target. Because of that, created config file for your library won't work. This is what CMake tells you in the error message.
Simple fix
The simplest fix would be using PRIVATE linking with spdlog_header_only target, so that linking won't be part of the config file. Beware, in that case a user of your (installed) library won't get access to the header files for spdlog.
(But a user could obtain these headers by other means.)
Hard fix
But if you want a user of your library to have access to spdlog headers (or, worse, the public headers of your library use #include for headers from spdlog), then you cannot drop PUBLIC linking. In that case you need to install spdlog_header_only target too. E.g. by enabling SPDLOG_INSTALL option
set(SPDLOG_INSTALL ON)
before
add_subdirectory(../external/3rd_party/spdlog ${CMAKE_BINARY_DIR}/spdlog EXCLUDE_FROM_ALL)
(Note, that aside from enabling SPDLOG_INSTALL option, several other adjustments needs to be done for make the config file for your library to work.)
I have 2 folders "inc" and "lib" in my project which have headers and static libs respectively. How do I tell cmake to use those 2 directories for include and linking respectively?
The simplest way of doing this would be to add
include_directories(${CMAKE_SOURCE_DIR}/inc)
link_directories(${CMAKE_SOURCE_DIR}/lib)
add_executable(foo ${FOO_SRCS})
target_link_libraries(foo bar) # libbar.so is found in ${CMAKE_SOURCE_DIR}/lib
The modern CMake version that doesn't add the -I and -L flags to every compiler invocation would be to use imported libraries:
add_library(bar SHARED IMPORTED) # or STATIC instead of SHARED
set_target_properties(bar PROPERTIES
IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/lib/libbar.so"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/include/libbar"
)
set(FOO_SRCS "foo.cpp")
add_executable(foo ${FOO_SRCS})
target_link_libraries(foo bar) # also adds the required include path
If setting the INTERFACE_INCLUDE_DIRECTORIES doesn't add the path, older versions of CMake also allow you to use target_include_directories(bar PUBLIC /path/to/include). However, this no longer works with CMake 3.6 or newer.
You had better use find_library command instead of link_directories. Concretely speaking there are two ways:
designate the path within the command
find_library(NAMES gtest PATHS path1 path2 ... pathN)
set the variable CMAKE_LIBRARY_PATH
set(CMAKE_LIBRARY_PATH path1 path2)
find_library(NAMES gtest)
the reason is as flowings:
Note This command is rarely necessary and should be avoided where there are other choices. Prefer to pass full absolute paths to
libraries where possible, since this ensures the correct library will
always be linked. The find_library() command provides the full path,
which can generally be used directly in calls to
target_link_libraries(). Situations where a library search path may be
needed include: Project generators like Xcode where the user can
switch target architecture at build time, but a full path to a library
cannot be used because it only provides one architecture (i.e. it is
not a universal binary).
Libraries may themselves have other private library dependencies that
expect to be found via RPATH mechanisms, but some linkers are not able
to fully decode those paths (e.g. due to the presence of things like
$ORIGIN).
If a library search path must be provided, prefer to localize the
effect where possible by using the target_link_directories() command
rather than link_directories(). The target-specific command can also
control how the search directories propagate to other dependent
targets.
might fail working with link_directories, then add each static library like following:
target_link_libraries(foo /path_to_static_library/libbar.a)
I'm trying to write the necessary scripts for find_package to import targets for some libraries which do not have cmake builds or at least aren't exporting their targets, but are otherwise in the installation path: CMAKE_PREFIX_PATH. For example, for some library Foo that exports a target, there are the generated files:
lib/cmake/Foo/FooTargets.cmake
lib/cmake/Foo/FooTargets-noconfig.cmake
...and the project-provided:
lib/cmake/Foo/FooConfig.cmake (and maybe FooConfigVersion.cmake)
...which finds dependencies and then includes the generated FooTargets.cmake.
It seems the generated FooTargets.cmake generally just includes the noconfig version, via wildcard:
# Load information for each installed configuration.
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
file(GLOB CONFIG_FILES "${_DIR}/FooTargets-*.cmake")
foreach(f ${CONFIG_FILES})
include(${f})
endforeach()
But what are these configurations in reference to? What is "noconfig"?
I'm guessing configuration refers to debug or release, and when these aren't distinguished in the CMakeLists.txt script, it's just generating "noconfig" as a default?
CMAKE_BUILD_TYPE variable specifies the build type (configuration) on single-configuration generators (for example when generating build scripts for make or ninja). The value of this variable can be the empty string (for example when loading a CMake project in Qt Creator and selecting the Default configuration).
If CMAKE_BUILD_TYPE is set to Release then install(EXPORT ...) generates
lib/cmake/Foo/FooTargets.cmake
lib/cmake/Foo/FooTargets-release.cmake
If CMAKE_BUILD_TYPE is set to the empty string then install(EXPORT ...) generates
lib/cmake/Foo/FooTargets.cmake
lib/cmake/Foo/FooTargets-noconfig.cmake
I'm trying to create a custom target in cmake which:
links a vendor supplied archive
adds target_compile_options(... PUBLIC ...)
adds target_include_directories(... PUBLIC ...)
In this way consumers can link against my new target and have requisite include directories and compile options set.
In boost-build you can use the <file> feature to "wrap" or "alias" a prebuilt library, and add additional "usage requirements" (compiler flags etc) which will be added to all targets using the library.
lib foo
:
: <file>vendor/library.a
:
: <include>vendor
;
This aliases vendor/library.a as a new library foo.
When another target uses foo it will also have its include paths updated.
The code using foo will then be able to #include "foo.h", and the file will be found because vendor has been added to the include paths.
I'm looking for a way to do the same thing in CMake.
The way I would currently do it would be something along these lines:
find_library(LIB_FOO library.a PATHS ${CMAKE_SOURCE_DIR}/path/to/vendor NO_DEFAULT_PATH)
target_link_library (my_target ${LIB_FOO})
target_include_directories(my_target PRIVATE "${CMAKE_SOURCE_DIR}/path/to/vendor")
However, if there are several targets which need to use foo then these 3 calls would have be repeated for each of them, which becomes quite onerous.
It would be far easier for consumers of foo to have some other target which has
target_link_libraries (foo ${CMAKE_CURRENT_LIST_DIR}/vendor/library.a)
target_compile_options (foo PUBLIC ...)
target_include_directories(foo PUBLIC ...)
Question:
Can I create a new library target which consists of a prebuilt library which will allow me to do what I've described?
Edit in response to comment:
I have tried to create an IMPORTED library (as described here):
add_library(foo STATIC IMPORTED)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_LIST_DIR}/vendor/library.a)
However, when I then try to set include directories I get an error:
target_include_directories(foo SYSTEM PUBLIC "${CMAKE_CURRENT_LIST_DIR}/vendor")
Error:
CMake Error at vendor/CMakeLists.txt:39 (target_include_directories):
Cannot specify include directories for imported target "foo".
You need to import a pre-built library. here is the tutorial how to do it.
To make the resulted target "complete" you need to add certain properties to it. Unfortunately this can't be done using usual techniques, because of this CMake issue. So you have to set these props manually, like described in this SO QA