Can't link to bullet physics library - LNK2019,LNK2001 - cmake

I have built bullet3-3.08 on Windows using cmake (commands have been executed from the build folder created in the bullet3-3.08 directory):
cmake -G "Visual Studio 15 2017" -A x64 -D "CMAKE_INSTALL_PREFIX:PATH=C:/MyLibs/bullet3-3.08" -D "USE_MSVC_RUNTIME_LIBRARY_DLL=ON" -D "INSTALL_LIBS=ON" ..
cmake --build . --config Release --parallel 8 --target install
I have an application which uses MD/MDd runtime library so I have built Bullet with the following option: USE_MSVC_RUNTIME_LIBRARY_DLL=ON. Bullet is built as a static library by default. I use Visual Studio 2017 and build my application using cmake. When I link to Bullet I get many linker errors (LNK2019, LNK2001), for example:
error LNK2019: unresolved external symbol "public: __cdecl btCollisionDispatcher::btCollisionDispatcher(class btCollisionConfiguration *)" (??0btCollisionDispatcher##QEAA#PEAVbtCollisionConfiguration###Z) referenced in function main
error LNK2001: unresolved external symbol "public: virtual void __cdecl btCollisionShape::getBoundingSphere(class btVector3 &,float &)const " (?getBoundingSphere#btCollisionShape##UEBAXAEAVbtVector3##AEAM#Z)
I use direct library paths in CMakeLists.txt:
target_link_libraries(${executableName} "C:/MyLibs/bullet3-3.08/lib/Bullet3Collision.lib"
"C:/MyLibs/bullet3-3.08/lib/Bullet3Dynamics.lib"
"C:/MyLibs/bullet3-3.08/lib/LinearMath.lib")
Here is CMakeSettings.json:
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [
"msvc_x64_x64"
],
"buildRoot": "${projectDir}\\build\\${name}",
"installRoot": "${projectDir}\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
},
{
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [
"msvc_x64_x64"
],
"buildRoot": "${projectDir}\\build\\${name}",
"installRoot": "${projectDir}\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
}
]
}
What is wrong ?

As Bullet provides a BulletConfig.cmake file it is quite simple to link against Bullet.
First you need to install Bullet (if not done so) and add -DCMAKE_PREFIX_PATH=C:/MyLibs/bullet3-3.08 (or the appropriate installation directory) to your cmake command line.
Then in your CMakeLists.txt file you need to add
find_package(Bullet REQUIRED)
# your add_executable call follows here
add_executable(${executableName} .......)
target_compile_definitions(${executableName} PRIVATE ${BULLET_DEFINITIONS})
target_include_directories(${executableName} PRIVATE ${BULLET_INCLUDE_DIRS})
target_link_libraries(${executableName} PRIVATE ${BULLET_LIBRARIES})
This should be the steps necessary to link to Bullet.

Related

Link errors when using CMake and VCPKG with GoogleTest

