How to find a library with cmake? - linker

To link an executable with a library that resides in a standard location, one can do the following in a CmakeLists.txt file:
create_executable(generate_mesh generate_mesh.cpp)
target_link_libraries(generate_mesh OpenMeshCore)
This would work if the library, that is being linked against, was placed in
/usr/local/lib/libOpenMeshCore.dylib
However, in this case the library resides under
/usr/local/lib/OpenMesh/libOpenMeshCore.dylib
How can I specify that target_link_libraries should really link against a library placed in a sibdirectory? I wonder there is some useful option to target_link_libraries that would specify that the library is in a subdirectory in a standandard location, e.g.
target_link_libraries(generate_mesh OpenMesh/OpenMeshCore)
If that is not possible, is there a way to use find_library to search /usr/local/lib recursively, including its sub-directories, for the given library file?

You can add different directories to find_library. To use this library call cmake by cmake -DFOO_PREFIX=/some/path ....
find_library( CPPUNIT_LIBRARY_DEBUG NAMES cppunit cppunit_dll cppunitd cppunitd_dll
PATHS ${FOO_PREFIX}/lib
/usr/lib
/usr/lib64
/usr/local/lib
/usr/local/lib64
PATH_SUFFIXES debug )
find_library( CPPUNIT_LIBRARY_RELEASE NAMES cppunit cppunit_dll
PATHS ${FOO_PREFIX}/lib
/usr/lib
/usr/lib64
/usr/local/lib
/usr/local/lib64
PATH_SUFFIXES release )
if(CPPUNIT_LIBRARY_DEBUG AND NOT CPPUNIT_LIBRARY_RELEASE)
set(CPPUNIT_LIBRARY_RELEASE ${CPPUNIT_LIBRARY_DEBUG})
endif(CPPUNIT_LIBRARY_DEBUG AND NOT CPPUNIT_LIBRARY_RELEASE)
set( CPPUNIT_LIBRARY debug ${CPPUNIT_LIBRARY_DEBUG}
optimized ${CPPUNIT_LIBRARY_RELEASE} )
# ...
target_link_libraries(foo ${CPPUNIT_LIBRARY})

Related

cmake: compile a shared library and executable

I would like to build:
A shared library
An executable using the compiled shared library in point 1.
For the library, I have the following CMakeLists.txt file:
cmake_minimum_required(VERSION 3.13.4)
project(driver C)
set(CMAKE_C_STANDARD 99)
set(LIB_PATH "../../libdriver")
include_directories(${LIB_PATH})
include_directories(driver ${LIB_PATH}/uart)
add_library(driver SHARED
${LIB_PATH}/libdriver.c
${LIB_PATH}/uart/uart.c)
I create the LIB_PATH variable, because the files needed for the lib compilation are outside of the tree.
Now I have my executable CMakeLists.txt and I would like to use the compiled previously dll file.
cmake_minimum_required(VERSION 3.13.4)
project(proj_exe C)
set(CMAKE_C_STANDARD 99)
set(LIB_PATH "../../libdriver") # so I have the headers
set(MAIN_PATH "../../project") # the main.c file located here
include_directories(${LIB_PATH})
include_directories(driver ${LIB_PATH}/uart)
# what to do now to compile the executable with the dll
You'll probably want to install the driver libs locally and use cmake's functionality to generate a import script. In the project using the lib, you can use find_package to add the dll as imported target allowing you to link it using target_link_library:
add_library(driver SHARED
${LIB_PATH}/libdriver.c
${LIB_PATH}/uart/uart.c)
# attach the info to the target here
target_include_directories(driver PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${LIB_PATH}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${LIB_PATH}/uart>
$<INSTALL_INTERFACE:include>
$<INSTALL_INTERFACE:include/uart>
)
install(TARGETS driver EXPORT driver
RUNTIME DESTINATION bin
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
)
# usually there's a lib just containing the public headers
# that can be copied, but yours seems to contain the source files too;
# not enough info though to separate headers from sources though, so we'll just copy everything
install(DIRECTORY ${LIB_PATH}/ DESTINATION include)
install(EXPORT base DESTINATION cmake FILE base-config.cmake)
After building you should be able to do
cmake --install build_dir --prefix install_path
This allows you use find_package to import the files from the install directory, assuming you've added it to the CMAKE_PREFIX_PATH
cmake -D CMAKE_PREFIX_PATH=install_path -S ... -B ...
find_package(driver REQUIRED CONFIG)
add_executable(foo ...)
target_link_libraries(foo PRIVATE driver)
You may need to adjust the paths in the description a bit to point use the correct locations relative to the current working directory...

