Search code examples
cgcccmakecross-platformtarget

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:

  • top CMakeLists: contains application files to compile (main.c) + 2 target dependencies (algo and hardware)
target_link_libraries(app_target PUBLIC algo_target hardware_target)
  • algo CMakeLists has only C files which do only computes (algo.c)
  • hardware CMakeLists which compiles all files about hardware dependencies (hardware.c) and compile options depending of the hardware specially -mcpu -mthumb -mfloat-abi -mfpu
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


Solution

  • 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).