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):
- How can I build two executables that share a main function in CMake?
- Cmake configuration to build same source with different options
- cmake: Building multiple versions of a program
- CMake : different configure_file() for each target
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)).