How to make cmake find a shared library in a subfolder - c++

I'm trying to learn how to make a shared library. And the following seems to work (please comment if you have some feedback to this method, I basically have no idea what I'm doing).
In my library project, I've put the header files into a folder named "include", and the source files into "src".
My library's CMakeLists.txt:
cmake_minimum_required(VERSION 2.4.0)
project(mycustomlib)
# Find source files
file(GLOB SOURCES src/*.cpp)
# Include header files
include_directories(include)
# Create shared library
add_library(${PROJECT_NAME} SHARED ${SOURCES})
# Install library
install(TARGETS ${PROJECT_NAME} DESTINATION lib)
# Install library headers
file(GLOB HEADERS include/*.h)
install(FILES ${HEADERS} DESTINATION include)
My application's CMakeLists.txt:
cmake_minimum_required(VERSION 2.4.0)
project(myprogram)
# Find source files
file(GLOB SOURCES src/*.cpp)
# Create executable
add_executable(${PROJECT_NAME} ${SOURCES})
# Find and link library
find_library(MYCUSTOMLIB mycustomlib)
target_link_libraries(${PROJECT_NAME} ${MYCUSTOMLIB})
And this is working. The problem is that I want to put both the headers and the library into subfolders (specifically: /usr/local/include/mycustomlib/ for the headers, and /usr/local/lib/mycustomlib/ for the library).
So this is my attempt:
My library's new CMakeLists.txt:
cmake_minimum_required(VERSION 2.4.0)
project(mycustomlib)
# Find source files
file(GLOB SOURCES src/*.cpp)
# Include header files
include_directories(include)
# Create shared library
add_library(${PROJECT_NAME} SHARED ${SOURCES})
# Install library
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})
# Install library headers
file(GLOB HEADERS include/*.h)
install(FILES ${HEADERS} DESTINATION include/${PROJECT_NAME})
My application's new CMakeLists.txt:
cmake_minimum_required(VERSION 2.4.0)
project(myprogram)
# Find source files
file(GLOB SOURCES src/*.cpp)
# Create executable
add_executable(${PROJECT_NAME} ${SOURCES})
# Find and link library
find_library(MYCUSTOMLIB mycustomlib/mycustomlib)
target_link_libraries(${PROJECT_NAME} ${MYCUSTOMLIB})
And this is not working. Now, I'm forced to specify the .so file of the library like this:
find_library(MYCUSTOMLIB mycustomlib/libmycustomlib.so)
How come?

I'll deal with your actual problem first and offer additional comments after that. Technically speaking, you are asking CMake to find a library named mycustomlib/mycustomlib, but what you really want to say is you want find mycustomlib and it can be found in a subdirectory called mycustomlib. A couple of alternative ways to call find_library() to achieve this for your second case would be:
find_library(MYCUSTOMLIB mycustomlib PATH_SUFFIXES mycustomlib)
find_library(MYCUSTOMLIB mycustomlib PATHS /usr/local/lib/mycustomlib)
The latter is making more assumptions than it should about where you have the library installed, so I'd favour the first option. The first option assumes CMake would already find libraries in /usr/local/lib, which it seems it is from your question. You can influence where CMake looks for libraries by modifying CMAKE_PREFIX_PATH and CMAKE_LIBRARY_PATH. I'd expect either of the above options to make your second case work.
Now to other observations. You've requested a very old minimum CMake version in the first line of each of your CMakeLists.txt files. You probably want to consider at the very least making this 2.8 (personally, I'd suggest more like 3.2 or later, but it depends on what your project needs to support).
You have used file globbing to obtain your list of sources and headers. This is not robust and should generally be avoided (see a discussion of this here). You will see plenty of example code use method this for simplicity, but it is not recommended for real world projects (the CMake documentation even says not to use it). Explicitly list out your source and header files individually if you want robust builds.
If you are happy to require CMake 2.8.11 or later (and you should be these days), rather than calling include_directories() which makes everything pick up the header search path you specified, you should prefer to attach the search path requirement to the target that needs it. You do this with target_include_directories(). The equivalent of your code above would be:
target_include_directories(${PROJECT_NAME} PUBLIC include)
This gives much better control of your inter-target dependencies as your project grows in size and complexity. For a more in-depth discussion of this topic, see this article and perhaps also this one (disclosure: I wrote both articles).
Are your library and program totally separate source code repositories? Can they be built in the same project? You can build multiple targets in one CMakeLists.txt file. The project name doesn't have to have any relationship to the names of any of the targets (you often see the PROJECT_NAME variable re-used for the target name in simple examples, which is unfortunate since it suggests a relationship between the two, but for all but simple projects this won't be the case). If they are in the same repository, building them together would be a much simpler build since you wouldn't have to install the library for the executable to find it and link to it.
If they must be built in separate projects, then something like the following for the application's project should get you close:
cmake_minimum_required(VERSION 2.8.11)
project(myprogram)
# List your program's sources here explicitly
add_executable(myprogram src/foo.cpp src/bar.cpp)
# Find and link library
find_library(MYCUSTOMLIB mycustomlib PATH_SUFFIXES mycustomlib)
target_link_libraries(myprogram PUBLIC ${MYCUSTOMLIB})
# Find library's headers and add it as a search path.
# Provide the name of one header file you know should
# be present in mycustomlib's include dir.
find_path(MCL_HEADER_PATH mycustomlib.h PATH_SUFFIXES mycustomlib)
target_include_directories(myprogram PUBLIC ${MCL_HEADER_PATH})
For extra points, you could try to confirm that the header path is in the same area as the library by checking the common path prefix, or you could just derive
the MCL_HEADER_PATH from the MYCUSTOMLIB path by assuming a directory structure. Both approaches have advantages and drawbacks. If you want to explore the latter, the get_filename_component() command will be your friend.
Hopefully that points you in the right direction.

Related

CMake add library with subdirectories

TL;DR
Using CMake, how can I include subdirectories into a library such that they can be included without referencing the directories they reside?
End TL;DR
In attempt to be brief and speak in higher level ideas of what and how, I have removed everything that I consider to be unnecessary details. I will make edits if need be. As such, this is a brief synopsis of my project structure.
ParentDir
--src
----source.cpp
----source.h
----entities_dir
------entity.cpp
------entity.h
------CMakeLists.txt
----CMakeLists.txt
--CMakeLists.txt
--main.cpp
as it currently stands, I have a library defined by the CMakeLists in the src directory. As such, I can include src files in main by #include as apposed to #include "src/file.h" I would like to be able to do the same for my headers that exist within the subdirectories of src.
CMakeLists.txt
cmake_minimum_required(VERSION 3.6)
project(Project)
add_executable(Project ${SOURCE_FILES} main.cpp)
include_directories(src)
add_subdirectory(src)
target_link_libraries(Project Library) # Engine Libraries
src/CMakeLists.txt
file(GLOB SOURCE_FILES *.cpp)
file(GLOB HEADER_FILES *.h)
add_library(Library STATIC ${SOURCE_FILES} ${HEADER_FILES})
main.cpp
#include <source.h> // this works
#include <entity.h> // this does not work but I want it to
#include <entities/entity.h> // this works but I don't want this
int main() {}
I am not sure how to do this exactly. I have tried to GLOB_RECURSE, add_subdirectory(entities), etc. I have also tried creating a library called Entities inside the src/entities/CMakeLists.txt and linking that with link_libraries. None of these have been successful. What is the proper way to accomplish this, because I think I am probably approaching this completely wrong.
You need that path in your compilers header search path, which is achieved with include_directories() call. You can amend your existing include_directories(src) call to be:
include_directories(src src/entities)
Also, this SO post is related and worth reading: Recursive CMake search for header and source files. There is an excerpt there from the CMake website itself recommending against the usage of file(GLOB ...), which lends to recommending against recursive solutions in general. As a heavy user of CMake, I agree with the arguments made against it.
You just need to add:
include_directories(${CMAKE_CURRENT_LIST_DIR})
In each CMakeLists.txt in your your hierarchy.
CMake in itself doesn't compile your project, it only calls your toolchain and passes parameters to it, and your toolchain doesn't 'know' that it is being called by CMake, or the structure of your project. As such, you need to tell the toolchain where to locate include files.
While it is commonplace to have a CMakeLists.txt in every subdirectory, this is by no means a requirement. The CMakeLists.txt in your src directory could contain all instructions necessary to generate a build. Generally, CMakeLists.txt are put at each level to make the structure easier to manage, eg. each directory only needs to know what it needs to do (presumably, with the files in that directory).

SWIG and CMake: make use of information provided by `target_include_directories()`

With CMake 2.8+ you can avoid setting include directories redundantly by using target_include_directories().
E.g. by writing
add_libary(mylib SHARED ${SOURCES})
target_include_directories(mylib PUBLIC ./include)
.. you just have to link against mylib to add the needed include folder to your target.
But how can I make use of this information when I have to use CMake modules which don't make use of this capability yet? (in my case SWIG)
When I configure a SWIG project I currently have to hard code a lot of information:
set(SWIG_MODULE_${PYTHON_MODULE_NAME}_EXTRA_DEPS
"../long/relative/path/1/include/some/header1.h"
"../long/relative/path/1/include/some/header2.h"
"../long/relative/path/2/include/some/header1.h"
"../long/relative/path/2/include/some/header2.h")
I also have to use the old fashioned include_directories() to make the swig generator know what it needs to know:
include_directories(
"../long/relative/path/1/include
"../long/relative/path/2/include)
Otherwise the %include statements inside .i files won't work any more.
Of course I could set variables containing the paths but then I would provide the information I wanted to get rid of..
Is there a way to either extract the directory information from a target or (better of course) make the SWIG CMake module correctly use it?
My current solution:
With some (very beautiful) CMake magic you can automate listing all header files from the interface part of a library and set the include directories:
function(swig_add_library_dependencies swig_module library_names)
foreach(library_name ${library_names})
get_property(LIBRARY_INCLUDES
TARGET ${library_name}
PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
foreach(INCLUDE_PATH ${LIBRARY_INCLUDES})
include_directories(${INCLUDE_PATH})
file(GLOB_RECURSE header_files "${INCLUDE_PATH}/*.h")
list(APPEND SWIG_MODULE_${swig_module}_EXTRA_DEPS ${header_files})
# export variable to parent scope
set(SWIG_MODULE_${swig_module}_EXTRA_DEPS
${SWIG_MODULE_${swig_module}_EXTRA_DEPS} PARENT_SCOPE)
endforeach()
endforeach()
endfunction()
to be used like this:
swig_add_library_dependencies(<swig_module_name> "library1;library2")
or discretely like this:
swig_add_library_dependencies(<swig_module_name> library1)
swig_add_library_dependencies(<swig_module_name> library2)
Disadvantages:
uses GLOB_RECURSE
only works if target_include_directories had been used correctly
creates dependencies to all header files found in include directories
Have a look at the documentation for get_property:
https://cmake.org/cmake/help/v3.0/command/get_property.html?highlight=get_property
you would do something like this:
get_property(MY_INCLUDES TARGET my_target PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
to get the interface include directories from the target my_target and store them in the variable MY_INCLUDES

Cmake cannot find library using "link_directories"

I Ubuntu, I am learning about cmake and make, and just trying a simple example. I have two directories: src and build. In src, I have two files: main.cpp, and CMakeLists.txt, which has (only) the following text:
add_executable(test main.cpp)
link_directories(/usr/lib/x86_64-linux-gnu)
target_link_libraries(test protobuf)
In /usr/lib/x86_64-linux-gnu, there is a shared library called libprotobuf.so, which I want to link against. My main.cpp uses functions in this library, by including the releveant header file, #include <google/protobuf/message.h>.
Now, in my build directory, I run cmake ../src, and then make. However, I then get linker errors telling me that there are undefined references to some of the functions in the protobuf library. If I do a search through all the files and subdirectories in build, there is not mention of anything related to protobuf.
However, if I remove the link_directories line in my CMakeLists.txt file, and instead write the full path to the library when specifying the executable, i.e. target_link_libraries(test /usr/lib/x86_64-linux-gnu/libprotobuf.so), it compiles and links fine.
Why is link_directories not allowing cmake to find this library?
Do not use link_directories like this in CMake.
This is a common beginner's mistake, as many other build environments work like this, but in CMake it's just asking for trouble. Even the official documentation specifically advises against it:
Note that this command [link_directories] is rarely necessary. Library locations returned
by find_package() and find_library() are absolute paths. Pass these
absolute library file paths directly to the target_link_libraries()
command. CMake will ensure the linker finds them.
So instead, always pass absolute paths to target_link_libraries and use find_library to resolve the link directory:
find_library(PROTOBUF_LIBRARY protobuf HINTS /usr/lib/x86_64-linux-gnu)
target_link_libraries(test PUBLIC ${PROTOBUF_LIBRARY})
This has the huge benefit that you will probably get a diagnostic at CMake configure time if the expected library cannot be found, instead of a random linker error at compile time. Also, this allows the user to specify a library location via the GUI if the target machine has a non-standard directory layout.
So if it doesn't work right away, be sure to check the result of the find_library call and consult the official documentation to track down why it doesn't find your library as intended.
Make sure that your call to link_directories takes place before your call to the relevant add_executable.
I had mistakenly believed it only needed to be before the call to target_link_libraries, but that's not the case. After moving the call, the library is linked properly.
Make sure that the order will be link_directories, set PROJECT_LINK_LIBS, add_executable and then target_link_libraries.
Below is example to demonstarte it:
cmake_minimum_required(VERSION 2.8.9)
project (Logging)
include_directories(include)
file(GLOB LOGGINGSOURCES "libsrc/*.cpp")
file(GLOB SOURCES "src/*.cpp")
add_library(convertString SHARED ${LOGGINGSOURCES})
install(TARGETS convertString DESTINATION /root/Deepak/)
link_directories( /root/Deepak/ )
set(PROJECT_LINK_LIBS libconvertString.so)
add_executable(hello ${SOURCES})
target_link_libraries(hello ${PROJECT_LINK_LIBS} )
Perhaps it's very old topic but none of proposed solutions worked for me. So I had to make my own dirty hack. I do crosscompiling with buildroot and include toolchainfile.cmake.
#...
set(LIB_PATH ${PROJECT_SOURCE_DIR}/relative/path/to/your/lib)
#...
include_directories(/path/to/library/include)
set(LIB_MYLIB ${LIB_PATH}/libmylib.so)
#...
add_executable(${PROJECT_NAME} ${APP_SOURCES})
target_link_libraries(${PROJECT_NAME}
${LIB_MYLIB}
)
Hope this will help

splitting a project into a library and a application

I'm using cmake for my project. No I want to split some parts into a library and use this for 2 different applications.
Now I don't how how to do this subprojects in cmake. My first attempt was to use the add_subdirectory command:
cmake_minimum_required(VERSION 2.8)
add_subdirectory(MSI)
message("Building MsiQtWizard with: ${MSI_INCLUDE_DIR}")
add_subdirectory(MsiQtWizard)
So MSI would be my library. Inside the MSI folder is another cmakelists which is basically a standalone list for building the library. I thought I could make the MsiQtWizard project also a standalone cmakelists, so I could theoretically build MSI and use the library to build MsiQtWizard (and other projects).
The cmakelists in the root directory would just be a helper to build the library and the GUI in one single step.
The problem is, for building MsiQtWizard, I need the include path to msi and the static library binaries. I tried to do something like that at the end of MIS/CMakelists.txt:
### Set variables, other scripts may use ###
SET(MSI_INCLUDE_DIR include)
MESSAGE("Include directory is: ${MSI_INCLUDE_DIR}")
and in the MsiQtWizard/CMakelists:
##### external libraries #####
#MSI
find_path(MSI_INCLUDE_DIR REQUIRED msi/Image.hpp
PATH_SUFFIXES MSI/include include)
My intend is, that MsiQtWizard will search for msi, if the varaible was not previously set (so that you could use this cmakelists as a standalone). When building MSI, I want to save the include path (and later binary locations) and pass it to MsiQtWizard - but the value is gone as soon as I'm back in my root cmakelists.
So that is, what I tried. My Question is now: How would I correctly split my project into a library and a (later multiple) application and can I do it in a way that I can also build them independently?
Or, more specific: How can I pass values from a node CMakelist to the root CMakeList (like I tried with MSI_INCLUDE_DIR)?
If your building a library - its best to completely separate it from the application build. Otherwise you are coupling your library with your application with cmake, which in my view defeats the purpose of building a library.
When building your library you will want something like
project (MSILibrary)
ADD_LIBRARY(MSILibrary src/MSI1.cpp src/MSI2.cpp)
install (TARGETS MSILibrary DESTINATION lib)
where src contains your library code. You can then make then sudo make install your library to your standard library location (e.g. /usr/lib).
You can then use your library in any subsequent project. Put these in a new directory and create a new CMakeLists.txt for them.
You will want something like,
#include find modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
project (MSI-project-1)
find_package(MSILibrary REQUIRED)
IF(MSILibrary_FOUND)
include_directories(${MSILibrary_INCLUDE_DIRS}
ENDIF(MSILibrary_FOUND )
target_link_libraries (MSI-project-1 ${MSILibrary_LIBRARIES})
install (TARGETS MSI-project-1 DESTINATION bin)
Now all you need to do is help cmake find you library.
You can include a module for this. In the file ./cmake/Modules/FindMSILibrary.cmake type something like:
# - Try to find MSILibrary library
# Once done, this will define
#
# MSILibrary_FOUND - system has MSILibrary
# MSILibrary_INCLUDE_DIRS - the MSILibrary include directories
# MSILibrary_LIBRARIES - link these to use MSILibrary
## Google this script (I think its fairly standard, but was not bundled with my CMAKE) - it helps find the macros.
include(LibFindMacros)
# Dependencies
libfind_package(MSILibrary)
# Use pkg-config to get hints about paths
libfind_pkg_check_modules(MSILibrary_PKGCONF MSILibrary)
# Include dir
find_path(MSILibrary_INCLUDE_DIR
NAMES MSI.hpp
PATHS ${MSI_Library_PKGCONF_INCLUDE_DIRS}
)
# Finally the library itself
find_library(MSILibrary_LIBRARY
NAMES MSILibrary
PATHS ${MSILibrary_PKGCONF_LIBRARY_DIRS}
)
# Set the include dir variables and the libraries and let libfind_process do the rest.
# NOTE: Singular variables for this library, plural for libraries this this lib depends on.
set(MSILibrary_PROCESS_INCLUDES MSILibrary_INCLUDE_DIR MSILibrary_INCLUDE_DIRS)
set(MSILibrary_PROCESS_LIBS MSILibrary_LIBRARY MSILibrary_LIBRARIES)
libfind_process(MSILibrary)
That should be it.
EDIT:
If you really want to package your applications with your library (perhaps some example applications), then you can do something like so:
In your root CMakeLists.txt
cmake_minimum_required (VERSION 2.6)
project (MSIProject)
# The version number.
set (MSIProject_VERSION_MAJOR 0)
set (MSIProject_VERSION_MINOR 1)
set (MSIProject_PATCH_LEVEL 3 )
# project options
OPTION( BUILD_SHARED_LIBS "Set to OFF to build static libraries" ON )
OPTION( BUILD_EXAMPLES "Set to OFF to skip building the examples" ON )
# Put the libaries and binaries that get built into directories at the
# top of the build tree rather than in hard-to-find leaf
# directories.
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
##########################################################################
# Build the library
##########################################################################
add_subdirectory(MSI-src)
##################
# Build your example Apps if requested
############
IF( BUILD_EXAMPLES )
add_subdirectory(example/MSI-project-1)
add_subdirectory(example/MSI-project-2)
ENDIF( BUILD_EXAMPLES )
Your library MSI-src/CMakeFiles.txt will be as before, and your example/MSI-project-1/CMakeLists.txt will be something like
## Make the InferData example project
project (MSI-project-1)
#include MSI library
include_directories ("${MSILibrary_SOURCE_DIR}/include")
#include the includes of this project
include_directories ("${MSI-project-1_SOURCE_DIR}/../include")
#build
add_executable(MSI-project-1 src/P1.cpp)
target_link_libraries (MSI-project-1 MSILibrary) #link
install (TARGETS MSI-project-1 DESTINATION bin)

CMake header recognition:

I'm currently using CMake to build a project, and I have the following problem:
I have a library, say 'C', that the files for an executable 'L' need to use (the files in L call on headers from the library in C)
Both the library and the executable have to be built in the same project, and though they both go through CMake fine, the files in L can't seem to see the headers provided by the library C. I'm having to specify relative paths to the exact destination in the header files, which isn't nice at all since some file hierarchy might change at some point of time.
I'm not sure what type of a command to use to make the L files be directly be able to see the C headers, so that I can say something like
#include "display.h"
directly in L. I don't want to have to copy headers all over the place since I have many files like L.
My Cmake files are as shown:
For the library C (which is closer to the top of the folder heirarchy):
FIND_PACKAGE(VTK REQUIRED)
IF(NOT VTK_USE_RENDERING)
MESSAGE(FATAL_ERROR "Example ${PROJECT_NAME} requires VTK_USE_RENDERING.")
ENDIF(NOT VTK_USE_RENDERING)
INCLUDE(${VTK_USE_FILE})
#INCLUDE_DIRECTORIES(${CRANIOLIB_SOURCE_DIR}/include)
SET(cranioDir ${CMAKE_CURRENT_SOURCE_DIR})
SET(SOURCES
twoD.cxx
display.cxx
rotate.cxx
symmetry.cxx
normalize.cxx
real_sym_eigens.cxx
debugLib.cxx
readInputLib.cxx)
SET(cranioLib_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/include)
ADD_LIBRARY(cranioLib ${SOURCES})
and for the executable L:
FIND_PACKAGE(VTK REQUIRED)
IF(NOT VTK_USE_RENDERING)
MESSAGE(FATAL_ERROR "Example ${PROJECT_NAME} requires VTK_USE_RENDERING.")
ENDIF(NOT VTK_USE_RENDERING)
INCLUDE(${VTK_USE_FILE})
INCLUDE_DIRECTORIES(${cranioDir})
ADD_EXECUTABLE(RotateSS RotateSideToSide.cxx)
TARGET_LINK_LIBRARIES(RotateSS vtkRendering cranioLib vtkHybrid vtkGraphics)
ADD_EXECUTABLE(RotateST RotateSideTwist.cxx)
TARGET_LINK_LIBRARIES(RotateST vtkRendering cranioLib vtkHybrid vtkGraphics)
ADD_EXECUTABLE(RotateUD RotateUpDown.cxx)
TARGET_LINK_LIBRARIES(RotateUD vtkRendering cranioLib vtkHybrid vtkGraphics)
Note that these files don't completely do the job - I need some help in nailing the 'include' features of CMake, wasn't able to get anything else online that would do the trick for me.
Best.
Both the library and the executable have to be built in the same project, and though they both go through CMake fine, the files in L can't seem to see the headers provided by the library C. I'm having to specify relative paths to the exact destination in the header files, which isn't nice at all since some file hierarchy might change at some point of time.
In my own projects, one line has always been sufficient:
include_directories(include)
(Where include is relative to the directory the CMakeLists.txt file resides in.) And all of my source files in src can find their headers in include. Specifying the full current source path has never been necessary.
Edit: For example, let's say you've got a project with this layout:
proj
/src
/include
/somelibrary/include
And in proj/, you have a CMakeLists.txt file that references your source files like so:
SET(SOURCES src/file1.cpp src/file2.cpp)
This is the only line you need to use both include and somelibrary/include:
include_directories(include somelibrary/include)
Or, if CMakeLists.txt is in src, like this:
include_directories(../include ../somelibrary/include)