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
Related
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.
I'm looking for the simplest way to compile a c++ program using SDL2 and SDL_image with cmake.
Here is my best attempt, after hours of searching:
CMakeLists.txt
project(shooter-cmake2)
cmake_minimum_required(VERSION 2.8)
set(SOURCES
shooter.cpp
classes.cpp
utils.cpp
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
add_executable(${PROJECT_NAME} ${SOURCES})
INCLUDE(FindPkgConfig)
PKG_SEARCH_MODULE(SDL2 REQUIRED sdl2)
PKG_SEARCH_MODULE(SDL2_image REQUIRED sdl2_image)
INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIRS} ${SDL2IMAGE_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${SDL2_LIBRARIES} ${SDL2IMAGE_LIBRARY})
I get these errors:
In function `loadTexture(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, SDL_Renderer*)':
undefined reference to `IMG_LoadTexture'
collect2: ld returned 1 exit status
Here is the function call:
#include "SDL.h"
#include "SDL_image.h"
SDL_Texture* loadTexture(const std::string &file, SDL_Renderer *ren){
SDL_Texture *texture = IMG_LoadTexture(ren, file.c_str());
texture != nullptr or die("LoadTexture");
return texture;
}
I think that the following will work, as it finds the libraries on my ubuntu system and the example function you provided can link:
project(shooter-cmake2)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
add_executable(${PROJECT_NAME} src/test.cpp)
INCLUDE(FindPkgConfig)
PKG_SEARCH_MODULE(SDL2 REQUIRED sdl2)
PKG_SEARCH_MODULE(SDL2IMAGE REQUIRED SDL2_image>=2.0.0)
INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIRS} ${SDL2IMAGE_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${SDL2_LIBRARIES} ${SDL2IMAGE_LIBRARIES})
If cmake is executed with --debug-output it outputs:
-- Found PkgConfig: /usr/bin/pkg-config (found version "0.26")
Called from: [2] /usr/share/cmake-2.8/Modules/FindPkgConfig.cmake
[1] $USER/stack-overflow/cmake-sdl2-image/CMakeLists.txt
-- checking for one of the modules 'sdl2'
Called from: [1] $USER/stack-overflow/cmake-sdl2-image/CMakeLists.txt
-- checking for one of the modules 'SDL2_image>=2.0.0'
Called from: [1] $USER/stack-overflow/cmake-sdl2-image/CMakeLists.txt
This made me check the contents of
/usr/lib/x86_64-linux-gnu/pkgconfig/sdl2.pc
/usr/lib/x86_64-linux-gnu/pkgconfig/SDL2_image.pc
I noticed that SDL2_image.pc contains
Name: SDL2_image
which I assumed should match the third parameter to PKG_SEARCH_MODULE for this library.
There are two blog posts about this here:
Using SDL2 with CMake
Using SDL2_image with CMake
Basically you need a FindSDL2.cmake and FindSDL2_image.cmake module. They can be based of the ones that work for SDL 1.2 which are included in CMake already. Using these Find modules will also work on Windows.
If you are on Linux and only need SDL2 you don't even need the FindSDL2.cmake as the following already works:
cmake_minimum_required(VERSION 3.7)
project(SDL2Test)
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
add_executable(SDL2Test Main.cpp)
target_link_libraries(SDL2Test ${SDL2_LIBRARIES})
I was having trouble with these answers, I think cmake changed the way to import targets. Following #trenki blog post I needed to change my CMakeLists.txt to:
project(SDL2Test)
find_package(SDL2 REQUIRED COMPONENTS SDL2::SDL2)
add_executable(SDL2Test main.cpp)
target_link_libraries(SDL2Test SDL2::SDL2)
Currently this works out of the box on Arch Linux.
I introduced a modern and portable approach for linking to the SDL2, SDL2_image. These SDL2 CMake modules let you build an SDL2 & SDL2_image project as follows :
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/sdl2)
find_package(SDL2 REQUIRED)
find_package(SDL2_image REQUIRED)
target_link_libraries(${PROJECT_NAME} SDL2::Main SDL2::Image)
You should just clone the repo in your project:
git clone https://github.com/aminosbh/sdl2-cmake-modules cmake/sdl2
Note: If CMake didn't find the SDL2/SDL2_image libraries (in Windows), we can specify the CMake options SDL2_PATH and SDL2_IMAGE_PATH as follows:
cmake .. -DSDL2_PATH="/path/to/sdl2" -DSDL2_IMAGE_PATH="/path/to/sdl2-image"
It supports also other related libraries : SDL2_ttf, SDL2_net, SDL2_mixer and SDL2_gfx. For more details, please read the README.md file.
You can find a list of examples/samples and projects that uses these modules here : https://github.com/aminosbh/sdl-samples-and-projects
Since 2.6 version, SDL2_image installation is shipped with CMake config script SDL2_imageConfig.cmake/SDL2_image-config.cmake.
So find_package(SDL2_image) works without any additional FindSDL2_image.cmake module, and creates IMPORTED target SDL2_image::SDL2_image:
find_package(SDL2_image REQUIRED)
target_link_libraries(<executable-target> SDL2_image::SDL2_image)
Note, that variables like SDL2_IMAGE_LIBRARIES or SDL2_IMAGE_INCLUDE_DIRS are NOT set in this case, so using them is meaningless.
The following commands works fine for me:
set(SDL_INCLUDE_DIR "/usr/include/SDL2")
set(SDL_LIBRARY "SDL2")
include(FindSDL)
if(SDL_FOUND)
message(STATUS "SDL FOUND")
endif()
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.
I've written several c text processing functions that I've placed in a the files: string_functions.c and string_functions.h.
I was using these functions for one project and that worked out well. Now I want to use these same functions in a completely different project at the same time. I'm using gcc in Debian.
Is there a good way to use the same c source code in multiple projects at the same time. The projects are in different sub-directories with the same parent directory.
How do I structure the make files to do this?
Or do I just place a copy of the string_functions.c(h) in both projects. This seems like it would make it harder to maintain the source code.
Best way to do this is to build your C files (.h and .c) into a shared library.
There are many tutorials available on how to do this with gcc; one is at this link
Once the shared library is built, you can then link it into many other projects.
Briefly, these are the steps.
Ensure your string_functions.c includes string_functions.h and builds, of course.
Then compile position independent (that's what -fPIC is for)
$gcc -Wall -fPIC -c string_functions.c
Finally build your shared library like this
$gcc -shared -o my_stringfunctions.so string_functions.o
To link to your new shared library from some other program, ensure that whatever directory
you put it in is in your LD_LIBRARY_PATH.
Then you may link using something like
$gcc my_otherprogram.c -L/path/to/my/lib -lmy_stringfunctions
As pointed out, one should put include files (.h) used by a shared library in some directory path, and add the location to the include search path using the -I option:
$gcc my_otherprogram.c -I/path/to/include/files -L/path/to/my/lib -lmy_stringfunctions
If this is how your directory looks:
/parent
/project1
...
string_functions.h
string_functions.c
/project2
...
string_functions.h
string_functions.c
Then all you have to do is store it in a common location, and then point to that location when building your code. This is the standard way of doing it for custom installed libraries in /opt/, for example.
Hence, one suggestion is to do your directory structure like this:
/parent
/include
string_functions.h
string_functions.c
/project1
...
/project2
...
And when building your respective projects, you include that search path when compiling (using the -I flag):
gcc mainfile.c -I/parent/include <other options>
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)