I'm trying to test my program by using Catch2 tests and build the application using CMake. The program works and the application gets build when the Catch2 tests are not implemented.
Once I implemented the Catch2 tests, the application won't build anymore. I included #define CONFIG_CATCH_MAIN and #include "catch.hpp" into main.cpp.
But I always get something like this when I try to build the application with the tests included:
CMakeFiles\intent_recognition.dir/objects.a(main.cpp.obj):main.cpp:(.text+0x43d): undefined reference to Catch::StringRef::StringRef(char const*)' CMakeFiles\intent_recognition.dir/objects.a(main.cpp.obj):main.cpp:(.text+0x4b9): undefined reference to Catch::AssertionHandler::AssertionHandler(Catch::StringRef const&, Catch::SourceLineInfo const&, Catch::StringRef, Catch::ResultDisposition::Flags)'
I don't know what's going...
My main.cpp file looks like this:
#define CONFIG_CATCH_MAIN
#include "catch.hpp"
#include <iostream>
#include <string>
#include "Intent.h"
std::string func (std::string input)
{
std::cout << input << std::endl;
Intent IntentObj = Intent(input); // create Intent Object with input
IntentObj.analyze();
return IntentObj.result();
}
TEST_CASE("Get Weather", "[func]")
{
REQUIRE( func("What is the weather like today?") == "Intent: Get Weather" );
REQUIRE( func("Tell me what the weather is like.") == "Intent: Get Weather") ;
REQUIRE( func("I want to know the weather for tomorrow") == "Intent: Get Weather" );
REQUIRE( func("Can you tell me the weather for Wednesday?") == "Intent: Get Weather") ;
}
My CMakeLists.txt looks like this:
cmake_minimum_required(VERSION 3.20.2)
project(intent_recognition)
add_executable(intent_recognition main.cpp)
Having a quick look at the Catch2 tutorial you are at least missing the specification of the Catch2 library to link.
The tutorial states that you are at least need to have this code which your file above seems to miss:
find_package(Catch2 REQUIRED)
add_executable(tests test.cpp)
target_link_libraries(tests PRIVATE Catch2::Catch2)
The target_link_libraries... is missing in your example. That's probably why you are getting those linker errors.
The find_package will introduce the library under the name Catch2::Catch2 which you can then link to your test executable.
If the find_package command is not working in your setup, you may need to check how you did install Catch2.
I'm assuming that you want to use Catch2 as a header only library. The problem may be because you are including include/catch.hpp instead of single_include/catch2/catch.hpp.
Related
Is there a modern approach to use target architecture within a condition for a generator expression in CMake? There are some answers that are somewhat outdated. I am looking for a modern or at least very robust and reliable custom script for using target architecture within a generator expression.
The docs do not seem to contain that kind of info.
One of the ideas for a workaround I see is to use $<<STRING:MY_DETECTED_ARCH:ARCH_ARM>:src_for_arm.cpp>
I checked a solution from this answer and it worked.
cmake_minimum_required(VERSION 3.14.2 FATAL_ERROR)
project(cmake_target_arch)
set(CMAKE_CXX_STANDARD 17)
include(TargetArch.cmake)
target_architecture(TARGET_ARCH)
message(STATUS "target_architecture: ${TARGET_ARCH}")
add_executable(cmake_target_arch
main.cpp
$<$<STREQUAL:"${TARGET_ARCH}","x86_64">:x86_64.cpp>
$<$<STREQUAL:"${TARGET_ARCH}","i386">:i386.cpp>
$<$<STREQUAL:"${TARGET_ARCH}","armv7">:armv7.cpp>
)
main.cpp
#include <iostream>
#include <string>
extern std::string hello_message();
int main()
{
std::cout << hello_message() << std::endl;
return 0;
}
i386.cpp
#include <string>
std::string hello_message()
{
return "Hello i386!";
}
Example output for i386 binary:
C:\Users\serge\dev\repos\cmake_target_arch\cmake-build-release-visual-studio-win32\cmake_target_arch.exe
Hello i386!
I guess one can come up with a CMake Macro to wrap strings comparison
I have a C++ header which contains a class with a method like below. Lets say its a header only library I have.
// MyHeader.hpp
#pragma once
#include <string>
namespace myheaderonlylibrary {
class MyClass {
public:
unsigned int Func1(const std::string& str) {
if (!str.empty())
return 10;
else
return 0;
}
};
}
To test the above method I heve the following gtest source file inside a folder called unittest which is like the convention followed.
// MyTests.cpp
#include <limits.h>
#include "gtest/gtest.h"
#include "../MyHeader.hpp"
TEST(MyTest, Test_Something) {
myheaderonlylibrary::MyClass my_object;
unsigned int expected_value = 0;
EXPECT_EQ(expected_value, my_object.Func1(""));
}
All I want to do is build the test using Cmake and run the test. So, following is my CMakeLists.txt.
// CMakeLists.txt
cmake_minimum_required(VERSION 3.4)
set(MyheaderOnlyLibrary_HEADERS
MyHeader.hpp
)
set(MyheaderOnlyLibrary_unittest
unittest/MyTests.cpp
)
find_package(GTest REQUIRED)
enable_testing ()
add_executable(MyheaderOnlyLibraryTests ${MyheaderOnlyLibrary_unittest})
After the above is set, I do a cmake .. and make which results with the following error.
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
Question:
I am not a pro in CMake. My question is that if all I want to do is to be able run tests, then should I be doing an add_executable? Is this correct approach when the only thing I want is to build and run the tests? Seems like the error when calling make is because add_executable is looking for main method like in a normal executable and its not finding it.
What is the correct way to build a test only project where a header file is the only thing to be tested?
How to run the tests after building? Looks like there is a command CTest to run the tests built using CMake?
Environment:
I am building this on a macOS with clang as the compiler with C++11.
Just like a normal C++ program you need to have a main function to execute, for a googletest unit test you can use the following:
int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
This will run your TEST function when executed.
To use ctest to actually run your tests you should have the following lines in your cmake file:
find_package(GTest)
include_directories(${GTEST_INCLUDE_DIRS})
include(CTest)
include(GoogleTest)
enable_testing()
target_link_libraries(testName ${GTEST_LIBRARIES})
gtest_discover_tests(testName)
EDIT:
As an easier place to start before jumping into cmake, I would recommend you start with compiling and running your command on the command line. You can do that with the following:
g++ unittest/myunittest.cpp -o unittest/myunittest.out -lgtest -std=c++11
./unittest/myunittest.out
Once you know it works, take a look at cmake’s documentation and reproduce your results with a cmake file. Then you can start to scale up your programs. Understanding how to run these commands through the command line will give you an appreciation and a better understanding of what cmake is doing.
I am new to c++, please don't grill too much. I am trying to use spdlog in C++ program and using CLion from Jetbrains.
Here is my screenshot but don't know what I am doing wrong. Please advise what I am doing wrong .
This is my main.cpp
#include <iostream>
#include "include/spdlog/spdlog.h"
int main() {
std::cout << "Hello, World!" << std::endl;
spdlog::info("hello world");
return 0;
}
This my CMakeList.txt
cmake_minimum_required(VERSION 3.17) project(Lesson01)
set(CMAKE_CXX_STANDARD 14)
add_executable(Lesson01 main.cpp) include_directories(spdlog)
Thanks
In the prototype you have created you have mentioned twice the keyword include
First after the # and a second one in between the quotation mark.
I have been looking in internet and basic writting of spdlog prototype is just. Here the link: https://github.com/gabime/spdlog
#include "spdlog/spdlog.h"
Test is without the second include in your code.
All the best
Mathieu
In your code you have #include "include/spdlog/spdlog.h", and going by the screenshot you linked, this include is found. However it is then complaining that spdlog.h tries to #include <spdlog/common.h>, and this file is not found.
Together this sounds like you are not setting the right include directory for the library, and just getting the first file right by over-specifying the path.
I would try to change include_directories(spdlog) to include_directories(include) (or possibly include_directories(include/spdlog), not entirely sure which folder is the base one with the library in). If you want, then you can also add all of those; I don't think anything will break from adding too many here, but it may impact compile speed so try to only keep the correct one.
Also, after making this change you may need to change your original include from #include "include/spdlog/spdlog.h" to #include "spdlog/spdlog.h".
Yesterday I have started including spdlog into a personal project of mine to use for logging. So far I have had some problems with getting library inclusion to work but those are now solved completely.
Now everything compiles just fine, with all headers found however when I try to create loggers or simply set the pattern for logging the code crashes with a segmentation fault. More specifically no matter which function I call from the spdlog namespace for the very first time in the program causes the crash.
I have a class abstracting some parts from spdlog (based on this repo) as follows:
//Logger.hpp
#ifndef TE_LOGGER_HPP
#define TE_LOGGER_HPP
#include <spdlog/spdlog.h>
namespace te {
class Logger {
public:
static void Init();
inline static std::shared_ptr<spdlog::logger> &getCoreLogger() {
return sCoreLogger;
}
inline static std::shared_ptr<spdlog::logger> &getClientLogger() {
return sClientLogger;
}
private:
static std::shared_ptr<spdlog::logger> sCoreLogger;
static std::shared_ptr<spdlog::logger> sClientLogger;
};
}
#endif //TE_LOGGER_HPP
//Logger.cpp
#include "Logger.hpp"
#include <spdlog/sinks/stdout_color_sinks.h>
std::shared_ptr<spdlog::logger> te::Logger::sCoreLogger;
std::shared_ptr<spdlog::logger> te::Logger::sClientLogger;
void te::Logger::Init() {
//The first of any of the following three lines cause a crash
//no matter the order, regardless of the pattern used in set_pattern
spdlog::set_pattern("%v");
sCoreLogger = spdlog::stdout_color_mt("CORE");
sClientLogger = spdlog::stdout_color_mt("CORE");
sCoreLogger->set_level(spdlog::level::trace);
sClientLogger->set_level(spdlog::level::trace);
}
From the stack traces it seems that the issue is with the formatter class in spdlog being set null for some reason somewhere within the library. I am using the latest CLion, C++14 (I am aware that spdlog is C++11, but I need features from 14 later down the line, also setting -std=c++11 doesn't solve the issue) and the latest version of spdlog as of yesterday (pulled straight from their GitHub repo) on Ubuntu 18.04.
EDIT: As per the request in the comments I have created a small project (single cpp file, include spdlog the way I do in the real project, or the same code and library setup as in the real project referenced from the main.cpp file and linked to accordingly) that aims to reproduce the issue and here are my findings:
* The issue is not present when I use spdlog directly in the executable
* The issue is present if the Logger class is moved into a shared library and linked to from there
Here is the error message I am getting:
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
And the CMakeLists.txt files I am using (I nest the library's one into the project since as of now CLion does not support "multiple projects in the same solution" like for example VS does):
#CMakeLists.txt for Library
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(TokenEngine VERSION 0.0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
set(SOURCE_FILES src/Application.cpp src/Application.hpp src/EntryPoint.hpp src/Logger.cpp src/Logger.hpp)
#include_directories("${CMAKE_CURRENT_SOURCE_DIR}/libs/")
add_library(TokenEngine SHARED ${SOURCE_FILES})
target_include_directories(TokenEngine PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/libs/spdlog-1.x/include")
#Expose the public API of the engine to any project that might use it
target_include_directories(TokenEngine PUBLIC include)
#CMakeLists.txt for top level project
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
add_definitions(-DTE_PLATFORM_LINUX)
project(Build CXX)
add_subdirectory(TokenEngine)
add_subdirectory(Sandbox)
You are using the same name for both loggers, when you run it you'll get:
$ ./logger
libc++abi.dylib: terminating with uncaught exception of type spdlog::spdlog_ex: logger with name 'CORE' already exists
Abort trap: 6
If you change the name of the client logger to something else it works fine:
sCoreLogger = spdlog::stdout_color_mt("CORE");
sClientLogger = spdlog::stdout_color_mt("CLIENT");
The problem is probably that spdlog’s static objects are defined twice - from inside the shared library and from client code that includes your logger header (which incldes spdlog.h).
Try to remove the include to spdlog.h from the header file and (and use forward declaration of spdlog::logger instead), and include spdlog.h only from your Logger.cpp file.
Edit:
spdlog::logger cannot be forward declared across compilation unit boundries.
The solution is to wrap the logger with some simple class defined in logger.cpp and only export it in logger.h
I have a C++ library that is built with cmake. The library is tested by unit tests which I would like to run as part of the build. I want the tests to run when the library or unit test source changes.
I found some help in past posts on the cmake mailing list:
http://www.cmake.org/pipermail/cmake/2010-January/034419.html
This approach, using add_custom_command() on the unit test program, works on Linux when the library is shared or static and on Windows when the libary is static. The problem is building the library shared on Windows. In that case, the test runs in the initial build, but doesn't run when the library changes.
Here is an example that illustrates the problem:
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(xyz)
SET(BUILD_SHARED_LIBS ON)
#----------- build library
if(${BUILD_SHARED_LIBS})
add_definitions(-DXYZ_SHARED)
endif(${BUILD_SHARED_LIBS})
add_library(${PROJECT_NAME} xyz.cpp xyz.h)
#----------- build unit tests program
add_executable(${PROJECT_NAME}_unit_tests unit_tests.cpp)
target_link_libraries(${PROJECT_NAME}_unit_tests ${PROJECT_NAME})
#----------- run unit tests program after it is built
add_custom_command(TARGET ${PROJECT_NAME}_unit_tests
POST_BUILD COMMAND ${PROJECT_NAME}_unit_tests)
xyz.h
#pragma once
#ifdef XYZ_SHARED
#ifdef _WIN32
#ifdef xyz_EXPORTS
#define XYZ_EXPORT __declspec(dllexport)
#else
#define XYZ_EXPORT __declspec(dllimport)
#endif
#else //_WIN32
#define XYZ_EXPORT
#endif //_WIN32
#else //XYZ_SHARED
#define XYZ_EXPORT
#endif //XYZ_SHARED
XYZ_EXPORT bool xyz_return_true();
xyz.cpp
#include "xyz.h"
XYZ_EXPORT bool xyz_return_true()
{
return true;
}
unit_tests.cpp
#include <iostream>
#include "xyz.h"
int main(int argc, char *argv[])
{
using namespace std;
cout << "running unit tests: ";
if (xyz_return_true()) {
//no error
cout << "pass" << endl;
return 0;
}
//error
cout << "fail" << endl;
return 1;
}
If I build on Windows, then change xyz.cpp's function to return false and build again, the tests aren't run. What can I do to make the tests run in this case?
I think the tests aren't being run because at build time xyz_unit_tests.exe only depends on the import library (xyz.lib). The import library doesn't change if the library interface doesn't change.
I'm using Visual Studio 10 and cmake 2.8.11.2.
You could trigger a test rebuild by deleting the test executable as a post-build event of the library. So, for example:
if(WIN32 AND BUILD_SHARED_LIBS)
#----------- trigger rebuild of tests if lib has changed
get_target_property(TestPath ${PROJECT_NAME}_unit_tests LOCATION)
add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove ${TestPath}
COMMENT "Removing ${TestPath}")
endif()
You have to use the LOCATION target property rather than a generator expression in the COMMAND argument since that doesn't introduce a cyclic dependency in CMake's dependency graph. Ideally, we'd have the COMMAND argument as:
COMMAND ${CMAKE_COMMAND} -E remove $<TARGET_FILE:${PROJECT_NAME}_unit_tests>
but this would cause the library to depend on the executable, and clearly the target_link_libraries call causes the exe to depend on the lib.
There's probably a better way to make the test executable go out-of-date rather than just deleting it; it seems a bit brute-force to me. But it should work.