Why does CMake fail to link my static library? - c++

I am trying to link a static library which I have compiled from the assimp source to my executable CMake project so that it does not have to compile assimp as often since it's such a large library.
I compiled it using CMake with the following commands:
git clone https://github.com/assimp/assimp/
cd assimp
mkdir build
cd build
cmake -DBUILD_SHARED_LIBS=OFF ../CMakeLists.txt
cmake --build .
My project structure looks like this:
.
OpenGL
├── OpenGL
│ ├── res
│ ├── src
│ | └── (my .cpp and .h files)
│ └── CMakeLists.txt
├── libraries
│ └── glfw, glew, imgui...
└── CMakeLists.txt
I now copy assimp/include and assimp/build/bin/Debug/config.h to OpenGL/libraries/assimp/include,
then assimp/build/lib/Debug to OpenGL/libraries/assimp/lib
My CMakeLists.txt(s) look like this:
cmake_minimum_required(VERSION 3.8)
project(OpenGL LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_FIND_DEBUG_MODE)
if (NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.1")
endif()
if (CMAKE_BUILD_TYPE STREQUAL Debug)
add_definitions(-DDEBUG) # preprocessor macro
else()
if (MSVC)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") # if running on release mode remove the cmd prompt on windows
endif()
endif()
add_subdirectory(OpenGL)
add_subdirectory(libraries/glfw)
add_subdirectory(libraries/glew)
add_subdirectory(libraries/nativefiledialog)
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/libraries/glfw/include
${CMAKE_CURRENT_SOURCE_DIR}/libraries/glew/include
OpenGL/src
libraries
libraries/stb
libraries/assimp/include
libraries/nativefiledialog/src/include
)
and
cmake_minimum_required(VERSION 3.8)
file(GLOB_RECURSE OpenGL_sources *.cpp ../libraries/imgui/*.cpp ../libraries/stb/*.cpp)
add_executable(${PROJECT_NAME} ${OpenGL_sources} "src/engine/input/Key.cpp" "src/engine/input/Buttons.h" "src/engine/input/Button.h" "src/engine/input/Button.cpp" "src/engine/input/MouseButton.h" "src/engine/input/MouseButton.cpp" "src/engine/rendering/objects/Model.h" "src/engine/rendering/objects/Model.cpp")
target_link_libraries(${PROJECT_NAME} PUBLIC
glfw
libglew_static
${CMAKE_SOURCE_DIR}/libraries/assimp/lib/assimp-vc143-mtd.lib
nativefiledialog
)
or
cmake_minimum_required(VERSION 3.8)
add_library( assimp STATIC IMPORTED )
set_property( TARGET assimp PROPERTY IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libraries/assimp/lib/assimp-vc143-mtd.lib)
file(GLOB_RECURSE OpenGL_sources *.cpp ../libraries/imgui/*.cpp ../libraries/stb/*.cpp)
add_executable(${PROJECT_NAME} ${OpenGL_sources} "src/engine/input/Key.cpp" "src/engine/input/Buttons.h" "src/engine/input/Button.h" "src/engine/input/Button.cpp" "src/engine/input/MouseButton.h" "src/engine/input/MouseButton.cpp" "src/engine/rendering/objects/Model.h" "src/engine/rendering/objects/Model.cpp")
target_link_libraries(${PROJECT_NAME} PUBLIC
glfw
libglew_static
assimp
nativefiledialog
)
(Both configurations give the same error)
Running cmake .. from OpenGL/build successfully completes which I think means that CMake was able to find the assimp library.
Now when I run cmake --build . I am greeted with the following error.
assimp-vc143-mtd.lib(AssbinLoader.obj) : error LNK2019: unresolved external symbol uncompress referenced in function "public: virtual void __cdecl Assimp::AssbinImporter::InternReadFile(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,struct aiScene *,class Assimp::IOSystem *)" (?InternReadFile#AssbinImporter#Assimp##UEAAXAEBV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##PEAUaiScene##PEAVIOSystem#2##Z) [F:\dev\cpp\OpenGL\build\OpenGL\OpenGL.vcxproj]
assimp-vc143-mtd.lib(Compression.obj) : error LNK2019: unresolved external symbol inflate referenced in function "public: unsigned __int64 __cdecl Assimp::Compression::decompress(void const *,unsigned __int64,class std::vector<char,class std::allocator<char> > &)" (?decompress#Compression#Assimp##QEAA_KPEBX_KAEAV?$vector#DV?$allocator#D#std###std###Z) [F:\dev\cpp\OpenGL\build\OpenGL\OpenGL.vcxproj]
etc...
I believe this means that the function bodies were not found which should be compiled within the .lib.
How do I successfully link the library?

These functions (uncompress and inflate) belong to the ZLIB library. Although assimp CMake links ZLIB in order to use it - which is enough to build assimp by itself, you have to link ZLIB directly to your executable as well in order to use it in your executable context.

Related

cmake include external library

I have 2 static libraries A:\\A.lib, B:\\B.lib I want to link to using MSVS2019
This is my cmakelists.txt
add_executable(main ${src}/main.c)
SET(A "A:/")
SET(B "B:/")
find_library(AA "${A}/A.lib")
find_library(BB "${B}/B.lib")
target_link_libraries(main ${AA})
target_link_libraries(main ${BB})
I have got a lot of unresolved external symbols then.
Don't hardcode paths to external libs.
cmake_minimum_required(VERSION 3.18)
project(foo)
add_executable(bar ...)
find_library(libA NAMES A REQUIRED)
find_library(libB NAMES B REQUIRED)
target_link_libraries(bar PRIVATE ${libA} ${libB})
cmake -S . -B build -DCMAKE_LIBRARY_PATH="<path/to/libA/folder>;<path/to/libB/folder>"
Though, it would be far more robust if these two external libraries could provide a CMake config file with imported targets.

CMake when FetchContent rabbitmq

I am trying to integrate rabbitmq into one of the modules of my solution in order to switch from mailslots to rabbitmq. The problem is that during the fectcontent, the module "breaks" the links of the other modules. The errors generated are "unresolved external symbols".
##########################
# CODE CMAKELISTS
##########################
file(GLOB sources "src/*.cpp" "src/*.c")
file(GLOB headers "inc/*.hpp" "inc/*.h")
project("${MODULE_NAME}${MODULE_TYPE}")
set(OUTPUT_FILE "${MODULE_NAME}${MODULE_TYPE}")
OPTION(BUILD_TESTS "Build the tests" OFF)
MESSAGE(STATUS "Building : ${PROJECT_NAME}...")
if(BUILD_TESTS)
enable_testing()
ADD_SUBDIRECTORY(test)
MESSAGE(STATUS "Building : ${PROJECT_NAME} test...")
endif()
################################
# RabbitMQ
################################
SET(ENABLE_SSL_SUPPORT OFF CACHE PATH "Shall RabbitMQ be built with ssl support" FORCE)
include(FetchContent)
FetchContent_Declare(
rabbitmq
GIT_REPOSITORY https://github.com/alanxz/rabbitmq-c.git
GIT_TAG v0.10.0
)
FetchContent_GetProperties(rabbitmq)
if(NOT rabbitmq_POPULATED)
FetchContent_Populate(rabbitmq)
add_subdirectory(${rabbitmq_SOURCE_DIR} ${rabbitmq_BINARY_DIR})
endif()
SET_SOURCE_FILES_PROPERTIES( ${sources} PROPERTIES LANGUAGE CXX )
SET_SOURCE_FILES_PROPERTIES( ${headers} PROPERTIES LANGUAGE CXX )
Type of error:
LNK2001 external symbol not resolved "struct fsTUN_GlobalSection_t tun" (?tun##3UfsTUN_GlobalSection_t##A) MOD.LIB fsMOD_maxSpeed.obj 1
LNK2019 external symbol not resolved "long __cdecl fsRTS_MaxSpeed(long,struct _iobuf *,long,long,long,long,struct fs_products *,struct fsSEQ_Adaptation *,double,double,double,double,double *,double *,struct rts_mod *)" (?fsRTS_MaxSpeed##YAJJPEAU_iobuf##JJJJPEAUfs_products##PEAUfsSEQ_Adaptation##NNNNPEAN3PEAUrts_mod###Z) referenced in the function "long __cdecl fsMOD_maxSpeed(struct fs_products *,struct fsSEQ_Adaptation *)" (?fsMOD_maxSpeed##YAJPEAUfs_products##PEAUfsSEQ_Adaptation###Z) MOD.LIB ..\fsMOD_maxSpeed.obj 1

How to include external modules headers when FetchContent_Declare is used?

I want to use modern CMake project structure convention in my new project to follow good practices and to be consistent with many other projects, but I'm facing problem with include path.
My directories structure looks as follows:
.
├── build
├── CMakeLists.txt
├── extern
│ └── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
I want to use FetchContent_Declare instead of git submodules to manage the third-party libraries.
The root CMakeLists.txt:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
set(ProjectName "MyApp")
project(${ProjectName})
add_subdirectory(extern)
add_subdirectory(src)
The extern CMakeLists.txt:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
include(FetchContent)
FetchContent_Declare(
extern_spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.8.2)
FetchContent_GetProperties(extern_spdlog)
if(NOT extern_spdlog_POPULATED)
FetchContent_Populate(extern_spdlog)
add_subdirectory(
${extern_spdlog_SOURCE_DIR}
${extern_spdlog_BINARY_DIR}
)
endif()
The src CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
set(BINARY ${CMAKE_PROJECT_NAME})
file(GLOB_RECURSE SOURCES LIST_DIRECTORIES true *.h *.cpp)
set(SOURCES ${SOURCES})
add_executable(${BINARY}_run ${SOURCES})
add_library(${BINARY}_lib STATIC ${SOURCES})
target_link_libraries(${BINARY}_run extern_spdlog)
And the main.cpp:
#include <spdlog/spdlog.h>
int main(int argc, char* argv[]) {
spdlog::info("Welcome to spdlog!");
return 0;
}
CMake executes and the spdlog library is cloned properly and can be found in the build directory at: build/_deps/extern_spdlog-src.
If I call make in the build directory then I get fatal error: spdlog/spdlog.h: No such file or directory.
How to add include directories for the external libraries used in the application?
I was initially confused about this as well. It doesn't help that the names of the FetchContent targets often match the names of the imported targets. What worked for me was similar (though not identical) to what the first comment states. I used target_link_libraries(target spdlog::spdlog) (if you want to use the pre-compiled library or spdlog::spdlog_header_only if you want to use the header source only version.

CMake unit testing - Unresolved external symbol

I am rather new to C++ and CMake. I'm trying to make a library, and I'm getting the errors when I try to run my unit tests. From my research I've already gathered what the "Unresolved external symbol" errors mean, but I'm unable to figure out how to fix it.
Here's my project structure:
lib
glfw - GLFW source folder
src
ogl-renderer.cpp
ogl-renderer.h
CMakeLists.txt
...additional source files
test
ogl-test.cpp
ogl-test.h
CMakeLists.txt
CMakeLists.txt
CMakeLists.txt:
cmake_minimum_required (VERSION 3.8)
project("ogl-renderer")
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory ("lib/glfw-3.3.2")
add_subdirectory ("src")
add_subdirectory ("test")
enable_testing()
add_test (ogl-renderer olg-test)
src/CMakeLists.txt:
cmake_minimum_required (VERSION 3.8)
add_library (ogl-renderer "engine/renderer.cpp" "engine/renderer.h" "engine/renderer.cpp" "engine/renderer.h" "engine/Window.cpp" "engine/Window.h" "engine/Shape.cpp" "engine/Shape.h" "engine/message-queue.cpp" "engine/message-queue.h" "engine/messages/window-mgmt.h" "engine/messages/window-mgmt.cpp")
target_link_libraries(ogl-renderer glfw)
test/CMakeLists.txt:
add_executable (ogl-test "ogl-test.cpp" "ogl-test.h")
target_link_libraries(ogl-test ogl-renderer)
test/ogl-test.cpp:
#include "../src/ogl-renderer.h"
void testWindow() {
NglRenderer::startRenderer();
int windowId = NglRenderer::createWindow("Test", 640, 480);
}
int main() {
testWindow();
}
Errors:
Error LNK2019 unresolved external symbol "void __cdecl NglRenderer::startRenderer(void)" (?startRenderer#NglRenderer##YAXXZ) referenced in function "void __cdecl testWindow(void)" (?testWindow##YAXXZ) C:\Users\chansen\source\repos\ogl-renderer\out\build\x64-Debug\ogl-renderer C:\Users\chansen\source\repos\ogl-renderer\out\build\x64-Debug\ogl-test.cpp.obj 1
Error LNK2019 unresolved external symbol "int __cdecl NglRenderer::createWindow(char *,int,int)" (?createWindow#NglRenderer##YAHPEADHH#Z) referenced in function "void __cdecl testWindow(void)" (?testWindow##YAXXZ) C:\Users\chansen\source\repos\ogl-renderer\out\build\x64-Debug\ogl-renderer C:\Users\chansen\source\repos\ogl-renderer\out\build\x64-Debug\ogl-test.cpp.obj 1
I am able to fix it by always including the .h and .cpp files in every location, but I want to figure this out the right way. I understand that I need to compile the source project into a library, then link that to the test executable, but I can't figure out how to do it correctly. I'm not even entirely certain how correct my CMakeList.txt files even are. I either guessed or copied on all these configurations.
Each CMakeLists.txt file defines a project to be compiled. And each project must contain all of its files, dependencies and build options.
So "test/CMakeLists.txt" must contain all relevant test source files. And "src/CMakeLists.txt" must contain all relevant src source files. This is why adding the .h and .cpp files fixed the error.
After each project is configured, linked libraries are defined with target_link_libraries().
For example in src the line target_link_libraries(ogl-renderer glfw) will link to glfw. And in test the line target_link_libraries(ogl-test ogl-renderer) will link to your src executable.
As long as the target_link_libraries() commands make sense, linking will be automated.
Some threads with more information:
What does enable_testing() do in cmake?
How to start working with GTest and CMake

CMake CUDA separate compilation static lib link error on Windows but not on Ubuntu

I am using CMake to compile a CUDA project, which contains a static lib and a main file. MWE here. The directory is:
├── CMakeLists.txt
├── src
├── mylib.h
├── mylib.cu
├── test
├── CMakeLists.txt
├── main.cpp
On Ubuntu everything works fine. But on Windows I got a link error:
mylib.lib(mylib.cu.obj) : error LNK2019: unresolved external symbol __cudaRegisterLinkedBinary_40_tmpxft_00006024_00000000_7_mylib_cpp1_ii_935b38c5 referenced in function "void __cdecl __sti____cudaRegisterAll(void)" (?__sti____cudaRegisterAll##YAXXZ)\build\test\Release\main.exe : fatal error LNK1120: 1 unresolved externals
This issue only relates to the first CMakeLists.txt:
cmake_minimum_required(VERSION 3.8)
project(MyTest LANGUAGES CXX CUDA)
# check requirements
find_package(CUDA 8.0 REQUIRED)
# set include and link directories
if (UNIX)
set(CUDA_SAMPLE_INC ${CUDA_TOOLKIT_ROOT_DIR}/samples/common/inc)
set(CUDA_TARGET_INC ${CUDA_TOOLKIT_ROOT_DIR}/targets/x86_64-linux/include)
set(CUDA_SAMPLE_LKN ${CUDA_TOOLKIT_ROOT_DIR}/targets/x86_64-linux/lib)
endif (UNIX)
if (WIN32)
set(CUDA_SAMPLE_INC C:/ProgramData/NVIDIA\ Corporation/CUDA\ Samples/v9.0/common/inc)
set(CUDA_TARGET_INC C:/Program\ Files/NVIDIA GPU\ Computing\ Toolkit/CUDA/v9.0/include)
set(CUDA_SAMPLE_LKN C:/Program\ Files/NVIDIA\ GPU\ Computing\ Toolkit/CUDA/v9.0/lib/x64)
endif (WIN32)
include_directories(src ${CUDA_SAMPLE_INC} ${CUDA_TARGET_INC})
link_directories(${CUDA_SAMPLE_LKN})
# define and compile our static library
set(STATIC_MY_LIB mylib)
add_library(${STATIC_MY_LIB} STATIC src/mylib.cu)
# install
install(TARGETS ${STATIC_MY_LIB}
ARCHIVE DESTINATION ${CMAKE_SOURCE_DIR}/lib
LIBRARY DESTINATION ${CMAKE_SOURCE_DIR}/lib)
# comment it out to suppress the error
set_target_properties( ${STATIC_MY_LIB} PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
# add our test project
add_subdirectory(test)
If I comment out set_target_properties( ${STATIC_MY_LIB} PROPERTIES CUDA_SEPARABLE_COMPILATION ON), the link error is gone.
Environments:
Ubuntu 16.04, gcc 5.4, Tesla Titan X, CUDA 9.1, CMake 3.10.1
Windows 10, VS 2015, K20c, CUDA 9.0, CMake 3.10.1
I have tried suggestions in 1, 2. But none of them works.
Why this happens? And how to overcome?
After struggling with this issue for several days, I think I have found a solution. When creating the static library using CMake, set CUDA_RESOLVE_DEVICE_SYMBOLS, i.e. with
set_target_properties(your_project PROPERTIES CUDA_RESOLVE_DEVICE_SYMBOLS ON)
According to this article from Nvidia, this setting forces CMake to compile and link all CUDA symbols (function calls and the like) when the library is built.
If you need separable compilation device linking to occur before
consumption by a shared library or executable,you can explicitly
request CMake to invoke device linking by setting the target property
CUDA_RESOLVE_DEVICE_SYMBOLS