CMAKE generate a static library that depends on shared libraries

First of all, I am learning and I am very new to CMake, so please, have mercy. I have done some research about this but I have not found anything that actually works for me. I have made many changes to the CMakeLists.txt but I will try to show you summarize what I am trying to do and how I am trying to achieve that.
I have a project folder tree like this:
Mainfolder/:
CMakeLists.txt (CMakeLists1.txt to identify it better)
lib1:
source files (.h .c)
CMakeLists.txt (CMakeLists2.txt)
lib2:
source files (.h .c)
CMakeLists.txt (CMakeLists3.txt)
executable:
source files (.h .c)
CMakeLists.txt (CMakeLists4.txt)
Once this has been said, lib1 and lib2 are meant to be built as static libraries and depends on "rt" and "pthread" shared libs for example. Besides, lib2 depends on lib1.
I am crosscompiling, so I load the environment using and script and all the variables seem to be recognized properly by CMake (I have printed them and I verified the crosscompilation by compiling a Hello world example that ran properly on the target).
The CMakeLists1.txt serves as an entry point:
project(
MainApp
VERSION 0.1
DESCRIPTION "Main Application CMake compilation project"
LANGUAGES C)
add_subdirectory(lib1)
add_subdirectory(lib2)
add_subdirectory(executable)
How do I compile lib1 or lib2? Using the following CMakeLists2.txt:
cmake_minimum_required(VERSION 3.14...3.16)
project(
lib1
VERSION 0.1
DESCRIPTION "lib1 library"
LANGUAGES C)
find_library(RT_LIBRARY rt)
find_library(PTHREAD_LIBRARY pthread)
list(APPEND SRCS
source1.c
source2.c)
find_library(RT_LIBRARY rt)
find_library(PTHREAD_LIBRARY pthread)
add_library(lib1 ${SRCS})
# Include libraries required
target_link_libraries(lib1 ${PTHREAD_LIBRARY} ${RT_LIBRARY})
CMakelists3.txt is the same as CMakelists2.txt but including the lib1 dependencies:
find_library(LIB1_LIBRARY lib1)
add_library(lib2 ${SRCS})
target_link_libraries(lib2 ${PTHREAD_LIBRARY} ${RT_LIBRARY} ${LIB1_LIBRARY})
target_include_directories (lib2 PUBLIC {CMAKE_CURRENT_SOURCE_DIR}/../lib1)
I have to say I also tried to include the lib1 dependency using ${CMAKE_CURRENT_SOURCE_DIR}/../lib1/liblib1.a but it did not work either.
These seem to compile well and I see many "U" undefined symbols that I guess will be obtained from the shared library (sorry, I am doubting here and I am not sure if it should be considered as a fail already). In any case, I am not sure if the lib1 is linked to the lib2, and the same occurs with the shared libraries (rt and pthread).
And finally, CMakeLists4.txt which aims to put everything together and generate the executable:
cmake_minimum_required(VERSION 3.14...3.16)
project(
Execuable
VERSION 0.1
DESCRIPTION "MainApp library"
LANGUAGES C)
list(APPEND SRCS
source1.c
source2.c)
find_library(RT_LIBRARY rt)
find_library(PTHREAD_LIBRARY pthread)
find_library(RT_LIBRARY rt)
find_library(LIB1_LIBRARY lib1)
find_library(LIB2_LIBRARY lib2)
add_executable(Executable ${SRCS})
target_include_directories (Executable PUBLIC {CMAKE_CURRENT_SOURCE_DIR}/../lib1 {CMAKE_CURRENT_SOURCE_DIR}/../lib2)
target_link_libraries(Executable ${RT_LIBRARY} ${PTHREAD_LIBRARY} ${LIB1_LIBRARY} ${LIB2_LIBRARY})
If I compile each of the libraries separately, they seem to generate properly .a files, but as I try to execute the CMakeLists1.txt, it fails during the linking, showing undefined references in the lib1.a about, for example, shm_open (I found it is related to "rt" shared lib). So, I am not sure how to solve this situation.
Any help will be more than welcome.
Best regards,
Fulgo.
Tsyvarev gave me the answer. I had to avoid using find_library for the libraries built in the same CMake project. This allowed me to compile the project properly.