As a bit of background, I have a project that I have been developing with Visual Studio and VCPKG manifest mode for some time, it contains one static library project and one unit test project. Everything has been working correctly. I'm now trying to migrate this solution to use CMake, this is my first time using CMake.
With CMake the VCPKG dependencies install correctly, and both the static library and unit tests compile, however it fails on the linking step with a large number of linker errors all related to the GoogleTest library. Here is the first, as an example:
CppSlippiTest.obj : error LNK2019: unresolved external symbol "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl testing::internal::FormatMatcherDescription(bool,char const *,class std::vector<char const*,class std::allocator<char const *> > const &,class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > const &)" (?FormatMatcherDescription#internal#testing##YA?AV$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##_NPEBDAEBV?$vector#PEBDV$allocator#PEBD#std###4#AEBV?$vector#V?$basic_string#DU?$char_traits#D#std##V$allocator#D#2##std##V?$allocator#V?$basic_string#DU$char_traits#D#std##V?$allocator#D#2##std###2##4##Z) referenced in function "private: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl cpp_slippi::MatchOptionalMatcherP2<class testing::internal::Eq2Matcher,class std::optional<unsigned char> >::gmock_Impl<class std::optional<unsigned char> const &>::FormatDescription(bool)const " (?FormatDescription#$gmock_Impl#AEBV?$optional#E#std###$MatchOptionalMatcherP2#VEq2Matcher#internal#testing##V?$optional#E#std###cpp_slippi##AEBA?AV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##_N#Z) [C:\Users\Derek\Projects\CppSlippi\build\Test.vcxproj]
There are 36 more of these.
Here is my CMakeLists.txt, slightly abridged for clarity:
cmake_minimum_required(VERSION 3.12...3.24)
# Must be before project()
set(VCPKG_TARGET_TRIPLET x64-windows-static)
project(CppSlippi
VERSION 1.0
DESCRIPTION "Slippi replay file parsing library for C++."
LANGUAGES CXX)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(GTest CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
add_library(CppSlippi STATIC
CppSlippi/src/CppSlippi.cpp
CppSlippi/src/CppSlippi.h
...)
target_include_directories(CppSlippi PUBLIC CppSlippi/src)
target_compile_features(CppSlippi PUBLIC cxx_std_20)
target_compile_options(CppSlippi PUBLIC /MTd)
set_target_properties(CppSlippi PROPERTIES CXX_EXTENSIONS OFF)
target_link_libraries(CppSlippi PUBLIC nlohmann_json::nlohmann_json)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest)
include(GoogleTest)
endif()
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING)
add_executable(Test
Test/src/CppSlippiTest.cpp
...)
target_include_directories(Test PUBLIC Test/src)
target_compile_features(Test PUBLIC cxx_std_20)
target_compile_options(Test PRIVATE /bigobj /MTd)
set_target_properties(Test PROPERTIES CXX_EXTENSIONS OFF)
target_link_libraries(Test PUBLIC
CppSlippi
nlohmann_json::nlohmann_json
GTest::gtest_main)
gtest_discover_tests(Test)
endif()
Here is my VCPKG manifest:
{
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
"name": "cpp-slippi",
"version": "1.0.0",
"description": "C++ Slippi replay parser.",
"builtin-baseline": "68b7fec22eb5fd9c0236b1e42b3c0deb8e771b37",
"dependencies": [
"gtest",
"nlohmann-json"
],
"supports": "windows"
}
And to build this I am running:
cmake --build build --target Test
I turned on --verbose to get more information, and this is the link command that CMake is running:
Link:
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933\bin\HostX64\x64\link.exe /ERRORREPO
RT:QUEUE /OUT:"C:\Users\Derek\Projects\CppSlippi\build\Debug\Test.exe" /INCREMENTAL /ILK:"Test.dir\Debug\Test.ilk" /N
OLOGO /NATVIS:"C:\Users\Derek\Projects\CppSlippi\build\vcpkg_installed\x64-windows-static\share\nlohmann_json\nlohman
n_json.natvis" Debug\CppSlippi.lib "vcpkg_installed\x64-windows-static\debug\lib\manual-link\gtest_main.lib" "vcpkg_i
nstalled\x64-windows-static\debug\lib\gtest.lib" kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib
oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /manifes
t:embed /DEBUG /PDB:"C:/Users/Derek/Projects/CppSlippi/build/Debug/Test.pdb" /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE
/NXCOMPAT /IMPLIB:"C:/Users/Derek/Projects/CppSlippi/build/Debug/Test.lib" /MACHINE:X64 /machine:x64 Test.dir\Debug
\CppSlippiTest.obj ...
Note the presence of gtest_main.lib and gtest.lib, these are the libraries that I believe should include the missing functions. I have checked that these files are present at the locations shown.
I know that in Visual Studio using the GoogleTest main requires adding an AdditionalDependency manually, but from all the instructions I can find this should not be necessary in CMake and the .lib is already in the command line. I did try using target_link_directories anyways, but this did not help.
At this point I am baffled and searching on Google and Stack Overflow has failed to turn up any help.
You link only one of 4 GoogleTest libraries to your app
target_link_libraries(Test PUBLIC
CppSlippi
nlohmann_json::nlohmann_json
GTest::gtest_main)
Depending on the application needs, it should be linked to at least one more library
target_link_libraries(Test PUBLIC
CppSlippi
nlohmann_json::nlohmann_json
GTest::gtest_main
GTest::gtest)
Or
target_link_libraries(Test PUBLIC
CppSlippi
nlohmann_json::nlohmann_json
GTest::gmock_main
GTest::gmock
GTest::gtest)

