1

Set-up

I have the following files.

empty.cc:

#include <cstdlib>

CMakeLists.txt:

set(CMAKE_MINIMUM_VERSION 3.8)

cmake_minimum_required(VERSION ${CMAKE_MINIMUM_VERSION})

find_package(GTest REQUIRED)

set(gtest_test gtest-test)
add_executable(${gtest_test} empty.cc)
#target_link_libraries(${gtest_test} ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES}) # Line A
target_link_libraries(${gtest_test} GTest::GTest GTest::Main) # Line B

toolchain.cmake:

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 4.14.0)
set(CMAKE_SYSTEM_PROCESSOR arm)


set(CMAKE_C_COMPILER /opt/zynq/xtl/bin/arm-linux-musleabihf-gcc)
set(CMAKE_CXX_COMPILER /opt/zynq/xtl/bin/arm-linux-musleabihf-g++)

set(CMAKE_SYSROOT /opt/zynq/xtl/arm-linux-musleabihf)

set(CMAKE_FIND_ROOT_PATH /opt/zynq/xtl/arm-linux-musleabihf)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER)


set(CMAKE_INSTALL_PREFIX /opt/zynq/xtl/arm-linux-musleabihf)

Problem

I build it using:

cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake .. && make VERBOSE=1 -j

When I link against GTest using Line A, i.e., target_link_libraries(${gtest_test} ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES}), then everything is OK. But when I use Line B, i.e., target_link_libraries(${gtest_test} GTest::GTest GTest::Main), then I've got the following error:

/opt/zynq/xtl/bin/arm-linux-musleabihf-g++ --sysroot=/opt/zynq/xtl/arm-linux-musleabihf   -isystem /opt/zynq/xtl/arm-linux-musleabihf/include   -o CMakeFiles/gtest-test.dir/empty.cc.o -c /home/xxx/git/cmake/cmake-with-gtest/empty.cc
In file included from /home/xxx/git/cmake/cmake-with-gtest/empty.cc:1:
/opt/zynq/xtl/arm-linux-musleabihf/include/c++/8.3.0/cstdlib:75:15: fatal error: stdlib.h: No such file or directory
 #include_next <stdlib.h>
               ^~~~~~~~~~
compilation terminated.
CMakeFiles/gtest-test.dir/build.make:62: recipe for target 
'CMakeFiles/gtest-test.dir/empty.cc.o' failed
make[2]: *** [CMakeFiles/gtest-test.dir/empty.cc.o] Error 1

g++ version

...$ /opt/zynq/xtl/bin/arm-linux-musleabihf-g++ --version
arm-linux-musleabihf-g++ (GCC) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Normal build, i.e., not cross compiling, works fine using either

target_link_libraries(${gtest_test} ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES}) # Line A

or

target_link_libraries(${gtest_test} GTest::GTest GTest::Main) # Line B

Question

Why is it that using one way of linking results in a missing header and the other works just fine?

shycha
  • 440
  • 5
  • 13
  • Small notes (unrelated to the problem): 1. A directory set in the `CMAKE_SYSROOT` variable is **automatically** treated as one in the `CMAKE_FIND_ROOT_PATH` list; no needs to explicitely add sysroot to `CMAKE_FIND_ROOT_PATH` list. 2. Setting `CMAKE_INSTALL_PREFIX` generally is not part of the toolchain. See also pattern for setting default install prefix: https://stackoverflow.com/questions/39481958/setting-cmake-install-prefix-from-cmakelists-txt-file. – Tsyvarev May 31 '19 at 21:02

1 Answers1

1

It is known that using -isystem for standard compiler include directories breaks #include_next directive which is widely used for standard C++ header files. See e.g. this question: -isystem on a system include directory causes errors.

CMake has a mechanism for filtering out standard compiler include directories, which uses variable CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES as a list of include directories which are needed to be filtered out. Unfortunately, this list is not autodetected with a compiler (see that closed bugreport: https://gitlab.kitware.com/cmake/cmake/issues/16291). Instead, directory /usr/include (relative to sysroot) is just added to that list.

Because in your toolchain compiler include directory is not a /usr/include but /include one, you need to add this directory to the exception list explicitely:

# in toolchain.cmake
list(APPEND CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES ${CMAKE_SYSROOT}/include)
# Setting the exclude directory for C compiler is optional:
# C standard headers rarely use `#include_next` directive
list(APPEND CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES ${CMAKE_SYSROOT}/include)

Difference between

target_link_libraries(${gtest_test} ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES}) # Line A

and

target_link_libraries(${gtest_test} GTest::GTest GTest::Main) # Line B

is that the Line B uses targets names for link with, and include directories assosiated with that targets are automatically added to the your executable. That is why you got -isystem.

The second way (Line B) is treated as "modern" one and is suggested to be used in newly written code. But both methods should work.

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • Well... It doesn't work. I didn't mention, but I have `usr` in `/opt/zynq/xtl/arm-linux-musleabihf` pointing to this directory, i.e., `usr -> .`. So `/opt/zynq/xtl/arm-linux-musleabihf/include` if effectively the same as `/opt/zynq/xtl/arm-linux-musleabihf/usr/include`. – shycha May 31 '19 at 23:39