P. Paul-Alexandre
P. Paul-Alexandre

Reputation: 189

how propagate compilation options from a target to all others on Cmake

I'm implementing modern Cmake on a ARM project. I have 3 different CMakeLists:

target_link_libraries(app_target PUBLIC algo_target hardware_target)
target_compile_options(hardware_target -mcpu=${CPU} -mthumb -mfloat-abi=hard -mfpu=${FPU})

The problem is, the compile options are propagate to the top, but not on algo_target. I have the following error:

app uses VFP register arguments, algo.a(algo.cpp.obj) does not

How propagate compilation options on all targets ? I don't want to set compile options in compile option variable, in the future, the application will run on 2 different hardware targets

Upvotes: 7

Views: 3713

Answers (1)

Arda Aytekin
Arda Aytekin

Reputation: 1301

Because you do not provide a minimal working example, I might not be able to answer your question fully. However, since CMake is all about targets and dependency management among those targets, I guess that any target that needs some settings/dependencies from another one should depend on that target.

Assume that we have the following directory structure:

.
├── algo
│   ├── CMakeLists.txt
│   ├── include
│   │   └── algo.hpp
│   └── src
│       └── algo.cpp
├── CMakeLists.txt
├── hardware
│   ├── CMakeLists.txt
│   ├── include
│   │   └── hardware.hpp
│   └── src
│       └── hardware.cpp
└── main.cpp

6 directories, 8 files

with the following file contents for algo

# algo/CMakeLists.txt
add_library(algo
  include/algo.hpp
  src/algo.cpp
)
target_include_directories(algo
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)

# if (TARGET hardware)
#   target_link_libraries(algo PRIVATE hardware)
# endif()

/* algo/include/algo.hpp */
#pragma once

double func1(const double);

/* algo/src/algo.cpp */
#include "algo.hpp"

double func1(const double x) {
#ifdef IMPORTANT_DEF
  return 2 * x;
#else
  return x;
#endif
}

for hardware

# hardware/CMakeLists.txt
add_library(hardware
  include/hardware.hpp
  src/hardware.cpp
)
target_include_directories(hardware
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)
target_compile_definitions(hardware
  PUBLIC
    IMPORTANT_DEF
)

/* hardware/include/hardware.hpp */
#pragma once

#ifdef IMPORTANT_DEF
double func2(const double, const double);
#else
int func2(int, const int);
#endif

/* hardware/src/hardware.cpp */
#include "hardware.hpp"

#ifdef IMPORTANT_DEF
double func2(const double x, const double y) { return x + y; }
#else
int func2(int x, const int y) { return x - y; }
#endif

and, finally, for app

# CMakeLists.txt
cmake_minimum_required(VERSION 3.9)

project(propagate LANGUAGES C CXX)

add_subdirectory(hardware)
add_subdirectory(algo)

add_executable(app main.cpp)
target_link_libraries(app
  PRIVATE
    hardware
    algo
)

/* main.cpp */
#include <iostream>
using namespace std;

#include "hardware.hpp"
#include "algo.hpp"

int main(int argc, char* argv[]) {
  cout << func1(5) << '\n';
  cout << func2(5, 3) << '\n';
  return 0;
}

When we build the above project and run it, we get

./app
5
8

This is because we have not told CMake that algo depends on hardware. When we uncomment the part

# if (TARGET hardware)
#   target_link_libraries(algo PRIVATE hardware)
# endif()

in algo/CMakeLists.txt and rebuild the project, this time we get

./app
10
8

Basically, target_* commands are used to define dependencies that should or should not propagate to the consumers of the targets. Hence, we should make the algo target a consumer of the hardware target.

Please note also that, for the propagation to happen, target_* friends also need to populate the INTERFACE_* properties of the target, i.e., target_* commands need to define the properties as either PUBLIC (appearing both in header and implementation files) or INTERFACE (appearing only in header files), but not PRIVATE (appearing only in the implementation files).

Upvotes: 4

Related Questions