Wednesday, August 31, 2022

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

Issue

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.



Answered By - David Fong
Answer Checked By - Cary Denson (WPSolving Admin)