1

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.

  • If you want to merge an existing shared library into your final library, that's not possible (without lots of pain) – n. m. could be an AI May 24 '22 at 10:25
  • 2
    OTOH if the other library is static, then it is usually built without PIC and so is unusable in a shared library. – n. m. could be an AI May 24 '22 at 10:28
  • 1
    If you expose glfw symbols in public headers of your own library, what you are trying to achieve is bad. If you do not expose glfw symbols in public headers, then it's neater and you shared lib is sufficient, but indeed glfw static lib must be compiled with PIC. – SpacePotatoes May 24 '22 at 11:13
  • @SpacePotatoes At the end, I don't want the user to care about GLFW at all, so I'm not intending to put the GLFW headers in my lib. The issue is that the implementation of my lib is using it, and throws errors whenever my lib is used. – LucioleMaléfique May 24 '22 at 13:02
  • @n.1.8e9-where's-my-sharem. I got the sources of glfw, so I can compile it how I want to. May I ask what is PIC, and how would it help me to merge GLFW in my lib ? – LucioleMaléfique May 24 '22 at 13:04
  • So it's quite simple, compile glfw static with PIC (Position Independent code), then link glfw into your shared lib. To compile glfw with PIC, just inject `CMAKE_POSITION_INDEPENDENT_CODE ON` during its CMake configuration. – SpacePotatoes May 24 '22 at 14:32
  • But in your CMakeLists, I don't even see a `target_link_libraries(myLib PRIVATE glfw)`... So how could it work in the first place? Is glfw compiled externally, is it vendored? It's quite confusing. It seems vendored, but you should have a `add_subdirectory()` somewhere to build glfw, including header files only of a non-header-only lib doesn't make sense. – SpacePotatoes May 24 '22 at 14:40

1 Answers1

1

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
SpacePotatoes
  • 659
  • 5
  • 11
  • This worked ! Thanks ! I have a question tho, is it useful to save the BUILD_SHARED_LIB and CMAKE_POSITION_INDEPENDENT_CODE variables to reassign them afterwards ? or is it just a good practice ? – LucioleMaléfique May 25 '22 at 09:20