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.
Related
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.
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.
I'm trying to build a simple C++ program in Visual Studio 2017. I had no issues while doing it on Linux but for some reason it doesn't work on Windows.
Here is the program:
// CMakeProject1.cpp : Defines the entry point for the application.
//
#include "CMakeProject1.h"
#include "fmt/core.h"
#include "fmt/xchar.h"
#include "spdlog/sinks/win_eventlog_sink.h"
#include "spdlog/sinks/msvc_sink.h"
#include "spdlog/sinks/wincolor_sink.h"
#include "spdlog/spdlog.h"
#include <exception>
#include <vector>
int main()
{
std::vector<spdlog::sink_ptr> sinks;
sinks.push_back(std::make_shared<spdlog::sinks::win_eventlog_sink_mt>("me"));
sinks.push_back(std::make_shared<spdlog::sinks::msvc_sink_mt>());
sinks.push_back(std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>());
auto logger = std::make_shared<spdlog::logger>("main-logger", begin(sinks), end(sinks));
SPDLOG_LOGGER_CALL(logger, spdlog::level::info, L"{}", L"Hello World!");
return 0;
}
Here is my CMakeLists.txt
# CMakeList.txt : Top-level CMake project file, do global configuration
# and include sub-projects here.
#
cmake_minimum_required(VERSION 3.20)
get_filename_component(Project ${CMAKE_CURRENT_SOURCE_DIR} NAME)
string(REPLACE " " "-" Project ${Project})
project(${Project} CXX C)
set(CMAKE_VERBOSE_MAKEFILE ON)
include(FetchContent)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 8.0.1
)
FetchContent_MakeAvailable(fmt)
if (NOT fmt_POPULATED)
FetchContent_Populate(fmt)
add_subdirectory(${fmt_SOURCE_DIR} ${fmt_BINARY_DIR})
endif()
message(STATUS "fmt_SOURCE_DIR = ${fmt_SOURCE_DIR}")
message(STATUS "fmt_BINARY_DIR = ${fmt_BINARY_DIR}")
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.9.2
)
FetchContent_MakeAvailable(spdlog)
if (NOT spdlog_POPULATED)
FetchContent_Populate(spdlog)
set(SPDLOG_BUILD_EXAMPLES OFF)
set(SPDLOG_BUILD_BENCH OFF)
set(SPDLOG_BUILD_TESTS OFF)
add_subdirectory(${spdlog_SOURCE_DIR} ${spdlog_BINARY_DIR})
endif()
message(STATUS "spdlog_SOURCE_DIR = ${spdlog_SOURCE_DIR}")
message(STATUS "spdlog_BINARY_DIR = ${spdlog_BINARY_DIR}")
set(SourceDir ${CMAKE_CURRENT_SOURCE_DIR})
set(ExecutableSources ${SourceDir}/main.cpp)
set(LinkLibraries fmt::fmt spdlog::spdlog)
set(Executable ${Project})
message(STATUS CMAKE_VERSION = ${CMAKE_VERSION})
message(STATUS LinkLibraries = ${LinkLibraries})
add_executable(${Executable} ${ExecutableSources})
add_definitions(-DSPDLOG_WCHAR_TO_UTF8_SUPPORT)
target_compile_options(${Executable} PUBLIC ${CompilationFlags})
target_link_libraries(${Executable} PUBLIC ${LinkLibraries})
The way I generate a build files is the same way I do it on Linux
mkdir build
cd build
cmake ..
I then open the solution in Visual Studio 2017 and build it. However I'm getting the following errors:
4>main.cpp
4>main.obj : error LNK2019: unresolved external symbol "void __cdecl spdlog::details::os::wstr_to_utf8buf(class fmt::v8::basic_string_view<wchar_t>,class fmt::v8::basic_memory_buffer<char,250,class std::allocator<char> > &)" (?wstr_to_utf8buf#os#details#spdlog##YAXV?$basic_string_view#_W#v8#fmt##AAV?$basic_memory_buffer#D$0PK#V?$allocator#D#std###56##Z) referenced in function "protected: void __thiscall spdlog::logger::log_<wchar_t const (&)[13]>(struct spdlog::source_loc,enum spdlog::level::level_enum,class fmt::v8::basic_string_view<wchar_t>,wchar_t const (&)[13])" (??$log_#AAY0N#$$CB_W#logger#spdlog##IAEXUsource_loc#1#W4level_enum#level#1#V?$basic_string_view#_W#v8#fmt##AAY0N#$$CB_W#Z)
4>main.obj : error LNK2019: unresolved external symbol "void __cdecl spdlog::details::os::utf8_to_wstrbuf(class fmt::v8::basic_string_view<char>,class fmt::v8::basic_memory_buffer<wchar_t,250,class std::allocator<wchar_t> > &)" (?utf8_to_wstrbuf#os#details#spdlog##YAXV?$basic_string_view#D#v8#fmt##AAV?$basic_memory_buffer#_W$0PK#V?$allocator#_W#std###56##Z) referenced in function "protected: virtual void __thiscall spdlog::sinks::win_eventlog::win_eventlog_sink<class std::mutex>::sink_it_(struct spdlog::details::log_msg const &)" (?sink_it_#?$win_eventlog_sink#Vmutex#std###win_eventlog#sinks#spdlog##MAEXABUlog_msg#details#4##Z)
4>C:\Users\user\source\repos\CMakeProject1\build\Debug\CMakeProject1.exe : fatal error LNK1120: 2 unresolved externals
4>Done building project "CMakeProject1.vcxproj" -- FAILED.
========== Rebuild All: 3 succeeded, 1 failed, 0 skipped ==========
It looks like Visual Studio 2017 is not linking against the libraries. Does anyone know how to fix it?
Expanding on #fabian's comment, you should
set(SPDLOG_WCHAR_TO_UTF8_SUPPORT ON)
before the add_subdirectory call to have CMake set that preprocessor symbol for you.
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.
I've just rebuilt boost 1.57 on Windows using MSVC because I want to add support for zlib:
.\b2 toolset=msvc-12.0 link=shared threading=multi address-model=64 --build-type=complete install -s NO_ZLIB=0 -s ZLIB_SOURCE=D:\zlib128
This builds fine and generates new files boost_zlib-vc120-mt-1_57.dll and boost_zlib-vc120-mt-1_57.lib.
I'm then using cmake to add boost to my project:
FIND_PACKAGE(Boost ${BOOST_VERSION} EXACT REQUIRED COMPONENTS system date_time filesystem iostreams regex thread zlib)
if(Boost_FOUND)
ADD_DEFINITIONS(${BOOST_DEFINITIONS} -DBOOST_ALL_NO_LIB)
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
LINK_DIRECTORIES(${Boost_LIBRARY_DIRS})
TARGET_LINK_LIBRARIES(foobar ${Boost_LIBRARIES})
endif()
In Visual Studio, I see in "Additional Dependencies" that the boost_zlib-vc120-mt-1_57.lib is now also listed there. But trying to compile this example fails with a linker error:
Error 1730 error LNK2019: unresolved external symbol "int const boost::iostreams::zlib::default_compression" (?default_compression#zlib#iostreams#boost##3HB) referenced in function "public: __cdecl boost::iostreams::detail::zlib_decompressor_impl >::zlib_decompressor_impl >(int)" (??0?$zlib_decompressor_impl#V?$allocator#D#std###detail#iostreams#boost##QEAA#H#Z)
How can I fix this?