1# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2# file Copyright.txt or https://cmake.org/licensing for details.
3
4# This is used internally by CMake and should not be included by user code.
5
6# helper function that parses implicit include dirs from a single line
7# for compilers that report them that way.  on success we return the
8# list of dirs in id_var and set state_var to the 'done' state.
9function(cmake_parse_implicit_include_line line lang id_var log_var state_var)
10  # clear variables we append to (avoids possible pollution from parent scopes)
11  unset(rv)
12  set(log "")
13
14  # Cray compiler (from cray wrapper, via PrgEnv-cray)
15  if(CMAKE_${lang}_COMPILER_ID STREQUAL "Cray" AND
16     line MATCHES "^/" AND line MATCHES "/ccfe |/ftnfe " AND
17     line MATCHES " -isystem| -I")
18    string(REGEX MATCHALL " (-I ?|-isystem )(\"[^\"]+\"|[^ \"]+)" incs "${line}")
19    foreach(inc IN LISTS incs)
20      string(REGEX REPLACE " (-I ?|-isystem )(\"[^\"]+\"|[^ \"]+)" "\\2" idir "${inc}")
21      list(APPEND rv "${idir}")
22    endforeach()
23    if(rv)
24      string(APPEND log "  got implicit includes via cray ccfe parser!\n")
25    else()
26      string(APPEND log "  warning: cray ccfe parse failed!\n")
27    endif()
28  endif()
29
30  # PGI compiler
31  if(CMAKE_${lang}_COMPILER_ID STREQUAL "PGI")
32    # pgc++ verbose output differs
33    if((lang STREQUAL "C" OR lang STREQUAL "Fortran") AND
34        line MATCHES "^/" AND
35        line MATCHES "/pgc |/pgf901 |/pgftnc " AND
36        line MATCHES " -cmdline ")
37      # cmdline has unparsed cmdline, remove it
38      string(REGEX REPLACE "-cmdline .*" "" line "${line}")
39      if("${line}" MATCHES " -nostdinc ")
40        set(rv "")    # defined, but empty
41      else()
42        string(REGEX MATCHALL " -stdinc ([^ ]*)" incs "${line}")
43        foreach(inc IN LISTS incs)
44          string(REGEX REPLACE " -stdinc ([^ ]*)" "\\1" idir "${inc}")
45          string(REPLACE ":" ";" idir "${idir}")
46          list(APPEND rv ${idir})
47        endforeach()
48      endif()
49      if(DEFINED rv)
50        string(APPEND log "  got implicit includes via PGI C/F parser!\n")
51      else()
52        string(APPEND log "  warning: PGI C/F parse failed!\n")
53      endif()
54    elseif(lang STREQUAL "CXX" AND line MATCHES "^/" AND
55           line MATCHES "/pggpp1 " AND line MATCHES " -I")
56      # oddly, -Mnostdinc does not get rid of system -I's, at least in
57      # PGI 18.10.1 ...
58      string(REGEX MATCHALL " (-I ?)([^ ]*)" incs "${line}")
59      foreach(inc IN LISTS incs)
60        string(REGEX REPLACE " (-I ?)([^ ]*)" "\\2" idir "${inc}")
61        if(NOT idir STREQUAL "-")   # filter out "-I-"
62          list(APPEND rv "${idir}")
63        endif()
64      endforeach()
65      if(DEFINED rv)
66        string(APPEND log "  got implicit includes via PGI CXX parser!\n")
67      else()
68        string(APPEND log "  warning: PGI CXX parse failed!\n")
69      endif()
70    endif()
71  endif()
72
73  # SunPro compiler
74  if(CMAKE_${lang}_COMPILER_ID STREQUAL "SunPro" AND
75     (line MATCHES "-D__SUNPRO_C" OR line MATCHES "-D__SUNPRO_F"))
76    string(REGEX MATCHALL " (-I ?)([^ ]*)" incs "${line}")
77    foreach(inc IN LISTS incs)
78      string(REGEX REPLACE " (-I ?)([^ ]*)" "\\2" idir "${inc}")
79      if(NOT "${idir}" STREQUAL "-xbuiltin")
80        list(APPEND rv "${idir}")
81      endif()
82    endforeach()
83    if(rv)
84      if (lang STREQUAL "C" OR lang STREQUAL "CXX")
85        # /usr/include appears to be hardwired in
86        list(APPEND rv "/usr/include")
87      endif()
88      string(APPEND log "  got implicit includes via sunpro parser!\n")
89    else()
90      string(APPEND log "  warning: sunpro parse failed!\n")
91    endif()
92  endif()
93
94  # XL compiler
95  if((CMAKE_${lang}_COMPILER_ID STREQUAL "XL"
96      OR CMAKE_${lang}_COMPILER_ID STREQUAL "XLClang")
97     AND line MATCHES "^/"
98     AND ( (lang STREQUAL "Fortran" AND
99            line MATCHES "/xl[fF]entry " AND
100            line MATCHES "OSVAR\\([^ ]+\\)")
101           OR
102            ( (lang STREQUAL "C" OR lang STREQUAL "CXX") AND
103            line MATCHES "/xl[cC]2?entry " AND
104            line MATCHES " -qosvar=")
105         )  )
106    # -qnostdinc cancels other stdinc flags, even if present
107    string(FIND "${line}" " -qnostdinc" nostd)
108    if(NOT nostd EQUAL -1)
109      set(rv "")    # defined but empty
110      string(APPEND log "  got implicit includes via XL parser (nostdinc)\n")
111    else()
112      if(lang STREQUAL "CXX")
113        string(REGEX MATCHALL " -qcpp_stdinc=([^ ]*)" std "${line}")
114        string(REGEX MATCHALL " -qgcc_cpp_stdinc=([^ ]*)" gcc_std "${line}")
115      else()
116        string(REGEX MATCHALL " -qc_stdinc=([^ ]*)" std "${line}")
117        string(REGEX MATCHALL " -qgcc_c_stdinc=([^ ]*)" gcc_std "${line}")
118      endif()
119      set(xlstd ${std} ${gcc_std})
120      foreach(inc IN LISTS xlstd)
121        string(REGEX REPLACE " -q(cpp|gcc_cpp|c|gcc_c)_stdinc=([^ ]*)" "\\2"
122               ipath "${inc}")
123        string(REPLACE ":" ";" ipath "${ipath}")
124        list(APPEND rv ${ipath})
125      endforeach()
126    endif()
127    # user can add -I flags via CMAKE_{C,CXX}_FLAGS, look for that too
128    string(REGEX MATCHALL " (-I ?)([^ ]*)" incs "${line}")
129    unset(urv)
130    foreach(inc IN LISTS incs)
131      string(REGEX REPLACE " (-I ?)([^ ]*)" "\\2" idir "${inc}")
132      list(APPEND urv "${idir}")
133    endforeach()
134    if(urv)
135      if ("${rv}" STREQUAL "")
136        set(rv ${urv})
137      else()
138        list(APPEND rv ${urv})
139      endif()
140    endif()
141
142    if(DEFINED rv)
143      string(APPEND log "  got implicit includes via XL parser!\n")
144    else()
145      string(APPEND log "  warning: XL parse failed!\n")
146    endif()
147  endif()
148
149  # Fujitsu compiler
150  if(CMAKE_${lang}_COMPILER_ID STREQUAL "Fujitsu" AND
151     line MATCHES "/ccpcom")
152    string(REGEX MATCHALL " (-I *|--sys_include=|--preinclude +)(\"[^\"]+\"|[^ \"]+)" incs "${line}")
153    foreach(inc IN LISTS incs)
154      string(REGEX REPLACE " (-I *|--sys_include=|--preinclude +)(\"[^\"]+\"|[^ \"]+)" "\\2" idir "${inc}")
155      list(APPEND rv "${idir}")
156    endforeach()
157    if(rv)
158      string(APPEND log "  got implicit includes via fujitsu ccpcom parser!\n")
159    else()
160      string(APPEND log "  warning: fujitsu ccpcom parse failed!\n")
161    endif()
162  endif()
163
164  if(log)
165    set(${log_var} "${log}" PARENT_SCOPE)
166  else()
167    unset(${log_var} PARENT_SCOPE)
168  endif()
169  if(DEFINED rv)
170    set(${id_var} "${rv}" PARENT_SCOPE)
171    set(${state_var} "done" PARENT_SCOPE)
172  endif()
173endfunction()
174
175# top-level function to parse implicit include directory information
176# from verbose compiler output. sets state_var in parent to 'done' on success.
177function(cmake_parse_implicit_include_info text lang dir_var log_var state_var)
178  set(state start)    # values: start, loading, done
179
180  # clear variables we append to (avoids possible pollution from parent scopes)
181  set(implicit_dirs_tmp)
182  set(log "")
183
184  # go through each line of output...
185  string(REGEX REPLACE "\r*\n" ";" output_lines "${text}")
186  foreach(line IN LISTS output_lines)
187    if(state STREQUAL start)
188      string(FIND "${line}" "#include \"...\" search starts here:" rv)
189      if(rv GREATER -1)
190        set(state loading)
191        set(preload 1)      # looking for include <...> now
192        string(APPEND log "  found start of include info\n")
193      else()
194        cmake_parse_implicit_include_line("${line}" "${lang}" implicit_dirs_tmp
195                                          linelog state)
196        if(linelog)
197          string(APPEND log ${linelog})
198        endif()
199        if(state STREQUAL done)
200          break()
201        endif()
202      endif()
203    elseif(state STREQUAL loading)
204      string(FIND "${line}" "End of search list." rv)
205      if(rv GREATER -1)
206        set(state done)
207        string(APPEND log "  end of search list found\n")
208        break()
209      endif()
210      if(preload)
211        string(FIND "${line}" "#include <...> search starts here:" rv)
212        if(rv GREATER -1)
213          set(preload 0)
214          string(APPEND log "  found start of implicit include info\n")
215        endif()
216        continue()
217      endif()
218      if("${line}" MATCHES "^ ")
219        string(SUBSTRING "${line}" 1 -1 line)  # remove leading space
220      endif()
221      if ("${line}" MATCHES " \\(framework directory\\)$")
222        continue() # frameworks are handled elsewhere, ignore them here
223      endif()
224      string(REPLACE "\\" "/" path "${line}")
225      list(APPEND implicit_dirs_tmp "${path}")
226      string(APPEND log "    add: [${path}]\n")
227    endif()
228  endforeach()
229
230  set(implicit_dirs "")
231  foreach(d IN LISTS implicit_dirs_tmp)
232    if(IS_ABSOLUTE "${d}")
233      get_filename_component(dir "${d}" ABSOLUTE)
234      list(APPEND implicit_dirs "${dir}")
235      string(APPEND log "  collapse include dir [${d}] ==> [${dir}]\n")
236    elseif("${d}" MATCHES [[^\.\.[\/]\.\.[\/](.*)$]])
237      # This relative path is deep enough to get out of the CMakeFiles/CMakeTmp
238      # directory where the ABI check is done.  Assume that the compiler has
239      # computed this path adaptively based on the current working directory
240      # such that the effective result is absolute.
241      get_filename_component(dir "${CMAKE_BINARY_DIR}/${CMAKE_MATCH_1}" ABSOLUTE)
242      list(APPEND implicit_dirs "${dir}")
243      string(APPEND log "  collapse relative include dir [${d}] ==> [${dir}]\n")
244    else()
245      string(APPEND log "  skipping relative include dir [${d}]\n")
246    endif()
247  endforeach()
248  list(REMOVE_DUPLICATES implicit_dirs)
249
250  # Log results.
251  if(state STREQUAL done)
252    string(APPEND log "  implicit include dirs: [${implicit_dirs}]\n")
253  else()
254    string(APPEND log "  warn: unable to parse implicit include dirs!\n")
255  endif()
256
257  # Return results.
258  set(${dir_var} "${implicit_dirs}" PARENT_SCOPE)
259  set(${log_var} "${log}" PARENT_SCOPE)
260  set(${state_var} "${state}" PARENT_SCOPE)
261
262endfunction()
263