How can I use a library from a git submodule with CMake? - c++

I have a big project where I want to introduce a new library, both are mantained by me so full access to the code, CMakeLists etc
In Linux it is really easy, I just install the library with make install and then add it to my project with find_package(mynewlib REQUIRED). Then I use it as any other external library, target_link_libraries, add the headers and everything goes fine.
In Windows, I cannot make install, so I add it as a git submodule and use it like this:
find_package(MYLIB QUIET)
if(MYLIB_FOUND)
set_property(GLOBAL APPEND PROPERTY CUSTOM_STATUS MYLIB)
set_property(GLOBAL APPEND PROPERTY CUSTOM_STATUS_MYLIB
" MYLIB:" "version ${MYLIB_VERSION}")
else()
add_subdirectory(modules/mylib)
endif()
set(ALL_LIBS
Qt5::Core
Qt5::Widgets
MYLIB)
It doesn't work, linker starts complaining about unresolved symbols in different parts of the code, not related with the new introduced library. For example:
[build] Main.cpp.obj : error LNK2019: unresolved external symbol "int
__cdecl qInitResources_application(void)" (?qInitResources_application##YAHXZ) referenced in function main
[build] Main.cpp.obj : error LNK2001: unresolved external symbol
"public: static struct Version const Version::SOFTWARE_VERSION"
(?SOFTWARE_VERSION#Version##2U1#B) [build] targetapp\targetappcore.dll
: fatal error LNK1120: 52 unresolved externals
My question is, is my approach correct? MYLIB has a very big CMakeLists.txt I am currently analyzing in case it conflicts with the top level one, but if it was fine, is this how to do it?
I will consider using a package manager like vcpkg or conan in the future, but right now I would like to keep with git submodules.

Related

Unresolved External Symbol LNK2019 CMake

I have here a class called engine and im trying to use, but when i include it i get LNK2019 error. Im running Visual Studio 2019 x86_64 compiler. Any ideas what could be wrong?
I have constructor and destructor defined in cpp file.
#pragma once
namespace NRD {
const int SCREEN_WIDTH(1280);
const int SCREEN_HEIGHT(720);
class Engine {
public:
Engine();
Engine(const Engine&) = delete;
Engine& operator=(const Engine&) = delete;
~Engine();
static Engine& Ref() {
static Engine reference;
return reference;
}
};
static Engine& Core = Engine::Ref();
}
Here is my cpp file:
#include "nrdengine/engine.h"
#include "nrdengine/service_locator.h"
#include "platform/glfw_window.h"
#include <iostream>
namespace NRD {
Engine::Engine() {
std::cout << "Initializing window!" << std::endl;
ServiceLocator::Provide(new CustomWindow());
}
Engine::~Engine() {
ServiceLocator::GetWindow()->DestroyWindow();
}
}
[build] main.obj : error LNK2019: unresolved external symbol "public: __cdecl NRD::Engine::Engine(void)" (??0Engine#NRD##QEAA#XZ) referenced in function "public: static class NRD::Engine & __cdecl NRD::Engine::Ref(void)" (?Ref#Engine#NRD##SAAEAV12#XZ) [C:\Users\Dawid\Desktop\NRD-Engine\build\nrdengine_cpp_application.vcxproj]
[build] main.obj : error LNK2019: unresolved external symbol "public: __cdecl NRD::Engine::~Engine(void)" (??1Engine#NRD##QEAA#XZ) referenced in function "void __cdecl `public: static class Engine::Ref & __cdecl NRD::Engine::Ref(void)'::`2'::`dynamic atexit destructor for 'reference''(void)" (??__Freference#?1??Ref#Engine#NRD##SAAEAV12#XZ#YAXXZ) [C:\Users\Dawid\Desktop\NRD-Engine\build\nrdengine_cpp_application.vcxproj]
[build] C:\Users\Dawid\Desktop\NRD-Engine\build\Debug\nrdengine_cpp_application.exe : fatal error LNK1120: 2 unresolved externals [C:\Users\Dawid\Desktop\NRD-Engine\build\nrdengine_cpp_application.vcxproj]
I have two CMakeList files (one for Engine project, one for App)
APP:
cmake_minimum_required(VERSION 3.5)
project(nrdengine_cpp_application VERSION 0.0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
add_subdirectory(external/engine)
file(GLOB_RECURSE SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/src/*.c
${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
#nrdengine_cpp_application.exe
add_executable(nrdengine_cpp_application ${SOURCE_FILES})
target_link_libraries(nrdengine_cpp_application PUBLIC nrdengine_cpp_engine)
target_include_directories(nrdengine_cpp_application PUBLIC nrdengine_cpp_engine)
ENGINE:
cmake_minimum_required(VERSION 3.5)
project(nrdengine_cpp_engine VERSION 0.0.1 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
#add external libs
#find_package(OpenGL REQUIRED)
# install python and Jinja2
set(GLAD_SOURCES_DIR "${PROJECT_SOURCE_DIR}/external/glad")
add_subdirectory("${GLAD_SOURCES_DIR}/cmake" glad_cmake)
add_subdirectory(external/glfw)
add_subdirectory(external/glm)
add_subdirectory(external/assimp)
file(GLOB_RECURSE SOURCE_FILES
${CMAKE_SOURCE_DIR}/src/*.c
${CMAKE_SOURCE_DIR}/src/*.cpp)
glad_add_library(glad_gl_core_mx_31 REPRODUCIBLE MX API gl:core=3.1)
add_library(nrdengine_cpp_engine ${SOURCE_FILES})
target_include_directories(nrdengine_cpp_engine
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
glm
PRIVATE
glfw
assimp
)
target_link_libraries(${PROJECT_NAME}
PUBLIC
glm
PRIVATE
glad_gl_core_mx_31
glfw
assimp
)```
I'm kinda guessing since your question is kinda hard to answer since it could be a lot of things. But here is my inference.
It's not recommended to glob your source files like you are doing here.
file(GLOB_RECURSE SOURCE_FILES
${CMAKE_SOURCE_DIR}/src/*.c
${CMAKE_SOURCE_DIR}/src/*.cpp)
NOTE: My suggestion requires 3.13 or higher version of CMake.
This causes lots of issues and might be the cause of your problems. Please see this post by one of the CMake maintainers on how to add source files to your project.
https://crascit.com/2016/01/31/enhanced-source-file-handling-with-target_sources/
TLDR:
Make a CMakeLists.txt in your src/ directory like so.
target_sources(foobar PRIVATE
engine.cpp
engine.h
)
And make sure to call add_subdirectory(src) to process that new CMakeLists.txt
If you are interested in why globbing in build systems in general is bad. I can provide links.

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

Cant build project using Boost CMake vcpkg date_time linking error

I'm trying to use boost::asio through cmake and vcpkg in my project and
everytime I touch something releated to date_time (even implicitly) I get some linking errors releated to gregorian calendar.
Someone knows why this isn't linking?
I'm new to CMake & vcpkg.
Host Environment
OS: Windows 10
Compiler: MSVC++ 14.23
To Reproduce
./vcpkg install Boost
cd $MyProjectDir
mkdir build
cd build
cmake -G "Visual Studio 16 2019" -A x64 ..
cmake --build .
problematic C++ code
#include <boost/asio.hpp>
int main() {
boost::asio::io_service service;
//boost::asio::deadline_timer timer(service); // this one causes error
}
My CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
set(CMAKE_TOOLCHAIN_FILE "C:/Repos/vcpkg/scripts/buildsystems/vcpkg.cmake")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
project(Backend)
find_package(Boost REQUIRED)
add_executable(Backend
"source/main.cpp"
)
target_link_libraries(Backend PRIVATE ${Boost_LIBRARIES})
target_include_directories(Backend PRIVATE ${Boost_INCLUDE_DIRS})
Failure logs
2>------ Build started: Project: Backend, Configuration: Debug x64 ------
2>Building Custom Rule C:/Repos/fridge/backend/CMakeLists.txt
2>main.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: __cdecl boost::gregorian::greg_month::greg_month(unsigned short)" (__imp_??0greg_month#gregorian#boost##QEAA#G#Z) referenced in function "public: __cdecl boost::gregorian::date::date(enum boost::date_time::special_values)" (??0date#gregorian#boost##QEAA#W4special_values#date_time#2##Z)
2>main.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: __cdecl boost::gregorian::greg_month::operator unsigned short(void)const " (__imp_??Bgreg_month#gregorian#boost##QEBAGXZ) referenced in function "public: static unsigned int __cdecl boost::date_time::gregorian_calendar_base<struct boost::date_time::year_month_day_base<class boost::gregorian::greg_year,class boost::gregorian::greg_month,class boost::gregorian::greg_day>,unsigned int>::day_number(struct boost::date_time::year_month_day_base<class boost::gregorian::greg_year,class boost::gregorian::greg_month,class boost::gregorian::greg_day> const &)" (?day_number#?$gregorian_calendar_base#U?$year_month_day_base#Vgreg_year#gregorian#boost##Vgreg_month#23#Vgreg_day#23##date_time#boost##I#date_time#boost##SAIAEBU?$year_month_day_base#Vgreg_year#gregorian#boost##Vgreg_month#23#Vgreg_day#23##23##Z)
2>C:\Repos\fridge\backend\build\Debug\Backend.exe : fatal error LNK1120: 2 unresolved externals
2>Done building project "Backend.vcxproj" -- FAILED.
fix was:
find_package(Boost REQUIRED COMPONENTS date_time)
Without asio because as Alan Birtles pointed out, asio is header only.

CMake generated MSVC project cannot find symbols even if lib files are correctly generated for their correlated dlls

I've been bashing my head against this problem for the last few days (my repo is linked down below). I want CMake to generate a MSVC solution for which the Engine project is linked to the Demo project. While the .lib and .dll files are correctly generated and added to the projects in MSVC, I still get unresolved linker symbol errors from the Demo project referencing symbols from the Engine project. The Engine.lib file is correctly added to Demo's dependencies, and so are the headers. I have no globals for which I'd need to use the generated exported headers. What's the issue here?
Root CMakeLists.txt:
cmake_minimum_required(VERSION 3.12)
project(P_SentryAll)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
include(GenerateExportHeader)
# glob source and header files
file(GLOB_RECURSE EngineSources SENTRY.Engine/*.cpp SENTRY.Engine/*.hpp)
file(GLOB_RECURSE CoreSources SENTRY.Core/*.cpp SENTRY.Core/*.hpp)
file(GLOB_RECURSE RenderSources SENTRY.Render/*.cpp SENTRY.Render/*.hpp)
file(GLOB_RECURSE DemoSources SENTRY.Demo/*.cpp SENTRY.Demo/*.hpp)
file(GLOB_RECURSE EngineHeaders SENTRY.Engine/*.hpp)
file(GLOB_RECURSE CoreHeaders SENTRY.Core/*.hpp)
add_subdirectory(SENTRY.Core)
add_subdirectory(SENTRY.Engine)
add_subdirectory(SENTRY.Render)
add_subdirectory(SENTRY.Demo)
Root/Sentry.Core/CMakeLists.txt:
cmake_minimum_required(VERSION 3.12)
# define project
project(P_SentryCore)
add_library(SentryCore SHARED ${CoreSources})
target_include_directories(SentryCore PUBLIC src/module)
generate_export_header(SentryCore)
install(TARGETS SentryCore DESTINATION lib)
install(FILES ${CoreHeaders} DESTINATION include/SentryCore)
Root/Sentry.Engine/CMakeLists.txt:
cmake_minimum_required(VERSION 3.12)
project(P_SentryEngine)
add_library(SentryEngine SHARED ${EngineSources})
target_link_libraries(SentryEngine PUBLIC SentryCore)
target_include_directories(SentryEngine PUBLIC src/engine)
generate_export_header(SentryEngine)
install(TARGETS SentryEngine DESTINATION lib)
install(FILES ${EngineHeaders} DESTINATION include/SentryEngine)
Root/Sentry.Demo/CMakeLists.txt:
cmake_minimum_required(VERSION 3.12)
# define project
project(P_SentryDemo)
add_executable(SentryDemo ${DemoSources})
target_link_libraries(SentryDemo PUBLIC SentryEngine)
include_directories(SENTRY.Engine/src/engine SENTRY.Core/src/module)
# packaging
install(TARGETS SentryDemo DESTINATION build)
Error:
1>------ Build started: Project: ZERO_CHECK, Configuration: Debug x64 ------
2>------ Build started: Project: SentryEngine, Configuration: Debug x64 ------
2>Engine.cpp
2>Auto build dll exports
2> Creating library C:/Users/main/Desktop/Projects/SENTRY/Build/SENTRY.Engine/Debug/SentryEngine.lib and object C:/Users/main/Desktop/Projects/SENTRY/Build/SENTRY.Engine/Debug/SentryEngine.exp
2>SentryEngine.vcxproj -> C:\Users\main\Desktop\Projects\SENTRY\Build\SENTRY.Engine\Debug\SentryEngine.dll
3>------ Build started: Project: SentryDemo, Configuration: Debug x64 ------
3>SENTRY.Demo.obj : error LNK2019: unresolved external symbol "public: void __cdecl Engine<__int64,struct std::ratio<1,1000000> >::Init(void)" (?Init#?$Engine#_JU?$ratio#$00$0PECEA##std####QEAAXXZ) referenced in function main
3>SENTRY.Demo.obj : error LNK2019: unresolved external symbol "public: void __cdecl Engine<__int64,struct std::ratio<1,1000000> >::Run(void)" (?Run#?$Engine#_JU?$ratio#$00$0PECEA##std####QEAAXXZ) referenced in function main
3>SENTRY.Demo.obj : error LNK2019: unresolved external symbol "public: void __cdecl Engine<__int64,struct std::ratio<1,1000000> >::RegisterModule(class Module<__int64,struct std::ratio<1,1000000> > *)" (?RegisterModule#?$Engine#_JU?$ratio#$00$0PECEA##std####QEAAXPEAV?$Module#_JU?$ratio#$00$0PECEA##std#####Z) referenced in function main
3>C:\Users\main\Desktop\Projects\SENTRY\Build\SENTRY.Demo\Debug\SentryDemo.exe : fatal error LNK1120: 3 unresolved externals
3>Done building project "SentryDemo.vcxproj" -- FAILED.
4>------ Skipped Build: Project: INSTALL, Configuration: Debug x64 ------
4>Project not selected to build for this solution configuration
========== Build: 2 succeeded, 1 failed, 2 up-to-date, 1 skipped ==========
Repo
The line in Root/Sentry.Demo/CMakeLists.txt:
include_directories(SENTRY.Engine/src/engine SENTRY.Core/src/module)
appears to be incorrect. It uses relative paths, so I don't believe these are valid paths in your project:
Root/Sentry.Demo/SENTRY.Engine/src/engine
Root/Sentry.Demo/SENTRY.Core/src/module
Prefer to use absolute paths wherever possible, through use of the CMAKE_SOURCE_DIR variable. This variable provides the path to the top-level source directory. So try something like this instead:
include_directories(
${CMAKE_SOURCE_DIR}/SENTRY.Engine/src/engine
${CMAKE_SOURCE_DIR}/SENTRY.Core/src/module
)
I took another look at your repo, and perhaps more importantly, you must have the full definition of Engine template functions in the header file, not the source file.
So move these function definitions to the header file, within your Engine class definition:
template<typename T_rep, typename T_ratio>
void Engine<T_rep, T_ratio>::Init()
{
for (auto& module_ : Modules)
{
module_->Init();
}
}
template<typename T_rep, typename T_ratio>
void Engine<T_rep, T_ratio>::Run()
{
RunUpdateLoop = true;
auto TPStart = std::chrono::steady_clock::now();
auto TPEnd = TPStart;
while (RunUpdateLoop)
{
auto deltaT = TPEnd - TPStart;
TPStart = std::chrono::steady_clock::now();
for (auto& module_ : Modules)
{
module_->Run((deltaT));
}
TPEnd = std::chrono::steady_clock::now();
}
}
template<typename T_rep, typename T_ratio>
void Engine<T_rep, T_ratio>::RegisterModule(Module<T_rep, T_ratio>* ToRegister)
{
Modules.push_back(ToRegister);
}
This should help get you on the right track.

cmake how to link external dynamic library from the static version of the samelibrary

I have an external package exp1 which has both static and dynamic libraries
exp1
-- include
-- lib // 1.dll, 1.lib
Now in the cmake of the exp1 package i have added the commands
add_library(${XS_SHARED_LIB} SHARED IMPORTED GLOBAL)
set_target_properties(${XS_SHARED_LIB} PROPERTIES
IMPORTED_LOCATION ${XS_DIR}/lib/${XS_SHARED_LIB}_1.dll
IMPORTED_IMPLIB ${XS_DIR}/lib/${XS_SHARED_LIB}.lib
)
Now in other external package, I am trying to link the above exp1 package statically
target_link_libraries(xrlib
${XS_SHARED_LIB}
)
But that is resulting linking issue with the ${XS_SHARED_LIB}_1.dll
error LNK2019: unresolved external symbol
fatal error LNK1107: invalid or corrupt file: cannot read at 0x360
Let me know the solution for this.