I'm trying to integrate a normal compilation and a unit-test with gtest into one cmake file, but I don't know how to achieve this. Here is my project:
|---include
| |---Student.h
|
|---src
| |---Student.cpp
| |---main.cpp
|
|---unittest
| |---TestStudent.cpp
|
|---CMakeLists.txt # how to write this file?
So, Student.h, Student.cpp and main.cpp is the source code, TestStudent.cpp is the test code, which includes gtest/gtest.h and a main function, here it is:
#include "gtest/gtest.h"
#include "Student.h"
class TestStudent : public ::testing::Test
{
protected:
Student *ps;
void SetUp() override
{
ps = new Student(2, "toto");
}
void TearDown() override
{
delete ps;
}
};
TEST_F(TestStudent, ID)
{
EXPECT_TRUE(ps->GetID() == 2);
EXPECT_TRUE(ps->GetName() == "toto");
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Now, if I want to compile the source code, I need to run g++ -std=c++11 Student.cpp main.cpp -o a.out, whereas if I want to compile the test code, I need to run g++ -std=c++11 TestStudent.cpp Student.cpp -lgtest -lpthread -o test.out.
So, how could I write the CMakeLists.txt to allow me to compile the different targets, such as cmake NORMAL and cmake TEST?
As already pointed out in the comments use multiple targets via add_executable. The following CMakeLists.txt will produce two targets generating the executables student and student-test.
The last three lines can be omitted if you don't care about CTest.
cmake_minimum_required(VERSION 3.9)
project(Student CXX)
set(CMAKE_CXX_STANDARD 11)
set(SOURCES src/Student.cpp)
set(INCLUDES include)
find_package(GTest REQUIRED)
add_executable(student src/main.cpp ${SOURCES})
target_include_directories(student PRIVATE ${INCLUDES})
add_executable(student-test unittest/TestStudent.cpp ${SOURCES})
target_include_directories(student-test PRIVATE ${INCLUDES})
target_link_libraries(student-test GTest::GTest GTest::Main)
enable_testing()
include(GoogleTest)
gtest_add_tests(TARGET student-test AUTO)
Related
I am using Windows 10 and CMake to build my project (using MinGW Makefiles as the generator), while choosing Clang/Clang++ (LLVM) as my compiler due to my Language Server Protocol also being for Clang.
However, when trying to link a static library I had created to my main executable, none of the definitions in the translation units were able to be resolved.
For a simple recreation:
File system
+ root/
+ main.cpp
+ foobar.cpp
+ foobar.hpp
+ CMakeLists.txt
// main.cpp
#include <iostream>
#include "foobar.hpp"
int main( int argc, char** argv ) {
foo();
std::cin.get();
return 0;
}
// foobar.cpp
#include "foobar.hpp"
void foo() {
std::cout << "bar" << std::endl;
}
// foobar.hpp
#pragma once
#ifndef FOOBAR_HEADER_H
#define FOOBAR_HEADER_H
#include <iostream>
void foo();
#endif
# CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(linker_fail
VERSION 1.0.0
LANGUAGES C CXX)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_library(foobar
foobar.cpp
foobar.hpp)
add_executable(${PROJECT_NAME}
main.cpp)
target_link_libraries(${PROJECT_NAME} PUBLIC
foobar)
Then for building:
root> cmake -S . -B bin -G "MinGW Makefiles"
root> cmake --build bin
lld-link: error: undefined symbol: void __cdecl foo(void)
Does anyone know how to fix this? Should I have passed some arguments to the compiler/linker? Or does Clang just not allow static linking?
I want to build a simple application based on a .cpp and a .h file.
I couldn't make my project work so i started from a basic example but didn't succeed as i'm just starting creating project in Linux. From what i've seen, my CMakeLists should be like this :
My CMakeLists.txt :
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 11)
project(test C CXX)
add_executable(${PROJECT_NAME} main_new.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR})
My main_new.cpp :
#include <read.h>
int main(int argc, char* argv[])
{
int a, b, c, d, e = 0;
std::cout << "hello im example " << std::endl;
read_int(&a, &b, &c, &d, &e);*
return 0;
}
My read.h :
#ifdef __cplusplus
extern "C" {
#endif
int read_int(int *vectorA, int *vectorB, int *matrixA, int *matrixB, int *flags);
#ifdef __cplusplus
}
#endif
My .cpp and .h file are in the same folder. cmake . is giving me no error, but after using make i get
main_new.cpp:(.text+0x68) : undefined reference to « read_int »
I'm using the makefile created by the cmake command. Should i create a custom makefile ?
Edit : Added question :
I also have to implement .so files, but doing target_link_libraries(test ${CMAKE_SOURCE_DIR}/libA.so ${CMAKE_SOURCE_DIR}/libB.so) in my CMakeLists.txt file doesn't work. How can i link these libraries to my executable ?
I tried several things to resolve this issue, but I just can't get my head around CMake apparently...
The following is basically the entire project:
.
├── CMakeLists.txt
└── src
├── CMakeLists.txt
├── main.cpp
├── SubwordEncoder.cpp
└── SubwordEncoder.h
The first (./CMakeLists.txt) contains:
cmake_minimum_required(VERSION 3.5)
project(xlib)
set(CMAKE_CXX_STANDARD 11)
add_subdirectory(./src)
add_executable(main src/main.cpp)
and the second (src/MakeLists.txt)
file(GLOB_RECURSE xlib_SOURCES "*.cpp")
file(GLOB_RECURSE xlib_SURCES "*.h")
main.cpp only contains this:
#include <string>
#include <iostream>
#include <stdio.h>
#include "SubwordEncoder.h"
int main() {
std::cout << "Hello World!" << std::endl;
auto encoder = new SubwordEncoder();
auto encoded = encoder->encode("Hello World!");
for (auto i : encoded) {
std::cout << i << std::endl;
}
return 0;
}
SubwordEncoder.h
#ifndef XLIB_SUBWORDENCODER_H
#define XLIB_SUBWORDENCODER_H
#include <string>
#include <vector>
class SubwordEncoder {
public:
std::vector<int> encode(std::string decoded);
};
#endif //XLIB_SUBWORDENCODER_H
and SubwordEncoder.cpp
#include "SubwordEncoder.h"
std::vector<int> SubwordEncoder::encode(std::string decoded) {
std::vector<int> vect;
vect.push_back(10);
vect.push_back(20);
vect.push_back(30);
return vect;
}
Yet I keep getting this error:
/tmp/tmp.KYl9HEcObN/src/main.cpp:13: undefined reference to `SubwordEncoder::encode(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
What am I doing wrong?
Note:
I know I could do this:
add_executable(main src/main.cpp src/SubwordEncoder.cpp src/SubwordEncoder.h)
but I don't want to add every single file here. I want all files in src/ to be used for the compilation.
When you call add_executable(), you are only including main.cpp and not the others, so SubwordEncoder::encode is undefined when you try to use it in main(). Try changing your CMake files to something like this:
./CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(xlib)
set(CMAKE_CXX_STANDARD 11)
add_subdirectory(src)
./src/CMakeLists.txt
file(GLOB_RECURSE xlib_SOURCES "*.cpp")
file(GLOB_RECURSE xlib_HEADERS "*.h")
add_executable(main ${xlib_SOURCES} ${xlib_HEADERS})
# Include the current folder for including headers.
target_include_directories(main PRIVATE ${CMAKE_CURRENT_LIST_DIR})
The only line in either of your CMake files that actually causes files to be compiled is add_executable(main src/main.cpp) and the only file it adds is main.cpp. There is nothing in either CMake file that actually tells it to compile the other source files.
These lines
file(GLOB_RECURSE xlib_SOURCES "*.cpp")
file(GLOB_RECURSE xlib_HEADERS "*.h")
only set up variables containing lists of file names. There is nothing that then includes those files in any compilation. Your options are either add those files to your compilation of the main target as in squareskittles answer, or compile them as a separate library and then link it into main:
src/CMakeLists.txt:
add_library(SubwordEncoder ${xlib_SOURCES})
./CMakeLists.txt:
target_link_libraries(main SubwordEncoder)
This my project structure:
main
|
--src
|
--feed.h
--feed.cc
--other files
--CMakeLists2.txt
--test
|
--test.cpp
--CMakeLists3.txt
CMakeLists1.txt
CMakeLists1.txt
cmake_minimum_required (VERSION 2.8.11)
project (Feedparser)
set(CMAKE_MODULE_PATH cmake)
find_package(PTHREAD REQUIRED)
find_package(CURL REQUIRED)
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 ")
elseif(COMPILER_SUPPORTS_CXX0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x ")
else()
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
add_subdirectory (src)
add_subdirectory (test)
CMakeLists2.txt
cmake_minimum_required (VERSION 2.8.11)
add_library (Feedparser news.h xml2json.hpp jsonxx.cc curler.cc feed.cc )
target_link_libraries(Feedparser pthread)
target_link_libraries(Feedparser curl)
install(TARGETS Feedparser
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION /usr/lib)
install(FILES "feed.h" DESTINATION /usr/include )
target_include_directories (Feedparser PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
CMakeLists3.txt
cmake_minimum_required (VERSION 2.8.11)
add_executable(Feedtest test.cc)
target_link_libraries (Feedtest LINK_PUBLIC Feedparser)
Here is my Header file.
feed.h
#include "news.h"
#include "curler.cc"
#include "jsonxx.h"
#include "jsonxx.cc"
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <thread>
#include <stdio.h>
using namespace std;
using namespace jsonxx;
class feed{
map <string,string> info;
std::map<int, Object> items;
string url;
news News;
Object *item;
void strip_items();
public:
Object json;
feed(string url);
void create(string url){
this->url = url;
}
feed(){}
string get_topic(){
return info["title"];
}
bool fetch();
bool fetch_data();
bool parse();
string get_url(){
return url;
}
string get_item_title(int index){
return News.title[index];
}
string get_item_img(int index){
return News.image[index];
}
string get_item_link(int index){
return News.link[index];
}
int get_total(){
return News.num_item;
}
struct news get_news(){
return News;
}
};
Should I include feed.h in feed.cc and compile and how does the compiler directly link .h files with the .cxx files in archives?
How do i write a cmake script for installing this library?
Where is my mistake?
C++'s building process consists of 2 stages:
Compilation: The compiler runs on every source (.cc) file, generating an intermidiate binary.
Linking: The linker runs on every intermidiate binary, matching declarations with definitions.
Finally, it combines all of them to make the final binary.
With this in mind, you need to make sure the compiler doesn't come across something that it doesn't know about. That's why you include headers (.h).
The linker should be fed with everything the compiler creates.
I am a newcomer to the world of cmake. This question came up while I was experimenting with some basic cmake configurations in C++. To be precise, following is my directory structure :
/src----
|-> CMakeLists.txt
|-> main.cpp
|-> lib----
|-> libfsystem.so
|->filesystem----
|->CMakeLists.txt
|->listfiles.cpp
|->include-----
|->fsystem.h
Now, the /src/filesystem/CMakeLists.txt file is like this
cmake_minimum_required(VERSION 2.8)
project(fsystem)
set(CMAKE_BUILD_TYPE Release)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/build)
find_package(Boost REQUIRED system filesystem)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(fsystem SHARED listfiles.cpp)
While, the /src/CMakeLists.txt file is like this
cmake_minimum_required(VERSION 2.8)
project(vessel_detect)
add_subdirectory(filesystem)
add_executable(main main.cpp)
set(CMAKE_LIBRARY_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
find_library(libpath fsystem)
MESSAGE(${libpath})
target_link_libraries(main ${libpath})
The library libfsystem.so is successfully created.
The library libfsystem.so is also successfully found by /src/CMakeLists.txt
However when the linking of main.cpp is done, then it gives me several undefined reference errors which should not have happened as everything has already been defined. For greater completeness, following is the content of main.cpp file
Main.cpp
#include<iostream>
#include"filesystem/include/fsystem.h"
using namespace std;
int main(void)
{
string path ("~");
vector<string>* output;
output = listfiles(path);
return 0;
}
The contents of listfiles.cpp are
listfiles.cpp
#include"fsystem.h"
using namespace boost::filesystem;
vector<string>* listfiles(int argc, char* argv[])
{
if (argc<2)
{
std::cout<<"No file name provided"<<std::endl;
}
else
{
path p(argv[1]);
vector<string>* output;
if (exists(p))
{
if(is_directory(p))
{
std::cout<<"You specified a directory"<<std::endl;
std::cout<<"Its contents are as follows :-"<<std::endl;
typedef std::vector<path> vec;
vec v;
copy(directory_iterator(p),directory_iterator(),back_inserter(v));
sort(v.begin(),v.end());
for(vec::const_iterator it(v.begin());it!=v.end();++it)
output->push_back(it->filename().string());
// std::cout<<it->filename()<<std::endl;
}
else if (is_regular_file(p))
{
std::cout<<argv[1]<<" "<<file_size(p)<<std::endl;
}
else
{
std::cout<<"The file is neither a directory nor a regular file"<<std::endl;
}
}
else
{
std::cout<<"The speicified path does not exist"<<std::endl;
}
}
}
And finally, the fsystem.h contents are :
fsystem.h
#ifndef _fsystem_h
#define _fsystem_h
#include<iostream>
#include<string>
#include<vector>
#include"boost/filesystem.hpp"
using namespace std;
vector<string>* listfiles(string);
#endif
Could someone provide me a reason for the undefined reference errors I am getting during the linking of main.cpp ? I would also be grateful if you could provide me with a resolution of this issue.
Thanks
Your not linking boost. you need to add target_link_libraries(fsystem ${Boost_LIBRARIES}) to the end of /src/filesystem/CMakeLists.txt and include_directories(${Boost_INCLUDE_DIRS}) between the find_package and add_library.
cmake_minimum_required(VERSION 2.8)
project(fsystem)
set(CMAKE_BUILD_TYPE Release)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/build)
find_package(Boost REQUIRED system filesystem)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
include_directories(${Boost_INCLUDE_DIRS})
add_library(fsystem SHARED listfiles.cpp)
target_link_libraries(fsystem ${Boost_LIBRARIES})
(1) For TARGET_LINK_LIBRARIES you should put the name of the target, thus:
TARGET_LINK_LIBRARIES(main fsystem)
(2) You declare listfiles as vector<string>* listfiles(string) while you define it as vector<string>* listfiles(int,char**)
Additionally you need to link with Boost per the other reply.