Is there a way to run cmake using intel compilers?

I was trying to run a program using intel compilers but while compiling the program it showed error.
It was was due to cmake.
cmake -G "Visual Studio 17 2022" -A x64 -T "Intel(R) oneAPI DPC++ Compiler" ..
-- CMAKE_BUILD_TYPE is unset, defaulting to Release
-- Selecting Windows SDK version 10.0.22000.0 to target Windows 10.0.25099.
CMake Error at CMakeLists.txt:81 (project):
Failed to run MSBuild command:
C:/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/amd64/MSBuild.exe
to get the value of VCTargetsPath:
MSBuild version 17.3.1+2badb37d1 for .NET Framework
Build started 9/2/2022 10:51:43 AM.
Project "C:\Users\mtc\source\repos\onednn\build\CMakeFiles\3.23.1\VCTargetsPath.vcxproj" on node 1 (default targets).
C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.CppBuild.targets(460,5): error MSB8020: The build tools for Intel(R) oneAPI DPC++ Compiler (Platform Toolset = 'Intel(R) oneAPI DPC++ Compiler') cannot be found.
To build using the Intel(R) oneAPI DPC++ Compiler build tools, please install Intel(R) oneAPI DPC++ Compiler build tools.
Alternatively, you may upgrade to the current Visual Studio tools by selecting the Project menu or right-click the solution, and then selecting "Retarget solution". [C:\Users\mtc\source\repos\onednn\build\CMakeFiles\3.23.1\VCTargetsPath.vcxproj]
Done Building Project "C:\Users\mtc\source\repos\onednn\build\CMakeFiles\3.23.1\VCTargetsPath.vcxproj" (default targets) -- FAILED
"C:\Users\mtc\source\repos\onednn\build\CMakeFiles\3.23.1\VCTargetsPath.vcxproj" (default target) (1) ->
(PrepareForBuild target) ->
C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.CppBuild.targets(460,5): error MSB8020: The build tools for Intel(R) oneAPI DPC++ Compiler (Platform Toolset = 'Intel(R) oneAPI DPC++ Compiler') cannot be found. To build using the Intel(R) oneAPI DPC++ Compiler build tools,
please install Intel(R) oneAPI DPC++ Compiler build tools. Alternatively, you may upgrade to the current Visual Studio tools by selecting the Project menu or right-click the solution, and then selecting "Retarget solution". [C:\Users\mtc\source\repos\onednn\build\CMakeFiles\3.23.1\VCTargetsPath.vcxproj]
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.15
Exit code: 1
Ninja is the only CMake generator for Windows that works with the oneAPI DPC++ Compiler.
Please adhere to the instructions in the following link if you wish to utilise the Intel oneAPI DPC++ compiler:
https://oneapi-src.github.io/oneDNN/dev_guide_build.html#id1
To use the Intel NextGen Compiler, could you please use the below command:
cmake -G ""Visual Studio 17 2022"" -T ""Intel C++ Compiler 2022"" ...
There are a couple things that might be causing your problem.
Make sure that the compiler is loaded in your current command environment. For the intel compiler this is usually C:\Program Files (x86)\Intel\oneAPI\setvars.bat.
Make sure that the installed version of CMake is newer than the toolset that you are trying to use. Specifically, CMake version > 3.22.2 if you are using intel 2022.
Your CMakeLists.txt is not posted, but according to Intel's documentation, you need to add find_package(IntelDPCPP REQUIRED).
Also, once you are able to get your target to build, I recommend that you switch to using a CMakePresets.json file to configure your CMake build. I have one that looks like this:
In this preset file, the cache variable INTEL_COMPILER is used to turn on/off specific intel compiler flags in my CMakeLists.txt such as /fp:precise.
CMakePresets.json
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 22,
"patch": 2
},
"configurePresets": [
{
"name": "base",
"hidden": true,
"binaryDir": "${sourceDir}/_build/${presetName}"
},
{
"name": "Windows",
"hidden": true,
"inherits": [
"base"
],
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": {
"hostOS": "Windows"
}
}
},
{
"name": "Debug",
"hidden": true,
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "Release",
"hidden": true,
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "Intel - IDE",
"hidden": true,
"toolset": "Intel C++ Compiler 2022",
"cacheVariables": {
"INTEL_COMPILER": "ON"
}
},
{
"name": "Visual Studio 2022 - Intel",
"displayName": "Visual Studio 2022 using Intel compiler",
"inherits": [
"Windows",
"Intel - IDE"
],
"generator": "Visual Studio 17 2022"
}
],
"buildPresets": [
{
"name": "Windows",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": {
"hostOS": "Windows"
}
}
},
{
"name": "Visual Studio 2022 - Intel",
"displayName": "Visual Studio 2022 using Intel compiler",
"inherits": [
"Windows"
],
"configurePreset": "Visual Studio 2022 - Intel"
}
]
}

