Search code examples
gcccmakeinclude-pathavr-gcccompiler-flags

Why does cmake always use `-isystem` on imported interface target?


I'm trying to write a CMakeLists.txt to compile my Arduino-Projects to get to know cmake better. I defined the Arduino-Core library as an imported interface and try to link my own target against it. The problem is, that, when calling make the avr-gcc is provided with the specified include-paths via -isystem instead of -I. This results in several errors.

CMakeLists.txt (minimal-version to reproduce the problem)

cmake_minimum_required(VERSION 3.1)
set(ARDUINO_DIR "/opt/arduino/arduino-1.8.13")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_C_COMPILER ${ARDUINO_DIR}/hardware/tools/avr/bin/avr-gcc)
set(CMAKE_CXX_COMPILER ${ARDUINO_DIR}/hardware/tools/avr/bin/avr-g++)
set(CMAKE_SYSTEM_NAME NONE)
set(CMAKE_SYSTEM_PROCESSOR NONE)

add_library(Arduino::Core INTERFACE IMPORTED)
target_include_directories(Arduino::Core INTERFACE 
  "${ARDUINO_DIR}/hardware/arduino/avr/cores/arduino/"
  "${ARDUINO_DIR}/hardware/arduino/avr/variants/eightanaloginputs/"
)
file(GLOB_RECURSE ARDUINO_CORE_SRC "${ARDUINO_DIR}/hardware/arduino/avr/cores/arduino/*.c[p]*")
file(GLOB_RECURSE ARDUINO_CORE_ASM "${ARDUINO_DIR}/hardware/arduino/avr/cores/arduino/*.S")
target_sources(Arduino::Core INTERFACE ${ARDUINO_CORE_SRC} ${ARDUINO_CORE_ASM})
project(Blinky)

set(${PROJECT_NAME}_SRC
  src/Blink.cpp
)
add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_SRC})
target_link_libraries(${PROJECT_NAME} Arduino::Core)

Here is my Blink.cpp:

#include <Arduino.h>
void setup()
{
  pinMode(13,OUTPUT);
}
void loop()
{
  digitalWrite(13,HIGH);
  delay(1000);
  digitalWrite(13,LOW);
  delay(1000);
}

make --trace gives me the following output:

[  5%] Building CXX object CMakeFiles/Blinky.dir/src/Blink.cpp.obj
/opt/arduino/arduino-1.8.13/hardware/tools/avr/bin/avr-g++  -isystem     /opt/arduino/arduino-1.8.13/hardware/arduino/avr/cores/arduino -isystem /opt/arduino/arduino-1.8.13/hardware/arduino/avr/variants/eightanaloginputs -std=gnu++11 -o CMakeFiles/Blinky.dir/src/Blink.cpp.obj -c /tmp/so/src/Blink.cpp

As you can see, linking against the imported target includes the dependencies with -isystem even though I haven't declared SYSTEM anywhere? How can I prevent that?! Is it, because it is an INTERFACE IMPORTED target? I tried taget_include_directories(Arduino::Core PRIVATE ${my_include_dirs}) but obviously that is not allowed for INTERFACE-Targets.

Thanks in advance for every hint.

P.S. please note, that I'm aware, that this would not compile my arduino-code. This is just a mcve to show you my problem.

edit

I'm using Arduino-SDK 1.8.13 and cmake 3.18.2


Solution

  • It is because it is an imported target.

    From the CMake docs on buildsystems at the section for "include directories and usage requirements" (one of the last paragraphs in the section):

    When the INTERFACE_INCLUDE_DIRECTORIES of an imported target is consumed, the entries in the property are treated as SYSTEM include directories, as if they were listed in the INTERFACE_SYSTEM_INCLUDE_DIRECTORIES of the dependency. This can result in omission of compiler warnings for headers found in those directories. This behavior for Imported Targets may be controlled by setting the NO_SYSTEM_FROM_IMPORTED target property on the consumers of imported targets, or by setting the IMPORTED_NO_SYSTEM target property on the imported targets themselves.

    See the docs for include_directories and target_include_directories for other documentation on SYSTEM.

    As stated in the docs, you can solve this by modifying consumers of the imported target like set_target_property(${PROJECT_NAME} PROPERTIES NO_SYSTEM_FROM_IMPORTED TRUE) or set_property(TARGET ${PROJECT_NAME} PROPERTY NO_SYSTEM_FROM_IMPORTED TRUE).

    But there's another option not mentioned in the docs I quoted that I think may be better: In the docs for NO_SYSTEM_FROM_IMPORTED, it says:

    See the IMPORTED_NO_SYSTEM target property to set this behavior on the target providing the include directories rather than consuming them.

    I think it's better because NO_SYSTEM_FROM_IMPORTED will change the "system-ness" of all imported targets that the consumer links to, which may not be desirable. Perhaps a similar argument could be made against IMPORTED_NO_SYSTEM, but I personally can't think of a good backing for such an argument at the moment.