15

From Catch2's example, I tried to run this example with cmake where structure of my project is like this:

/factorial
    +-- CMakeLists.txt
    +-- /bin
    +-- /include
    |     +-- catch.hpp
    |     +-- fact.hpp 
    +-- /src
    |     +-- CMakeLists.txt
    |    +-- fact.cpp
    +-- /test
         +-- CMakeLists.txt
         +-- test_fact.cpp

fact.cpp:

unsigned int factorial( unsigned int number ) {
    return number <= 1 ? number : factorial(number-1)*number;
}

fact.hpp:

#ifndef FACT_H
#define FACT_H

unsigned int factorial(unsigned int);

#endif

test_fact.cpp:

#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "fact.hpp"

TEST_CASE( "factorials are computed", "[factorial]" ) {
    REQUIRE( factorial(1) == 1 );
    REQUIRE( factorial(2) == 2 );
    REQUIRE( factorial(3) == 6 );
    REQUIRE( factorial(10) == 3628800 );
}

I tried several ways already to build this project with cmake but it's failed. Sometimes I got an error:

cpp:X:XX: fatal error: 'fact.hpp' file not found
...

and sometimes I got:

Undefined symbols for architecture x86_64:
  "_main", referenced from:
...

when I run make.

What should I have in factorial/CMakeLists.txt,factorial/src/CMakeLists.txt and factorial/test/CMakeLists.txt, if I want to have my execution files in factorial/bin?

Additional: This is my CMakeLists.txts (I think they are completely wrong).

factorial/CMakeLists.txt:

project(factorial)
cmake_minimum_required(VERSION 2.8.12)

add_definitions("-std=c++11")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)

add_subdirectory(src)
add_subdirectory(test)

factorial/src/CMakeLists.txt:

project(factorial)
cmake_minimum_required(VERSION 2.8.12)

add_executable(fact fact.cpp)

factorial/test/CMakeLists.txt:

project(factorial)
cmake_minimum_required(VERSION 2.8.12)

add_executable(test_fact test_fact.cpp)
target_include_directories(test_fact PRIVATE ${CMAKE_SOURCE_DIR}/include)
fronthem
  • 4,011
  • 8
  • 34
  • 55

2 Answers2

6

Many problems here:

add_executable(fact fact.cpp)

The call should be using add_library (You could also specify STATIC or SHARED), since you are only defining a factorial function, not an executable with a main function.

add_executable(fact fact.cpp)

The file should be test_fact.cpp and the target should have a different name to avoid conflict with the previous library you created. Also, your fact.cpp doesn't include fact.hpp. Last but not least, instead of doing target_include_directories, just write the following in your top-level CMakeLists.txt:

include_directories(include)

Now, all subdirectories should be able to access the header files. Beware that this removes control of the scoping of the header files (PRIVATE vs PUBLIC vs INTERFACE) and allows all subdirectories to access the header files. If you want to restrict this behavior, then use target_include_direcories for all targets (Your library and the test executable). For this example, since everything needs to access the header files, there is no problem with the statement above.

More problems:

project(factorial)
cmake_minimum_required(VERSION 2.8.12)

Either switch the order of these statements, or remove both of them. (You only need them in your top level CMake file)

Arnav Borborah
  • 11,357
  • 8
  • 43
  • 88
  • 4
    How can you suggest someone to use `include_directories` instead of `target_include_directories` and sleep at night? – Guillaume Racicot Mar 21 '18 at 18:50
  • 1
    @GuillaumeRacicot For this example, it isn't really necessary to specify includes on a target by target basis. However, I will add a note saying that `target_include_directories` is the better practice. – Arnav Borborah Mar 21 '18 at 18:52
  • "The call should be using `add_library`" and "The file should be test_fact.cpp" - `add_library(fact fact.cpp)` and then `add_executable(fact test_fact.cpp)` raises CMake error: "add_executable cannot create target "fact" because another target with the same name already exists.". Did I misunderstand the suggestion? – kovac Sep 15 '19 at 12:09
  • @swdon You're correct, I didn't realize I specified 2 targets with the same name, they should have been different. – Arnav Borborah Sep 15 '19 at 15:37
  • so what is causing the linking error here? It doesn't seem to be adressed – Mehdi Aug 03 '23 at 13:26
5

If you look at the CMake documentation, the PROJECT_SOURCE_DIR variable is define as that:

Top level source directory for the current project.

This is the source directory of the most recent project() command.

Since you called project many times, that variable will constantly change. I would suggest you to remove your project directive, or to use CMAKE_SOURCE_DIR, which always point to the source directory of the whole project.


As a side note, I suggest to use set(CMAKE_CXX_STANDARD 11) instead of add_definition

Community
  • 1
  • 1
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141