Olivier Croquette
2018-11-03 20:41:03 UTC
Hi,
I got recently build errors when introducing external dependencies in my project, the reason is that those components re-add standard SYSTEM include search paths, which changes the search order and causes #include_next to fail. The typical error message is:
C:\...\lib\gcc\x86_64-w64-mingw32\7.2.0\include\c++\cstdlib:75: error: stdlib.h: No such file or directory
at #include_next
The following bug report against GCC describes the same issue independently of CMake, and apparently no improvement is to be expected from the compiler itself:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129
So I rolled up my sleeves and implemented the following solution in CMake. It calls the preprocessor to get the standard include search paths and adds them to CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES and CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES.
When a project or an external component tries to add them, CMake ignores this, and the search order stays unharmed.
if("${CMAKE_MINGW_IMPLICIT_INCLUDE_DIRECTORIES}" STREQUAL "")
# Run the preprocessor in verbose mode on an empty input
execute_process(
COMMAND
"${CMAKE_CXX_COMPILER}"
"-E"
"-Wp,-v"
"-"
INPUT_FILE "NUL" # Special Windows file, equivalent to /dev/null
OUTPUT_VARIABLE _mingw_cpp_out # Capture stdout
ERROR_VARIABLE _mingw_cpp_error # Capture stderr
)
# Create list of lines from stderr output:
string(REGEX REPLACE ";" "\\\\;" _mingw_cpp_error "${_mingw_cpp_error}")
string(REGEX REPLACE "\n" ";" _mingw_cpp_error "${_mingw_cpp_error}")
# Look for this text block and gather the paths:
# #include search starts here:
# C:/..../bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/include
# C:/..../bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/include-fixed
# C:/..../bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/include
# End of search list.
set(_mingw_cpp_list)
foreach(_mingw_cpp_line ${_mingw_cpp_error})
if("${_mingw_cpp_line}" MATCHES "#include search starts here:")
# Block starts
set(_mingw_cpp_state "ON")
elseif("${_mingw_cpp_line}" MATCHES "End of search list.")
# Block ends
set(_mingw_cpp_state "OFF")
elseif("${_mingw_cpp_state}")
# Within block
# Clean up and beautify the path
string(STRIP "${_mingw_cpp_line}" _mingw_cpp_line)
get_filename_component(_mingw_cpp_line ${_mingw_cpp_line} REALPATH)
list(APPEND _mingw_cpp_list ${_mingw_cpp_line})
endif()
endforeach()
# Set the list in the cache, so that we don't have to run the external process again
set(CMAKE_MINGW_IMPLICIT_INCLUDE_DIRECTORIES ${_mingw_cpp_list} CACHE INTERNAL "List of MinGW system include paths")
endif()
list(APPEND CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES ${CMAKE_MINGW_IMPLICIT_INCLUDE_DIRECTORIES})
list(APPEND CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES ${CMAKE_MINGW_IMPLICIT_INCLUDE_DIRECTORIES})
My question is: shouldn't this be done within the standard CMake distribution, when using any GCC based compiler?
Olivier
I got recently build errors when introducing external dependencies in my project, the reason is that those components re-add standard SYSTEM include search paths, which changes the search order and causes #include_next to fail. The typical error message is:
C:\...\lib\gcc\x86_64-w64-mingw32\7.2.0\include\c++\cstdlib:75: error: stdlib.h: No such file or directory
at #include_next
The following bug report against GCC describes the same issue independently of CMake, and apparently no improvement is to be expected from the compiler itself:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129
So I rolled up my sleeves and implemented the following solution in CMake. It calls the preprocessor to get the standard include search paths and adds them to CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES and CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES.
When a project or an external component tries to add them, CMake ignores this, and the search order stays unharmed.
if("${CMAKE_MINGW_IMPLICIT_INCLUDE_DIRECTORIES}" STREQUAL "")
# Run the preprocessor in verbose mode on an empty input
execute_process(
COMMAND
"${CMAKE_CXX_COMPILER}"
"-E"
"-Wp,-v"
"-"
INPUT_FILE "NUL" # Special Windows file, equivalent to /dev/null
OUTPUT_VARIABLE _mingw_cpp_out # Capture stdout
ERROR_VARIABLE _mingw_cpp_error # Capture stderr
)
# Create list of lines from stderr output:
string(REGEX REPLACE ";" "\\\\;" _mingw_cpp_error "${_mingw_cpp_error}")
string(REGEX REPLACE "\n" ";" _mingw_cpp_error "${_mingw_cpp_error}")
# Look for this text block and gather the paths:
# #include search starts here:
# C:/..../bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/include
# C:/..../bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/include-fixed
# C:/..../bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/include
# End of search list.
set(_mingw_cpp_list)
foreach(_mingw_cpp_line ${_mingw_cpp_error})
if("${_mingw_cpp_line}" MATCHES "#include search starts here:")
# Block starts
set(_mingw_cpp_state "ON")
elseif("${_mingw_cpp_line}" MATCHES "End of search list.")
# Block ends
set(_mingw_cpp_state "OFF")
elseif("${_mingw_cpp_state}")
# Within block
# Clean up and beautify the path
string(STRIP "${_mingw_cpp_line}" _mingw_cpp_line)
get_filename_component(_mingw_cpp_line ${_mingw_cpp_line} REALPATH)
list(APPEND _mingw_cpp_list ${_mingw_cpp_line})
endif()
endforeach()
# Set the list in the cache, so that we don't have to run the external process again
set(CMAKE_MINGW_IMPLICIT_INCLUDE_DIRECTORIES ${_mingw_cpp_list} CACHE INTERNAL "List of MinGW system include paths")
endif()
list(APPEND CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES ${CMAKE_MINGW_IMPLICIT_INCLUDE_DIRECTORIES})
list(APPEND CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES ${CMAKE_MINGW_IMPLICIT_INCLUDE_DIRECTORIES})
My question is: shouldn't this be done within the standard CMake distribution, when using any GCC based compiler?
Olivier