Linking libraries CMake - c++

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)

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: build dependency downloaded with ExternalProject_Add before hand

I'm trying to get my test program main.cpp to link against a an externally downloaded library (in this case fmt 8.1.1) via cmake's 'ExternalProject_Add' function as follows
CMakeLists.txt
cmake_minimum_required(VERSION 3.23)
project(test)
set(CMAKE_CXX_STANDARD 20)
include(ExternalProject)
ExternalProject_Add(fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 8.1.1
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=20 -DFMT_TEST=OFF -DFMT_DOC=OFF -DFMT_INSTALL=ON -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/fmt-build
STEP_TARGETS build
)
set(fmt_BINARY_DIR "${CMAKE_BINARY_DIR}/fmt-prefix/src/fmt-build")
set(fmt_SOURCE_DIR "${CMAKE_BINARY_DIR}/fmt-prefix/src/fmt/include")
add_executable(${PROJECT_NAME} main.cpp)
add_dependencies(${PROJECT_NAME} fmt-build) # does not have an effect
target_link_libraries(${PROJECT_NAME} PRIVATE ${fmt_BINARY_DIR}/libfmt.a)
target_include_directories(${PROJECT_NAME} PUBLIC ${fmt_SOURCE_DIR})
main.cpp
#include <chrono>
#include <fmt/core.h>
#include <fmt/chrono.h>
int main() {
const auto now = std::chrono::system_clock::now();
fmt::print("The time is {}", now);
return 0;
}
The test program main.cpp runs OK if I build the target fmt-build before hand, but complains that
ninja: error: 'fmt-prefix/src/fmt-build/libfmt.a', needed by 'test', missing and no known rule to make it
indicating that the dependency fmt-build must be built before hand. While that's doable, how do I get cmake to autmoatically build fmt-build as a dependency for test without haing to run it manually first? I thought the line add_dependencies(${PROJECT_NAME} fmt-build) should take care of that but evidently it does not.
Based on what #Tsyvarev commented, simply adding the line
BUILD_BYPRODUCTS ${CMAKE_BINARY_DIR}/fmt-prefix/src/fmt-build/libfmt.a
to the ExternalProject_Add call does the trick, and one does not have to manually build fmt-build before hand.

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".

CMake find eigen incorrect results

I am trying to compile a code snippet with Eigen using CMake, but CMake seems to have trouble finding the correct path, even if the source code is there.
This is my CMakeLists.txt file.
cmake_minimum_required(VERSION 3.14.2)
project(test)
# set default build type
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
find_package(OpenCV 3 REQUIRED)
find_package (Eigen3 3.3 REQUIRED NO_MODULE)
include_directories(
include
${OpenCV_INCLUDE_DIRS}
${EIGEN3_INCLUDE_DIR}
)
add_executable(playground playground.cpp)
target_link_libraries(playground ${OpenCV_LIBS} Eigen3::Eigen)
This is how I import headers in playground.cpp:
#include <iostream>
#include <vector>
#include <map>
#include <math.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <string>
#include <Eigen>
My Eigen is installed under /usr/include/eigen3, here is the directory structure:
yuqiong#yuqiong-G7-7588:/usr/include/eigen3$ ls
Eigen signature_of_eigen3_matrix_library unsupported
So the correct path to Eigen library is /usr/include/eigen3/Eigen.
However, when I run cmake .. and then make for the aforementioned CMakeLists.txt file, with the -LH flag, CMake complains it can't find Eigen. This is the path it thinks where Eigen is:
yuqiong#yuqiong-G7-7588:/media/yuqiong/DATA/vignetting/catkin_ws/src/vig2/src/build$ cmake -LH ..
-- Configuring done
-- Generating done
-- Build files have been written to: /media/yuqiong/DATA/vignetting/catkin_ws/src/vig2/src/build
-- Cache values
// Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel ...
CMAKE_BUILD_TYPE:STRING=
// Install path prefix, prepended onto install directories.
CMAKE_INSTALL_PREFIX:PATH=/usr/local
// The directory containing a CMake configuration file for Eigen3.
Eigen3_DIR:PATH=/usr/local/share/eigen3/cmake
// The directory containing a CMake configuration file for OpenCV.
OpenCV_DIR:PATH=/opt/ros/kinetic/share/OpenCV-3.3.1-dev
This is the structure of the folder where it think Eigen is:
yuqiong#yuqiong-G7-7588:/usr/local/share/eigen3$ tree
.
└── cmake
├── Eigen3Config.cmake
├── Eigen3ConfigVersion.cmake
├── Eigen3Targets.cmake
└── UseEigen3.cmake
I am confused why CMake has such behavior? How can I let it find the correct path of Eigen in this case?
All this environment set-ups are so tricky to get it right...
Thanks for the comments above. I think the issue might be the Eigen team changed their directory structure somehow between different versions, and the old include path does not work anymore.
The solution for me was to change #include <Eigen/Dense> to #include <eigen3/Eigen/Dense>. After that g++ and CMake can find the path.
I did install Eigen following this official doc, which was why I was surprised the installation is still problematic.
This answer solved my problem.
This answer also solves the problem.
On another note, my CMake file was a bit messed up, with both include_directories and target_link_libraries.

Linking project source code with boost tests under CMake

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!