CMake relative linking, executable fails to find shared libraries - c++

I am trying to learn CMake and for that purpose I am working with an example project of the structure shown below. I am trying to configure all the CMakeLists.txt files such that after make install I can copy the resulting build directory and copy-paste it around freely such that other people are able run the executable.
Problem: after running make install (maxOS 10.14.6 (Darwin 18.7.0)) everything works if I run the executable inside build BUT if I move the build directory from its original location - for example to Desktop - the executable is unable to find the shared libraries. It seems that the reason for this is that the paths to the shared libraries are defined as absolute paths instead of relative paths with respect to the build directory.
Question: How can I build the project such that the executable finds the shared libs?
Project structure:
myapp
|
CMakeLists.txt (top-level)
- build
- app
| CMakeLists.txt (app)
| - inc
| - app
| app.h
| - src
| app.cpp
| main.cpp
|
- external
|
- mylib
CMakeLists.txt (mylib)
- inc
- mylib
mylib.h
- src
mylib.cpp
CMakeLists:
CMakeLists.txt (top-level)
cmake_minimum_required(VERSION 3.0)
project(myapp)
add_subdirectory(external/mylib)
add_subdirectory(app)
CMakeLists.txt (app)
# myapp program
cmake_minimum_required(VERSION 3.0)
project(myapp_prog)
set(SOURCES ./src/)
set(HEADERS ./inc/app/)
set(SOURCE_FILES
${SOURCES}/app.cpp)
set(HEADER_FILES
${HEADERS}/app.h)
# All sources that need to be tested in unit test go into a static library
add_library(myapp_lib SHARED ${HEADER_FILES} ${SOURCE_FILES})
target_include_directories(myapp_lib PUBLIC ${HEADERS})
# The main program
add_executable(prog ./src/main.cpp)
target_include_directories(prog PUBLIC ./inc/)
# Link the libraries
target_link_libraries(prog PRIVATE myapp_lib mylib)
install(TARGETS myapp_lib DESTINATION ${CMAKE_BINARY_DIR}/lib)
CMakeLists.txt (mylib)
# mylib
cmake_minimum_required(VERSION 3.0)
project(mylib)
set(SOURCES src/)
set(HEADERS inc/mylib/)
set(SOURCE_FILES
${SOURCES}/mylib.cpp)
set(HEADER_FILES
${HEADERS}/mylib.h)
add_library(mylib SHARED ${HEADER_FILES} ${SOURCE_FILES})
target_include_directories(mylib PUBLIC inc/)
install(TARGETS mylib DESTINATION ${CMAKE_BINARY_DIR}/lib)
C++ code:
mylib.h
void mylib_print_hello();
mylib.cpp
#include "library.h"
#include <iostream>
void mylib_print_hello() {
std::cout << "Hello from mylib!" << std::endl;
}
app.h
void myapp_hello();
app.cpp
#include <iostream>
#include "app.h"
void myapp_hello()
{
std::cout << "Hello from myapp!" << std::endl;
}
main.cpp
#include <iostream>
#include "app/app.h"
#include "mylib/mylib.h"
int main()
{
myapp_hello();
mylib_print_hello();
}

