CMake linking 2 libraries with relative path - c++

my project structure:
...
CMakeLists.txt
src/
project/
libA/
CMakeLists.txt
libA.h
libA.cpp
libB/
CMakeLists.txt
libB.h
libB.cpp
build/
CMakelists.txt
project(project)
# set some global dirs
set(LIB_A_DIR "src/project/libA")
set(LIB_B_DIR "src/project/libB")
# include header directories
include_directories(${LIB_A_DIR})
include_directories(${LIB_B_DIR})
# generating linkable libraries
add_subdirectory(${LIB_A_DIR})
add_subdirectory(${LIB_B_DIR})
src/project/libA/CMakeLists.txt
# define libA sources
set(LIB_A_SOURCES libA.cpp libA.h)
# generate libA library
add_library(libA SHARED ${LIB_A_SOURCES})
src/project/libB/CMakeLists.txt
# define libB sources
set(LIB_B_SOURCES libB.cpp libB.h)
# generate libB library
add_library(libB SHARED ${LIB_B_SOURCES})
# link libraries
target_link_libraries(libB PRIVATE libA)
now if i run cmake .. && make from the build folder any function call made from libB works just fine. But as soon as i want to move this file structure the link breaks.
For example when i did cp -r build/ build_copy/ and clear the build directory with rm -rf build/* any code that includes libB throws a error while loading shared libraries: liblibB.so: cannot open shared object file: No such file or directory
how do i get my link to use the relative file path?
side note: i need them to be 2 seperate shared libraries combined with a link

You need to set the runtime-path of libA to where it is installed. This is done with the -rpath linker option.
Since the C++ frontend g++ or clang++ will be used when linking, you need to use the -Wl flag to pass the -rpath option to the linker.
You can set flag with the LINK_FLAGS target property for libB with the set_target_properties command:
set_target_properties(libB
PROPERTIES LINK_FLAGS "-Wl,-rpath,/path/to/libA/installation")

as in CMake linking 2 libraries with relative path comment:
Assign a variable with the absolute path of the relative path to the library:
get_filename_component(LIB_A_PATH ../lib/liba.a ABSOLUTE)
Then use the absolute path to link the lib (here statically):
add_library( lib_a STATIC IMPORTED )
set_target_properties(lib_a PROPERTIES IMPORTED_LOCATION ${LIB_A_PATH})
target_link_libraries(${PROJECT_NAME} lib_a)

Related

Can one CMake target link against a *shared* version of another library target?

I have a project with a core library (LibA in the example below), an executable, and a second library (LibB) that depends on the first library. The second library (LibB) is always built in shared (dynamic library) form.
Is there any way that I can force LibB to always link against the shared version of LibA?
Here is a small CMakeLists.txt file illustrating the problem:
cmake_minimum_required(VERSION 3.16)
project(my_project LANGUAGES CXX)
# LibA should be buildable in either static or shared form.
add_library(LibA A.cpp)
# In the real case, there are many customizations to `LibA`:
# compile flags, include dirs, target properties, etc.
add_executable(ExecutableA main.cpp)
target_link_libraries(ExecutableA LibA)
# I want library myB to *always* link against the dynamic library libLibA.so.
add_library(LibB SHARED B.cpp)
target_link_libraries(LibB PUBLIC LibA m pthread)
It can be built with the following commands:
echo 'int main() {}' > main.cpp
touch A.cpp B.cpp
mkdir -p build
cmake -B build/ -S .
cmake --build build/
I know that I can force LibA to be always shared with add_library(LibA SHARED A.cpp), but I want to be able to build LibA as a static library.
The real context for this is that I have a core library (LibA), and want to link against it statically when creating executables, but link against it dynamically (as LibA.so) when creating a Python extension module (LibB.so).
This is not possible, libA can only be either static or dynamic, not both.
You need to have two versions of libA, one dynamic, one static:
cmake_minimum_required(VERSION 3.16)
project(my_project LANGUAGES CXX)
function( addLibA suffix type )
set( libname LibA${suffix} )
add_library(${libname} ${type} A.cpp)
# specify library properties here
endfunction()
# LibA should be buildable in either static or shared form.
addLibA("s" STATIC)
addLibA("" SHARED)
# In the real case, there are many customizations to `LibA`:
# compile flags, include dirs, target properties, etc.
add_executable(ExecutableA main.cpp)
target_link_libraries(ExecutableA LibAs)
# I want library myB to *always* link against the dynamic library libLibA.so.
add_library(LibB SHARED B.cpp)
target_link_libraries(LibB PUBLIC LibA m pthread)
What you are specifically asking for will require multiple targets, as has been mentioned in comments / jpo38's answer
However you might be interested in using CMake's Object Libraries instead to share the objects between a library target and an executable target. For example:
cmake_minimum_required(VERSION 3.16)
project(my_project LANGUAGES CXX)
# Objects get built exactly once
add_library(LibA.objects OBJECTS
A.cpp
)
target_compile_commands( ... )
target_include_directories( ... )
# Shared library for libA
add_library(LibA SHARED
$<OBJECTS:LibA.objects>
)
# LibB -- always shared, always linked against dynamic LibA
add_library(LibB SHARED
B.cpp
)
target_link_libraries(LibB PUBLIC LibA)
# ExecutableA, built using LibA's objects
# (effectively the same linking a static LibA.a)
add_executable(ExecutableA
main.cpp
$<OBJECTS:LibA.objects>
)
Using an object library, your objects get built once, but are shared between multiple targets. Using multiple library targets, on the other hand, will force a rebuild of each object, since each target may use different compile commands.
This allows ExecutableA to have LibA's objects built into it, so it behaves effectively as though you had statically linked in a LibA.a.
In this case, LibA is now built as a SHARED library explicitly, since this is desired for LibB to dynamically link against.
If you're using newer versions of CMake, you can also avoid the $<OBJECTS:...> generator expression, and just link against the object library directly with target_link_libraries -- e.g.:
target_link_libraries(libA PRIVATE LibA.objects)
...
target_link_libraries(ExecutableA PRIVATE LibA.objects)
This approach will also allow properties to be applied once to their respective targets. Any properties regarding the sourcefiles themselves just needs to apply directly to the Object library. Any properties regarding the library setup/layout (e.g. output location, suffixes, etc) applies strictly to the library targets.

How to build gtest static library from my project CMakeLists.txt?

I have a main project with a subproject tests.
In project/CMakeLists.txt I added
add_subdirectory(tests)
In project/tests/CMakeLists.txt I plug in "pre-compiled" gtest library (from Ubuntu repository). I cd to /usr/src/gtest and compile two *.a files into system lib directory.
And it works fine, until issue scribed in google test FAQ appeared.
How to build gtest static library files from project/tests/CMakeLists.txt and how to use this new *.a files instead of system ones?
Finaly I find out how to rebuild static libgtest.a without adding googletest as a subproject.
Using info from this link and from the bottom of this link.
Interesting part of my project/tests/CMakeLists.txt:
.....
# Locate GTest
#find_package(GTest REQUIRED)
#include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})
set(GTEST_SRC /usr/src/gtest/src/gtest-all.cc)
include_directories(SYSTEM /usr/src/gtest # <-- path to non-header files from gtest-all.cc
/usr/include) # <-- path to gtest headers
add_library(gtest STATIC ${GTEST_SRC}) # <-- make static library target before main project executable
target_link_libraries(gtest ${CMAKE_THREAD_LIBS_INIT})
# Link runTests with what we want to test and the GTest and pthread library
add_executable(tests ${SRC})
target_link_libraries(tests ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} gtest) # <-- finaly link this library as others
I'm starting from beginning.
First, download the googletest source code from
https://github.com/google/googletest
When we download this ZIP-files, we get the googletest and googlemock project, but here I will only use the googletest folder. I unpack the ZIP and copy the googletest folder to my project, e.g.:
/home/name/myProject/
The content from this folder could look like the following:
- CMakeLists.txt
- googletest /
- main.cpp
In the CMakeList file I can add gtest as a subdir and link my executable against it. I build a minimal example:
cmake_minimum_required(VERSION 2.6.4)
project(MyProject)
enable_testing()
add_subdirectory( googletest )
# this sets the output dir to /bin
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
include_directories(${CMAKE_SOURCE_DIR}/googletest/include)
add_executable(myExe main.cpp)
target_link_libraries(myExe gtest_main gtest)
The last three lines set the include path to googletest, add the executable and link the exe against gtest.
After downloading gtest and creating the cmake structure, you can build you program:
$ mkdir bin
$ cd bin
$ cmake ..
$ make
and call the exe:
$ ./myExe
I hope this will help.

CMake dynamically links `.a` files in `/usr/local/lib`

I want to statically compile my program against another static library, for this example I'm using zeromq. Here is my CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
add_executable( test test.cpp )
find_library(ZMQ NAMES libzmq.a)
message(STATUS ${ZMQ})
target_link_libraries( test ${ZMQ} )
It finds the .a file when I run mkdir build && cd build && cmake ..
-- /usr/local/lib/libzmq.a
However, if I examine the link.txt file, the library is dynamically linked:
/usr/bin/c++ CMakeFiles/test.dir/test.cpp.o \
-o test -rdynamic /usr/local/lib/libzmq.a
The weird bit is that if I move the file to a different directory, say /usr/lib and run cmake .. once more, it locates the new path to the library:
-- /usr/lib/libzmq.a
But now it has magically changed to static linking:
/usr/bin/c++ CMakeFiles/test.dir/test.cpp.o \
-o test -rdynamic -Wl,-Bstatic -lzmq -Wl,-Bdynamic
The same thing applies to other libraries I'm linking to.
Why are all my libraries in /usr/local/lib being dynamically linked?
You should not use the path directly, and create an imported target instead, so you can explicitly declare it static:
cmake_minimum_required(VERSION 2.6)
add_executable( test test.cpp )
find_library(zmq_location NAMES libzmq.a)
message(STATUS ${zmq_location})
add_library(zmq STATIC IMPORTED)
set_target_properties(zmq PROPERTIES IMPORTED_LOCATION ${zmq_location})
target_link_libraries( test zmq )
This may lead to a situation where the library appears to be linked dynamically, but the cmake source code has the answer:
If the target is not a static library make sure the link
type is shared. This is because dynamic-mode linking can handle
both shared and static libraries but static-mode can handle only
static libraries. If a previous user item changed the link type to
static we need to make sure it is back to shared.
Essentially, it's letting the linker handle detecting that the library is static if currently in dynamic-mode.
The answer to my original question about the difference between /usr/local/lib and /usr/lib is that, by default, /usr/local/lib is not one of the implicit link directories. Therefore, a quick fix is to include this line in the config:
set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES /usr/local/lib ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES})
However, as pointed out in this other answer, referencing files directly is not the way to go, instead one should use add_library.
Here is my DEMO:
find_library(VLQ_LIB NAMES vlq-shared PATHS /usr/local/lib/ REQUIRED)
find_path(VLQ_HEADER vlq.hpp /usr/local/include)
target_link_libraries(myproj PUBLIC VLQ_LIB)
target_include_directories(myproj PUBLIC VLQ_HEADER)
but during run, you still need to copy the shared lib to /lib/ folder, for static lib, you can keep it under /usr/lib

Various libs in subfolders, how do I export them to the parent?

I have a code folder structure like this:
project/
CMakeLists.txt
src/
project.cpp
lib/
libA/
CMakeLists.txt
src/
libA.cpp
include/
libA.h
libB/
CMakeLists.txt
src/
libB.cpp
include/
libB.h
Now, libA is totally standalone, so I have a CMakeLists.txt similar to this:
cmake_minimum_required(VERSION 2.8)
project ( libA )
set ( LIBA_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE )
include_directories ( include/ )
file (GLOB LIBA_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}
src/libA.cpp
)
add_library ( libA STATIC ${LIBA_SOURCES} )
However, libB depends on libA. How can I export the libA.a output path to be used for linking to libB, and the corresponding include path?
And furthermore, how do I pass that data to the parent CMakeLists.txt for the main project?
I haven't done this personally, so this answer may only be a starting point, but Qt uses cmake and is able to tie all the include directories and library dependencies into each 'module' (library). If you accomplish that, then you can simply do
add_executable(myExe ...)
target_link_libraries(myExe libB)
to the executable in your main cmake project.
Looking through the Qt scripts, it appears what you need to set (for each library) is:
add_library(libA ...)
set_target_properties(libA PROPERTIES
"INTERFACE_LINK_LIBRARIES" "${LIBRARIES_LIBA_DEPENDS_ON}"
"INTERFACE_INCLUDE_DIRECTORIES" "${LIBA_INCLUDE_DIRECTORIES}"
)
then for libB
add_library(libB ...)
set_target_properties(libB PROPERTIES
"INTERFACE_LINK_LIBRARIES" "libA"
"INTERFACE_INCLUDE_DIRECTORIES" "${LIBB_INCLUDE_DIRECTORIES}"
)
The cool thing about this method is that when done properly, you can chain together as many libraries as you like without having to worry about the combinatorial explosion of dependencies and include dirs.
Taking #epicbrew and #NicolasHolthaus answers/comments I just wanted to show how the complete example would look in a newer version of CMake and discuss some of the implications:
project/CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project( project )
add_subdirectory( lib/libB )
add_subdirectory( lib/libA )
add_executable( project src/project.cpp )
target_link_libraries( project libB )
project/lib/libA/CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project( libA )
add_library( libA STATIC src/libA.cpp include/libA.h )
target_include_directories( libA PUBLIC include )
project/lib/libB/CMakeLists.txt
add_library( libB STATIC src/libB.cpp include/libB.h )
target_include_directories( libB PUBLIC include )
target_link_libraries( libB libA )
As you can see
You need to know in the main CMakeLists.txt file that you also need libA. I was deliberately reversing the add_subdirectory() calls to show that the order doesn't matter when using target_link_libraries() (sort of allows target forward declarations).
You don't need the local include_directories() call anymore it's an implicit part of the target_include_directories().
With target_include_directories(... PUBLIC ...) those include paths are self-propagating.
You don't need to add the ${CMAKE_CURRENT_SOURCE_DIR} prefixes, those are the defaults.
I just added the source/header files directly to the add_executable()/add_library() calls for compactness of this example code. Normally I also would place those in local variables.
libB is not standalone, but you could change that e.g. by adding add_subdirectory( ../libA libA ) to its CMakeLists.txt file. Obviously you then have to remove add_subdirectory( lib/libA ) from the main CMakeLists.txt.
If you want to optimize the CMake processing a little you should not repeat the project() commands. If you still want libA to be self-sufficient you could add if ( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )/endif() around the cmake_minimum_required(VERSION 3.0)/project( libA ) calls.
CMake knows about all of the targets that you've built, so there's no need to export information up to the parent. You simply tell CMake that libB links against libA and CMake takes care of putting the correct path on the linker command line. So just specify libA as a link dependency of libB in your libB/CMakeLists.txt:
target_link_libraries(libB libA)
For the include path you either need to set variables in the parent along the lines of the following in the top level project/CMakeLists.txt:
set(LIBA_INCLUDES libA/include)
set(LIBB_INCLUDES libB/include)
Then reference these in the subprojects CMakeList.txt files.
Or, you could use a relative path in your libB/CMakeLists.txt file:
include_directories(../libA/include)

Cmake configuration for multiple sub-libraries in a "packages" directory

Here is a sample project I am trying to build with a "Packages" directory which includes all the libraries to be used in the main code.
I am trying to keep my root cmake file as clean as possible and avoid relative path such as
include_directory(packages/lib1)
but I am struggling. Is there a way of including sub-directories of a directory for the purposes of header inclusion.
First a few minor remarks:
always name the CMake configuration files CMakeLists.txt (because of)
bookmark the documentation on CMake: https://cmake.org/documentation/
Sometimes it's not that easy to read, but very specific once you adopt your head to the "CMake world" ;-)
make yourself comfortable with the scope of CMake variables
include_directories(DIR1 [DIR2 [...]])
Tells CMake where the compiler should look for header files, i.e. -IDIR1 -IDIR2 ....
add_library(NAME [STATIC|SHARED] SOURCES)
This command creates the required compiler commands to create a static or shared library out of a given list of source files. No need to add in the header files. The make target will be called NAME and the library target is known to CMake as NAME.
add_subdirectory(DIR)
Tells CMake to look into DIR and parse the included CMakeLists.txt with all its content.
target_link_libraries(TARGET LIB1 [LIB2 [...]])
Tells CMake to instruct the linker to link LIB1, LIB2, etc. to the TARGET, i.e. -LLIB1 -LLIB2 .... TARGET is a CMake/make target previously defined/created with a call to add_{library,executable,custom_target}.
CMakeLists.txt:
include_directories(libraries)
# a header file in `libraries/lib1/foo.hpp` can be included
# in the whole CMake project by `#include "lib1/foo.hpp"`.
add_subdirectory(libraries)
add_subdirectory(tests)
libraries/CMakeLists.txt:
add_subdirectory(lib1)
add_subdirectory(lib2)
libraries/lib1/CMakeLists.txt:
add_library(lib1 STATIC ${LIB1_SOURCES})
libraries/lib2/CMakeLists.txt:
add_library(lib2 STATIC ${LIB2_SOURCES})
tests/CMakeLists.txt:
add_executable(tests ${TEST_SOURCES})
target_link_libraries(tests lib1 lib2)