Including a C++ library into another? - c++

I'm trying to build a c++ library, which will be using itself another library.
I would like to output at the end a single .so file, so it is easily copied and used in any other project.
In this library I am using another library, GLFW.
Now, I can create my library fine, but when I am using it I am getting linking errors, where the GLFW functions are not defined. This makes me think that the GLFW lib is not exported with my library.
I've seen this that seemed to be a solution, but i gave me lot of duplicate symbol errors.
I'm quite a beginner with cmake, so maye there is something obvious I'm not seeing. Here is my CMakeLists.txt :
cmake_minimum_required(VERSION 3.22)
project(MyLib)
set(CMAKE_CXX_STANDARD 23)
# define folders path
get_filename_component(ROOT_DIR ${CMAKE_CURRENT_LIST_FILE} PATH)
set(HEADER "${ROOT_DIR}/include")
set(SRCS_PATHS "${ROOT_DIR}/src")
set(TESTS_SRC "${ROOT_DIR}/tests")
# add dependencies
set(DEP_HEADERS "${ROOT_DIR}/dependencies/GLFW/include")
# set the project sources and headers files
include_directories(${HEADER})
include_directories(${DEP_HEADERS})
set(SRCS [...])
add_library(MyLib SHARED ${SRCS})
# set the project property linker language
set_target_properties(MyLib PROPERTIES LINKER_LANGUAGE CXX)
# target tests
add_executable(window ${TESTS_SRC}/window.cpp)
target_link_libraries(window MyLib)
I've seen I'm not the only one with this issue, but most of the answers I've tried won't work and lead to the same problem.

From what I can deduce from your CMakeLists.txt, you should do something like this (I don't like vendoring, not an expert of this approach, so maybe there is something more elegant):
cmake_minimum_required(VERSION 3.20)
project(MyLib)
# glfw static PIC
set(CMAKE_POSITION_INDEPENDENT_CODE_SAVED ${CMAKE_POSITION_INDEPENDENT_CODE})
set(BUILD_SHARED_LIBS_SAVED ${BUILD_SHARED_LIBS})
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(dependencies/GLFW EXCLUDE_FROM_ALL)
set(CMAKE_POSITION_INDEPENDENT_CODE ${CMAKE_POSITION_INDEPENDENT_CODE_SAVED})
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_SAVED})
# MyLib
add_library(MyLib SHARED [...])
target_include_directories(MyLib PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
target_link_libraries(MyLib PRIVATE glfw)
target_compile_features(MyLib PUBLIC cxx_std_23)
# Tests
add_executable(window tests/window.cpp)
target_link_libraries(window PRIVATE MyLib)
target_compile_features(MyLib PRIVATE cxx_std_23)
But honestly it's bad to hardcode all these informations in a CMakeLists, you should have a generic CMakeLists and avoid vendoring:
cmake_minimum_required(VERSION 3.20)
project(MyLib)
# MyLib
find_package(glfw3 REQUIRED)
add_library(MyLib [...])
target_include_directories(MyLib PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
target_link_libraries(MyLib PRIVATE glfw)
target_compile_features(MyLib PUBLIC cxx_std_23)
# Tests
add_executable(window tests/window.cpp)
target_link_libraries(window PRIVATE MyLib)
target_compile_features(MyLib PRIVATE cxx_std_23)
And then you would decide at build time how to build each lib:
// build & install glfw once, as static PIC (glfw is not vendored in MyLib source code here)
cd <glfw_source_dir>
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=<glfw_install_dir>
cmake --build build
cmake --build build --target install
// build MyLib as shared
cd <mylib_source_dir>
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DCMAKE_PREFIX_PATH=<glfw_install_dir>
cmake --build build

Related

CMake: Cannot link to a static library in a subdirectory

I have the following folder structure in my c++ project
*--build
|---(building cmake here)
|
*--main.cpp
|
*--CMakeLists.txt (root)
|
*--modules
|---application
|------app.h
|------app.cpp
|------CMakeLists.txt
And the code below for both CMakeLists.txt files:
CMakeLists.txt (module)
cmake_minimum_required(VERSION 3.15.2)
file(GLOB APPLICATION_HEADERS *.h *.hpp)
file(GLOB APPLICATION_SRC *.c *.cpp)
add_library(app_lib STATIC
${APPLICATION_HEADERS}
${APPLICATION_SRC})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR})
CMakeLists.txt (root)
cmake_minimum_required(VERSION 3.15.2)
project(main)
enable_language(C CXX)
#set directories
set(CMAKE_BINARY_DIR build)
set(CMAKE_CONFIGURATION_TYPES UNIX)
set(CMAKE_MODULES_DIR ${SOURCE_DIR}/cmake)
add_executable(${PROJECT_NAME} main.cpp)
# Build sub-modules
include_directories(modules/application)
add_subdirectory(modules/application)
find_library(MY_APP_LIB app_lib REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC ${MY_APP_LIB})
However, when I do cmake .. in my build directory, it seems like my app library just doesn't build and it doesn't link to it. I end up with the following error:
CMake Error at CMakeLists.txt:80 (find_library):
Could not find MY_APP_LIB using the following names: app_lib
I tried looking at other stackoverflow questions but it seems like I'm missing something. Any help is appreciated!
Thanks!
You don't need to use find_* to locate the library. In fact you cannot locate the library this way, since find_library searches the file system for the library during configuration, i.e. before anything gets compiled.
There's good news though: If the targets are created in the same cmake project, you can simply use the name of the cmake target as parameter for target_link_libraries:
...
add_library(app_lib STATIC
${APPLICATION_HEADERS}
${APPLICATION_SRC})
# note: this should be a property of the library, not of the target created in the parent dir
target_include_directories(app_lib PUBLIC .)
...
add_subdirectory(modules/application)
target_link_libraries(${PROJECT_NAME} PUBLIC app_lib)
You don't need to do find_library for your own targets, just link directly to app_lib:
target_link_libraries(${PROJECT_NAME} PUBLIC app_lib)

