CMake define preprocessor macros used by all source files for each executable

79 Views Asked by At

I am working on a C++ multi-file project. I decided to use CMake to build the project (I am beginner with it). I have the following (simplified) file structure:

my_project/
├─ src/
│  ├─ foo/
│  │  ├─ foo.cpp
│  │  ├─ foo.hpp
│  ├─ baz/
│  │  ├─ baz.cpp
│  │  ├─ baz.hpp
│  ├─ main.hpp
│  ├─ CMakeLists.txt
├─ app/
│  ├─ scenario1/
│  │  ├─ scenario1.cpp
│  │  ├─ CMakeLists.txt
│  ├─ scenario2/
│  │  ├─ scenario2.cpp
│  │  ├─ CMakeLists.txt
├─ CMakeLists.txt
CMakeLists.txt

My source files all use on C preprocessor macros all defined in the main.hpp file. I would like to define different values of these macros for scenario1.cpp and scenario2.cpp.


Example

I tried with a minimal example to achieve this goal:

main.hpp

#ifndef MAIN_HPP
#define MAIN_HPP

#include <iostream>

#ifndef SCENARIO_VERSION
#define SCENARIO_VERSION 0
#endif

#endif // MAIN_HPP

foo.hpp

#ifndef FOO_HPP
#define FOO_HPP

#include "main.hpp"

#if SCENARIO_VERSION == 1
void foo(int);
#elif SCENARIO_VERSION == 2
int foo();
#else
void foo();
#endif

#endif // FOO_HPP

foo.cpp

#include "foo.hpp"

#if SCENARIO_VERSION == 1
void foo(int a)
{
    std::cout << "(foo) Version 1\n"; 
}
#elif SCENARIO_VERSION == 2
int foo()
{
    std::cout << "(foo) Version 2\n"; 
    return 0;
}
#else
void foo()
{
    std::cout << "(foo) Version DEFAULT\n"; 
}
#endif

scenario1.cpp

#include "foo.hpp"
#include "baz.hpp"

int main()
{
    std::cout << "Scenario version = " << SCENARIO_VERSION << '\n';

    baz(0);
    foo(0);

    return 0;
}

my_project/src/foo/CMakeLists.txt

set(HEADER_LIST "${my_project_SOURCE_DIR}/src/main.hpp" "${my_project_SOURCE_DIR}/src/foo/foo.hpp")

add_library(foo SHARED foo.cpp ${HEADER_LIST})

target_include_directories(foo PUBLIC . ..)

my_project/app/scenario1/CMakeLists.txt

set(HEADER_LIST "${my_project_SOURCE_DIR}/src/baz/baz.hpp" "${my_project_SOURCE_DIR}/src/foo/foo.hpp")

set(SCENARIO_VERSION 1)

add_executable(scenario1 scenario1.cpp ${HEADER_LIST})

target_link_libraries(scenario1 PRIVATE baz foo)

target_include_directories(scenario1 PRIVATE "${my_project_SOURCE_DIR}/src/baz" "${my_project_SOURCE_DIR}/src/foo")

target_compile_definitions(scenario1 PRIVATE SCENARIO_VERSION=${SCENARIO_VERSION})

When I try to build (cmake --build build) my project, there are problem of reference (none of the definition of foo() is working (either foo(0) (scenario 1) or foo() (default) )).

Note : when i remove the line target_compile_definitions(scenario1 PRIVATE SCENARIO_VERSION=${SCENARIO_VERSION})my_project/app/scenario1/CMakeLists.txt, it compiles and run (though it uses default value of SCENARIO_VERSION)


Assumptions so far

I am pretty sure it is due to some circular linking or something similar. I am also aware that target_compile_definitions(scenario1 PRIVATE SCENARIO_VERSION=${SCENARIO_VERSION}) it is not supposed to change the value of SCENARIO_VERSION for foo and baz targets, as they don't depends on scenario1 or scenario2 targets.

I also tried to use INTERFACE library for foo and baz without too much hope, but it didn't work.

I saw posts with questions similar to mine, but it didn't seems to work in my case (or at least I didn't succeed):

I guess I have to build the libraries for each scenarios as the code is supposed to be different for each scenario, but I don't see a way to do this properly (and according to the modern CMake standards (Effective Modern CMake)).

0

There are 0 best solutions below