Linker cannot find library from another project

guys,
I am relatively new to CMake and now I have a bug that I don't understand. Actually I thought I had no problems understanding the terms static and shared library and the functions of CMake. But currently...
So in my project projB I wanna use a static_library from project projA. ProjA is already compiled, linked, installed and used on my board. For ProjB the linker cannot find the library "example".
My issue: for me, i doesn't make sense why projA can find the shared library, projB not.
The error code:
Linking main ....
ld: cannot find -lexample
error: ld returned 1 exit status
My folder structure of projA:
include/ ... *.h (e. g. file.h)
src/ ... *.c (e. g. file.c)
CMakeFiles.txt
The CMakeLists.txt-File of projA:
cmake_minimum_required(VERSION 3.7)
PROJECT(projA)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_definitions(-g -O)
include_directories (include)
file (GLOB SOURCE_FILES src/*.c)
add_library (example STATIC ${SOURCE_FILES})
target_include_directories(example PUBLIC ${SOURCE_DIR}/include)
install(TARGETS example ARCHIVE DESTINATION usr/lib)
So projA will be later a library-component which can be used in many other projects. That's the reason why the created library is not part of projB and why I wanna import/include the created example.a file in a lot of other projects. The static library example.a should be stored in usr/lib.
In my projB the folder structure looks like:
CMakeFiles.txt
main.c
.... *.c
In my main.c-File I try to include the library with #<file.h> which is a part of my created library example.
The CMakeFiles.text of ProjB containing following Code:
cmake_minimum_required(VERSION 3.7)
PROJECT(projB)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
set (CMAKE_C_STANDARD 99)
set (CMAKE_CXX_STANDARD 14)
add_definitions(-g -O -fpermissive)
# build executable
file (GLOB SOURCES *.c )
add_executable (exe ${SOURCES})
# target_include_directories(exe PUBLIC ${CMAKE_SOURCE_DIR}/usr/lib)
# add_library(example SHARED IMPORTED)
# set_property(TARGET example PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/usr/libexample.a)
target_link_libraries (exe example pthread)
install (TARGETS exe RUNTIME DESTINATION usr/sbin)
An attempt to find the error is commented out. I also worked with find_library(), but the library "example" could not be found. Of course I also built the library example as a shared_library and searched for a .so, etc.
As you can see from my example, this is not real code (used in production), but a simplified description of my problem. I am more interested in the systematic. Wrong thinking?
Does anyone have any idea what else I can try or why it doesn't work?
Is it because they are different projects? I mean, the file exists on my system, but is not found ...
Thanks a lot for your help!
Greetings
Matthias

Cannot find API header file for shared library with cmake

I am building a shared library in one project and using it in another. They share a prefix, but I'm not building them together (e.g., <prefix>/mylib and <prefix>/myproject). Both mylib and myproject have src and include directories.
The CMakeList.txt for the shared library:
cmake_minimum_required(VERSION 3.5)
project(mylib)
add_library(mylib SHARED
src/mylib.c
)
target_include_directories(mylib PRIVATE include)
set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h)
install(TARGETS
mylib
LIBRARY DESTINATION lib
PUBLIC_HEADER DESTINATION include
RUNTIME DESTINATION bin)
This results in mylib.so being installed in install/mylib/lib/mylib.so and mylib.h being installed in install/mylib/include/mylib.h, which is what I intended.
I then want to build a project that uses mylib:
#include "mylib.h"
int main(void)
{
// use some functions in mylib
}
The associated CMakeList.txt file for main.c:
cmake_minimum_required(VERSION 3.5)
project(myproject)
find_package(mylib REQUIRED)
add_executable(myproject src/main.c)
target_link_libraries(myproject mylib)
install(TARGETS
myproject
DESTINATION lib/${PROJECT_NAME})
This produces:
main.c: fatal error: mylib.h: No such file or directory
#include "mylib.h"
^~~~~~~~~
If I change CMakeList.txt to include the following:
find_path(MYLIB_INCLUDE_DIR mylib.h)
...
target_include_directories(myproject PUBLIC ${MYLIB_INCLUDE_DIR})
Then it finds the header, but not the library. I get a linker error:
/usr/bin/ld: cannot find -lmylib
If I change CMakeList.txt to include the following:
find_library(MYLIB_LIB mylib)
...
target_link_libraries(myproject ${MYLIB_LIB})
Then it builds.
I (think I) understand why finding the library and include files manually works, but that seems to be the wrong way to go about things...
find_package(mylib) does seem to find the mylib package (I can print cmake cache variables and mylib_FOUND=1), but doesn't find the library and header in such a way that they are built with myproject.
You need to specify include directories for both "build" and "install" variants in target_include_directories in your library project:
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # build variant
$<INSTALL_INTERFACE:include> # install variant
)
BTW, such example is provided in documentation for target_include_directories command.
Additionally, you need to EXPORT the library during installation:
install(
TARGETS mylib
EXPORT mylib
LIBRARY DESTINATION lib
PUBLIC_HEADER DESTINATION include
RUNTIME DESTINATION bin)
See this answer for a tutorial on exporting libraries with cmake.

cpack cannot find libraries, target doesn't exist in this directory

EDIT ===================
The apr-1 is the apache portable runtime, which I downloaded and compiled myself to create the shared library (so its not made by cmake). I need to link against this library after I run CPack on the target system.
My development project directory:
read_config/bin
/libs
/include
/src
/build
For my CMakeLists.txt, everything installs ok, but now I have a linking problem, as it cannot find the new location of the library.
I have removed the LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/libs) as I don't need this now, as I am using the FIND_LIBRARY.
And added the following:
# Find the library in the libs folder
FIND_LIBRARY(APR_LIBS NAMES "apr-1" PATHS ${PROJECT_SOURCE_DIR}/libs)
# Print out the path to see if its correct
MESSAGE(${APR_LIBS})
# Link the library with the executable
TARGET_LINK_LIBRARIES(cfg ${APR_LIBS})
# Install the libs folder
INSTALL(DIRECTORY ${PROJECT_SOURCE_DIR}/libs/
DESTINATION libs)
# Install the executable in the bin folder
INSTALL(TARGETS cfg
RUNTIME DESTINATION bin)
Everything installs ok, but when I check the executable to see what libraries it links against it cannot find the apr-1 as it looks in the wrong place.
libapr-1.so.0 => not found
This is because I have extracted the installation package to a new location which is different to where it was compiled from. From the compiled directory I get this, which runs ok:
libapr-1.so.0 => /home/devel/projects/read_config/libs/libapr-1.so.0 (0xf7746000)
I after I install I have this directory structure on the target installation machine, so the executable will link with the new libs/apr-1 library:
target directory/
/bin
/libs/apr-1
Many thanks for any further suggestions,
========================================
gcc (GCC) 4.7.2
cmake version 2.8.9
Fedora 17
Hello,
I am getting in the following error:
CMake Error at src/CMakeLists.txt:14 (INSTALL):
install TARGETS given target "apr-1" which does not exist in this
directory.
I have the following directory:
read_config/
/build - out of source build
/CMakeLists.txt
/include - apr include files
/libs - apr libraries
/src - project source files *.h *.c CMakeLists.txt
In my root CMakeLists.txt I have the following:
# Name of project and compiler to use
PROJECT(read_cfg C)
# Set compiler flags
IF(CMAKE_COMPILER_IS_GNUCC)
MESSAGE(STATUS "COMPILER IS GNUCC")
SET(CMAKE_C_FLAGS "-Wall -Wextra -m32 -Wunreachable-code -ggdb -O0 -D_DEBUG -D_LARGEFILE64_SOURCE")
ENDIF(CMAKE_COMPILER_IS_GNUCC)
# Where the include files can be found
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/read_config/include)
# Where the libraries can be found
LINK_DIRECTORIES(${CMAKE_SOURCE_DIR}/read_config/libs)
ADD_SUBDIRECTORY(src)
In my src directory I have the following CMakeLists.txt
# Create the executable and link the libraries into it
ADD_EXECUTABLE(cfg test_config.c)
# Place executable in bin directory
SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin)
# Add libraries
IF(CMAKE_COMPILER_IS_GNUCC)
MESSAGE(STATUS "Linking cfg library")
TARGET_LINK_LIBRARIES(cfg apr-1)
ENDIF(CMAKE_COMPILER_IS_GNUCC)
INSTALL(TARGETS cfg
RUNTIME
DESTINATION bin)
INSTALL(TARGETS apr-1
ARCHIVE
DESTINATION lib)
SET(CPACK_PACKAGE_NAME "rd_cfg")
SET(CPACK_PACKAGE_VENDOR "sun.com")
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "rd_cfg - CPack read_config installation")
SET(CPACK_PACKAGE_VERSION "1.0.0")
SET(CPACK_PACKAGE_VERSION_MAJOR "1")
SET(CPACK_PACKAGE_VERSION_MINOR "0")
SET(CPACK_PACKAGE_VERSION_PATCH "0")
SET(CPACK_PACKAGE_INSTALL_DIRECTORY "Read_config_install_dir")
INCLUDE(CPack)
The problem is this line here:
INSTALL(TARGETS apr-1
ARCHIVE
DESTINATION lib)
I have tried doing the following
INSTALL(TARGETS ${CMAKE_SOURCE_DIR}/libs/apr-1
ARCHIVE
DESTINATION lib)
But it still fails to find the library.
And also using the path like this:
INSTALL(TARGETS src/libs
ARCHIVE
DESTINATION lib)
I would like to be able to just put the directory there like this so I don't have to specifiy each library, but just the directory like this:
INSTALL(TARGETS ${CMAKE_SOURCE_DIR}/libs
ARCHIVE
DESTINATION lib)
So it can find all the libraries in that directory. I have about 5 of them.
Many thanks for any suggestions,
The error seems to be a result of calling install in a different CMakeLists.txt to that in which apr-1 is defined via add_library.
I guess you're doing add_library(apr-1 ...) in your root CMakeLists.txt, which means that's where you need to call install(TARGETS apr-1 ARCHIVE DESTINATION lib).
Note that the error message "...does not exist in this directory." is not referring to actual directories in the filesystem sense, but rather the notional directories which come about via add_subdirectory commands.
Edit
In light of your comments, it seems that the issue is that apr-1 is not an actual CMake target, but rather just a library.
In this case, the install command cannot be the TARGETS version, it would have to be install(FILES...) or install(DIRECTORY...). This would probably mean you'd have to locate the library using e.g. find_library in order to get a full path.
Further Edit
This further problem is as a result of not setting the correct RPATH flags. You need to add the following lines to your src/CMakeLists.txt before the ADD_EXECUTABLE call:
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/libs")
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/libs" isSystemDir)
IF("${isSystemDir}" STREQUAL "-1")
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/libs")
ENDIF("${isSystemDir}" STREQUAL "-1")
For a full explanation on these CMake variables, see the CMake Wiki on RPATH handling
try adding this to your CMakeLists.txt
set(CMAKE_INSTALL_RPATH $LIBRARY_OUTPUT_PATH)

Resources