C++ file cannot find library linked with CMake

I wanted to use DearImGui therefore I needed to either copy ImGui into the project or use a package manager, so I chose the latter. I'm currently using Conan as a my package manager, the file looks like this:
conanfile.txt
[requires]
boost/1.79.0
imgui/1.88
glad/0.1.36
glfw/3.3.7
[generators]
cmake_find_package
cmake_paths
CMakeDeps
CMakeToolchain
It has all the dependancies as a normal ImGui project would need as well as boost which had been downloaded as a binary previously and works fine, using just a normal header like #include <boost/asio.hpp>. I figure this happens because it was aleady installed.
I have a basic file structure that looks like this, prebuild:
conanfile.txt
CMakeLists.txt
src -
main.cpp
With this main CMakeLists I set up a linker with conan and simple setup instructions like the project name, executable, and the libraries
cmake_minimum_required(VERSION 3.23)
project(RenderCam CXX)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(${CMAKE_CURRENT_SOURCE_DIR}/build/conan_paths.cmake)
add_executable(${PROJECT_NAME} src/main.cpp)
find_package(Boost 1.79.0 REQUIRED COMPONENTS thread system filesystem)
if(TARGET boost::boost)
target_link_libraries(${PROJECT_NAME} boost::boost)
endif()
find_package(glfw3 3.3 REQUIRED)
if(TARGET glfw)
message("Found GLFW")
target_link_libraries(${PROJECT_NAME} glfw)
endif()
find_package(imgui 1.88 REQUIRED)
if(TARGET imgui::imgui)
message("found imgui")
target_link_libraries(${PROJECT_NAME} imgui::imgui)
endif()
After running two commands (conan install .. and cmake .. -DCMAKE_BUILD_TYPE=Release) in the new build directory the CMake build responds with all the found messages for the project as well as the location of the files, which should be expected. Though when add the #include <imgui.h> to the main.cpp it states the file is not found which shouldn't be if I have linked it. On the other hand #include <boost/asio.hpp> has no errors and I can go upon my day with all of the commands. I would just like to include the directories and have tried multiple steps and guides before I took it here. For further reference, this is my conanprofile :
Configuration for profile default:
[settings]
os=Macos
os_build=Macos
arch=x86_64
arch_build=x86_64
compiler=apple-clang
compiler.version=13
compiler.libcxx=libc++
build_type=Release
[options]
[conf]
[build_requires]
[env]
Don't mix up mutually exclusive generators like cmake_find_package vs CMakeDeps, or cmake_paths vs CMakeToolchain.
Here is a basic example of non-intrusive integration of conan:
conanfile.txt
[requires]
boost/1.79.0
imgui/1.88
glfw/3.3.7
[generators]
CMakeToolchain
CMakeDeps
CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(RenderCam CXX)
find_package(Boost 1.79.0 REQUIRED thread system filesystem)
find_package(glfw3 3.3 REQUIRED)
find_package(imgui 1.88 REQUIRED)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE Boost::headers Boost::thread Boost::system Boost::filesystem glfw imgui::imgui)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14)
Install dependencies, configure & build:
mkdir build && cd build
conan install .. -s build_type=Release -b missing
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
cmake --build . --config Release

