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

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]

Related

CMake build target without specifying target_include_directories

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)

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?

CMake adding files with ends with "*.a" [duplicate]

Clion: how add or (use) prebuilt static library in my Project?
You're probably asking about how to link your project to the pre-built static library. If so, you can do like this by calling target_link_libraries.
Assume your project called myProj and the pre-built library myLib.lib, you can do like this:
target_link_libraries(myProj myLib)
I had great difficulty making this work as I was completely new to CLion and CMake.
In my scenario I was taking a class that required us to use the course library in every project.
Assuming you have a library called libClassLibrary.a, do the following in the CMakeLists.txt at the project root:
First, find the library's location:
find_library(LIB_TO_INCLUDE ClassLibrary /path/to/your/library)
LIB_TO_INCLUDE will contain the location of the library assuming it is found. Note that hardcoding the path could be problematic if you'd like your solution to be portable to other systems. You can add additional search paths separated by a space if the library could exist in multiple locations. A typical example is to include common install locations such as /usr/bin /usr/local/bin etc.
Next, ensure that header files (if applicable) are included in the header search paths:
find_path (LIB_INCLUDES ClassLibrary.h /path/to/header/files)
Again, include multiple search paths if the headers could be loaded in multiple locations. If there is more than one header file, you'll need to include all of them.
Now, include the directories using the include_directories command:
include_directories(${LIB_INCLUDES})
The above will instruct the build system to search all directories contained in LIB_INCLUDES or whatever you decide to call it.
Finally, add the executable and use the target_link_libraries command to link the libClassLibrary.a.
add_executable(MyExecutable main.cpp)
target_link_libraries(MyExecutable ${LIB_TO_INCLUDE})
That's all. You'll notice that under "External Libraries" > "Header Search Paths" in the Project organizer window the directories containing your header files appears.
PS - The book Mastering CMake by Ken Martin and Bill Hoffmann is an invaluable resource.
I've linked my static lib test.a with the related header files as following:
Project
├── main.cpp
├── CmakeLists.txt
├── libs
│ ├── includes
│ │ ├── *.h
│ ├── lib
│ │ ├── test.a
I've added this to the ./CMakeLists.txt:
include_directories(${CMAKE_SOURCE_DIR}/libs/include)
find_library(Test_LIB test "${CMAKE_SOURCE_DIR}/libs/lib")
add_executable(TestApp main.cpp)
target_link_libraries(TestApp ${Test_LIB})
By adding message(${Test_LIB}), you can print out and check the path.
Your question is unrelated to CLion, it is pure CMake. Modify the CMakeLists.txt from your project and use add_library. The CMake documentation might be helpful.
I misunderstood the question, target_link_library is probably the answer to the question.

How to add prebuilt static library in project using CMake?

Clion: how add or (use) prebuilt static library in my Project?
You're probably asking about how to link your project to the pre-built static library. If so, you can do like this by calling target_link_libraries.
Assume your project called myProj and the pre-built library myLib.lib, you can do like this:
target_link_libraries(myProj myLib)
I had great difficulty making this work as I was completely new to CLion and CMake.
In my scenario I was taking a class that required us to use the course library in every project.
Assuming you have a library called libClassLibrary.a, do the following in the CMakeLists.txt at the project root:
First, find the library's location:
find_library(LIB_TO_INCLUDE ClassLibrary /path/to/your/library)
LIB_TO_INCLUDE will contain the location of the library assuming it is found. Note that hardcoding the path could be problematic if you'd like your solution to be portable to other systems. You can add additional search paths separated by a space if the library could exist in multiple locations. A typical example is to include common install locations such as /usr/bin /usr/local/bin etc.
Next, ensure that header files (if applicable) are included in the header search paths:
find_path (LIB_INCLUDES ClassLibrary.h /path/to/header/files)
Again, include multiple search paths if the headers could be loaded in multiple locations. If there is more than one header file, you'll need to include all of them.
Now, include the directories using the include_directories command:
include_directories(${LIB_INCLUDES})
The above will instruct the build system to search all directories contained in LIB_INCLUDES or whatever you decide to call it.
Finally, add the executable and use the target_link_libraries command to link the libClassLibrary.a.
add_executable(MyExecutable main.cpp)
target_link_libraries(MyExecutable ${LIB_TO_INCLUDE})
That's all. You'll notice that under "External Libraries" > "Header Search Paths" in the Project organizer window the directories containing your header files appears.
PS - The book Mastering CMake by Ken Martin and Bill Hoffmann is an invaluable resource.
I've linked my static lib test.a with the related header files as following:
Project
├── main.cpp
├── CmakeLists.txt
├── libs
│ ├── includes
│ │ ├── *.h
│ ├── lib
│ │ ├── test.a
I've added this to the ./CMakeLists.txt:
include_directories(${CMAKE_SOURCE_DIR}/libs/include)
find_library(Test_LIB test "${CMAKE_SOURCE_DIR}/libs/lib")
add_executable(TestApp main.cpp)
target_link_libraries(TestApp ${Test_LIB})
By adding message(${Test_LIB}), you can print out and check the path.
Your question is unrelated to CLion, it is pure CMake. Modify the CMakeLists.txt from your project and use add_library. The CMake documentation might be helpful.
I misunderstood the question, target_link_library is probably the answer to the question.