CMake: Can't link simple project that uses shared zlib wrapper library - c++

In my situation I have two CMake projects (built using MSYS Makefiles if that matters):
One is a shared library that uses Zlib and Libzip to provide zipping functionality to the library user, let's call it mylib.
Another is an app that uses mylib to do some zipping stuff. It doesn't have to worry about Zlib, Libzip or anything. Let's call it app.
I have created a bare-bones example below that demonstrates the bizarre issue I'm having. Simply put, app refuses to link when running make, but only if some Libzip code is left uncommented in mylib (which compiles and links fine, no matter what).
Zlib and Libzip source code is located in C:/Dev/Libs.
lib/CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
# Set project
project(mylib)
set(LIBS_DIR "C:/Dev/Libs")
# Add mylib sources
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src")
file(GLOB_RECURSE SRCS "src/*.cpp")
add_library(mylib SHARED ${SRCS})
# Add Zlib
set(ZLIB_ROOT "${LIBS_DIR}/zlib-1.2.11")
include_directories(${ZLIB_ROOT})
target_include_directories(mylib PUBLIC ${ZLIB_ROOT})
file(GLOB ZLIB_SRCS "${ZLIB_ROOT}/*.c")
add_library(zlib STATIC ${ZLIB_SRCS})
# Add Libzip
set(LIBZIP_ROOT "${LIBS_DIR}/libzip-1.1.3")
include_directories("${LIBZIP_ROOT}/lib")
target_include_directories(mylib PUBLIC "${LIBZIP_ROOT}/lib")
file(GLOB LIBZIP_SRCS "${LIBZIP_ROOT}/lib/*.c")
add_library(libzip STATIC ${LIBZIP_SRCS})
set(LIBS_LINK zlib libzip)
set(LIBS_INSTALL zlib libzip)
set(LIBS_EXPORT zlib libzip)
target_link_libraries(mylib PUBLIC ${LIBS_LINK})
# https://rix0r.nl/blog/2015/08/13/cmake-guide/
# Define headers for this library. PUBLIC headers are used for
# compiling the library, and will be added to consumers' build
# paths.
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:include>
PRIVATE src)
# 'make install' to the correct locations (provided by GNUInstallDirs).
include(GNUInstallDirs)
install(TARGETS mylib ${LIBS_INSTALL} EXPORT mylibConfig
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) # This is for Windows
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# This makes the project importable from the install directory
# Put config file in per-project dir (name MUST match), can also
# just go into 'cmake'.
install(EXPORT mylibConfig DESTINATION share/mylib/cmake)
# This makes the project importable from the build directory
export(TARGETS mylib ${LIBS_EXPORT} FILE mylibConfig.cmake)
lib/src/lib.hpp
#pragma once
namespace lib
{
int func();
}
lib/src/lib.cpp
#include "lib.hpp"
#define ZIP_STATIC
#include "zip.h"
int lib::func()
{
zip_source_t *src;
zip_error_t error;
src = zip_source_buffer_create(0, 1, 0, &error);
return 10;
}
app/CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
project(app)
# Add app sources
file(GLOB_RECURSE SRCS "src/*.cpp")
add_executable(app ${SRCS})
# Add mylib
set(mylib_DIR "${CMAKE_CURRENT_BINARY_DIR}/../lib")
find_package(mylib REQUIRED)
configure_file("${mylib_DIR}/libmylib.dll" "libmylib.dll" COPYONLY)
target_link_libraries(app mylib)
app/src/app.hpp
#pragma once
#include <lib.hpp>
app/src/app.cpp
#include <iostream>
#include "app.hpp"
int main()
{
std::cout << lib::func() << std::endl;
return 0;
}
Output from building mylib
$ make
[ 13%] Built target zlib
[ 98%] Built target libzip
Scanning dependencies of target mylib
[ 99%] Building CXX object CMakeFiles/mylib.dir/src/lib.cpp.obj
[100%] Linking CXX shared library libmylib.dll
[100%] Built target mylib
Output from building app
$ make
Scanning dependencies of target app
[ 50%] Building CXX object CMakeFiles/app.dir/src/app.cpp.obj
[100%] Linking CXX executable app.exe
CMakeFiles/app.dir/objects.a(app.cpp.obj):app.cpp:(.text+0x17): undefined reference to `lib::func()'
collect2.exe: error: ld returned 1 exit status
make[2]: *** [app.exe] Error 1
make[1]: *** [CMakeFiles/app.dir/all] Error 2
make: *** [all] Error 2
If I comment src = zip_source_buffer_create(0, 1, 0, &error); in lib/src/lib.cpp, rebuild and then rebuild app:
$ make
-- Configuring done
-- Generating done
-- Build files have been written to: C:/Dev/Builds/cmaketest/app
[ 50%] Linking CXX executable app.exe
[100%] Built target app
$ ./app
10
Which yields the expected result.
The issue must lie in the CMakeLists of app, since mylib builds fine without any linking errors. Or am I adding zlib/libzip incorrectly in mylib? I need to link to them statically and I couldn't get it to work with find_package.
Thanks!

Got it working after copying over the .dll.a file as well:
configure_file("${mylib_DIR}/libmylib.dll.a" "libmylib.dll.a" COPYONLY)
and prefixing all my methods in lib with the following:
__declspec(dllexport)

Related

Cmake install nested static library target_link_library undefined reference

Install nested static library, and target_link_library not working
File structure:
HelloLib
WorldLib
CMakeLists.txt
WorldLib.cpp
WorldLib.h
CMakeLists.txt
HelloLib.cpp
HelloLib.h
CMakeLists.txt
main.cpp
main.cpp
#include <iostream>
#include "HelloLib/HelloLib.h"
int main() {
std::cout << hello() << std::endl;
return 0;
}
CMakeLists.txt (root)
cmake_minimum_required(VERSION 3.16.3)
project(HelloWorld)
add_executable(${PROJECT_NAME} main.cpp)
add_subdirectory(HelloLib)
target_link_libraries(
${PROJECT_NAME}
HelloLib
)
HelloLib.cpp
#include <string>
#include "WorldLib/WorldLib.h"
std::string hello() { return "Hello " + world(); }
HelloLib.h
#pragma once
#include <string>
std::string hello();
CMakeLists.txt (HelloLib)
add_library(HelloLib HelloLib.cpp)
add_subdirectory(WorldLib)
target_link_libraries(
HelloLib
WorldLib
)
WorldLib.cpp
#include <string>
std::string world() { return "World!"; }
WorldLib.h
#pragma once
#include <string>
std::string world();
CMakeLists.txt (WorldLib)
add_library(WorldLib WorldLib.cpp)
Build and run, successfully print out Hello World!
Now I would like to make HelloLib as static library
Change CMakeLists.txt (HelloLib), install to /usr/lib and /usr/include
add_library(HelloLib HelloLib.cpp)
add_subdirectory(WorldLib)
target_link_libraries(
HelloLib
WorldLib
)
install(
TARGETS HelloLib
ARCHIVE DESTINATION lib
)
install(
DIRECTORY "${CMAKE_SOURCE_DIR}/" # source directory
DESTINATION "include" # target directory
FILES_MATCHING # install only matched files
PATTERN "*.h" # select header files
)
Run (make install) (CMAKE_INSTALL_PREFIX=/usr)
mkdir -p out/build
cmake -S src -B out/build -DCMAKE_INSTALL_PREFIX=/usr
cd out/build
make
make install
[ 33%] Built target WorldLib
[ 66%] Built target HelloLib
[100%] Built target HelloWorld
Install the project...
-- Install configuration: ""
-- Installing: /usr/lib/libHelloLib.a
-- Up-to-date: /usr/include
-- Installing: /usr/include/HelloLib
-- Installing: /usr/include/HelloLib/HelloLib.h
-- Installing: /usr/include/HelloLib/WorldLib
-- Installing: /usr/include/HelloLib/WorldLib/WorldLib.h
Static library (.a) should generate to /usr/lib/libHelloLib.a
Let's test it, change CMakeLists.txt (root)
cmake_minimum_required(VERSION 3.16.3)
project(HelloWorld)
add_executable(${PROJECT_NAME} main.cpp)
add_subdirectory(HelloLib)
target_link_libraries(
${PROJECT_NAME}
/usr/lib/libHelloLib.a
)
Then build, it give undefined reference error
/bin/ld: /usr/lib/libHelloLib.a(HelloLib.cpp.o): in function `hello[abi:cxx11]()':
HelloLib.cpp:(.text+0x20): undefined reference to `world[abi:cxx11]()'
What's wrong with me? This can be successful with no nested library
What is the correct way to install nested static library?
The issue here is not the install process but the fact that you link only to libHelloLib.a.
Your libHelloLib.a need the symbol in libWorldLib.a because libHelloLib.a is a static lib and so only contains its own symbol. It does not contains the symbol world that is defined in libWorldLib.a.
To make your project works, you need to install WorldLib and HelloLib and links against this two lib.
target_link_libraries(
${PROJECT_NAME}
HelloLib
WorldLib
)
Or you can change HelloLib into a shared library. This way the libHelloLib.so will also contains the symbol of WorldLib.
You can also look at this Exported Target.
It's an install command that will create some FindXX.cmake file that you will be able to use with the find_package command. You also will be able to defined the dependency of your lib. But if you want that HelloLib stay a static library you will have to install WorldLib

Custom CMake library - What have I done wrong?

I am trying to install a custom library with CMake.
It consists of 2 'sub-libraries' and a main header file which includes the 'sub-libraries'.
I think it installs okay (The output looks like this:)
-- Configuring done
-- Generating done
-- Build files have been written to: /home/pi/Desktop/Projects/MaxLib/build
[ 20%] Building CXX object Geom/CMakeFiles/Geom.dir/Geom.cpp.o
[ 40%] Linking CXX shared library libGeom.so
[ 40%] Built target Geom
[ 60%] Building CXX object File/CMakeFiles/File.dir/File.cpp.o
[ 80%] Linking CXX shared library libFile.so
[ 80%] Built target File
[100%] Built target MaxLib
Install the project...
-- Install configuration: "Debug"
-- Installing: /usr/local/lib/libMaxLib.a
-- Installing: /usr/local/include/MaxLib.h
-- Installing: /usr/local/include/MaxLib/File.h
-- Installing: /usr/local/include/MaxLib/Geom.h
However, when I try to compile a program using it, I receive a number of "undefined reference to" errors. Where have I gone wrong?
The Main Header looks like:
#include "MaxLib/File.h"
#include "MaxLib/Geom.h"
It's CMakefile looks like this:
cmake_minimum_required(VERSION 3.5)
project(MaxLib)
add_library(${PROJECT_NAME}
"${CMAKE_CURRENT_SOURCE_DIR}/MaxLib.h"
)
add_subdirectory(File)
add_subdirectory(Geom)
target_link_libraries(${PROJECT_NAME} File)
target_link_libraries(${PROJECT_NAME} Geom)
# Install Library
install (TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION lib)
# Install Main Header File
INSTALL(FILES "${CMAKE_CURRENT_SOURCE_DIR}/MaxLib.h" DESTINATION include) #
INSTALL(FILES ...) or install(DIRECTORY ...)
# Build list of header files to install from other directorys. Root == "."
set(HEADER_DIRS "File" "Geom")
## Add Source Files from the other directories
foreach(DIR ${HEADER_DIRS})
# Find all source files & append to list
if(DIR STREQUAL ".")
file(GLOB HEADER_FILES_IN_FOLDER *.h)
else()
file(GLOB HEADER_FILES_IN_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/${DIR}/*.h)
endif()
list(APPEND HEADER_FILES ${HEADER_FILES_IN_FOLDER})
endforeach()
# Install Header files
INSTALL(FILES ${HEADER_FILES} DESTINATION include/${PROJECT_NAME}) # INSTALL(FILES ...) or install(DIRECTORY ...)
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic -g)
set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX)
The 'sub-libraries's CMakefiles look like this:
project(Geom)
add_library(${PROJECT_NAME} SHARED
${CMAKE_CURRENT_SOURCE_DIR}/Geom.cpp
)
target_include_directories(${PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
I am trying to include the library like this:
#include <MaxLib.h>
And I am adding -lMaxLib to its makefile
Edit:
A basic program like this:
#include <MaxLib.h>
int main() {
float y1 = MaxLib::Geom::CleanAngle(7654);
}
Will produce the undefined reference error:
/usr/bin/ld: warning: libFile.so, needed by //usr/local/lib/libMaxLib.so, not found (try using -rpath or -rpath-link) // This shows when add_library marked SHARED
/usr/bin/ld: warning: libGeom.so, needed by //usr/local/lib/libMaxLib.so, not found (try using -rpath or -rpath-link) // This shows when add_library marked SHARED
/home/pi/Desktop/Projects/TestProgram/main.cpp:43: undefined reference to `MaxLib::Geom::CleanAngle(double)'

