Linking project source code with boost tests under CMake - c++

I am trying to find the best way to link my project source code with my boost unit tests. I have a fairly basic project setup right now using CMake, however all examples of boost UTF that I have come across have shown very basic tests which do not touch source code in a project that is along side the tests.
As a minimal example I have the following:
CMakeLists.txt
cmake_minimum_required(version 2.8)
project(test-project)
find_package(Boost 1.55 REQUIRED COMPONENTS unit_test_framework )
include_directories(${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIRS})
add_subdirectory(src)
enable_testing()
add_subdirectory(test)
src/CMakeLists.txt
add_executable(example main.cpp foo.cpp)
src/foo.h
#include <string>
std::string hello(std::string name);
src/foo.cpp
#include "foo.h"
std::string hello(std::string name) { return "Hello " + name; }
src/main.cpp - Uses foo in a simple way
test/CMakeLists.txt
include_directories (../src)
set(TEST_REQUIRED_SOURCES ../src/foo.cpp)
add_executable (test test.cpp ${TEST_REQUIRED_SOURCES})
target_link_libraries(test ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
add_test(SimpleTest test)
test/test.cpp
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE SimpleTest
#include <boost/test/unit_test.hpp>
#include "foo.h"
BOOST_AUTO_TEST_CASE(ShouldPass) {
BOOST_CHECK_EQUAL(hello("fred"), "Hello fred")
}
While this works, I'd like to avoid the following:
Defining TEST_REQUIRED_SOURCES with a list of all of my files required to compile.
Avoiding the duplicate compilation of code.
Does my structure look correct for this sort of project? Would it make sense to compile my code under src into a library instead? Most of my experience with testing is from C# where this is much simpler.

You can have a look at how I did it there: https://github.com/NewbiZ/mengine/blob/master/CMakeLists.txt
Basically, I build an object file with my library, and reuse it in the main executable AND the tests. That way you only build once.
The interesting piece of CMake is:
# Just build the object files, so that we could reuse them
# apart from the main executable (e.g. in test)
ADD_LIBRARY(mengine_objects OBJECT ${MENGINE_SOURCES})
Then build the main executable:
ADD_EXECUTABLE(mengine $<TARGET_OBJECTS:mengine_objects>
src/main.cpp)
And the tests:
ADD_EXECUTABLE(test_tga $<TARGET_OBJECTS:mengine_objects>
test_tga.cpp)
Hope that helps!

Related

Unabled to load lohmann/json.hpp using CMake - getting fatal error: 'nlohmann/json.hpp' file not found

I have the following main.cpp, very simple script, trying to re-produce the problem and isolate to it's most basic.
#include<iostream>
#include<fmt/core.h>
// #include "json/json.hpp"
// #include <json/json.hpp>
// #include <nlohmann/json.hpp>
// #include "json.hpp"
// #include "nlohmann/json.hpp"
int main(){
fmt::print("Hello, world!\n");
return 0;
}
The commented out include statements are all of the paths I have tried to get this into my program.
My CMakeLists.txt file looks like this
cmake_minimum_required(VERSION 3.24)
project(main)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "-std=c++17")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build)
# include(FetchContent)
# FetchContent_Declare(
# json
# GIT_REPOSITORY https://github.com/nlohmann/json
# )
# FetchContent_MakeAvailable(json)
include(FetchContent)
FetchContent_Declare(fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_TAG 9.0.0
)
FetchContent_MakeAvailable(fmt)
add_executable(main main.cpp)
# target_link_libraries(main json)
target_link_libraries(main fmt)
I've commented out the json part since it's not working obviously, I can also json-src and json-build in my _deps folder so I know for a fact that it is being downloaded into my local machine.
After running cmake CMakeLists.txt and make and then running the executable I get the error in the title 'nlohmann/json.hpp' file not found
Any help would be appreciated.
Tried to reproduce the example with another popular C++ package which worked fine. Tried changing the include statements to see if maybe I had something wrong since I don't fully understand all the nuance with CMake and am quite new to it.
You can either specifically use the interface target already included in the nlohmann library, which will automatically populate the correct include path for you, with:
target_link_libraries(main nlohmann_json::nlohmann_json)
Or you would need to specifically include the include path yourself:
include_directories(${json_SOURCE_DIR}/include)
With either way, you will be able to use #include <nlohmann/json.hpp> in your source.

CMake bracket #include grpc

