CMake with custom install path and multiple libraries in different directories - cmake

I am new to cmake, I know a lot of similar questions are asked, but I don't seem to figure out how to do this.
I have very large project which can be simplified with this structure:
Parent_dir
--src
--main1.cpp
--main2.cpp.
--include
--lib_dir_1
--f1.cpp
--f1.h
--lib_dir_2
--f2.cpp
--f2.h
--build
--install
--include
--share
--lib
--bin
I do not have access to /usr/include, /usr/lib, /usr/bin, and /usr/share because it is an account on a cluster. My idea was to run cmake in build dir and install library .o and executives in install dir.
My CMakeFile.txt are:
Parent_dir/CMakeFile.txt
cmake_minimum_required(VERSION 3.13)
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
project(MyProject VERSION 2.0)
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install")
set(CMAKE_CXX_STANDARD 17)
add_subdirectory("include/")
add_subdirectory(src)
Parent_dir/include/CMakeFile.txt
add_subdirectory(lib_dir1)
add_subdirectory(lib_dir2)
Parent_dir/src/CMakeFile.txt
add_executable(M1 Main1.cpp)
target_include_directories(M1 PUBLIC "${CMAKE_SOURCE_DIR}/include/lib_dir1")
find_library(LIB_1 lib1)
target_link_libraries(M1 lib1)
install (TARGETS M1 DESTINATION ${CMAKE_INSTALL_PREFIX})
#add_executable(M2 Main2.cpp)
#target_include_directories(M2 PUBLIC "${CMAKE_SOURCE_DIR}/include/lib_dir2")
#find_library(LIB_2 lib2)
#target_link_libraries(M2 lib2)
#install (TARGETS M2 DESTINATION ${CMAKE_INSTALL_PREFIX})
Parent_dir/include/lib_dir1/CMakeFile.txt
add_library(lib1 SHARED f1.cpp f1.h)
FIND_PACKAGE(Boost COMPONENTS program_options REQUIRED)
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(lib1 ${Boost_LIBRARIES})
target_include_directories(lib1 PUBLIC ${CMAKE_CURRENT_LIST_DIR})
Parent_dir/include/lib_dir2/CMakeFile.txt
add_library(lib2 SHARED f2.cpp f2.h)
target_include_directories(lib2 PUBLIC ${CMAKE_CURRENT_LIST_DIR})
find_library(LIB1 lib1)
target_link_libraries(lib2 lib1)
At the moment I am only testing building M1 that depends on lib1, I commented stuff referring to M2 executable. I know that find_library(LIB1 lib1) should find lib1 library and put its path to LIB1 variable, so clearly I am making a mistake when targeting and linking because for example in Parent_dir/src/CMakeFile.txtit should be target_link_libraries(M1 ${LIB1}), but I am getting an error: `The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
LIB_1.
So find_library never found lib1, if I link as in files above, cmake is fine, make is fine, make install is fine, and in install directory I see M1 executable which does not work when used stating that error while loading shared libraries: liblib1.so: cannot open shared object file:...
So clearly I messed something up and I've been running in circles for two days. Biggest issue with online resources is that a lot of tutorials don't cover the issue how to navigate installation to custom dirs. Note that I have built other packages with typical configure --prefix approaches before and my desire to have install/include share lib bin directories is simply due to previous experience, in CMakeFiles above I only tried to navigate executable M1 to install dir, it worked, but I have no idea where lib1.so is built and whether I messed everything in CmakeFiles.
Also I am not sure if the code structure I am making is "good", perhaps I should move lib1_dir and lib2_dir to src? Problem is that in these libraries I have 100s of files so the structure above made most sense.
PS:
I do not have cmake-gui, cluster has 3.15 and 2.18 version and gui only exists for 2.18 version.
UPDATE:
Managed to get installation to work (check answer below), but some issues still remain:
Executables are now in install/bin, library files are in install/lib. I am just not sure if everything is linked properly, both lib1 and lib2 need to be shared libraries as I have about 6 executables that need them. The xml resource is needed by lib1, I don't know how to change the code to look for it other than the current dir. I could set its location to install/bin, but that is a bit dirty as if someone installs the package on other machine, the path to file would be different. I doubt that CMAKE environment variables work within C++?
I would also like to have all .h files from both libraries in install/include dir, how should I go about that?
Can someone explain whether this is correct. I am skeptical with line in src where I link libraries. I did not use find library, because it fails, if I hint it with install/lib location, than it says it cannot find .h files needed by the library. I am suspecting that by doing target_link_libraries(M1 lib1) I am directly recompiling the whole library to attach it to executable, rather than finding existing one and doing simple linking.

Related

How to build namespaced subprojects in cmake that find each other

I am trying to get a basic cmake project to work with 2 subprojects (each a library) where one depends on the other.
As such I have a self-contained project for lib1 and a self-contained project for lib2 (but that depends on lib1). Some lib2 authors could while others will not have access to lib1 source. If no access is desired, it seems that building and installing one after the other is the way to go. Is there a way to build them in one go using cmake? Currently, I am trying using an overall project, libs.
However, as the libraries are related I want to call them in a boost / poco like fashion. That is for lib2 I would like to write:
find_package(libs COMPONENTS lib1 REQUIRED)
target_link_libraries(lib2 PUBLIC libs::lib1)
I have created config files for both lib1 and lib2 and libs. However, I keep getting the same error
CMake Error at libs/lib2/CMakeLists.txt:67 (find_package):
By not providing "Findlibs.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "libs", but
CMake did not find one.
Could not find a package configuration file provided by "libs" with any of
the following names:
libsConfig.cmake
libs-config.cmake
Add the installation prefix of "libs" to CMAKE_PREFIX_PATH or set
"libs_DIR" to a directory containing one of the above files. If "libs"
provides a separate development package or SDK, be sure it has been
installed.
I don't understand where to put these files. It searches for them under prefix/ etc but at the time of building none of these libraries is yet installed. What is the proper place to put the -config.cmake files? I have put them all in
${CMAKE_BINARY_DIR}/libs
which seems similar to Poco, but the error remains. How can the libraries find each other?
// update:
From the comments I understand that lib1 first needs to be build in order to use find_package. Is there a way (in cmake) to
build lib1, install it, build lib2
or is this better to do using a bash / python / etc script?

CMake build and install shared library from subdirectory before building main directory

Here is my source code structure:
cd my_git_repo/
CMakeLists.txt
src/
main.cpp
mylibrary/
a.hpp
b.hpp
a.cpp
b.cpp
CMakeLists.txt
Root CMakeLists.txt:
cmake_minimum_required(VERSION 3.9)
project(myexe CXX)
add_subdirectory(src/mylibrary)
find_library(mylib NAMES mylibrary.so PATHS "./src/mylibrary/mylibrary.so")
add_executable(myexe src/main.cpp)
target_link_libraries(myexe ${mylib})
mylibrary/CMakeLists.txt is very simple. It builds a shared library and installs them.
Ideally, mylibrary target should be built and installed before myexe is built. But this doesn't happen. mylibrary is built followed by myexe. Installation happens later. Because of this, find_library fails. pkg_check_modules() works for other shared libraries but fails here because of the same reason.
I appreciate your help.
Edit:
This question differs from the duplicate because the answers posted to that question seem to be statically linking the library target_link_libraries(game engine). I want to dynamically link the .so library.
The idea in CMake is to build modules and then link them together.
You haven't shared the CMakeLists.txt for my library, so we cannot tell what it is doing. However, assuming that it is something like:
ADD_LIBRARY(mylibrary
file1.cpp
file2.cpp
)
Since you specified that you want mylibrary to always be linked as shared, you need to tell CMake that as well by either setting BUILD_SHARED_LIBS TO ON or by specifying SHARED in add_library:
ADD_LIBRARY(mylibrary SHARED
file1.cpp
file2.cpp
)
This is your library module. We will keep it simple for now and not worry about packing the library archive and installation here.
Now, back to your main CMakeLists.txt and how to make myexe consume it. Since you have already add_subdirectory(src/mylibrary), CMake knows about mylibrary. So simply link it using the module name. There is no need to find_library as you have already defined the module.
add_executable(myexe src/main.cpp)
target_link_libraries(myexe mylibrary)
This should suffice.
Do note, however, this is a very basic example to explain to you how CMake is designed to work. If you aren't building the library, and it is already installed, you would call find_library. Modern CMake is a bit more sophisticated and uses generator expressions, so be sure to read up on that as you progress to more complex projects.

forcing cmake install through add_dependencies?

I'm sorry if this is duplicate. I see a lot of post that are similar, but I am either not "getting" it or it is not exactly the same issue.
I need to build two shared libraries. They are in the same directory structure, and they both get but in to bin. One is dependent on the other.
I've been reading up on add_dependencies, but that seems to deal with project builds outside of cmake. These are both inside
Directory structure
src
CMakeLists.txt
bin
xml
CMakeLists.txt
XML.c
utils.c
XMLSPCL.c
src/CMakelists.txt
set (BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bin")
add_subdirectory(xml/shrxml)
xml/CMakeLists.txt (AFTER the suggestion below)
set(CMAKE_BUILD_TYPE Release)
project(shrxml)
add_library(shrxml SHARED XML.c utils.c)
install(TARGETS shrxml LIBRARY DESTINATION ${BIN_DIR})
project(shrxmlSPCL)
add_dependencies(shrxmlSPCL shrxml)
add_library(shrxmlSPCL SHARED XMLSPCL.c)
target_link_libraries(shrxmlSPCL LINK_PUBLIC shrxml)
install(TARGETS shrxmlSPCL LIBRARY DESTINATION ${BIN_DIR})
I can get it to work, but I know there has to be a correct way to do this.
To get it to work, I:
comment out the xmlSPCL project
cmake ..
make shrxml
run make install so I can get shrxml.so in to the bin directory
un-comment out xmlSPCL
make (again)
make install (again)
What I want is to run make once, have the shrxml.so go into the bin dir so that when shrxmlSPCL is linked, it sees the dependent lib. Can that be done with the extra steps?
to make an example project run I had to make 3 things different
1.
target_link_libraries(shrxmlSPCL LINK_PUBLIC shrxml)
-> So neither lib nor .so as prefix/suffix
2.
add_dependencies(shrxmlSPCL shrxml)
-> somewhere below the add_library() having the dependency
3.
install(TARGETS shrxmlSPCL LIBRARY DESTINATION ${BIN_DIR})
-> add LIBRARY into your install command doc

CMake package dependencies handling

I am facing the current problem while using CMake:
I have libA, which I build using CMake and install it (in a custom directory, ~/install, using CMAKE_PREFIX_PATH) together with its exported targets and config (generated by CMake). The installed files look more or less like this:
~/install/lib/libA.so
~/install/lib/cmake/AConfig.cmake
~/install/lib/cmake/ATargets.cmake
I have libB, which links (privately) against libA:
find_package(A CONFIG REQUIRED)
add_library(B b.c)
target_link_libraries(B PRIVATE A)
I also install it together with its exported targets and config (generated by CMake).
Finally, I have an application which uses libB:
find_package(B CONFIG REQUIRED)
add_executable(app app.c)
target_link_libraries(app B)
The problem is that when the linker is executed, it will link against libB using the full path (~/install/lib/libB.so), but libA is only linked with -lA. As the libraries are not installed in any "standard" folder, it is not found (unless I manually add the folder using link_directories).
Am I doing anything wrong? What is the best solution to handle this case?
Thanks,

CMAKE RPATH not working - could not find shared object file

I am trying to get rid of setting LD_LIBRARY_PATH everytime time I run my program. After adding in the library and targeting my executable to the library, when I run it tells me it can not open shared object library, no such file or directory.
In my CMakeLists.txt I have:
add_library(heart SHARED ${HEART_FILES})
add_executable(run ${RUN_FILES})
target_link_libraries(run heart)
set(CMAKE_SKIP_BUILD_PATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "~/person/target/usr/local/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
I set an absolute link to my library folder to test out whether this would create an rpath to my library and it seems like there isn't. I have checked and made sure that the shared library is indeed in lib. libheart.so is the file that is being linked. What else am I missing?
It is because you build heart and run from the same cmake project:
CMAKE_INSTALL_RPATH_USE_LINK_PATH is an interesting and very useful option. When building a target with RPATH, CMake determines the RPATH by using the directories of all libraries to which this target links. Some of these libraries may be located in the same build tree, e.g. libbar.so, these directories are also added to the RPATH.
If this option is enabled, all these directories except those which are also in the build tree will be added to the install RPATH automatically. The only directories which may then still be missing from the RPATH are the directories where the libraries from the same project (i.e. libbar.so) are installed to. If the install directory for the libraries is not one of the systems default library directories, you have to add this directory yourself to the install RPATH by setting CMAKE_INSTALL_RPATH accordingly
You can try this:
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
More documentation here cmake rpath handling
EDIT:
Only this should work:
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
add_library(heart SHARED ${HEART_FILES})
add_executable(run ${RUN_FILES})
target_link_libraries(run heart)
install(
TARGETS heart run
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
)
Clean your build directory and then:
cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=/home/person/target/usr/local ..
make install
At the end of the g++ line Linking CXX executable run you should see like -Wl,-rpath,/home/person/target/usr/local/lib
If you want a fully relocatable package:
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
PS: are you sur that it is libheart.so that is not found ?
In your CMake file, set the RPATH before defining the targets. The CMAKE_INSTALL_RPATH must be defined before calling add_executable(), otherwise it has no effect.
I had a similar issue as the original post. I created executables which linked to external shared libraries. This approach compiled and executed fine from the build directory. However, the executable that was installed to a separate directory could not find a shared library at runtime:
error while loading shared libraries: libxxxx.so.1: cannot open shared object file: No such file or directory
To solve, I
1) upgraded to CMake 3.17
2) used Craig Scott's recommended:
set(CMAKE_INSTALL_RPATH $ORIGIN)
as explained in his talk
3) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) as directly mentioned to solve this error in the second common question in Kitware's documention
4) Put all this before adding the targets as mentioned in this post
5) Used the "$ORIGIN/../lib" syntax instead of Craig's Scott's mentioned $ORIGIN as mentioned by #explo91
In summary, and to my suprise, only the "$ORIGIN/../lib" before the target definition was necessary from above (I tested the other combinations which did not fix the cannot open shared object file runtime issue).
Anyway the solution I finally applied, which may be of better, fine-grained CMake style or at least may be helpful to others on their RPATH journey is:
set_target_properties(target_defined_above PROPERTIES INSTALL_RPATH "$ORIGIN/../lib")