I am educating myself on a CMakeLists.txt of implot library. The imgui library, which is a library on which implot is based, declares the link libraries to be of type PUBLIC, as shown below.
# ...
add_library(imgui ${IMGUI_HEADERS} ${IMGUI_SRC})
if(MSVC)
target_compile_options(imgui PRIVATE /W4 /WX /arch:AVX2 /fp:fast)
endif()
target_link_libraries(imgui PUBLIC glfw glad OpenGL::GL imm32)
target_compile_definitions(imgui PRIVATE IMGUI_DLL_EXPORT)
# ...
add_library(implot ${IMPLOT_HEADERS} ${IMPLOT_SRC})
target_link_libraries(implot PUBLIC imgui)
target_compile_definitions(implot PUBLIC IMPLOT_DEBUG IMPLOT_DLL_EXPORT IMPLOT_BACKEND_ENABLE_OPENGL3 IMGUI_IMPL_OPENGL_LOADER_GLAD)
set_property(TARGET implot PROPERTY CXX_STANDARD 11)
if(MSVC)
target_compile_options(implot PRIVATE /W4 /WX /arch:AVX2 /fp:fast /permissive-)
else()
target_compile_options(implot PRIVATE -Wall -Wextra -pedantic -Werror -mavx2 -Ofast)
endif()
Since imgui is PUBLICly linked against glfw, glad, OpenGL::GL, imm32, does it mean that implot and whoever that uses implot will also be PUBLICly linked against glfw, glad, OpenGL::GL, imm32?
Is the reason behind this because of imgui being a shared library?
Since imgui is PUBLICly linked against glfw, glad, OpenGL::GL, imm32, does it mean that implot and whoever that uses implot will also be PUBLICly linked against glfw, glad, OpenGL::GL, imm32?
Yes, it is. More details could be found in the official documentation in the section Build Specification and Usage Requirements.
Is the reason behind this because of imgui being a shared library?
Yes, it is. Since add_library() does not have any SHARED or STATIC named parameter, a user may decide to build it statically by providing -DBUILD_SHARED_LIBS:BOOL=OFF cmake(1) parameter. So to link with it, a linker needs all the dependencies list. And this is a good practice to give him a choice on how to build your project ;-)
Related
I want to create a shared library with 'sqlite3(SHARED)' and 'glog(STATIC)' installed.
add_library(sqlite3 SHARED IMPORTED)
add_library(glog STATIC IMPORTED)
set_target_properties(sqlite3 PROPERTIES IMPORTED_LOCATION /usr/local/lib/libsqlite3.so)
set_target_properties(glog PROPERTIES IMPORTED_LOCATION /usr/local/lib/libglog.a)
add_library(${MY_LIBRARY} SHARED ${MY_SOURCE})
target_link_libraries(${MY_LIBRARY} sqlite3 glog)
This causes following error
/usr/local/lib/libglog.a: can not be used when making a shared object; recompile with -fPIC
What is wrong in CMakeLists.txt?
Your problem isn't with CMake (though you should never have hardcoded paths... use find_package or find_library), it's that you're trying to do something impossible. As your compiler tells you,
/usr/local/lib/libglog.a: can not be used when making a shared object; recompile with -fPIC
libglog.a was not compiled with position-independent code, so it cannot be used in a shared library, period. There's more information here on SO: How to recompile with -fPIC
You'll need to recompile libglog.a with -fPIC.
To suppress compiler warnings that originate from libraries I use in my application, I manually include their directories with target_include_directories(myapp SYSTEM ...) as system libraries before adding them with target_link_libraries like so:
add_executable(myapp myapp.cpp)
target_include_directories(myapp SYSTEM
PRIVATE "extern/lib/include"
)
target_link_libraries(myapp lib::lib)
However, that kind of feels hacky and will also break if the developers of lib decide to change the include path. This wouldn't be a problem if using only target_link_library but then, of course, they are included via -I and again I would get compiler warnings coming from this include.
Is there any more elegant and fail-safe way of doing this? It would be great if target_link_libraries had a SYSTEM option to tell cmake to include it as a system library.
I defined a function to handle this for me:
function(target_link_libraries_system target)
set(libs ${ARGN})
foreach(lib ${libs})
get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(${target} SYSTEM PRIVATE ${lib_include_dirs})
target_link_libraries(${target} ${lib})
endforeach(lib)
endfunction(target_link_libraries_system)
I can now call target_link_libraries_system(myapp lib::lib) and the include directories are read from the target's properties.
This can be extended to optionally specify the PUBLIC|PRIVATE|INTERFACE scope:
function(target_link_libraries_system target)
set(options PRIVATE PUBLIC INTERFACE)
cmake_parse_arguments(TLLS "${options}" "" "" ${ARGN})
foreach(op ${options})
if(TLLS_${op})
set(scope ${op})
endif()
endforeach(op)
set(libs ${TLLS_UNPARSED_ARGUMENTS})
foreach(lib ${libs})
get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
if(lib_include_dirs)
if(scope)
target_include_directories(${target} SYSTEM ${scope} ${lib_include_dirs})
else()
target_include_directories(${target} SYSTEM PRIVATE ${lib_include_dirs})
endif()
else()
message("Warning: ${lib} doesn't set INTERFACE_INCLUDE_DIRECTORIES. No include_directories set.")
endif()
if(scope)
target_link_libraries(${target} ${scope} ${lib})
else()
target_link_libraries(${target} ${lib})
endif()
endforeach()
endfunction(target_link_libraries_system)
This extended version will also print a warning if a library didn't set its INTERFACE_INCLUDE_DIRECTORIES property.
I modified Sebastian's solution to include the scope.
function(target_link_libraries_system target scope)
set(libs ${ARGN})
foreach(lib ${libs})
get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(${target} SYSTEM ${scope} ${lib_include_dirs})
target_link_libraries(${target} ${scope} ${lib})
endforeach(lib)
endfunction(target_link_libraries_system)
This was asked in the CMake discourse and #ben.boeckel (CMake developer) answered:
IMPORTED targets should already have their include directories
treated as SYSTEM though. There is the NO_SYSTEM_FROM_IMPORTED target
property to disable it.
To start with, you haven't specified whether your target that you're linking to is IMPORTED or not. I'm assuming that it is IMPORTED because include directories for IMPORTED targets are SYSTEM by default. Note: This behaviour, can be disabled. Pre-CMake v3.25, one would use one of NO_SYSTEM_FROM_IMPORTED or IMPORTED_NO_SYSTEM. For CMake 3.25 and later, one would modify the SYSTEM property of the target.
That typically just leaves the cases of targets added via add_subdirectory, which includes those added by FetchContent in its non-find_package mode. In that case, you can see the FetchContent Q&A here, and the add_subdirectory Q&A here. In summary, for pre-CMake v3.25, use a workaround in which you copy/move the INTERFACE_INCLUDE_DIRECTORIES target property to the INTERFACE_SYSTEM_INCLUDE_DIRECTORIES target property, and for CMake v3.25 or later, you can modify the SYSTEM target property, or the SYSTEM directory property, or use the SYSTEM argument of add_subdirectory/FetchContent_Declare.
Possible gotcha: If you are like me, and start every project by enabling all warnings in the toplevel directory…
# Warning level
add_compile_options(
-Wall -Wextra -Wpedantic
-Werror=switch
-Werror=return-type
-Werror=uninitialized
-Werror=format-security
-Werror=reorder
-Werror=delete-non-virtual-dtor
$<$<CONFIG:Debug>:-Werror>
)
… then, this obviously gets inherited by all subprojects (like git submodules, or what have you).
If this is the case, the solution is simple – be specific: Do it in a subdirectory, and/or use target_compile_options, for good measure:
target_compile_options(myTarget PRIVATE
...
)
Linking against the Intel MKL static libraries introduces circular dependencies. When I import the libraries,
set(LIBRARIES mkl_intel_lp64 mkl_sequential mkl_core)
foreach(_lib ${LIBRARIES})
add_library(${_lib} UNKNOWN IMPORTED)
set_target_properties(${_lib} PROPERTIES IMPORTED_LOCATION
/opt/intel/mkl/lib/intel64/lib${_lib}.a)
endforeach()
and link to my executable,
target_link_libraries(main PRIVATE ${LIBRARIES})
I get a ton of undefined references to the linear algebra calls. For example,
ztrevc3_gen.f:(.text+0x1af7): undefined reference to `mkl_blas_zdscal'
One way to get around this is to use the appropriate linker flags:
target_link_libraries(main PRIVATE -Wl,--start-group ${LIBRARIES} -Wl,--end-group)
Another option is to do this:
target_link_libraries(main PRIVATE ${LIBRARIES} ${LIBRARIES} ${LIBRARIES})
However, as I was searching for a more elegant solution I came across the LINK_INTERFACE_MULTIPLICITY property. If set this property along with the imported library location,
set(LIBRARIES mkl_intel_lp64 mkl_sequential mkl_core)
foreach(_lib ${LIBRARIES})
add_library(${_lib} UNKNOWN IMPORTED)
set_target_properties(${_lib} PROPERTIES IMPORTED_LOCATION
/opt/intel/mkl/lib/intel64/lib${_lib}.a
LINK_INTERFACE_MULTIPLICITY 3)
endforeach()
I get the same undefined references as before, so appearently this is not working. What is the proper way to use LINK_INTERFACE_MULTIPLICITY and is there a more elegant way to get around circular dependencies?
EDIT
Here is a minimal example that fails, this time with the correct IMPORTED_LINK_INTERFACE_MULTIPLICITY variable.
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(test Fortran)
add_executable(main main.f90)
set(LIBRARIES mkl_intel_lp64 mkl_sequential mkl_core)
foreach(_lib ${LIBRARIES})
add_library(${_lib} UNKNOWN IMPORTED)
set_target_properties(${_lib} PROPERTIES IMPORTED_LOCATION
/opt/intel/mkl/lib/intel64/lib${_lib}.a
IMPORTED_LINK_INTERFACE_MULTIPLICITY 3)
endforeach()
list(APPEND LIBRARIES dl pthread)
target_link_libraries(main ${LIBRARIES})
#target_link_libraries(main ${LIBRARIES} ${LIBRARIES} ${LIBRARIES})
# main.f90
call zpotrf
end program
If you uncomment the last line then the build succeeds. Unfortunately, the MKL in not free (except in some cases) but hopefully somebody can test this. I should note that it fails with some linear algebra calls and not others like dgemm.
Property LINK_INTERFACE_MULTIPLICITY is for "normal" STATIC library. For IMPORTED library IMPORTED_LINK_INTERFACE_MULTIPLICITY property should be used instead:
set_target_properties(${_lib} PROPERTIES
IMPORTED_LOCATION /opt/intel/mkl/lib/intel64/lib${_lib}.a
IMPORTED_LINK_INTERFACE_MULTIPLICITY 3)
Given this cmake sample project:
lib1/CMakeLists.txt:
add_library(lib1 src1.cpp)
target_include_directories(lib1 PUBLIC include)
lib2/CMakeLists.txt:
add_library(lib2 src2.cpp)
target_include_directories(lib2 PUBLIC include)
target_link_libraries(lib2 PRIVATE lib1)
lib3/CMakeLists.txt:
add_library(lib3 src3.cpp)
target_include_directories(lib3 PUBLIC include)
target_link_libraries(lib3 PRIVATE lib2)
All .cpp and .h files are dummy files with no dependency between them.
From what I understood from the documentation, adding a "PRIVATE" target library from lib2 to lib1 means that lib1 include directory should not be added when compiling lib3. However, when launching compiling this using cmake (3.3.2) and "make VERBOSE=1", command line to compile "src3.cpp" contains "-I/.../lib1/include":
c++ -I/.../lib3/include -I/.../lib2/include -I/.../lib1/include -o .../src3.cpp.o -c /.../lib3/src3.cpp
What did I get wrong ?
From what I understood from the documentation, adding a "PRIVATE"
target library from lib2 to lib1 means that lib1 include directory
should not be added when compiling lib3.
Your assumptions are correct.
However, due to legacy reasons in how properties propagate between dependent targets, this correct behavior is deactivated when specifying an old version in cmake_minimum_required.
By changing the version to 3.0 or later, you will get the correct behavior:
cmake_minimum_required(VERSION 3.0)
...
See also this thread on the CMake mailing list.
I have a little bit of a convoluted question. I have a 3rd party dependency, which comes in static (libthird.a) and shared pic form (libthird.so).
I have a library, util, that depends on libthird.
And I have applications that depend on util that want to link libthird statically, and I have some shared libraries I need to produce that depend on util and need to link libthird dynamically.
My current (working) approach is something like the following:
add_library(third INTERFACE)
target_link_libraries(third INTERFACE /path/to/libthird.a)
add_library(third_shared INTERFACE)
target_link_libraries(third_shared INTERFACE /path/to/libthird.so)
add_library(util ${UTIL_SOURCES})
add_library(util_shared ${UTIL_SOURCES}) # same sources again!!
target_link_libraries(util PUBLIC third)
target_link_libraries(util_shared PUBLIC third_shared)
add_executable(some_app ...)
target_link_libraries(some_app PRIVATE util)
add_library(some_shared_object ...)
target_link_libraries(some_shared_object PUBLIC util_shared)
This works. But I'm building util (and, in reality, another half dozen libraries or so) twice... just to get different linker dependencies. Is there a saner way of doing this in cmake?
If I just target_link_libraries() on the top-level some_app and some_shared_object, I get the linker flags emitted in the wrong order, since util does depend on third.
The approach that you're taking is definitely one I've seen and also used myself before. It is fine if it is used for a small archive and/or one-off usage.
For the following examples, I assume a project structure like the following:
foo/
CMakeLists.txt
include/
foo.h
src/
foo.c
So, I've also (naively?) used the following approach based on the knowledge that you can create (on Unix-based systems at least) a shared library from a static archive.
project(foo C)
set(SOURCES
"src/foo.c")
set(LIBNAME "foo")
add_library(${LIBNAME} STATIC ${SOURCES})
target_include_directories(${LIBNAME} PUBLIC "include")
target_compile_options(${LIBNAME} PUBLIC "-fPIC")
#
get_property(CUR_PREFIX TARGET ${LIBNAME} PROPERTY PREFIX)
get_property(CUR_SUFFIX TARGET ${LIBNAME} PROPERTY SUFFIX)
get_property(CUR_NAME TARGET ${LIBNAME} PROPERTY NAME)
get_property(CUR_OUTPUT_NAME TARGET ${LIBNAME} PROPERTY OUTPUT_NAME)
get_property(CUR_ARCHIVE_OUTPUT_NAME TARGET ${LIBNAME} PROPERTY ARCHIVE_OUTPUT_NAME)
message(STATUS "prefix: ${CUR_PREFIX}")
message(STATUS "suffix: ${CUR_SUFFIX}")
message(STATUS "name: ${CUR_NAME}")
message(STATUS "output name: ${CUR_OUTPUT_NAME}")
message(STATUS "archive name: ${CUR_ARCHIVE_OUTPUT_NAME}")
add_custom_command(TARGET ${LIBNAME} POST_BUILD
COMMAND ${CMAKE_C_COMPILER} -shared -o libfoo.so -Wl,--whole-archive libfoo.a -Wl,--no-whole-archive
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
My unresolved problem with this approach is that I haven't found a reliable and portable way to get the name of the static archive (libfoo.a). Hence, all those message commands for the target properties; they might behave differently on your platforms.
The most efficient way, that is also well-supported by cmake, is to use an object library:
# cmake file
cmake_minimum_required(VERSION 3.2)
project(foo C)
set(SOURCES
"src/foo.c")
set(LIBNAME "foo")
set(LIBNAME_OBJ "${LIBNAME}_obj")
add_library(${LIBNAME_OBJ} OBJECT ${SOURCES})
target_include_directories(${LIBNAME_OBJ} PUBLIC "include")
target_compile_options(${LIBNAME_OBJ} PUBLIC "-fPIC")
#
add_library(${LIBNAME} SHARED $<TARGET_OBJECTS:${LIBNAME_OBJ}>)
add_library(${LIBNAME}_static STATIC $<TARGET_OBJECTS:${LIBNAME_OBJ}>)
All examples were tested with cmake version 3.6.1.
Hope this helps. Let us know, if you've found a better way.