I am trying to integrate grpc with a large project by trying to integrate the code in grpc example helloworld directory: greeter_client.cc
I used the cmake option where assuming grpc is already installed in my system in $MY_INSTALL_DIR as described in grpc's document: cmake -DCMAKE_PREFIX_PATH=$MY_INSTALL_DIR
When I include it in my "large project", I changed the greeter_client.cc into .h and .cc file.
greeter_client.h
#include <grpcpp/grpcpp.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;
class GreeterClient {
public:
GreeterClient(std::shared_ptr<Channel> channel)
: stub_(Greeter::NewStub(channel)) {}
std::string SayHello(const std::string& user);
private:
std::unique_ptr<Greeter::Stub> stub_;
};
void libFun();
greeter_client.cc
#include <iostream>
#include <memory>
#include <string>
#include "greeter_client.h"
std::string GreeterClient::SayHello(const std::string& user) {
// the original implementation unchanged...
}
void libFun() {
GreeterClient client(grpc::CreateChannel(
"localhost:50051", grpc::InsecureChannelCredentials()));
client.SayHello("world");
}
and in the target destination (say the file is main.cc) I added
#include "/path/to/greeter_cient.h"
int main (){
libFun();
return 0;
}
The directory structure is
helloworld
greeter_client.h
greeter_client.cc
CMakeLists (the original one with little tweak to also include greeter_client.h in target greeter_client)
protos (the protos directory under examples in grpc project)
test
main.cc
CmakeList
The CMakeList for main.cc is like the following
cmake_minimum_required(VERSION 3.5.1)
project(test C CXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# This part is copied from the grpc example CMakeFileList
set(protobuf_MODULE_COMPATIBLE TRUE)
find_package(Protobuf CONFIG REQUIRED)
message(STATUS "Using protobuf ${Protobuf_VERSION}")
set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf)
# Find gRPC installation
# Looks for gRPCConfig.cmake file installed by gRPC's cmake installation.
find_package(gRPC CONFIG REQUIRED)
message(STATUS "Using gRPC ${gRPC_VERSION}")
set(_GRPC_GRPCPP gRPC::grpc++)
add_subdirectory("../helloworld" "../helloworld")
add_executable(program "main.cc")
# THIS IS THE QUESTION: why adding ${_GRPC_GRPCPP} will work?
target_link_libraries(program greeter_client
${_GRPC_GRPCPP})
target_include_directories(program PRIVATE "../helloworld" )
The question is: initially, I did not add find_project(gRPC) in the main.cc 's CMakeList and I did not add ${_GRPC_GRPCPP} in the target_link_libraries of program target. And I will get error
complaining "/test/../helloworld/greeter_client.h:23:10: fatal error: 'grpcpp/grpcpp.h' file not found
#include <grpcpp/grpcpp.h>"
I read some threads and it seems that bracket include finds the files in system include paths. I am not sure why the grpc example can make it work by adding a dependency in target_link_libraries?
Another question is: ideally, I want main.cc does not care about anything inside greeter_client target. However, since I have to include greeter_client.h which in turn includes <grpcpp/grpcpp.h> I have to add the find_project and link the target in main.cc's CMakeList as well. How to avoid re-deal with this include <grpcpp/grpcpp.h> in main.cc's CMakeList?
Thanks!
If I understand you correctly, the real question here is
it seems that bracket include finds the files in system include paths.
I am not sure why the grpc example can make it work by adding a
dependency in target_link_libraries?
In short, #include <foo.h> will search system include paths first, but if that fails it will retry as if you wrote #include "foo.h".

DLL Made with CMake makes the program crash