cmake - Header files of shared library not found

I am making a custom library that I want to be installable for users. However, when I try to use my own library in a cmake executable, I get a build error saying that the library headers were not found.
The library CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(mylibrary)
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 14)
# Register a library - This will created lib[xxx].so
add_library(mylibrary SHARED src/library.cpp)
configure_file(mylibrary.pc.in mylibrary.pc #ONLY)
# List the /include directory
target_include_directories(mylibrary PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
install(TARGETS mylibrary
EXPORT mylibraryConfig
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
export(TARGETS mylibrary
FILE "${CMAKE_CURRENT_BINARY_DIR}/mylibraryConfig.cmake")
install(EXPORT mylibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mylibrary/cmake"
NAMESPACE mylibraryConfig::)
install(
DIRECTORY include
DESTINATION ${CMAKE_INSTALL_PREFIX})
install(FILES ${CMAKE_BINARY_DIR}/mylibrary.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
Which I successfully build and install with:
$ cmake .. -DCMAKE_INSTALL_PREFIX=~/libraries/local # Use non-standard destination
$ make && make install
The executableCMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(myexecutable)
set(CMAKE_CXX_STANDARD 14)
find_package(mylibrary REQUIRED)
add_executable(myexecutable src/main.cpp)
target_link_libraries(myexecutable PUBLIC mylibrary)
target_include_directories(myexecutable PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
I can prepare cmake for this project:
$ cmake .. -DCMAKE_PREFIX_PATH=~/libraries/local # Use non-standard location
However, building it fails:
$ make
fatal error: mylibrary/library.h: No such file or directory
2 | #include <mylibrary/library.h>
To my understanding the location of the library (binaries and headers) is embedded in the installed package. And through find_package() that information retrieved, so why isn't it working here?
Similar questions:
I largely based my library cmake on: How to create a shared library with cmake?
Same problem but I am already using target_include_directories: Cmake Linking Shared Library: "No such file or directory" when include a header file from library
When a shared library target is namespaced in the config file you need to reference it with the full name in the downstream packages when using find_package, i.e. you need to use
target_link_libraries(myexecutable PUBLIC mylibraryConfig::mylibrary)
Alternatively, remove the namespace from the install by replacing
install(EXPORT mylibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mylibrary/cmake"
NAMESPACE mylibraryConfig::)
...with:
install(EXPORT mylibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mylibrary/cmake")

CMake error: linker command failed with exit code 1 and cpp.o files

I am using CMake to compile a small project.
Here is what I have written in CMakeLists.txt:
cmake_minimum_required(VERSION 3.2)
set (CMAKE_CXX_STANDARD 11)
project(DAF)
find_package(OpenCV REQUIRED)
include_directories(include)
include_directories(${OpenCV_INCLUDE_DIRS} )
file(GLOB Src_Sources "src/*.cpp")
file(GLOB Test_Sources "test/*.cpp")
add_executable(executable ${Src_Sources} ${Test_Sources})
target_link_libraries( executable include ${OpenCV_LIBS} )
I have two directories src and test. The src directory contains only the files that hold the functions, whereas the test directory contains the main file.
Once I use the cmake command and then the make command I get this error:
Scanning dependencies of target executation
[ 25%] Building CXX object CMakeFiles/executation.dir/src/image.cpp.o
[ 50%] Building CXX object CMakeFiles/executation.dir/test/starter_1.cpp.o
[ 75%] Linking CXX executable executation
ld: library not found for -linclude
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [executation] Error 1
make[1]: *** [CMakeFiles/executation.dir/all] Error 2
make: *** [all] Error 2
So what I find strange is that it creates .cpp.o files. Is that normal?
And then how do I fix the error library not found for -linclude?
Yes, it is normal for CMake to take the name of the source file, and append a .o when compiling the respective object files. For example, the source file image.cpp will be compiled into image.cpp.o. The Makefiles generated by CMake will contain a unique target <someSourceFileName>.o for each source file, where <someSourceFileName> can be a .cc, .cpp, .c, etc.
The error:
ld: library not found for -linclude
indicates that you have tried to link a library called include to the executable. This is likely not what you intend. It appears include is actually your include directory, and you have already specified the include directories in your CMake with this line:
include_directories(include)
To remove the error, just take out the include from the target_link_libraries() command, like so:
target_link_libraries( executable ${OpenCV_LIBS} )

How to use the c++ library gtsam in my project package?

this is the first time I use an external c++ library aside from OpenCV. In the code I use quite a lot of ROS functionality, but I believe my issue is not related to ROS. I want to build a project with the GTSAM library.
Therefore I cloned the repository into my /usr/lib folder and installed it as instructed.
I then wrote the CMakeLists.txt,added two includes into my - otherwise functioning .h file - and tried to compile.
The beginning of my .h file, the compile error and my CMakeLists.txt are shown below. Interestingly, if I comment out the second include and just include Pose2.h, compilation works. That should mean that the compiler at least finds some headers from the library, ergo it is correctly installed. The part of my cmake code which is supposed to link the library is extracted from an example project given here. Any help is appreciated.
car_lib.h:
#ifndef CAR_LIB_H
#define CAR_LIB_H
// GTSAM headers
#include <gtsam/geometry/Pose2.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
using namespace gtsam;
// rest of file follows....
When compiling, I get the following error:
...
[100%] Linking CXX executable /home/marc/catkin_ws/devel/lib/car/car_node
[100%] Linking CXX shared library /home/marc/catkin_ws/devel/lib/libcar_lib.so
[100%] Built target car_lib
CMakeFiles/car_node.dir/src/car_node.cpp.o: In function `__static_initialization_and_destruction_0(int, int)':
/usr/local/include/gtsam/inference/Key.h:41: undefined reference to `gtsam::_defaultKeyFormatter[abi:cxx11](unsigned long)'
/usr/local/include/gtsam/inference/Key.h:52: undefined reference to `gtsam::_multirobotKeyFormatter[abi:cxx11](unsigned long)'
collect2: error: ld returned 1 exit status
car/CMakeFiles/car_node.dir/build.make:113: recipe for target '/home/marc/catkin_ws/devel/lib/car/car_node' failed
make[2]: *** [/home/marc/catkin_ws/devel/lib/car/car_node] Error 1
CMakeFiles/Makefile2:384: recipe for target 'car/CMakeFiles/car_node.dir/all' failed
make[1]: *** [car/CMakeFiles/car_node.dir/all] Error 2
Makefile:138: recipe for target 'all' failed
make: *** [all] Error 2
Invoking "make -j4 -l4" failed
My CMakeLists.txt:
cmake_minimum_required(VERSION 2.8.3)
project(car)
## Compile as C++11, supported in ROS Kinetic and newer
add_compile_options(-std=c++11)
find_package(catkin REQUIRED COMPONENTS
geometry_msgs
message_generation
nav_msgs
roscpp
sensor_msgs
std_msgs
)
###### GTSAM STUFF STARTS HERE
# Include GTSAM CMake tools
find_package(GTSAMCMakeTools)
#include(GtsamBuildTypes) # Load build type flags and default to Debug mode
#include(GtsamTesting) # Easy functions for creating unit tests and scripts
#include(GtsamMatlabWrap) # Automatic MATLAB wrapper generation
# Ensure that local folder is searched before library folders
#include_directories(BEFORE "${PROJECT_SOURCE_DIR}")
###################################################################################
# Find GTSAM components
find_package(GTSAM REQUIRED) # Uses installed package
include_directories(${GTSAM_INCLUDE_DIR})
###################################################################################
# Build static library from common sources
#set(CONVENIENCE_LIB_NAME ${PROJECT_NAME})
#add_library(${CONVENIENCE_LIB_NAME} STATIC include/car/car_lib.h src/car_lib.cpp)
#target_link_libraries(${CONVENIENCE_LIB_NAME} gtsam)
###### GTSAM STUFF ENDS HER
catkin_package(
INCLUDE_DIRS include
LIBRARIES car_lib
CATKIN_DEPENDS
geometry_msgs
message_runtime
nav_msgs
roscpp
sensor_msgs
std_msgs
)
include_directories(
include
${catkin_INCLUDE_DIRS}
)
## Declare a C++ library
add_library(car_lib
include/${PROJECT_NAME}/car_lib.h
src/car_lib.cpp
)
add_executable(car_node src/car_node)
target_link_libraries(car_node ${catkin_LIBRARIES})
target_link_libraries(car_lib
${catkin_LIBRARIES}
)
Try replacing the bit under ## Declare a C++ library with
## Declare a C++ library
add_library(car_lib src/car_lib.cpp)
target_link_libraries(car_lib
gtsam
${catkin_LIBRARIES}
)
add_executable(car_node src/car_node)
target_link_libraries(car_node
car_lib
gtsam
${catkin_LIBRARIES}
)
For those who are trying to do the same thing using makefile, Here is the solution. [ note this is for simple c++ program without ROS ].
all: main.cpp
#g++ main.cpp \
-std=c++11 \
-I /usr/include/eigen3 \
-lboost_system -lboost_filesystem \
-lgtsam \
-o main
clear:
#rm -rf main