CMake build target without specifying target_include_directories - cmake

I have a project with the following file layout:
Project
├── CMakeLists.txt
├── app
│ ├── CMakeLists.txt
│ └── main.cpp
└── ext
├──CMakeLists.txt
└── lib
├── CMakeLists.txt
├── include
│ └── foo.h
└── foo.cpp
foo is a 3rd party library that I downloaded the source code of that I want to use in main.cpp. I can build main.cpp using a cmake file like:
add_executable(app main.cpp)
target_include_directories(app PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/extern/foo/include)
target_link_libraries(app foo)
However I feel it should be the responsibility of the foo lib to specify what files should be included when using it. Is there a way to make this work without the target_include_directories call?

Just add
target_include_directories(foo PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
into extern/foo/CMakeLists.txt.
That way the include directory will be accessible when you build foo library and for anyone who links with that library.
Note, that automatic propagation of include directory and other library properties works only when you link with the library target:
# Assuming you have 'add_library(foo)' somewhere,
# PUBLIC and INTERFACE properties of the `foo` library will be propagated
# to the 'app'.
target_link_libraries(app PUBLIC foo)
This would work even if add_library(foo) is issued after target_link_libraries call.
Propagation won't work when link with the library file:
# Propagation won't work with a name of the library file:
target_link_libraries(app PUBLIC foo.a)
# Propagation won't work with a full path to the library file:
target_link_libraries(app PUBLIC /path/to/foo.a)
# If 'add_library(foo)' in inaccessible from the project,
# then linking with 'foo' means linking with a library file,
# so propagation won't work.
target_link_directories(app PUBLIC /path/to/)
target_link_libraries(app PUBLIC foo)

Related

Create a library by linking link external library

I am using yocto build environment for my project. There are multiple applications in the project and some of them are depending on one third party library(It contains the *.s0 files, header files). So I am planning to create one static wrapper library around the third party library and link the wrapper library for all the applications.
The structure of the project:
.
├── App1
├── App2
├── App3
└── third-party
└── inc
└── src
└── lib
└── libdvm-hash.so
└── libhash-ipc.so
└── CMakeLists.txt
CMakeLists.txt
project(hash LANGUAGES CXX VERSION "1.0.0")
set(LIBRARY_NAME hash)
set(HASH_LIBRARY_FILES ${CMAKE_CURRENT_SOURCE_DIR}/lib/libdvm-hash.so ${CMAKE_CURRENT_SOURCE_DIR}/lib/libhash-ipc.so
add_library(${LIBRARY_NAME} STATIC test.cpp)
target_include_directories(${LIBRARY_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc/>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(${LIBRARY_NAME} PUBLIC ${HASH_LIBRARY_FILES} ssl)
set_target_properties(${LIBRARY_NAME} PROPERTIES
OUTPUT_NAME ${LIBRARY_NAME})
install( FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
install(
TARGETS ${LIBRARY_NAME}
EXPORT ${PROJECT_NAME}-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(EXPORT ${PROJECT_NAME}-targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} NAMESPACE dvm::)
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/inc/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT dev)
Now the problem is in the exported hash-targets.cmake the path to the library is hardcoded.
hash-targets.cmake
set_target_properties(hash PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
INTERFACE_LINK_LIBRARIES "/home/mypc/path/to/the/ibdvm-hash.so;/home/mypc/pathto/the/libhash-ipc.so;ssl"
)
Is there any way to fix the hardcode path .*so and use the installed *.so from /usr/lib?
Edit
I installed *.so files in the target using yocto(using do_install_append command in bb file).
Instead of linking with absolute paths, create an IMPORTED library for every such path and link with that libraries:
# CMakeLists.txt, building the library
# Create an IMPORTED target which points to the library shipped with the project.
add_library(dvm-hash IMPORTED)
set_target_properties(dvm-hash PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib/libdvm-hash.so
)
# Another IMPORTED target
add_library(hash-ipc IMPORTED)
set_target_properties(hash-ipc PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib/libhash-ipc.so
)
...
# Link with those IMPORTED targets
target_link_libraries(${LIBRARY_NAME} PUBLIC dvm-hash hash-ipc)
That way, when create an export file for your hash library, CMake will create its linkage with targets dvm-hash and hash-ipc, but won't create that targets.
Next step is creating those IMPORTED targets in the export file for your project.
# hash-config.cmake
# Create an IMPORTED target. That time it points to the library in the target system.
add_library(dvm-hash IMPORTED)
find_library(DVM_HASH_LIBRARY dvm-hash)
set_target_properties(dvm-hash PROPERTIES
IMPORTED_LOCATION ${DVM_HASH_LIBRARY}
)
# Another IMPORTED target.
add_library(hash-ipc IMPORTED)
find_library(HASH_IPC_LIBRARY hash-ipc)
set_target_properties(hash-ipc PROPERTIES
IMPORTED_LOCATION ${HASH_IPC_LIBRARY}
)
# Include the export file for the target `hash`
# That file uses IMPORTED targets defined above.
include("${CMAKE_CURRENT_LIST_DIR}/hash-targets.cmake")

CMake: Linking a static library (.a) to small c++ script

I am trying to link a prebuilt static library, leveldb, to a small c++ script however I am getting a file not found error when trying to include the .h file from the lib. I don't know CMake that well so any help is appreciated.
//script.cc
#include <iostream>
#include "leveldb/db.h" // <<<<<<<< " fatal error: leveldb/db.h: No such file or directory"
int main(int argc, char** argv){
std::cout << "hello \n";
return 0;
}
As per other stack overflow posts static linking is done with target_link_libraries().
This is my current cmake file
CMakeLists.txt
cmake_minimum_required(VERSION 3.13.4)
project(my_project)
link_directories(${CMAKE_SOURCE_DIR}/third_party/)
add_executable(runscript script.cc)
target_link_libraries(runscript ${CMAKE_SOURCE_DIR}/third_party/libleveldb.a)
this is essentially what my project directory looks like
.
├── CMakeLists.txt
├── script.cc
└── third_party
└── libleveldb.a
To link with another cmake project, usually the way is to:
Clone the repo somewhere. For example, if your project is meant to be under git control, most people add the third_party repos as git submodules. Note: that is also ExternalProject_Add.
Then add add_subdirectory(the_dir_with_the_repo). This will source the third_party CMakeLists.txt file and include all the targets that are there.
After it, you can just target_link_libraries(runscript PUBLIC leveldb) use the targets from the third_party CMakeLists.txt like your own.
A static library, a file with .a extension, is just a archive of object files, that's all. Object files do not contain the information from header files, function declarations, external variables declarations, macros, etc. In C language headers are meant to be shipped separately, as entirely independent files.
Solution. (Thanks to KamilCuk's comment above)
CMakeLists.txt
cmake_minimum_required(VERSION 3.13.4)
project(my_proj)
add_subdirectory(third_party/leveldb)
add_executable(runscript script.cc)
target_link_libraries(runscript PUBLIC leveldb)
.
├── CMakeLists.txt
├── script.cc
└── third_party
└── [leveldb repo]

Automatically add linking options to dependants of shared lib

Let's say I have a shared lib, A, and something else that links against it, B. They are in two separate projects.
In my current setup, for A's functionality to be able to work properly, B needs to add -rdynamic to its linker options, like this:
target_link_libraries(B -rdynamic)
The thing is that there may be many, many dependants of A, so having to explicitly include the line above for each of them is a hassle.
Is there a way for A to have all its dependants automatically use -rdynamic?
Edit: here's a more detailed explanation of my situation.
I have a shared library, mylib. It cannot be a static library. It is defined in a CMake project of the same name.
mylib
├── CMakeLists.txt
└── src
   └── ...
Its CMakeLists.txt looks like this:
# mylib/CMakeLists.txt
project(mylib)
...
add_library(mylib SHARED ${SOURCES})
...
In separate projects, there are executables, client_i, that use/link against mylib, sometimes indirectly (i.e. link against something that links against mylib).
client_0
├── CMakeLists.txt
└── src
   └── ...
client_1
├── CMakeLists.txt
└── src
   └── ...
client_2
├── CMakeLists.txt
└── src
   └── ...
...
One of those CMakeLists.txt looks like:
# client_i/CMakeLists.txt
project(client_i)
...
add_executable(client_i ${SOME_OTHER_SOURCES})
target_link_libraries(client_i mylib -rdynamic)
...
Notice the -rdynamic. Without this option, some functionality provided by mylib doesn't work. I need it.
My problem is that there may be thousands of different client_is, each defined in its own project by different people/users of mylib, which I provide (and may be used as a binary).
I'd like to avoid having to add -rdynamic to each of the client_is, since some users might not know about it (since it may be used indirectly), some might forget about it, it might lead to headaches, etc.
Ideally, the CMakeLists.txt for mylib would look like:
# mylib/CMakeLists.txt
project(mylib)
...
add_library(mylib SHARED ${SOURCES})
target_link_libraries(mylib -rdynamic)
...
And a client_i CMakeLists.txt would simply be:
# client_i/CMakeLists.txt
project(client_i)
...
add_executable(client_i ${SOME_OTHER_SOURCES})
target_link_libraries(client_i mylib)
...
And it would have -rdynamic in its linker options. But I have tried this and it does not seem to work.
This is a simple solution you can try:
# Get a list of A's direct library dependencies.
get_target_properties(LIB_DEPENDENCIES A LINK_LIBRARIES)
# Loop through each library, adding the link option to each.
foreach(LIB ${LIB_DEPENDENCIES})
target_compile_options(${LIB} PRIVATE "-rdynamic")
endforeach()
This can be expanded, similar to the answer #KamilCuk linked here, to account for static/shared/imported libraries and recursion depending on the variety and complexity of your dependencies.
How to link A against B when A and B are in different projects?
If A is a different project than B, the you have to export package A and use find_package in project B to find A. Here is a good tutorial show-casing the use-case.

use set_tests_properties from test in other directory

(related to add standard libraries to RPATH for build tree executables)
I have the following directory structure as minimal working example
.
├── baz.cc
├── baz.h
├── CMakeLists.txt
└── sub
├── CMakeLists.txt
└── foo.cc
sub is in practice a git submodule and should not be edited for my specific corner case but remain generic.
sub/CMakeLists.txt defines a test that does not run in my setup because a shared library is not added to the RPATH by cmake. To reproduce this behavior, it can be thought be
enable_testing()
add_executable(foo foo.cc)
set_property(TARGET foo PROPERTY SKIP_BUILD_RPATH TRUE)
target_link_libraries(foo ${CMAKE_CURRENT_BINARY_DIR}/../libbaz.so)
include_directories(..)
add_test(NAME footest COMMAND foo)
(foo.cc can be:
#include "baz.h"
int main() {
return bazinga() - 42;
}
let's assume a shared library with the bazinga symbol will always be available, e.g. from /usr/lib)
My top level CMakeLists.txt is
add_subdirectory(sub)
add_library(baz SHARED baz.cc)
I want to get the footest running by manipulating its LD_LIBRARY_PATH as suggested here. This would be by adding
set_tests_properties(footest PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=/path_where_libbaz.so_is_on_my_target_system")
This line does the job when I add it to sub/CMakeLists.txt, but fails with the following error when I edit the top level CMakeLists.txt
CMake Error at CMakeLists.txt:3 (set_tests_properties):
set_tests_properties Can not find test to add properties to: footest
given the above reason, I want to do the fixup without editing anything in the subdirectory.
Is there a way to set properties of tests that are defined in other directories than the current one?

Produce .lib of header-only library that depends on external resources

EDIT: I've read up and understood the initial issue was caused by scanning-header-only not having cpp files and thus a lib file not being generated. Edited the question to reflect that extra understanding:
My current project folder structure and relevant CMakeLists content:
leveling
├── CMakeLists.txt: add_subdirectory(deps)
└── deps
├── CMakeLists.txt: add_subdirectory(scanning-header-only)
└── scanning
├── CMakeLists.txt: add_subdirectory(deps)
│ add_library(scanning-header-only file.h)
│ target_include_directories(scanning-header-only PUBLIC ${CMAKE_CURRENT_LIST_DIR}/deps/tinyxml2)
│ target_link_libraries(scanning-header-only PUBLIC tinyxml2)
└── deps
├── CMakeLists.txt: add_subdirectory(tinyxml2)
└── tinyxml2
But a scanning-header-only library file is not being generated, and thus the root project can't target_link_libraries(leveling scanning-header-only) and has had to target_include_directories(leveling ${CMAKE_CURRENT_LIST_DIR}/deps/scanning-header-only/deps/tinyxml2)
Is it possible to target_link_library a header-only library that depends on external resources?
I see that a header-only library without external resource dependency could be add_library(.. INTERFACE), but I'm failing to do so with the dependency on tinyxml2
A dirty workaround is adding and empty cpp file to scanning-header-only so a lib file is generated, but is there a correct way to do this?
Here is minimal example v1: https://www.dropbox.com/s/r1lbajz3xoat1bg/leveling-header-only-test%20v1.zip?dl=0
leveling CMakeLists.txt:
cmake_minimum_required(VERSION 3.8)
set(LEVELING_NAME leveling)
project(${LEVELING_NAME})
#
# To put tinyxml.dll next to the executable, to workaround having to make tinyxml2.dll reachable in PATH
#
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
math(EXPR platform_bits "${CMAKE_SIZEOF_VOID_P} * 8")
set(platform_dir bin/${CMAKE_SYSTEM_NAME}-${platform_bits})
foreach(config DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
foreach(var
CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config}
CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config}
CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config}
)
set(${var} "${CMAKE_BINARY_DIR}/${platform_dir}/${config}")
string(TOLOWER "${${var}}" ${var})
endforeach()
endforeach()
#
# ----------------------------------------------------------------------
#
add_subdirectory(deps)
add_executable(${LEVELING_NAME} main.cpp)
target_include_directories(${LEVELING_NAME} PUBLIC
${CMAKE_CURRENT_LIST_DIR}/deps/scanning
)
target_link_libraries(${LEVELING_NAME}
xml-reading
)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${LEVELING_NAME}) # Set Startup Project in VS. Implemented in CMake v3.6
set_target_properties(${LEVELING_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") # Set Working Directory of project in VS. Implemented in CMake v3.8
scanning CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
set(XML_NAME xml-reading)
project(${XML_NAME})
#
# To put tinyxml.dll next to the executable, to workaround having to make tinyxml2.dll reachable in PATH
#
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
math(EXPR platform_bits "${CMAKE_SIZEOF_VOID_P} * 8")
set(platform_dir bin/${CMAKE_SYSTEM_NAME}-${platform_bits})
foreach(config DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
foreach(var
CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config}
CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config}
CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config}
)
set(${var} "${CMAKE_BINARY_DIR}/${platform_dir}/${config}")
string(TOLOWER "${${var}}" ${var})
endforeach()
endforeach()
#
# ----------------------------------------------------------------------
#
add_subdirectory(deps)
add_library(${XML_NAME} INTERFACE CamerasXML.h)
target_include_directories(${XML_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/deps/tinyxml2
)
target_link_libraries(${XML_NAME}
INTERFACE tinyxml2
)
which yields
CMake Error at deps/scanning/CMakeLists.txt:33 (add_library):
add_library INTERFACE library requires no source arguments.
A .lib is when you create a STATIC (.lib) or SHARED (.lib and .dll) library on Windows. What you want is an INTERFACE library and it generates no files. http://mariobadr.com/creating-a-header-only-library-with-cmake.html has an example. Then you can use the following commands listed here, https://cmake.org/cmake/help/latest/command/add_library.html#interface-libraries, to populate the interface. Notice that it uses INTERFACE not PUBLIC.
target_link_libraries(INTERFACE),
target_link_options(INTERFACE),
target_include_directories(INTERFACE),
target_compile_options(INTERFACE),
target_compile_definitions(INTERFACE), and
target_sources(INTERFACE),
I've never actually used this but I assume it works as documented.
A simple add_library(${XML_NAME} INTERFACE) (not specifying any source files), while having target_include_directories(${XML_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/deps/tinyxml2) and target_link_libraries(${XML_NAME} INTERFACE tinyxml2) will do the trick.
The tinyxml2 includes are made available to the parent project, and the tinyxml2 library is linked in the parent project.