Deplying a C++ application on Linux- linking everything statically to simplify deployment?

I am building a C++ project from Github and want to deploy the code to a remote Linux machine. This is all new to me.
The project has a main.cpp, which includes the various headers/sources like a library.
The CMake outputs an executable (to represent main.cpp) AND a separate static library. The project also uses OpenSSL, which I have linked statically.
I presume the OpenSSL functions are included within the static library? So when I deploy, I don't need to copy-over or install any OpenSSL on the remote machine?
Is it possible to modify the CMake so the application and the library are merged in to one file?
I am trying to make deployment as simple as copying over a single file, if this is possible.
Any additional advice/references are most-welcome.
UPDATE the CMake script:
cmake_minimum_required(VERSION 3.20)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
project(helloworld C CXX)
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_BUILD_TYPE Release)
set (BUILD_MAIN TRUE)
set (BUILD_SHARED_LIBS FALSE)
set (OPENSSL_USE_STATIC_LIBS TRUE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set( HELLOWORLD_HEADERS helloworld/File1.h helloworld/File2.h )
set( HELLOWORLD_SOURCES helloworld/File1.cpp helloworld/File2.cpp )
# Static library
add_library( helloworld ${HELLOWORLD_SOURCES} ${HELLOWORLD_HEADERS} )
# Rapidjson
include_directories(/tmp/rapidjson/include/)
# OpenSSL
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
add_definitions(${OPENSSL_DEFINITIONS})
target_include_directories(helloworld PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
target_link_libraries(helloworld PRIVATE ${OPENSSL_LIBRARIES})
set( HELLOWORLD_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
include(GNUInstallDirs)
target_include_directories(helloworld PUBLIC
$<BUILD_INTERFACE:${HELLOWORLD_INCLUDE_DIRS}/>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/helloworld>
)
set_target_properties(helloworld PROPERTIES PUBLIC_HEADER "${HELLOWORLD_HEADERS}")
add_library(helloworld::helloworld ALIAS helloworld)
option(HELLOWORLD_INSTALL "Install HelloWorld" TRUE)
if (HELLOWORLD_INSTALL)
install(TARGETS helloworld
EXPORT helloworld
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/helloworld/
)
configure_file("${CMAKE_CURRENT_LIST_DIR}/helloworld-config.cmake.in" "${CMAKE_BINARY_DIR}/helloworld-config.cmake" #ONLY)
install(FILES "${CMAKE_BINARY_DIR}/helloworld-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/helloworld")
install(EXPORT helloworld
FILE helloworld-targets.cmake
NAMESPACE helloworld::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/helloworld
)
endif()
if (BUILD_MAIN)
add_executable(main main.cpp)
target_link_libraries(main helloworld)
endif()
ITNOA
I it is very helpful to make URL of your GitHub's project, but I write some public notes about that
In generally in CMake for static linking your library to your executable, you can write simple like below (from official CMake example)
add_library(archive archive.cpp zip.cpp lzma.cpp)
add_executable(zipapp zipapp.cpp)
target_link_libraries(zipapp archive)
In above example your executable file is just work without needing .a library file and you can simple copy single file.
if you want to make all of thing static, you make sure all dependencies make static link to your project, like CMake: how to produce binaries "as static as possible"
if you want to prevent library creation, Probably in your CMake file, you can find add_library command, and add_executable command. you can remove add_library command and add all sources to add_executable command.
for example add_executable(a.out main.cpp lib.cpp)

CMake imported targets in add_subdirectory not available in main CMakeLists.txt

I want to build an application that depends on the OpenCV (version 3.4.6) viz module. This module has the VTK library (version 7.1.1) as dependency. I want to use ExternalProject to build both, the vtk library and the opencv viz module and subsequently want to build the main application, all in one cmake run.
.
├── CMakeLists.txt
├── deps
│   └── CMakeLists.txt
└── main.cpp
I am using the cmake ExternalProject module to build both opencv and vtk inside a subdirectory like this:
deps/CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(dependencies)
include(ExternalProject)
ExternalProject_add(
vtklib
GIT_REPOSITORY https://github.com/Kitware/vtk
GIT_TAG v7.1.1
GIT_PROGRESS TRUE
UPDATE_COMMAND ""
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
-DBUILD_TESTING=OFF
-DBUILD_EXAMPLES=OFF
-DVTK_DATA_EXCLUDE_FROM_ALL=ON
-DVTK_USE_CXX11_FEATURES=ON
-Wno-dev
)
add_library(vtk INTERFACE IMPORTED GLOBAL)
add_dependencies(vtk vtklib)
ExternalProject_add(
ocv
GIT_REPOSITORY https://github.com/opencv/opencv
GIT_TAG 3.4.6
GIT_PROGRESS TRUE
UPDATE_COMMAND ""
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
-DWITH_VTK=ON
-Wno-dev
)
# ExternalProject_Get_Property(ocv install_dir)
# include_directories(${install_dir}/src/ocv/include)
include_directories(${CMAKE_INSTALL_PREFIX}/include)
set(ocv_libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_VS_PLATFORM_NAME}/vc15)
set(OCV_VERSION 346)
add_dependencies(ocv vtklib)
add_library(opencv_core SHARED IMPORTED)
set_target_properties(opencv_core PROPERTIES
IMPORTED_IMPLIB "${ocv_libdir}/lib/opencv_core${OCV_VERSION}.lib"
IMPORTED_LOCATION "${ocv_libdir}/bin/opencv_core${OCV_VERSION}.dll"
)
add_library(opencv_viz SHARED IMPORTED)
set_target_properties(opencv_viz PROPERTIES
IMPORTED_IMPLIB "${ocv_libdir}/lib/opencv_viz${OCV_VERSION}.lib"
IMPORTED_LOCATION "${ocv_libdir}/bin/opencv_viz${OCV_VERSION}.dll"
)
the main CMakeLists.txt looks like this:
cmake_minimum_required(VERSION 3.14)
project(cmaketest VERSION 0.1 DESCRIPTION "" LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_Flags "${CMAKE_CXX_FLAGS} -std=c++17")
# include_directories(${CMAKE_INSTALL_PREFIX}/include)
add_subdirectory(deps)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} opencv_core opencv_viz)
install(
TARGETS ${PROJECT_NAME}
EXPORT "${PROJECT_NAME}-targets"
LIBRARY DESTINATION lib/
ARCHIVE DESTINATION lib/${CMAKE_PROJECT_NAME}
RUNTIME DESTINATION bin
PUBLIC_HEADER DESTINATION include/${CMAKE_PROJECT_NAME}/${PROJECT_NAME}
)
the main.cpp for completeness:
#include <opencv2/viz.hpp>
int main(){}
but it seems that the include_directories and add_library calls inside deps/CMakeLists.txt do not work on the correct scope as i am getting the following error messages:
error C1083: File (Include) can not be opened: "opencv2/viz.hpp"
if i uncomment the include_directories inside the main CMakeLists.txt then i get a linker error (this is not what i want, included directories should be specified inside deps/CMakeLists.txt):
LNK1181: opencv_core.lib can not be opened
If i just copy the contents of deps/CMakeLists.txt in the main CMakeLists.txt in place of the add_subdirectory call everything works fine.
So, how do i get the include directories and the created targets from the subdirectory in my main CMakeLists?
edit:
call to cmake configure:
cmake.exe -B build -S . -G "Visual Studio 17 2022" -A x64 -T v141 -DCMAKE_INSTALL_PREFIX=D:/test
call to cmake build:
cmake.exe --build build --config Release --target install
Unlike to normal targets, which are global, an IMPORTED target by default is local to the directory where it is created.
For extend visibility of the IMPORTED target, use GLOBAL keyword:
add_library(opencv_core SHARED IMPORTED GLOBAL)
This is written in the documentation for add_library(IMPORTED):
The target name has scope in the directory in which it is created and below, but the GLOBAL option extends visibility.
cmake has couple steps:
configuration
generation (depends on configuration)
build (depends on generation)
test (depends on build)
install (depends on build)
Now problem is that your build step depends on result of install step. This happens here:
set(ocv_libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_VS_PLATFORM_NAME}/vc15)
and when this variable is used.
This is causing that to be able to complete build step you need success in install step. And cmake will do install step after successful build. So you have dependency cycle.
Instead importing opencv as shared library:
add_library(opencv_viz SHARED IMPORTED)
Link your target with targets created by imported project of opnecv.