I think the issue is that you might be confusing the build step with the install step. By using ${CMAKE_BINARY_DIR}, you are passing an absolute path to your binary directory (build), which may interfere with the moving of the build afterwards. If you would like to ouput your libraries to a folder after the build, you may set the target of the library with:
set_target_property(<library-name> PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
This will affect only the build stage of your project. As far as installs go, you are better off using only relative paths in the install directives. These paths will be relative to your ${CMAKE_INSTALL_PREFIX} variable. For example, you would use:
install(TARGETS mylib DESTINATION lib)
To install the library under the ${CMAKE_INSTALL_PREFIX}/lib folder. Best practice states that you should NEVER manipulate this variable directly in your CMakeLists.txt files, as it could break a build somewhere down the line. Starting with CMake 3.15, it is possible to use cmake --install to install a project. This command allows setting an installation prefix, such as:
cmake --install . --prefix desired/install/path
With this, all your link path should stay valid.

Please add a macos tag to this question.
I think that you are looking for a relative RPATH. CMake builds the executable with an absolute RPATH, so if you move or rename the build directory the program stop working. In your example, you only need to modify the main program CMakeLists.txt, to change the value of the RPATHs that CMake inserts in your executable.
# The main program
add_executable(prog ./src/main.cpp)
target_include_directories(prog PUBLIC ./inc/)
set_target_properties(prog PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE INSTALL_RPATH "#executable_path/;#executable_path/../external/mylib")
You can verify the values of the LC_RPATHs with this command from your build directory:
$ otool -l app/prog
For dependencies between libraries, it may also be useful "#loader_path" instead of #executable_path. And for other Unix operating systems, use "$ORIGIN". More information here.

Related

cmake using two shared libraries that are dependant on each other

I'm trying to build a program, that is dependant on two other projects (ugh and thermough). thermough is dependant on ugh and both have multiple libraries.
The code species.cpp is the following:
#include "Test.h"
#include "tabstream.h"
#include <fstream>
int main()
{
ugh::Test tp("water");
std::ofstream file;
tabstream tfile(file,16);
file.open("water");
tfile << "T"
<< std::endl;
return 0;
}
This is my CMakeLists.txt:
####################################
# General setup
####################################
cmake_minimum_required (VERSION 3.5.2)
project (getSpeciesTest VERSION 1.0)
set(CMAKE_VERBOSE_MAKEFILE OFF)
include(CMakePrintHelpers)
####################################
# Executable
####################################
add_executable(specieTestEx species.cpp)
#configure_file(TutorialConfig.h.in TutorialConfig.h)
####################################
# Libraries, etc
####################################
set(PATH_TO_SRC ${PROJECT_SOURCE_DIR}/../src)
set(PATH_TO_THERMOUGH ${PROJECT_SOURCE_DIR}/../../build)
set(PATH_TO_UGH ${PROJECT_SOURCE_DIR}/../../../ugh/build)
set(PATH_TO_UGH_SRC ${PROJECT_SOURCE_DIR}/../../../ugh/git/src)
include_directories (${PATH_TO_SRC})
include_directories (${PATH_TO_SRC}/ughMixture)
include_directories (${PATH_TO_SRC}/mathUtils)
include_directories (${PATH_TO_UGH})
include_directories (${PATH_TO_UGH_SRC}/ughBase)
include_directories (${PATH_TO_UGH_SRC}/ughMixture)
include_directories (${PATH_TO_UGH_SRC}/ughMath)
include_directories (${PATH_TO_UGH_SRC}/ughMath/math1)
include_directories (${PATH_TO_UGH_SRC}/ughMath/math2)
include_directories (${PATH_TO_UGH_SRC}/ughMath/math3)
include_directories (${PATH_TO_UGH_SRC}/ughMath/math4)
list(APPEND LIBS_THERMOUGH [...])
list(APPEND LIBS_UGH [...])
##this is where the already built libraries are
link_directories(${PATH_TO_THERMOUGH})
link_directories(${PATH_TO_UGH})
target_link_libraries(specieTestEx ${LIBS_THERMOUGH})
target_link_libraries(specieTestEx ${LIBS_UGH})
I did not write both projects, I'm just trying to use so functionalities from thermough and therefore need to link both libraries. The libraries are as shared libraries in the build folders (.so files). I checked in the CMakeList.txt of thermough and the libraries of both projects are linked there.
My questions now:
As far as I understand it, in the libraries only the .cpp files and not the headers are compiled. Therefore I need to include all the directories to the header files (and if one called function is dependant on another file, i need to include the path to that directory and so on). This leads me to having so many include_directories commands. Is there an easier way?
Secondly I get the following error when compiling:
[SRCdirectory]/../../../ugh/git/src/ughMath/math4/mathImplementation.h:8:32: fatal error: ughMath/MathLog.h: No such file or directory
The path to ughMath is linked in my CMakeList.txt. Why does this happen then and what can I correct?
Thank you for your answer in advance.
Last but not least this is the structure of the code I'm working with:
code
ugh
git
build (contains all ugh_[..].so libraries)
thermough
git
- src
- myTestCase (contains species.cpp and the CMakeList.txt I'm talking about)
build (contains all thermough_[..].so libraries)

CMake import local shared library and how to include it in a header

I'm stuck to import a shared library in another project with CMake.
my-utils-lib
My lib files are generated in my-utils-lib project:
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(my-utils-lib VERSION 1.0.0 DESCRIPTION "Utils for C++.")
set(CMAKE_CXX_STANDARD 14)
add_library(
my-utils-lib SHARED
./src/string_utils/find_matches.cpp
./src/string_utils/split.cpp
./src/string_utils/format.cpp
./src/vector_utils/print_vector.cpp
)
set_target_properties(
my-utils-lib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 1
)
target_include_directories(my-utils-lib PRIVATE src)
include(GNUInstallDirs)
install(
TARGETS my-utils-lib
DESTINATION ${CMAKE_INSTALL_LIBDIR}
EXPORT my-utils-lib
)
configure_file(my-utils-lib.pc.in my-utils-lib.pc #ONLY)
install(
FILES ${CMAKE_BINARY_DIR}/my-utils-lib.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig
)
I run mkdir build && cd ./build/ && cmake .. && sudo make install to generate and install my library files.
Finally, my-utils-lib project tree looks like this:
CMakeLists.txt
my-utils-lib.pc.in
src/
|_main.cpp
|_main.h
|_string_utils/
|_find_matches.cpp
|_find_matches.h
|_format.cpp
|_format.h
|_split.cpp
|_split.h
|_vector_utils/
|_print_vector.cpp
|_print_vector.h
my-project
In another project, I'd like to import "split.h" in one of my local headers without having to enter the full path to the actual file. Something like:
#include "my-utils-lib/string_utils/split.h"
I tried many solution so far, none of them is working. My last attempt is:
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(longer_sub_sequence)
set(CMAKE_CXX_STANDARD 14)
find_package(Qt5Widgets REQUIRED)
find_library(my-utils-lib libmy-utils-lib.1.0.0.dylib)
add_library(my-utils-lib SHARED IMPORTED)
add_executable(
longer_sub_sequence
main.cpp
visualization/draw_matrix.cpp
visualization/draw_matrix.h
)
target_link_libraries(longer_sub_sequence my-utils-lib Qt5::Widgets)
CMake autobuild runs fine (I am using CLion as an IDE) but I can't find how to import any code from my library.
I checked in /usr/local/lib (where CMake generated my lib files), and found the correct lib files :
find . -maxdepth 1 -name "*my-utils-lib*" -print
./libmy-utils-lib.1.0.0.dylib
./libmy-utils-lib.1.dylib
./libmy-utils-lib.dylib
What am I missing here ?
You are missing to install the library header.
You should add an install command for header just like you did with the library binary.
Moreover you should consider using comparison signs (< and >) instead of double quote for include directive :
#include <my-utils-lib/string_utils/split.h>
One last remark, you are generating and installing a .pc file which is used by pkg-config only and you're not using it.

CMAKE: How do I install executable along with required dylib to custom directory?

So I'm using CMake to build a C++ project (on Mac OS) and my project relies on a dylib (I'm using TBB https://www.threadingbuildingblocks.org/ but the specific library itself doesn't matter)
If I do a standard "cmake" and "make" it builds the executable where I want it and when I run my app, the dylib links correctly and everything works perfectly.
The problem comes in when I try to do a "make install" and try to run the resulting executable from the install directory. I get an "image not found" error:
dyld: Library not loaded: #rpath/libtbb.dylib
Referenced from:
/Users/MyName/Desktop/ProjectRoot/install/./MyApp
Reason: image not found
Interestingly, if I do a regular "make" without an install, and then manually copy over the executable to the install directory, then that will link against my dylib properly. I have no idea why that is.
My directory structure is as follows:
Root
CMakeLists.txt
Source/
Libraries/
tbb/
include/
lib/
libtbb.dylib
install/
...and my CMakeLists.txt file is below:
# Start of CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project (MyApp)
# Set C++ version and output paths
set (CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})
set(CMAKE_MACOSX_RPATH 1)
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install")
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
# Find TBB
find_library (
TBB_LIBRARIES
NAMES tbb libtbb # what to look for
HINTS "${CMAKE_SOURCE_DIR}/Libraries/tbb/lib" # where to look
NO_DEFAULT_PATH # do not search system default paths
)
# Set Custom Include Files + TBB header files
include_directories(Source/Headers Libraries/tbb/include)
# Set Source Files
file(GLOB_RECURSE SRC_FILES "Source/*.cpp")
add_executable(MyApp ${SRC_FILES})
# Link Libraries
target_link_libraries(MyApp ${TBB_LIBRARIES})
# Set compile flags
set_target_properties(MyApp PROPERTIES CXX_STANDARD 14) #LINK_FLAGS "-Wl")
target_compile_features(MyApp PUBLIC cxx_std_14)
# Install executable
install(TARGETS MyApp DESTINATION .)
If I try to also add the following line, and install the dylib as well:
install(TARGETS ${TBB_LIBRARIES} DESTINATION lib)
then when I do a "make install" I get the following error instead:
install TARGETS given target
"/Users/MyName/Desktop/ProjectRoot/Libraries/tbb/lib/libtbb.dylib"
which does not exist in this directory.
So I just can't seem to get this install to work. How do I fix it so that both my executable and my library get installed in the right place and that my executable will be able to link against my library when run?

Cmake Linking Shared Library: "No such file or directory" when include a header file from library

I am learning to build a library using Cmake. The code structure for building library is like below:
include:
Test.hpp
ITest.hpp // interface
src:
Test.cpp
ITest.cpp
In CMakeLists.txt, the sentences I used to build library is :
file(GLOB SRC_LIST "src/iTest.cpp" "src/Test.cpp" "include/Test.hpp"
"include/iTest.hpp" "include/deadreckoning.hpp")
add_library(test SHARED ${SRC_LIST})
target_link_libraries( test ${OpenCV_LIBS}) // link opencv libs to libtest.so
Then I wrote another test file (main.cpp), copy and paste the library under the same directory, link library and call functions inside the library.
This CMakeLists.txt is
cmake_minimum_required(VERSION 2.8)
project(myapp)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread -O3 -Wall -ftree-vectorize -ffast-math -funroll-loops")
add_executable(myapp main.cpp)
target_link_libraries(myapp "/home/labUser/test_lib/libtest.so")
The main.cpp compiles and runs succussfully if I don't include the header files inside the library:
#include <iostream>
using namespace std;
int main(){
cout << "hello world" << endl;
return -1;
}
But when I include the header file #include "ITest.hpp", it has error:
fatal error: iTest.hpp: No such file or directory
#include "iTest.hpp"
compilation terminated.
I don't understand why it happens.I think I have already linked the library successfully because when I run main.cpp without including header file, it doesn't give any "linking" error. And I think apparently the header files are inside the library. Why I can't include it? Can anyone help me figure this out?
Big thanks!
You have a couple of issues here.
Propagating headers to users of your target:
Whilst you've added the include file to your library target, you need to let consumers of your library target know how to find the header.
As such, when your app myapp links against your library target test, you need to tell cmake to add ./include to myapp's include search path.
There is a special cmake variable, ${CMAKE_CURRENT_LIST_DIR} which resolves to the path to the directory in which the current CMakeLists.txt being processed is.
In your instance, that is the parent folder of both src and include.
./ <-- ${CMAKE_CURRENT_LIST_DIR} is this directory
+--- CMakeLists.txt
+--- src/
| +---Test.cpp
| +---ITest.cpp
+--- include/
+---Test.hpp
+---ITest.hpp
In order to tell cmake to add a path to its include search path you use target_include_directories
For this the path will then be ${CMAKE_CURRENT_LIST_DIR}/include
So the syntax you'd be looking for is:
target_include_directories(test PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
Note that this means you don't have to add "include/iTest.hpp" and "include/Test.hpp" to your SRC_LIST glob, as the compiler will be able to find them from the above target_include_directories
Linking to your test library:
Now that you've created your library and added the include directories, to actually use it in your app, you should again use target_link_libraries, but don't specify the path to the generated .so file, instead refer to the name of the library target you created, test
target_link_libraries(myapp test)
Now myapp will know how to find Test.hpp because it will get that information from the "dependency link" you've created between myapp and test
As such, assuming the following directory structure, the following CMakeLists.txt files may work
src/
+--- library/
| +--- < sources for your shared library >
+--- app/
+--- < sources for your application >
src/CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(myapp)
add_subdirectory(library)
add_subdirectory(app)
src/library/CMakeLists.txt
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}
-std=c++11
-pthread
-O3
-Wall
-ftree-vectorize
-ffast-math
-funroll-loops")
find_package(OpenCV REQUIRED)
add_library(test SHARED "src/iTest.cpp src/Test.cpp")
target_link_libraries(test ${OpenCV_LIBS}) // link opencv libs to libtest.so
target_include_directories(test PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
src/app/CMakeLists.txt
add_executable(myapp main.cpp)
target_link_libraries(myapp test)

Building of executable and shared library with cmake, runtimelinker does not find dll

I am working with gcc(cygwin), gnu make, windows 7 and cmake.
my cmake testprojekt has the following structure
rootdir
|-- App
| |-- app.cpp
| +-- CMakeLists.txt
|-- Lib
| |-- lib.cpp
| |-- CMakeLists.txt
|-- MakeFileProject
+ CMakeLists.txt
rootdir/App/app.cpp:
#include<string>
void printThemMessageToScreen(std::string input);//prototype
int main(int argc,char **argv){
printThemMessageToScreen("this will be displayed by our lib");
return 0;
}
rootdir/Lib/lib.cpp:
#include<iostream>
#include<string>
void printThemMessageToScreen(std::string input){
std::cout<<input;
}
rootdir/CMakeLists.txt:
cmake_minimum_required(VERSION 2.6)
project(TestProject)
add_subdirectory(App)
add_subdirectory(Lib)
rootdir/Lib/CMakeLists.txt:
add_library(Lib SHARED lib.cpp)
rootdir/App/CMakeLists.txt:
# Make sure the compiler can find include files from our Lib library.
include_directories (${LIB_SOURCE_DIR}/Lib)
# Make sure the linker can find the Lib library once it is built.
link_directories (${LIB_BINARY_DIR}/Lib)
# Add executable called "TestProjectExecutable" that is built from the source files
add_executable (TestProjectExecutable app.cpp)
# Link the executable to the lib library.
target_link_libraries (TestProjectExecutable Lib)
Now, when i run cmake and make, everything will get generated & built with no errors, but when i try to execute the binary, it will fail because the library which was generated could not be found.
BUT: when i copy the lib dll into the same directory like the app exe, it will get executed!
also: if i configure the library to be static, it will also execute.
how to tell the runtime linker where to look for my dll?
UPDATE:
Solution according to the Method proposed by User Vorren:
I opened up the registry editor, and navigated to the following Key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
, here i created a new key with the name of my Applikation:
in this case : TestProjectExecutable.exe
after that, the (default) value was set to the full path of TestProjectExecutable.exe including the filename and extension. Then i created another String Value called "Path" and set the value to the folder where the dll was located:
Your problem lies not with linker or compiler, but with the way Windows searches for DLL's.
The OS will use the following algorithm to locate the required DLL's:
Look in:
The directories listed in the Application-specific Path registry key;
The directory where the executable module for the current process is located;
The current directory;
The Windows system directory;
The Windows directory;
The directories listed in the PATH environment variable;
Thus you have two reasonable options if you don't want to clutter the OS directories with your app-specific dll:
Create an app-specific Path registry entry (I would go with this option);
Put your DLL in the same folder as your EXE;
Modify the PATH variable (but why would you do that, if you can go with option 1?);
A solution I prefer that hasn't really been mentioned, is build your shared-libs into the same directory as your executables. This tends to be a much simpler solution.
One way to do this with cmake is
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
Or you can also set output directories based on build flavours.
See how do I make cmake output into a 'bin' dir?
I discovered (what I believe to be) quite a nice way of handling this. It follows the approach of adding the .dll to the same directory as the .exe. You can do it in CMake like so:
if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
TARGET <app-target> POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
$<TARGET_FILE_DIR:<lib-target>>
$<TARGET_FILE_DIR:<app-target>)
endif()
where app-target is the name of the application or library you're building (created through add_executable or add_library) and lib-target is the imported library brought in with find_package.
# full example
cmake_minimum_required(VERSION 3.14)
project(my-app-project VERSION 0.0.1 LANGUAGES CXX)
find_package(useful-library REQUIRED)
add_executable(my-application main.cpp)
target_link_libraries(my-application PUBLIC useful-library::useful-library)
if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
TARGET my-application POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
$<TARGET_FILE_DIR:useful-library::useful-library>
$<TARGET_FILE_DIR:my-application>)
endif()
I tried the option 1 from accepted answer (by pdeschain).
I even created a cmake hook to register paths of linked libraries automatically
function (xtarget_link_libraries target libs) # same as target_link_libraries but with additional improvements to allow windows find the library at runtime
LIST(REMOVE_AT ARGV 0)
SET(LIBS ${ARGV}) # this is to pass list into this function
target_link_libraries(${target} ${LIBS}) # call standard routine
if(WIN32)
set(TFILE ".")
get_property(slibs TARGET ${target} PROPERTY all_libs) # recall libs linked before
set(LIBS ${slibs};${LIBS})
set_property(TARGET ${target} PROPERTY all_libs ${LIBS}) # save all libs
FOREACH(lib ${LIBS}) # compose a list of paths
set(TFILE "${TFILE};$<TARGET_LINKER_FILE_DIR:${lib}>")
ENDFOREACH()
#add reg key
add_custom_command(TARGET ${target} POST_BUILD COMMAND reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${target}.exe" /v "Path" /d "${TFILE}" /f )
endif()
endfunction()
Can be used as xtarget_link_libraries(test lib1 lib2). The application will be able to find dynamic libraries at their absolute paths.
BUT, there is a big problem with this, that the App Paths mechanism https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx#appPaths
does not allow to have different entries for say 'Debug/test.exe' and 'Release/test.exe'. So to me this is a poor option.
You may add the following line to fill the Default key as path to the program as suggested in the post.
add_custom_command(TARGET ${target} POST_BUILD COMMAND reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${target}.exe" /ve /d "$<TARGET_FILE:${target}>" /f )
Now you can enjoy running test.exe from anywhere in the system... I guess my next try will be option
Create symbolic links to dlls with cmake.