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# In Android NDK r19 and above there is a single clang toolchain.
5if(CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED)
6  if(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION AND NOT CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION STREQUAL "clang")
7    message(FATAL_ERROR
8      "Android: The CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION value '${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}' "
9      "is not supported by this NDK.  It must be 'clang' or not set at all."
10      )
11  endif()
12  message(STATUS "Android: Selected unified Clang toolchain")
13  set(_ANDROID_TOOL_NDK_TOOLCHAIN_VERSION "clang")
14  set(_ANDROID_TOOL_C_COMPILER "${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}/bin/clang${_ANDROID_HOST_EXT}")
15  set(_ANDROID_TOOL_C_TOOLCHAIN_MACHINE "${CMAKE_ANDROID_ARCH_TRIPLE}")
16  set(_ANDROID_TOOL_C_TOOLCHAIN_VERSION "")
17  set(_ANDROID_TOOL_C_COMPILER_EXTERNAL_TOOLCHAIN "")
18  set(_ANDROID_TOOL_C_TOOLCHAIN_PREFIX "${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}/bin/${CMAKE_ANDROID_ARCH_TRIPLE}-")
19  set(_ANDROID_TOOL_C_TOOLCHAIN_SUFFIX "${_ANDROID_HOST_EXT}")
20  set(_ANDROID_TOOL_CXX_COMPILER "${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}/bin/clang++${_ANDROID_HOST_EXT}")
21  set(_ANDROID_TOOL_CXX_TOOLCHAIN_MACHINE "${CMAKE_ANDROID_ARCH_TRIPLE}")
22  set(_ANDROID_TOOL_CXX_TOOLCHAIN_VERSION "")
23  set(_ANDROID_TOOL_CXX_COMPILER_EXTERNAL_TOOLCHAIN "")
24  set(_ANDROID_TOOL_CXX_TOOLCHAIN_PREFIX "${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}/bin/${CMAKE_ANDROID_ARCH_TRIPLE}-")
25  set(_ANDROID_TOOL_CXX_TOOLCHAIN_SUFFIX "${_ANDROID_HOST_EXT}")
26  set(_CMAKE_TOOLCHAIN_PREFIX "${CMAKE_ANDROID_ARCH_TRIPLE}-")
27  return()
28endif()
29
30# In Android NDK releases there is build system toolchain selection logic in
31# these files:
32#
33# * <ndk>/build/core/init.mk
34# * <ndk>/build/core/setup-toolchain.mk
35# * <ndk>/[build/core/]toolchains/<toolchain>/{config.mk,setup.mk}
36#
37# We parse information out of the ``config.mk`` and ``setup.mk`` files below.
38#
39# There is also a "toolchains" directory with the prebuilt toolchains themselves:
40#
41# * <triple-or-arch>-<gcc-version>/prebuilt/<host>/bin/<triple>-gcc(.exe)?
42#   The gcc compiler to be invoked.
43#
44# * llvm*/prebuilt/<host>/bin/clang
45#   The clang compiler to be invoked with flags:
46#     -target <triple>
47#     -gcc-toolchain <ndk>/toolchains/<triple-or-arch>-<gcc-version>
48
49# Glob available toolchains in the NDK, restricted by any version request.
50if(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION STREQUAL "clang")
51  set(_ANDROID_TOOL_PATTERNS "*-clang" "*-clang[0-9].[0-9]")
52elseif(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION)
53  if(NOT CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION MATCHES "^(clang)?[0-9]\\.[0-9]$")
54    message(FATAL_ERROR
55      "Android: The CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION value '${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}' "
56      "is not one of the allowed forms:\n"
57      "  <major>.<minor>       = GCC of specified version\n"
58      "  clang<major>.<minor>  = Clang of specified version\n"
59      "  clang                 = Clang of most recent available version\n"
60      )
61  endif()
62  set(_ANDROID_TOOL_PATTERNS "*-${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}")
63else()
64  # If we can find any gcc toolchains then use one by default.
65  # Otherwise we look for clang toolchains (e.g. NDK r18+).
66  file(GLOB _ANDROID_CONFIG_MKS_FOR_GCC
67    "${CMAKE_ANDROID_NDK}/build/core/toolchains/*-[0-9].[0-9]/config.mk"
68    "${CMAKE_ANDROID_NDK}/toolchains/*-[0-9].[0-9]/config.mk"
69    )
70  if(_ANDROID_CONFIG_MKS_FOR_GCC)
71    set(_ANDROID_TOOL_PATTERNS "*-[0-9].[0-9]")
72  else()
73    set(_ANDROID_TOOL_PATTERNS "*-clang")
74  endif()
75  unset(_ANDROID_CONFIG_MKS_FOR_GCC)
76endif()
77set(_ANDROID_CONFIG_MK_PATTERNS)
78foreach(base "build/core/toolchains" "toolchains")
79  foreach(pattern IN LISTS _ANDROID_TOOL_PATTERNS)
80    list(APPEND _ANDROID_CONFIG_MK_PATTERNS
81      "${CMAKE_ANDROID_NDK}/${base}/${pattern}/config.mk"
82      )
83  endforeach()
84endforeach()
85unset(_ANDROID_TOOL_PATTERNS)
86file(GLOB _ANDROID_CONFIG_MKS ${_ANDROID_CONFIG_MK_PATTERNS})
87unset(_ANDROID_CONFIG_MK_PATTERNS)
88
89# Find the newest toolchain version matching the ABI.
90set(_ANDROID_TOOL_NAME "")
91set(_ANDROID_TOOL_VERS 0)
92set(_ANDROID_TOOL_VERS_NDK "")
93set(_ANDROID_TOOL_SETUP_MK "")
94foreach(config_mk IN LISTS _ANDROID_CONFIG_MKS)
95  # Check that the toolchain matches the ABI.
96  file(STRINGS "${config_mk}" _ANDROID_TOOL_ABIS REGEX "^TOOLCHAIN_ABIS :=.* ${CMAKE_ANDROID_ARCH_ABI}( |$)")
97  if(NOT _ANDROID_TOOL_ABIS)
98    continue()
99  endif()
100  unset(_ANDROID_TOOL_ABIS)
101
102  # Check the version.
103  if("${config_mk}" MATCHES [[/([^/]+-((clang)?([0-9]\.[0-9]|)))/config.mk$]])
104    set(_ANDROID_CUR_NAME "${CMAKE_MATCH_1}")
105    set(_ANDROID_CUR_VERS "${CMAKE_MATCH_4}")
106    set(_ANDROID_CUR_VERS_NDK "${CMAKE_MATCH_2}")
107    if(_ANDROID_TOOL_VERS STREQUAL "")
108      # already the latest possible
109    elseif(_ANDROID_CUR_VERS STREQUAL "" OR _ANDROID_CUR_VERS VERSION_GREATER _ANDROID_TOOL_VERS)
110      set(_ANDROID_TOOL_NAME "${_ANDROID_CUR_NAME}")
111      set(_ANDROID_TOOL_VERS "${_ANDROID_CUR_VERS}")
112      set(_ANDROID_TOOL_VERS_NDK "${_ANDROID_CUR_VERS_NDK}")
113      string(REPLACE "/config.mk" "/setup.mk" _ANDROID_TOOL_SETUP_MK "${config_mk}")
114    endif()
115    unset(_ANDROID_CUR_TOOL)
116    unset(_ANDROID_CUR_VERS)
117    unset(_ANDROID_CUR_VERS_NDK)
118  endif()
119endforeach()
120
121# Verify that we have a suitable toolchain.
122if(NOT _ANDROID_TOOL_NAME)
123  if(_ANDROID_CONFIG_MKS)
124    string(REPLACE ";" "\n  " _ANDROID_TOOLS_MSG "after considering:;${_ANDROID_CONFIG_MKS}")
125  else()
126    set(_ANDROID_TOOLS_MSG "")
127  endif()
128  if(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION)
129    string(CONCAT _ANDROID_TOOLS_MSG
130      "of the version specified by CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION:\n"
131      "  ${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}\n"
132      "${_ANDROID_TOOLS_MSG}")
133  endif()
134  message(FATAL_ERROR
135    "Android: No toolchain for ABI '${CMAKE_ANDROID_ARCH_ABI}' found in the NDK:\n"
136    "  ${CMAKE_ANDROID_NDK}\n"
137    "${_ANDROID_TOOLS_MSG}"
138    )
139endif()
140unset(_ANDROID_CONFIG_MKS)
141
142# For clang toolchains we still need to find a gcc toolchain.
143if(_ANDROID_TOOL_NAME MATCHES "-clang")
144  set(_ANDROID_TOOL_CLANG_NAME "${_ANDROID_TOOL_NAME}")
145  set(_ANDROID_TOOL_CLANG_VERS "${_ANDROID_TOOL_VERS}")
146  set(_ANDROID_TOOL_NAME "")
147  set(_ANDROID_TOOL_VERS "")
148else()
149  set(_ANDROID_TOOL_CLANG_NAME "")
150  set(_ANDROID_TOOL_CLANG_VERS "")
151endif()
152
153# Parse the toolchain setup.mk file to extract information we need.
154# Their content is not standardized across toolchains or NDK versions,
155# so we match known cases.  Note that the parsing is stateful across
156# lines because we need to substitute for some Make variable references.
157if(CMAKE_ANDROID_NDK_TOOLCHAIN_DEBUG)
158  message(STATUS "loading: ${_ANDROID_TOOL_SETUP_MK}")
159endif()
160file(STRINGS "${_ANDROID_TOOL_SETUP_MK}" _ANDROID_TOOL_SETUP REGEX "^(LLVM|TOOLCHAIN)_[A-Z_]+ +:= +.*$")
161unset(_ANDROID_TOOL_SETUP_MK)
162set(_ANDROID_TOOL_PREFIX "")
163set(_ANDROID_TOOL_NAME_ONLY "")
164set(_ANDROID_TOOL_LLVM_NAME "llvm")
165set(_ANDROID_TOOL_LLVM_VERS "")
166foreach(line IN LISTS _ANDROID_TOOL_SETUP)
167  if(CMAKE_ANDROID_NDK_TOOLCHAIN_DEBUG)
168    message(STATUS "setup.mk: ${line}")
169  endif()
170
171  if(line MATCHES [[^TOOLCHAIN_PREFIX +:= +.*/bin/([^$/ ]*) *$]])
172    # We just matched the toolchain prefix with no Make variable references.
173    set(_ANDROID_TOOL_PREFIX "${CMAKE_MATCH_1}")
174  elseif(_ANDROID_TOOL_CLANG_NAME)
175    # For clang toolchains we need to find more information.
176    if(line MATCHES [[^TOOLCHAIN_VERSION +:= +([0-9.]+) *$]])
177      # We just matched the gcc toolchain version number.  Save it for later.
178      set(_ANDROID_TOOL_VERS "${CMAKE_MATCH_1}")
179    elseif(line MATCHES [[^TOOLCHAIN_NAME +:= +(.*\$\(TOOLCHAIN_VERSION\)) *$]])
180      # We just matched the gcc toolchain name with a version number placeholder, so substitute it.
181      # The gcc toolchain version number will have already been extracted from a TOOLCHAIN_VERSION line.
182      string(REPLACE "$(TOOLCHAIN_VERSION)" "${_ANDROID_TOOL_VERS}" _ANDROID_TOOL_NAME "${CMAKE_MATCH_1}")
183    elseif(line MATCHES [[^TOOLCHAIN_NAME +:= +([^$/ ]+) *$]])
184      # We just matched the gcc toolchain name without version number.  Save it for later.
185      set(_ANDROID_TOOL_NAME_ONLY "${CMAKE_MATCH_1}")
186    elseif(line MATCHES [[^TOOLCHAIN_PREFIX +:= +.*/bin/(\$\(TOOLCHAIN_NAME\)-) *$]])
187      # We just matched the toolchain prefix with a name placeholder, so substitute it.
188      # The gcc toolchain name will have already been extracted without version number from a TOOLCHAIN_NAME line.
189      string(REPLACE "$(TOOLCHAIN_NAME)" "${_ANDROID_TOOL_NAME_ONLY}" _ANDROID_TOOL_PREFIX "${CMAKE_MATCH_1}")
190    elseif(line MATCHES [[^LLVM_VERSION +:= +([0-9.]+)$]])
191      # We just matched the llvm prebuilt binary toolchain version number.  Save it for later.
192      set(_ANDROID_TOOL_LLVM_VERS "${CMAKE_MATCH_1}")
193    elseif(line MATCHES [[^LLVM_NAME +:= +(llvm-\$\(LLVM_VERSION\)) *$]])
194      # We just matched the llvm prebuilt binary toolchain directory name with a version number placeholder,
195      # so substitute it. The llvm prebuilt binary toolchain version number will have already been extracted
196      # from a LLVM_VERSION line.
197      string(REPLACE "$(LLVM_VERSION)" "${_ANDROID_TOOL_LLVM_VERS}" _ANDROID_TOOL_LLVM_NAME "${CMAKE_MATCH_1}")
198    elseif(line MATCHES [[^LLVM_TOOLCHAIN_PREBUILT_ROOT +:= +\$\(call get-toolchain-root.*,([^$ ]+)\) *$]])
199      # We just matched the llvm prebuilt binary toolchain directory name.
200      set(_ANDROID_TOOL_LLVM_NAME "${CMAKE_MATCH_1}")
201    elseif(line MATCHES [[^TOOLCHAIN_ROOT +:= +\$\(call get-toolchain-root.*,(\$\(TOOLCHAIN_NAME\)-[0-9.]+)\) *$]])
202      # We just matched a placeholder for the name followed by a version number.
203      # The gcc toolchain name will have already been extracted without version number from a TOOLCHAIN_NAME line.
204      # Substitute for the placeholder to get the full gcc toolchain name.
205      string(REPLACE "$(TOOLCHAIN_NAME)" "${_ANDROID_TOOL_NAME_ONLY}" _ANDROID_TOOL_NAME "${CMAKE_MATCH_1}")
206    elseif(line MATCHES [[^TOOLCHAIN_ROOT +:= +\$\(call get-toolchain-root.*,([^$ ]+)\) *$]])
207      # We just matched the full gcc toolchain name without placeholder.
208      set(_ANDROID_TOOL_NAME "${CMAKE_MATCH_1}")
209    endif()
210  endif()
211endforeach()
212unset(_ANDROID_TOOL_NAME_ONLY)
213unset(_ANDROID_TOOL_LLVM_VERS)
214unset(_ANDROID_TOOL_SETUP)
215
216# Fall back to parsing the version and prefix from the tool name.
217if(NOT _ANDROID_TOOL_VERS AND "${_ANDROID_TOOL_NAME}" MATCHES "-([0-9.]+)$")
218  set(_ANDROID_TOOL_VERS "${CMAKE_MATCH_1}")
219endif()
220if(NOT _ANDROID_TOOL_PREFIX AND "${_ANDROID_TOOL_NAME}" MATCHES "^(.*-)[0-9.]+$")
221  set(_ANDROID_TOOL_PREFIX "${CMAKE_MATCH_1}")
222endif()
223
224# Help CMakeFindBinUtils locate things.
225set(_CMAKE_TOOLCHAIN_PREFIX "${_ANDROID_TOOL_PREFIX}")
226
227set(_ANDROID_TOOL_NDK_TOOLCHAIN_VERSION "${_ANDROID_TOOL_VERS_NDK}")
228
229# _ANDROID_TOOL_PREFIX should now match `gcc -dumpmachine`.
230string(REGEX REPLACE "-$" "" _ANDROID_TOOL_C_TOOLCHAIN_MACHINE "${_ANDROID_TOOL_PREFIX}")
231
232set(_ANDROID_TOOL_C_TOOLCHAIN_VERSION "${_ANDROID_TOOL_VERS}")
233set(_ANDROID_TOOL_C_TOOLCHAIN_PREFIX "${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_NAME}/prebuilt/${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG}/bin/${_ANDROID_TOOL_PREFIX}")
234set(_ANDROID_TOOL_C_TOOLCHAIN_SUFFIX "${_ANDROID_HOST_EXT}")
235
236set(_ANDROID_TOOL_CXX_TOOLCHAIN_MACHINE "${_ANDROID_TOOL_C_TOOLCHAIN_MACHINE}")
237set(_ANDROID_TOOL_CXX_TOOLCHAIN_VERSION "${_ANDROID_TOOL_C_TOOLCHAIN_VERSION}")
238set(_ANDROID_TOOL_CXX_TOOLCHAIN_PREFIX "${_ANDROID_TOOL_C_TOOLCHAIN_PREFIX}")
239set(_ANDROID_TOOL_CXX_TOOLCHAIN_SUFFIX "${_ANDROID_TOOL_C_TOOLCHAIN_SUFFIX}")
240
241if(_ANDROID_TOOL_CLANG_NAME)
242  message(STATUS "Android: Selected Clang toolchain '${_ANDROID_TOOL_CLANG_NAME}' with GCC toolchain '${_ANDROID_TOOL_NAME}'")
243  set(_ANDROID_TOOL_C_COMPILER "${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_LLVM_NAME}/prebuilt/${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG}/bin/clang${_ANDROID_HOST_EXT}")
244  set(_ANDROID_TOOL_C_COMPILER_EXTERNAL_TOOLCHAIN ${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_NAME}/prebuilt/${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG})
245  set(_ANDROID_TOOL_CXX_COMPILER "${CMAKE_ANDROID_NDK}/toolchains/${_ANDROID_TOOL_LLVM_NAME}/prebuilt/${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG}/bin/clang++${_ANDROID_HOST_EXT}")
246  set(_ANDROID_TOOL_CXX_COMPILER_EXTERNAL_TOOLCHAIN "${_ANDROID_TOOL_C_COMPILER_EXTERNAL_TOOLCHAIN}")
247else()
248  message(STATUS "Android: Selected GCC toolchain '${_ANDROID_TOOL_NAME}'")
249  set(_ANDROID_TOOL_C_COMPILER "${_ANDROID_TOOL_C_TOOLCHAIN_PREFIX}gcc${_ANDROID_TOOL_C_TOOLCHAIN_SUFFIX}")
250  set(_ANDROID_TOOL_C_COMPILER_EXTERNAL_TOOLCHAIN "")
251  set(_ANDROID_TOOL_CXX_COMPILER "${_ANDROID_TOOL_CXX_TOOLCHAIN_PREFIX}g++${_ANDROID_TOOL_CXX_TOOLCHAIN_SUFFIX}")
252  set(_ANDROID_TOOL_CXX_COMPILER_EXTERNAL_TOOLCHAIN "")
253endif()
254
255if(CMAKE_ANDROID_NDK_TOOLCHAIN_DEBUG)
256  message(STATUS "_ANDROID_TOOL_NAME=${_ANDROID_TOOL_NAME}")
257  message(STATUS "_ANDROID_TOOL_VERS=${_ANDROID_TOOL_VERS}")
258  message(STATUS "_ANDROID_TOOL_VERS_NDK=${_ANDROID_TOOL_VERS_NDK}")
259  message(STATUS "_ANDROID_TOOL_PREFIX=${_ANDROID_TOOL_PREFIX}")
260  message(STATUS "_ANDROID_TOOL_CLANG_NAME=${_ANDROID_TOOL_CLANG_NAME}")
261  message(STATUS "_ANDROID_TOOL_CLANG_VERS=${_ANDROID_TOOL_CLANG_VERS}")
262  message(STATUS "_ANDROID_TOOL_LLVM_NAME=${_ANDROID_TOOL_LLVM_NAME}")
263endif()
264
265unset(_ANDROID_TOOL_NAME)
266unset(_ANDROID_TOOL_VERS)
267unset(_ANDROID_TOOL_VERS_NDK)
268unset(_ANDROID_TOOL_PREFIX)
269unset(_ANDROID_TOOL_CLANG_NAME)
270unset(_ANDROID_TOOL_CLANG_VERS)
271unset(_ANDROID_TOOL_LLVM_NAME)
272