Cannot build cmake project with including directories in cmake project

|---Engine
|----CMakeList.txt
|----Engine.cpp
|----Engine.h
|---Models
|----CMakeList.txt
|----Snake.cpp
|----Snake.hpp
|---Type
|----CMakeList.txt
|----Point.hpp
|---CMakeList.txt
|---main.cpp
I just can't tell the main sheet that I want to add a directory with files to it ...
Each time, errors of the following type appear:
CMake Error at CMakeLists.txt: 12 (target_include_directories):
Cannot specify include directories for target "Snake" which is not built by
this project.
CMake Error at CMakeLists.txt: 13 (target_link_directories):
Cannot specify link directories for target "Snake" which is not built by
this project.
CMake Error at Engine / CMakeLists.txt: 8 (target_include_directories):
Cannot specify include directories for target "Snake" which is not built by
this project.
CMake Error at CMakeLists.txt: 17 (target_link_libraries):
Cannot specify link libraries for target "Snake" which is not built by this
project.
- Configuring incomplete, errors occurred!
Which, for several hours of intensified attempts, are not corrected in any way. Can anyone help with this?
CMakeLists.txt from Engine folder:
set(HEADERS
Engine.h)
set(SOURCES
Engine.cpp)
add_library(Engine ${HEADERS} ${SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR})
Main CMakeList.txt:
cmake_minimum_required(VERSION 3.16.3)
project(Snake)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(PROJECT_SOURCES
main.cpp
)
target_include_directories(${PROJECT_NAME} PUBLIC Engine)
target_link_directories(${PROJECT_NAME} PUBLIC Engine)
add_subdirectory(Engine)
target_link_libraries(${PROJECT_NAME} Engine)
add_executable(${PROJECT_NAME}
${PROJECT_SOURCES}
)
find_package(SFML 2.5.1 COMPONENTS graphics audio REQUIRED)
include_directories(${SFML_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} sfml-graphics sfml-audio)
set(SFML_STATIC_LIBRARIES FALSE)
There are two errors. The first one is to call a PROJECT_NAME that hasn't been built yet. add_executable(${PROJECT_NAME} ...) and add_library(${PROJECT_NAME ...) should be used before the target_include_directories and target_link_directories that refers to it. Because, as #Stephen Newell noted, otherwise you will be using a PROJECT NAME that hasn't been declared/built yet.
The second error is that you are calling a PROJECT_NAME inside Engine/CMakeLists. Maybe you are missing a project(...) inside of it.
Maybe you should move set(SFML_STATIC_LIBRARIES FALSE) before linking it, as well.