CMake project cannot link with Boost.Test statically or dynamically - c++

I am trying to setup a simple C++ project with CMake and Boost libraries (Boost.Test in this case). I am following this guide except trying to link statically: https://www.jetbrains.com/help/clion/boost-test-support.html. I have installed Boost using this guide: https://www.boost.org/doc/libs/1_73_0/more/getting_started/windows.html and successfully followed step 5 to generate the library binaries.
Using the below CMake configuration, I get this error when trying to build with cmake --build .:
test1.obj : error LNK2001: unresolved external symbol "public: virtual void __cdecl boost::unit_test::unit_test_log_t::test_start(unsigned long)" (?test_start#unit_test_log_t#unit_test#boost##UEAAXK#Z)
tests2.obj : error LNK2001: unresolved external symbol "public: virtual void __cdecl boost::unit_test::unit_test_log_t::test_start(unsigned long)" (?test_start#unit_test_log_t#unit_test#boost##UEAAXK#Z)
MSVCRTD.lib(exe_main.obj) : error LNK2019: unresolved external symbol main referenced in function "int __cdecl invoke_main(void)" (?invoke_main##YAHXZ)
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(cmake_boost_test)
set(CMAKE_CXX_STANDARD 14)
add_executable(cmake_boost_test main.cpp)
add_subdirectory(Boost_tests)
Boost_tests/CMakeLists.txt
set(Boost_DEBUG ON)
set(Boost_USE_STATIC_LIBS ON)
find_package(Boost REQUIRED COMPONENTS unit_test_framework)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(Boost_Tests_run test1.cpp tests2.cpp)
target_link_libraries(Boost_Tests_run ${Boost_LIBRARIES})
Boost_tests/test1.cpp
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
Boost_tests/tests2.cpp
#include <boost/test/unit_test.hpp>

There are basically 3 steps:
building boost and the relevant libraries and their variant (see here for Boost.Test)
configure the project with CMake (an example here)
build the project
It seems like you have all the steps, but what makes things difficult is that those 3 steps are related and should be done in a coherent manner:
Step 1+2: if you are building specific variants of Boost (or Boost.Test) in step 1, you have to instruct CMake with that variant
Step 3: when you build your project, your code program should in some cases be build with defines that instruct what is the variant being built. For instance if you want to link with the shared library version of Boost.Test, you have to define BOOST_TEST_DYN_LINK (in the code before any include of Boost.Test or with target_compile_definitions(test_executable PRIVATE "BOOST_TEST_DYN_LINK=1"))
In the case of Boost.Test, there is another gotcha: on Windows there is the autolink feature that will instruct the linker to link automatically with one version of a .lib: this is something I usually disable by passing a define BOOST_ALL_NO_LIB.
If you are able to make CMake detect for Boost.Test, I would not bother at the beginning with all those linking issues and use the header variant for which no linking issue is involved.
Coming back to what you have currently:
the error Could NOT find Boost (missing: unit_test_framework) (found version 1.60.0") is related to step 1+2: CMake is unable to find the libraries with the configuration you are indicating. Sometimes you need to give more variables to CMake such that it finds the right libraries, but my guess is that you just did not build the shared variant (see here). For instance I use Boost_COMPILER and Boost_ARCHITECTURE sometimes.
Boost_USE_STATIC_LIBS is an instruction for CMake only: it will not enforce the compilation of your with the right defines.
If you post the error that you get with CMake by passing -DBoost_DEBUG=ON, we will be able to support you with more precision.

Related

Boost::python does not build in Debug mode

I am working on a system that needs to embed python in a C++ program on Windows. I installed boost with the boost python packages and everything seems to be correct. When I run CMake it finds the debug and the release libraries from Boost python (the mt and mt-gd ones) and links them correctly with the program via TARGET_LINK_LIBRARIES. However when I build the project in Debug mode the compiler refuses to build, but when I build in Release mode everything is builds fine.
Here is the way I link the library:
FIND_PACKAGE(Python3 COMPONENTS Interpreter Development REQUIRED)
FIND_PACKAGE(Boost COMPONENTS python REQUIRED)
add_executable(ExampleProject "Example.cpp")
TARGET_LINK_LIBRARIES(ExampleProject Boost::python)
TARGET_INCLUDE_DIRECTORIES(ExampleProject PUBLIC ${Boost_INCLUDE_DIR})
I have checked that the correct boost library is linked and I have used the Boost_PYTHON_LIBRARY_DEBUG, to which the compiler says it cannot open the library:
fatal error LNK1104: cannot open file 'boost_python310-vc143-mt-gd-x32-1_79.lib'
I did notice that it does not say libboost here, but when checking the linker it does say libboost, I guess the error output is just different.
When I use Boost::python in CMake it does link but it throws unresolved external symbols like this one:
error LNK2019: unresolved external symbol "__declspec(dllimport) void __cdecl boost::python::throw_error_already_set(void)" (__imp_?throw_error_already_set#python#boost##YAXXZ) referenced in function "struct _object * __cdecl boost::python::expect_non_null<struct _object>(struct _object *)" (??$expect_non_null#U_object###python#boost##YAPAU_object##PAU2##Z)
I have also tried to set the Boost_USE_DEBUG_LIBS and Boost_USE_RELEASE_LIBS, but that did not change anything.
Furthermore the compiler does give the warning that release and debug are mixed:
warning LNK4098: defaultlib 'MSVCRT' conflicts with use of other libs; use /NODEFAULTLIB:library
Which does indicate that they have been mixed up, but when I check the linker it uses the libboost_python310-vc143-mt-gd-x32-1_79.lib which is the Debug version of the python library.
Did I miss anything? Did I do something wrong? I do not understand why this happens and I am still fairly new to CMake and Boost, but I feel like I did all that I could to make it work.

Assimp - Windows - CMakeList - Failing to import

I know this question has been asked before (lot of times).
How to include Assimp with CMakeLists?
CMakeLists Including OpenGL, glew, glfw, glm and assimp
But I still cannot get it to work (normally).
Question: How to install Assimp and use with find_package in cmakelist.txt
What I tried:
Made a build with the cmake GUI and then with Visual studio build the
sln file
Installed it with vcpck and added the path to
cmakelist.txt
add_library(assimp "lib/assimp/assimp.dll")
But all of the methods give errors like: cannot not find assimp, or missing Assimp:Importer
include_directories("${LIB_DIR}/assimp/include")
# target_link_libraries(${PROJECT_NAME} assimp)
find_package(assimp REQUIRED)
if (assimp_FOUND)
set(ASSIMP_LIBRARY "assimp")
add_library(${ASSIMP_LIBRARY} SHARED IMPORTED)
set_target_properties(${ASSIMP_LIBRARY} PROPERTIES IMPORTED_LOCATION "${LIB_DIR}/assimp/libassimp.so")
target_link_libraries(${PROJECT_NAME} ${ASSIMP_LIBRARIES})
endif(assimp_FOUND)
This seems to build, but then I get errors like unresolved external symbol "public: __cdecl Assimp::Importer::Importer(void)" (??0Importer#Assimp##QEAA#XZ) referenced in function "private: void __cdecl
So there is something I am missing entirely and I dont understand it apparently.
Is there something simple I'm doing wrong? Thanks!

Using wxWidgets with CMAKE and visual studio

Every time I try to build my project with CMAKE having the wxWidgets as library I get a lot of linker errors LNK I even get two errors for just compiling my main.cpp that only includes this :
#include "wx/wx.h"
int main(){
return 0;
}
I just downloaded the source code of wxwidgets, put it in a lib folder and linked it with cmake
This is my CMakeLists.txt :
cmake_minimum_required(VERSION 3.10)
# set the project name
project(wxtest VERSION 1.0)
add_subdirectory(libs/wxWidgets)
add_executable(wx_test WIN32 src/main.cpp)
target_link_libraries(wx_test wx::net wx::core wx::base)
Here are the errors:
Error LNK1120 1 unresolved externals
Error LNK2019 unresolved external symbol WinMain referenced in function "int __cdecl invoke_main(void)" (?invoke_main##YAHXZ)
Is there anything wrong with linking the wxWidgets lib to my project ?
wxWidgets programs are compiled as GUI Win32 applications and so use WinMain() entry point by default, which you don't define.
The usual way to define it is by using wxIMPLEMENT_APP() macro, but you may also do it manually if you really need to -- but you must define it, one way or the other.

Tensorflow 2.3 unresolved external symbols in machine-generated files when building C++ project on Windows

I was wondering if anyone has had success linking against Tensorflow 2.3 DLL on Windows.
I am trying to integrate some tensorflow functionality in a tiny VS2019 test project to see if I can use tensorflow C++ API at all.
I managed to build tensorflow with MSVC 14.16.27023 and followed the instructions given in the official links:
https://docs.bazel.build/versions/master/windows.html#build-c
https://www.tensorflow.org/install/source_windows
I built .dll and .lib using the following Bazel commands:
bazel build --config=opt //tensorflow:tensorflow.dll
bazel build --config=opt //tensorflow:tensorflow.lib
VS2019 project properties:
add relevant Additional Include Directories
add Additional Library Directories, which is only path-to-tensorflow-source\bazel-bin\tensorflow, where bazel-bin directory is generated by the build system (ie Bazel)
in Linker->Input->Additional Dependencies add generated tensorflow.lib
The source code is tiny and is comprised of a few lines I looked up in Joe Antognini's example:
#pragma once
#define NOMINMAX
#include "tensorflow/core/public/session.h"
#include "tensorflow/cc/ops/standard_ops.h"
int main()
{
tensorflow::Scope root = tensorflow::Scope::NewRootScope();
auto X = tensorflow::ops::Placeholder(root.WithOpName("x"), tensorflow::DT_FLOAT,
tensorflow::ops::Placeholder::Shape({ -1, 2 }));
}
As explained in Ashley Tharp's guide, building right away results in linker errors complaining about unresolved external symbols:
LNK2001 unresolved external symbol "private: class tensorflow::Scope __cdecl tensorflow::Scope::WithOpNameImpl(...
LNK2001 unresolved external symbol "public: static class tensorflow::Scope __cdecl tensorflow::Scope::NewRootScope(void)"...
LNK2001 unresolved external symbol "public: __cdecl tensorflow::Scope::~Scope(void)"...
LNK2001 unresolved external symbol "public: __cdecl tensorflow::ops::Placeholder::Placeholder(...
Here is the explanation from her guide:
The reason this is happening is because you can only expose 60,000 symbols in a dll. This is just some limitation of the dll format. The tensorflow library code has more than 60000 symbols, so as the programmer building this dll (a dll is just a binary file for accessing a library at runtime) you will have to manually indicate which symbols you want exposed if they are not already. Google has chosen some default set, but it may not work for everyone.
And, as suggested in the guide, I went into tensorflow headers and prepended relevant symbols with TF_EXPORT macro, which is your usual DLL import-export pattern:
#ifdef TF_COMPILE_LIBRARY
#define TF_EXPORT __declspec(dllexport)
#else
#define TF_EXPORT __declspec(dllimport)
#endif // TF_COMPILE_LIBRARY
The above-mentioned workaround works for the first 3 errors:
// scope.h
~Scope(); // change to: TF_EXPORT ~Scope();
...
static Scope NewRootScope(); // change to: TF_EXPORT static Scope NewRootScope();
...
Scope WithOpNameImpl(const string& op_name) const; // change to TF_EXPORT Scope...
After I am done editing, I re-run bazel like so bazel build --config=opt //tensorflow:tensorflow.lib, and errors are gone.
However, the issue arises when I try to perform similar manipulation to fix the last remaining unresolved symbol, namely Placeholder. The symbol is located inarray_ops.h, which is a machine-generated file. So whatever I edit inside the file is overwritten and lost as soon as I try to build .lib.
Question: How can I expose symbols that appear in machine-generated files? Pointers in the right direction would be much appreciated, perhaps I'm missing something obvious.
The folder containing dynamic library tensorflow_cc.dll and import library tensorflow_cc.dll.if.lib has also two files:
tensorflow_filtered_def_file.def : contains import symbols
tensorflow_cc.dll-2.params : has all built libraries
now if you have some unresolved symbols when building your app, all you have to do is to rebuild dynamic-library with updated tensorflow_filtered_def_file.def file. To this file you have to add missing symbols, for your sample code, it is:
EXPORTS
??1Scope#tensorflow##QEAA#XZ
?NewRootScope#Scope#tensorflow##SA?AV12#XZ
??0Placeholder#ops#tensorflow##QEAA#AEBVScope#2#W4DataType#2#AEBUAttrs#012##Z
?WithOpNameImpl#Scope#tensorflow##AEBA?AV12#AEBV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std###Z
the rest symbols ...
After you updated .def file, you rebuild dll by calling the following command:
link.exe /nologo /DLL /SUBSYSTEM:CONSOLE -defaultlib:advapi32.lib -DEFAULTLIB:advapi32.lib
-ignore:4221 /FORCE:MULTIPLE /MACHINE:X64
#bazel-out/x64_windows-opt/bin/tensorflow/tensorflow_cc.dll-2.params /OPT:ICF /OPT:REF
/DEF:bazel-out/x64_windows-opt/bin/tensorflow/tensorflow_filtered_def_file.def /ignore:4070
before rebuilding dll/lib remove these files (they are only read-only).
More info here , see rafix07 comment.

vcpkg does not work for google test

I installed and integrated latest version of vcpkg:
e:\work\vcpkg>vcpkg version
Vcpkg package management program version 0.0.65-692a363701156f1bc319306fbde93fb6748325f6
See LICENSE.txt for license information.
e:\work\vcpkg>vcpkg integrate install
Applied user-wide integration for this vcpkg root.
All C++ projects can now #include any installed libraries.
Linking will be handled automatically.
Installing new libraries will make them instantly available.
I installed google test:
e:\work\vcpkg>vcpkg list
gtest:x64-windows 1.8 GoogleTest and GoogleMock testing frameworks.
gtest:x86-windows 1.8 GoogleTest and GoogleMock testing frameworks.
I included gtest.h in my project in Visual Studio 2015 Update 3:
#include <gtest/gtest.h>
It compiles fine, but I have linker errors:
1>main.obj : error LNK2001: unresolved external symbol "void __cdecl testing::InitGoogleTest(int *,char * *)" (?InitGoogleTest#testing##YAXPEAHPEAPEAD#Z)
1>main.obj : error LNK2001: unresolved external symbol "public: int __cdecl testing::UnitTest::Run(void)" (?Run#UnitTest#testing##QEAAHXZ)
1>main.obj : error LNK2001: unresolved external symbol "public: static class testing::UnitTest * __cdecl testing::UnitTest::GetInstance(void)" (?GetInstance#UnitTest#testing##SAPEAV12#XZ)
Apparently, Visual Studio does not know it should link with gtest.lib. And I cannot understand why. Vcpkg only says that "Linking will be handled automatically." No idea how it is going to do this.
In "Additional Library Dependencies" of my project I can see these inherited values:
$(VcpkgRoot)lib
$(VcpkgRoot)lib\manual-link
And $(VcpkgRoot) is resolved to e:\work\vcpkg\installed\x64-windows\. So it seems like integration was successful. But how Visual Studio knows it should link with gtest.lib?
Note that if I add gtest.lib to "Additional Dependencies" manually, all works fine, and gtest.dll is automatically copied to output directory.
I think that the autolinking behavior has been intentionally disabled for gtest, see vcpkg issue #306.
Original comment on the issue: here.
The vcpkg implementation requires manual linking because Google Test can redefine main(), and the gtest functionality is duplicated in all of the four separate library files.
Official documentation.
The required per project configuration:
In: Configuration Properties > Linker > Input > Additional Dependencies
For release-builds:
$(VcpkgRoot)lib\manual-link\gtest_main.lib
For debug-builds:
$(VcpkgRoot)debug\lib\manual-link\gtest_main.lib
If you want to create your own custom main(), replace gtest_main.lib with gtest.lib.
If you want to use gmock, you can replace it with gmock_main.lib or gmock.lib.
This is an old thread but I would like to point out what I've found.
You need to link the libs in the manual-link directory, but you need to link them in the right order.
First link gmock_main then gtest_main.
The other way around just results in 0 test.