Cannot find API header file for shared library with cmake - c

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.

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 and issues with header files in subfolders

I'm trying to set up a project where header files can be found by the subfolder libraries src code as well as the src code in the top level using Cmake. Currently i am getting an error stating that the header file can not be found. The structure of my project looks like this:
root/
src/
CMakeLists.txt #(top level)
main.c
lib/
lib1.c
CMakeLists.txt #(lower level)
headers/
lib1.h
build/
My top level CMakeLists.txt looks like this:
cmake_minimum_required(VERSION 3.13.4)
project(CmakeTUT_Proj)
add_executable(${PROJECT_NAME} main.c)
target_include_directories(${PROJECT_NAME} PUBLIC Lib/headers/)
add_subdirectory(Lib/)
target_link_directories(${PROJECT_NAME} PRIVATE Lib/headers/)
target_link_libraries(${PROJECT_NAME} name_of_lib)
My lower level CMakeLists.txt looks like:
add_library(name_of_lib adder.c)
My main.c and my lib1.c programs include the library as #include "lib1.h", cmake runs fine without any errors but when i build the project with make i get an error like:
root/src/Lib/lib1.c:2:10: fatal error: lib1.h: No such file or directory
#include "lib1.h"
I want to structure my project so that main.c and lib1.c have access to lib1.h. Any ideas? Thank you.
As the name implies, target_include_directories only affects one target. So when you set target_include_directories(${PROJECT_NAME} PUBLIC Lib/headers/), this adds to the include path for the executable target named ${PROJECT_NAME} but not to the include path for the library name_of_lib.
To fix this, you can add the include path for your library in the lower level CMakeLists.txt:
target_include_directories(name_of_lib PUBLIC headers)
As a bonus, because it's PUBLIC, this path is also automatically added to any target that depends on name_of_lib. So in the top-level CMakeLists.txt, you can remove this line:
target_include_directories(${PROJECT_NAME} PUBLIC Lib/headers/)
Aside, this line looks useless and can probably be removed as well:
target_link_directories(${PROJECT_NAME} PRIVATE Lib/headers/)
Link libraries are not usually placed in headers directories.

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

Set up cmakelist correctly to compile project

This is my working directory:
app
|___include
|___file1.h
|___file2.h
|___file3.h
!___ ...
|___src
|___file1.c
|___file2.c
|___file3.c
|___ ...
|__CMakeLists.txt
|__mainapp.c
And this is my CMakeLists.txt:
cmake_minimum_required(VERSION 3.5.1)
project(app)
include_directories(include)
file(GLOB SOURCES src/*.c)
add_executable(file3 src/file3.c)
target_link_libraries(file3 m ${SOURCES})
Then I do the following:
$cmake .
$make
But I get errors:
[ 50%] Building C object CMakeFiles/file3.dir/src/file3.c.o
[100%] Linking C executable file3
src/file3.c:1:19: fatal error: file1.h: File does not exist
Where file3.c is just:
#include "file1.h"
#include "file2.h"
int foo3(int a, int b){
return foo1(a,b) + foo2(a,b);
}
How do I set up CMakeLists correctly?
You are linking source files to your executable but you should compile them.
target_link_libraries(file3 m ${SOURCES})
Normally you put all your source files into the add_executable for compilation and only link libraries like m (libm for math functions).
add_executable(file3 ${SOURCES})
target_link_libraries(file3 m)
This of course only works if none of the other files contain a main function.
Your include_directories(include) is appended to the gcc call as -Iinclude which is used during compilation. https://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html. You can see your gcc calls with make VERBOSE=1.
During Linking gcc doesn't look there anymore, that's why it tells you it can't find that function (after that it would probably fail elsewhere)
You could also build a library with your sources and link your executable against that. But then don't include file3 in the sources for the library.
Using
#include "file1.h"
searches for file.h in the current directory and not in the other included directories.
Use
#include <file.h>
instead.
Hope this helps

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