I'm struggling to use a DLL generated using CMAKE and C++. I'm able to build the library, include it and build the target project, the problem is that when I run the target build it crashes immediately.
My code is super easy and I don't know what I'm missing.
The DLL is built using CMAKE in a separate project. Here's the code
DLL PROJECT:
CMakeLists
cmake_minimum_required(VERSION 3.5)
project(LibProj LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_definitions("-DBUILD_LIB")
file(GLOB
INCLUDE_FILES
baselibraryclass.h
)
file(GLOB
SOURCE_FILES
baselibraryclass.cpp
)
add_library(yourlib SHARED ${INCLUDE_FILES} ${SOURCE_FILES} )
baselibraryclass.h
#ifndef BASELIBRARYCLASS_H
#define BASELIBRARYCLASS_H
#ifdef BUILD_LIB
#define EXT_DLL __declspec(dllexport)
#else
#define EXT_DLL __declspec(dllimport)
#endif
#include <string>
class EXT_DLL BaseLibraryClass
{
public:
BaseLibraryClass();
};
#endif // BASELIBRARYCLASS_H
baselibraryclass.cpp
#include "baselibraryclass.h"
#include <iostream>
EXT_DLL BaseLibraryClass::BaseLibraryClass()
{
std::cout << "Hi from the library Class Object " << std::endl;
}
Target project
CMakeLists
cmake_minimum_required(VERSION 3.5)
project(TargetProject LANGUAGES CXX)
#Including the path of the library header
include_directories(D:/TestingDLLNativeCpp/Library/include)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(TargetProject main.cpp)
#The path of the built library is D:/TestingDLLNativeCpp/Library/Debug/yourlib.lib
target_link_libraries(TargetProject PRIVATE D:/TestingDLLNativeCpp/Library/Debug/yourlib.lib)
Target project main.cpp
#include <iostream>
#include <baselibraryclass.h>
using namespace std;
int main()
{
BaseLibraryClass testObk;
return 0;
}
As I wrote above, cmake configures properly and the compiler is able to build for both projects, however the target executables crashes immediately.
What am I doing wrong o.O??
Thanks for the attention
The issue of the dll not being included in the build directory might be solved by setting the CMAKE_RUNTIME_OUTPUT_DIRECTORY. However, a better practice would be to set the output directory of the .dll on a target basis. This will ensure that you have no unwanted side-effects in the long run.
set_target_property(yourlib PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")

Can build a trivial project with Meson but not with CMake

I want to get two executables from my C++ source code (test and release). I have two main() functions in two separate C++ files.
With the Meson build system it is easy:
project('PrjName', 'cpp')
mainSrc = ['header1.hpp', 'source1.cpp', 'source2.cpp']
testSrc = ['header2.hpp', 'source2.cpp', 'test.cpp']
mainExe = executable('prjName', mainSrc)
testExe = executable('prjNameTest', testSrc)
I could not get the same with CMake:
cmake_minimum_required(VERSION 3.10)
project("PrjName")
set(SOURCES
"header1.hpp"
"source1.cpp"
"source2.cpp"
)
set(TEST_SOURCES
"header2.hpp"
"source2.cpp"
"test.cpp"
)
add_executable("prjName" ${SOURCES})
add_executable("prjNameTest" ${TEST_SOURCES})
I get the first executable (prjName), but not the second, with the error:
Multiple definition of 'main'
However, the main() functions are defined in "source1.cpp" and "test.cpp", so there should be no conflict.
How can I fix this issue, considering that as it seems from the Meson build the code should be fine?
Building the same code on a different PC, I had no such issue.
I would close/delete this question.

Linking libraries CMake

I'm not sure whether the title is correct, but anyway:
I'm writing the project with boost and eigen3 libraries, further I would like to test CMake. So far I've written everything in one .cpp file, the header is like:
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <iostream>
#include "eigen/Eigen/Dense"
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Hello
#include <boost/test/unit_test.hpp>
#include <cmath>
#include <boost/timer/timer.hpp>
#include <fstream>
Further I added some boost unit tests in my "main" function in the following way
BOOST_AUTO_TEST_CASE(universeInOrder)
{
Eigen::Matrix3d m;
m << 0, 1, 2,
3, 4, 5,
6, 7, 8;
BOOST_CHECK(Min(m) == 1);
}
where Min is a separate function returning the lowest coeff. of matrix.
In my directory I have both FindEigen3.CMake and FindBoost.CMake files. I also have CMakeLists.txt file with the code:
cmake_minimum_required (VERSION 3.0)
project (hello)
find_package (Eigen3 3.3 REQUIRED NO_MODULE)
add_executable (hello hello.cpp)
target_link_libraries (hello Eigen3::Eigen)
enable_testing()
I'm running
mkdir build
cd build
cmake ..
and there are no errors, everything is ok.
To run the program I use
g++ -I /home/fdhd/Documents/boost/ file-name.cpp -lboost_unit_test_framework -lboost_timer -o file-name
And here is my question - can I avoid linking boost in that command above and plug in into CMakeLists.txt file and run just
g++ file-name.cpp ,
so that it will work because everything is in CMakeLists file?
Any ideas? I think that the solution must be just behind the corner, but I can't really see it.
Thanks!
To run the program I use
Nop, that's not the cmake idiomatic way to run/test your program. Since you have enable_testing(), cmake already has a custom target for you to run your binary. According to the CTest wiki, all you need is to type make test after adding add_test function in you CMakeLists.txt. More or less like this:
cmake_minimum_required (VERSION 3.0)
project (hello)
find_package (Eigen3 3.3 REQUIRED NO_MODULE)
add_executable (hello hello.cpp)
target_link_libraries (hello Eigen3::Eigen)
enable_testing()
add_test(test hello)