How to call a bash script from cmake and pass a generator dependent string as argument?

While using the Unix Makefiles generator I have added the following to a CMakeLists.txt file:
add_custom_target(maintainer-clean
# The current directory is CMAKE_CURRENT_BINARY_DIR.
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cwm4/scripts/cmake_maintainer_clean.sh $(MAKE) \"${GITACHE_PACKAGES}\"
)
This cmake_maintainer_clean.sh script is make specific, and it needs to use $(MAKE) in the generated Makefile when calling the script.
However, when switching to the generator Ninja this custom command is put as-is in the build.ninja file, causing the $ of the $(MAKE) to cause problems (ninja refuses to run any target, failing to parse build.ninja).
Therefore, I wish to make this generator-specific. How can I use $(MAKE) as first argument to the script when the generator is Unix Makefiles and something else, without a $ - e.g. "ninja" - when the generator is Ninja?
Can I do something like:
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cwm4/scripts/cmake_maintainer_clean.sh $<UNIX:$(MAKE),ninja> \"${GITACHE_PACKAGES}\"
?
I would make separate presets for each generator, so you can associate a particular environment variable with it. Here is how the CMakePresets.json file may look like:
{
"version": 2,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
},
"configurePresets": [
{
"name": "base",
"binaryDir": "${sourceDir}/build",
"hidden": true
},
{
"name": "Ninja",
"inherits": "base",
"displayName": "Ninja Config",
"generator": "Ninja",
"environment": {
"SCRIPT_ARG": "Ninja"
}
},
{
"name": "Make",
"inherits": "base",
"displayName": "Make Config",
"generator": "Unix Makefiles",
"environment": {
"SCRIPT_ARG": "Make"
}
}
]
}
Where the SCRIPT_ARG can be read later inside of the CMakeLists.txt configuration:
cmake_minimum_required(VERSION 3.20)
project(Hello)
add_executable(Hello main.cpp)
add_custom_target(my-script
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/script.sh $ENV{SCRIPT_ARG}
)
add_dependencies(Hello my-script)
Providing script.sh is as simple as this:
#!/bin/sh
echo "Hello from script, $1!"
You will end up with the following output during build phase:
% cmake --preset Make
...
% cmake --build build
Hello from script, Make!
[ 0%] Built target my-script
...
Corresponding output for Ninja generator would be:
% cmake --preset Ninja
...
% cmake --build build
Hello from script, Ninja!
[ 0%] Built target my-script
...
If you need something more complex than an environment variable, you can introduce condition statement based on value of CMAKE_GENERATOR:
cmake_minimum_required(VERSION 3.20)
project(Hello)
add_executable(Hello main.cpp)
if(CMAKE_GENERATOR STREQUAL Ninja)
add_custom_target(my-script COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/script.sh $ENV{SCRIPT_ARG})
elseif(CMAKE_GENERATOR STREQUAL "Unix Makefiles")
add_custom_target(my-script COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/another_script.sh)
endif()
add_dependencies(Hello my-script)
The provided solutions work at build step, and for the part whether it's possible to distinguish between generators during build system generation phase (e.g. with use of generator expressions) i don't think it's possible, because the configuration is generator-agnostic at this point.

