LucioleMaléfique
LucioleMaléfique

Reputation: 770

Including a C++ library into another?

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.

Upvotes: 1

Views: 383

Answers (1)

SpacePotatoes
SpacePotatoes

Reputation: 859

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

Upvotes: 1

Related Questions