Using CMake compile flags in preset for non-external projects only

I am adding a few external projects to my application. For instance, gtest.
In my CMake preset I set the following...
{
"version": 4,
"cmakeMinimumRequired": {
"major": 3,
"minor": 23,
"patch": 0
},
"configurePresets": [
{
"name": "debug",
"displayName": "debug",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_CXX_FLAGS": "/W4 /Wall /EHsc",
"CMAKE_CXX_STANDARD": "20",
"CMAKE_CXX_STANDARD_REQUIRED": "YES",
"CMAKE_CXX_EXTENSIONS": "OFF"
}
}
]
}
When I build with the above preset, I get a bunch of warnings from building gtest. I only would like to see the warnings coming from my internal build, not external projects.
In my root CMakeLists.txt I have the following...
cmake_minimum_required(VERSION 3.23.0)
project(ProjectName LANGUAGES C CXX CUDA)
include(CTest)
add_subdirectory(external) # This has a bunch of external dependencies.
add_subdirectory(src) # This builds a normal executable.
add_subdirectory(tests) # This has various unit tests.
Is there a way for me to make sure the flags are only used for my personal project, and nothing external?
I have looked into "https://cmake.org/cmake/help/v3.23/manual/cmake-presets.7.html" but nothing stood out to me.
Thank you
If you want to localize your settings you should not use globals. Use target_compile_options and other target_* commands instead of setting CMAKE_CXX_* global (cache) variables in your preset.
Alternatively, you can choose not to build external projects as part of your local project build. With that organization you wouldn't have the problem in the first place.

Can I use the environment to add/replace to the CMAKE_MODULE_PATH?

Within a CMakeLists.txt file, you can write something like:
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
but what if I want to "prime" that variable before the invocation of CMake? Using the environment, perhaps? Is this possible?
The documentation says it is empty by default, but I was hoping to diverge from this default.
CMAKE_MODULE_PATH is not an "cmake-env-variable". It won't read same name environment variable to initialize it automatically. As an example, CMAKE_EXPORT_COMPILE_COMMANDS can be initialized from env var (ref).
An alternative for your situation:
if(DEFINED ENV{CMAKE_MODULE_PATH})
set(CMAKE_MODULE_PATH "$ENV{CMAKE_MODULE_PATH}")
else()
message(WARNING "CMAKE_MODULE_PATH env var not defined, using the default empty one")
endif()
My preferred option would be to pass this to cmake at the time of configuration.
Using command line options
cmake -D "CMAKE_MODULE_PATH:STRING=${CMAKE_MODULE_PATH}" -S ...
or
Using a cache configuration script
# initialCache.cmake
set(CMAKE_MODULE_PATH $ENV{CMAKE_MODULE_PATH} CACHE STRING "path to look for cmake modules")
cmake -C initialCache.cmake -S ...
or
Using a preset
CMakePresets.json
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 19,
"patch": 0
},
"configurePresets": [
{
"name": "mypreset",
"displayName": "My preset",
"description": "Preset using the environment variable to set CMAKE_MODULE_PATH",
"cacheVariables": {
"CMAKE_MODULE_PATH": {
"type": "STRING",
"value": "$env{CMAKE_MODULE_PATH}"
}
}
}
]
}
cmake --preset mypreset -S ...