1# Ask xcode-select where to find /Developer or fall back to ancient location.
2execute_process(COMMAND xcode-select -print-path
3  OUTPUT_VARIABLE _stdout
4  OUTPUT_STRIP_TRAILING_WHITESPACE
5  ERROR_VARIABLE _stderr
6  RESULT_VARIABLE _failed)
7if(NOT _failed AND IS_DIRECTORY ${_stdout})
8  set(OSX_DEVELOPER_ROOT ${_stdout})
9elseif(IS_DIRECTORY "/Developer")
10  set(OSX_DEVELOPER_ROOT "/Developer")
11else()
12  set(OSX_DEVELOPER_ROOT "")
13endif()
14
15execute_process(COMMAND sw_vers -productVersion
16  OUTPUT_VARIABLE CURRENT_OSX_VERSION
17  OUTPUT_STRIP_TRAILING_WHITESPACE)
18
19# Save CMAKE_OSX_ARCHITECTURES from the environment.
20set(CMAKE_OSX_ARCHITECTURES "$ENV{CMAKE_OSX_ARCHITECTURES}" CACHE STRING
21  "Build architectures for OSX")
22
23if(NOT CMAKE_CROSSCOMPILING AND
24   CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND
25   CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(arm64|x86_64)$")
26  execute_process(COMMAND sysctl -q hw.optional.arm64
27    OUTPUT_VARIABLE _sysctl_stdout
28    ERROR_VARIABLE _sysctl_stderr
29    RESULT_VARIABLE _sysctl_result
30    )
31  # When building on an Apple Silicon host, we need to explicitly specify
32  # the architecture to the toolchain since it will otherwise guess the
33  # architecture based on that of the build system tool.
34  # Set an *internal variable* to tell the generators to do this.
35  if(_sysctl_result EQUAL 0 AND _sysctl_stdout MATCHES "hw.optional.arm64: 1")
36    set(_CMAKE_APPLE_ARCHS_DEFAULT "${CMAKE_HOST_SYSTEM_PROCESSOR}")
37  endif()
38  unset(_sysctl_result)
39  unset(_sysctl_stderr)
40  unset(_sysctl_stdout)
41endif()
42
43# macOS, iOS, tvOS, and watchOS should lookup compilers from
44# Platform/Apple-${CMAKE_CXX_COMPILER_ID}-<LANG>
45set(CMAKE_EFFECTIVE_SYSTEM_NAME "Apple")
46
47#----------------------------------------------------------------------------
48# _CURRENT_OSX_VERSION - as a two-component string: 10.5, 10.6, ...
49#
50string(REGEX REPLACE "^([0-9]+\\.[0-9]+).*$" "\\1"
51  _CURRENT_OSX_VERSION "${CURRENT_OSX_VERSION}")
52
53#----------------------------------------------------------------------------
54# CMAKE_OSX_DEPLOYMENT_TARGET
55
56# Set cache variable - end user may change this during ccmake or cmake-gui configure.
57if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND _CURRENT_OSX_VERSION VERSION_GREATER 10.3)
58  set(CMAKE_OSX_DEPLOYMENT_TARGET "$ENV{MACOSX_DEPLOYMENT_TARGET}" CACHE STRING
59    "Minimum OS X version to target for deployment (at runtime); newer APIs weak linked. Set to empty string for default value.")
60endif()
61
62#----------------------------------------------------------------------------
63# CMAKE_OSX_SYSROOT
64
65if(CMAKE_OSX_SYSROOT)
66  # Use the existing value without further computation to choose a default.
67  set(_CMAKE_OSX_SYSROOT_DEFAULT "${CMAKE_OSX_SYSROOT}")
68elseif(NOT "x$ENV{SDKROOT}" STREQUAL "x" AND
69        (NOT "x$ENV{SDKROOT}" MATCHES "/" OR IS_DIRECTORY "$ENV{SDKROOT}"))
70  # Use the value of SDKROOT from the environment.
71  set(_CMAKE_OSX_SYSROOT_DEFAULT "$ENV{SDKROOT}")
72elseif(CMAKE_SYSTEM_NAME STREQUAL iOS)
73  set(_CMAKE_OSX_SYSROOT_DEFAULT "iphoneos")
74elseif(CMAKE_SYSTEM_NAME STREQUAL tvOS)
75  set(_CMAKE_OSX_SYSROOT_DEFAULT "appletvos")
76elseif(CMAKE_SYSTEM_NAME STREQUAL watchOS)
77  set(_CMAKE_OSX_SYSROOT_DEFAULT "watchos")
78elseif("${CMAKE_GENERATOR}" MATCHES Xcode
79       OR CMAKE_OSX_DEPLOYMENT_TARGET
80       OR CMAKE_OSX_ARCHITECTURES MATCHES "[^;]"
81       OR NOT EXISTS "/usr/include/sys/types.h")
82  # Find installed SDKs in either Xcode-4.3+ or pre-4.3 SDKs directory.
83  set(_CMAKE_OSX_SDKS_DIR "")
84  if(OSX_DEVELOPER_ROOT)
85    foreach(_d Platforms/MacOSX.platform/Developer/SDKs SDKs)
86      file(GLOB _CMAKE_OSX_SDKS ${OSX_DEVELOPER_ROOT}/${_d}/*)
87      if(_CMAKE_OSX_SDKS)
88        set(_CMAKE_OSX_SDKS_DIR ${OSX_DEVELOPER_ROOT}/${_d})
89        break()
90      endif()
91    endforeach()
92  endif()
93
94  if(_CMAKE_OSX_SDKS_DIR)
95    # Find the latest SDK as recommended by Apple (Technical Q&A QA1806)
96    set(_CMAKE_OSX_LATEST_SDK_VERSION "0.0")
97    file(GLOB _CMAKE_OSX_SDKS RELATIVE "${_CMAKE_OSX_SDKS_DIR}" "${_CMAKE_OSX_SDKS_DIR}/MacOSX*.sdk")
98    foreach(_SDK ${_CMAKE_OSX_SDKS})
99      if(IS_DIRECTORY "${_CMAKE_OSX_SDKS_DIR}/${_SDK}"
100         AND _SDK MATCHES "MacOSX([0-9]+\\.[0-9]+)[^/]*\\.sdk"
101         AND CMAKE_MATCH_1 VERSION_GREATER ${_CMAKE_OSX_LATEST_SDK_VERSION})
102        set(_CMAKE_OSX_LATEST_SDK_VERSION "${CMAKE_MATCH_1}")
103      endif()
104    endforeach()
105
106    if(NOT _CMAKE_OSX_LATEST_SDK_VERSION STREQUAL "0.0")
107      set(_CMAKE_OSX_SYSROOT_DEFAULT "${_CMAKE_OSX_SDKS_DIR}/MacOSX${_CMAKE_OSX_LATEST_SDK_VERSION}.sdk")
108    else()
109      message(WARNING "Could not find any valid SDKs in ${_CMAKE_OSX_SDKS_DIR}")
110    endif()
111
112    if(NOT CMAKE_CROSSCOMPILING AND NOT CMAKE_OSX_DEPLOYMENT_TARGET
113       AND (_CURRENT_OSX_VERSION VERSION_LESS _CMAKE_OSX_LATEST_SDK_VERSION
114            OR _CMAKE_OSX_LATEST_SDK_VERSION STREQUAL "0.0"))
115      set(CMAKE_OSX_DEPLOYMENT_TARGET ${_CURRENT_OSX_VERSION} CACHE STRING
116        "Minimum OS X version to target for deployment (at runtime); newer APIs weak linked. Set to empty string for default value." FORCE)
117    endif()
118  else()
119    # Assume developer files are in root (such as Xcode 4.5 command-line tools).
120    set(_CMAKE_OSX_SYSROOT_DEFAULT "")
121  endif()
122endif()
123
124# Set cache variable - end user may change this during ccmake or cmake-gui configure.
125# Choose the type based on the current value.
126set(_CMAKE_OSX_SYSROOT_TYPE STRING)
127foreach(_v CMAKE_OSX_SYSROOT _CMAKE_OSX_SYSROOT_DEFAULT)
128  if("x${${_v}}" MATCHES "/")
129    set(_CMAKE_OSX_SYSROOT_TYPE PATH)
130    break()
131  endif()
132endforeach()
133set(CMAKE_OSX_SYSROOT "${_CMAKE_OSX_SYSROOT_DEFAULT}" CACHE ${_CMAKE_OSX_SYSROOT_TYPE}
134  "The product will be built against the headers and libraries located inside the indicated SDK.")
135
136# Resolves the SDK name into a path
137function(_apple_resolve_sdk_path sdk_name ret)
138  execute_process(
139    COMMAND xcrun -sdk ${sdk_name} --show-sdk-path
140    OUTPUT_VARIABLE _stdout
141    OUTPUT_STRIP_TRAILING_WHITESPACE
142    ERROR_VARIABLE _stderr
143    RESULT_VARIABLE _failed
144  )
145  set(${ret} "${_stdout}" PARENT_SCOPE)
146endfunction()
147
148function(_apple_resolve_supported_archs_for_sdk_from_system_lib sdk_path ret ret_failed)
149  # Detect the supported SDK architectures by inspecting the main libSystem library.
150  set(common_lib_prefix "${sdk_path}/usr/lib/libSystem")
151  set(system_lib_dylib_path "${common_lib_prefix}.dylib")
152  set(system_lib_tbd_path "${common_lib_prefix}.tbd")
153
154  # Newer SDKs ship text based dylib stub files which contain the architectures supported by the
155  # library in text form.
156  if(EXISTS "${system_lib_tbd_path}")
157    file(STRINGS "${system_lib_tbd_path}" tbd_lines REGEX "^(archs|targets): +\\[.+\\]")
158    if(NOT tbd_lines)
159      set(${ret_failed} TRUE PARENT_SCOPE)
160      return()
161    endif()
162
163    # The tbd architectures line looks like the following:
164    #   archs:           [ armv7, armv7s, arm64, arm64e ]
165    # or for version 4 TBD files:
166    #   targets:         [ armv7-ios, armv7s-ios, arm64-ios, arm64e-ios ]
167    list(GET tbd_lines 0 first_arch_line)
168    string(REGEX REPLACE
169           "(archs|targets): +\\[ (.+) \\]" "\\2" arches_comma_separated "${first_arch_line}")
170    string(STRIP "${arches_comma_separated}" arches_comma_separated)
171    string(REPLACE "," ";" arch_list "${arches_comma_separated}")
172    string(REPLACE " " "" arch_list "${arch_list}")
173
174    # Remove -platform suffix from target (version 4 only)
175    string(REGEX REPLACE "-[a-z-]+" "" arch_list "${arch_list}")
176
177    if(NOT arch_list)
178      set(${ret_failed} TRUE PARENT_SCOPE)
179      return()
180    endif()
181    set(${ret} "${arch_list}" PARENT_SCOPE)
182  elseif(EXISTS "${system_lib_dylib_path}")
183    # Old SDKs (Xcode < 7) ship dylib files, use lipo to inspect the supported architectures.
184    # Can't use -archs because the option is not available in older Xcode versions.
185    execute_process(
186      COMMAND lipo -info ${system_lib_dylib_path}
187      OUTPUT_VARIABLE lipo_output
188      OUTPUT_STRIP_TRAILING_WHITESPACE
189      ERROR_VARIABLE _stderr
190      RESULT_VARIABLE _failed
191    )
192    if(_failed OR NOT lipo_output OR NOT lipo_output MATCHES "(Non-fat file:|Architectures in the fat file:)")
193      set(${ret_failed} TRUE PARENT_SCOPE)
194      return()
195    endif()
196
197    # The lipo output looks like the following:
198    # Non-fat file: <path> is architecture: i386
199    # Architectures in the fat file: <path> are: i386 x86_64
200    string(REGEX REPLACE
201           "^(.+)is architecture:(.+)" "\\2" arches_space_separated "${lipo_output}")
202    string(REGEX REPLACE
203            "^(.+)are:(.+)" "\\2" arches_space_separated "${arches_space_separated}")
204
205    # Need to clean up the arches, with Xcode 4.6.3 the output of lipo -info contains some
206    # additional info, e.g.
207    # Architectures in the fat file: <path> are: armv7 (cputype (12) cpusubtype (11))
208    string(REGEX REPLACE
209            "\\(.+\\)" "" arches_space_separated "${arches_space_separated}")
210
211    # The output is space separated.
212    string(STRIP "${arches_space_separated}" arches_space_separated)
213    string(REPLACE " " ";" arch_list "${arches_space_separated}")
214
215    if(NOT arch_list)
216      set(${ret_failed} TRUE PARENT_SCOPE)
217      return()
218    endif()
219    set(${ret} "${arch_list}" PARENT_SCOPE)
220  else()
221    # This shouldn't happen, but keep it for safety.
222    message(WARNING "No way to find architectures for given sdk_path '${sdk_path}'")
223    set(${ret_failed} TRUE PARENT_SCOPE)
224  endif()
225endfunction()
226
227# Handle multi-arch sysroots. Do this before CMAKE_OSX_SYSROOT is
228# transformed into a path, so that we know the sysroot name.
229function(_apple_resolve_multi_arch_sysroots)
230  if(DEFINED CMAKE_APPLE_ARCH_SYSROOTS)
231    return() # Already cached
232  endif()
233
234  list(LENGTH CMAKE_OSX_ARCHITECTURES _num_archs)
235  if(NOT (_num_archs GREATER 1))
236    return() # Only apply to multi-arch
237  endif()
238
239  if(CMAKE_OSX_SYSROOT STREQUAL "macosx")
240    # macOS doesn't have a simulator sdk / sysroot, so there is no need to handle per-sdk arches.
241    return()
242  endif()
243
244  if(IS_DIRECTORY "${CMAKE_OSX_SYSROOT}")
245    if(NOT CMAKE_OSX_SYSROOT STREQUAL _CMAKE_OSX_SYSROOT_DEFAULT)
246      message(WARNING "Can not resolve multi-arch sysroots with CMAKE_OSX_SYSROOT set to path (${CMAKE_OSX_SYSROOT})")
247    endif()
248    return()
249  endif()
250
251  string(REPLACE "os" "simulator" _simulator_sdk ${CMAKE_OSX_SYSROOT})
252  set(_sdks "${CMAKE_OSX_SYSROOT};${_simulator_sdk}")
253  foreach(sdk ${_sdks})
254    _apple_resolve_sdk_path(${sdk} _sdk_path)
255    if(NOT IS_DIRECTORY "${_sdk_path}")
256      message(WARNING "Failed to resolve SDK path for '${sdk}'")
257      continue()
258    endif()
259
260    _apple_resolve_supported_archs_for_sdk_from_system_lib(${_sdk_path} _sdk_archs _failed)
261
262    if(_failed)
263      # Failure to extract supported architectures for an SDK means that the installed SDK is old
264      # and does not provide such information (SDKs that come with Xcode >= 10.x started providing
265      # the information). In such a case, return early, and handle multi-arch builds the old way
266      # (no per-sdk arches).
267      return()
268    endif()
269
270    set(_sdk_archs_${sdk} ${_sdk_archs})
271    set(_sdk_path_${sdk} ${_sdk_path})
272  endforeach()
273
274  foreach(arch ${CMAKE_OSX_ARCHITECTURES})
275    set(_arch_sysroot "")
276    foreach(sdk ${_sdks})
277      list(FIND _sdk_archs_${sdk} ${arch} arch_index)
278      if(NOT arch_index EQUAL -1)
279        set(_arch_sysroot ${_sdk_path_${sdk}})
280        break()
281      endif()
282    endforeach()
283    if(_arch_sysroot)
284      list(APPEND _arch_sysroots ${_arch_sysroot})
285    else()
286      message(WARNING "No SDK found for architecture '${arch}'")
287      list(APPEND _arch_sysroots "${arch}-SDK-NOTFOUND")
288    endif()
289  endforeach()
290
291  set(CMAKE_APPLE_ARCH_SYSROOTS "${_arch_sysroots}" CACHE INTERNAL
292    "Architecture dependent sysroots, one per CMAKE_OSX_ARCHITECTURES")
293endfunction()
294
295_apple_resolve_multi_arch_sysroots()
296
297# Transform CMAKE_OSX_SYSROOT to absolute path
298set(_CMAKE_OSX_SYSROOT_PATH "")
299if(CMAKE_OSX_SYSROOT)
300  if("x${CMAKE_OSX_SYSROOT}" MATCHES "/")
301    # This is a path to the SDK.  Make sure it exists.
302    if(NOT IS_DIRECTORY "${CMAKE_OSX_SYSROOT}")
303      message(WARNING "Ignoring CMAKE_OSX_SYSROOT value:\n ${CMAKE_OSX_SYSROOT}\n"
304        "because the directory does not exist.")
305      set(CMAKE_OSX_SYSROOT "")
306    endif()
307    set(_CMAKE_OSX_SYSROOT_PATH "${CMAKE_OSX_SYSROOT}")
308  else()
309    _apple_resolve_sdk_path(${CMAKE_OSX_SYSROOT} _sdk_path)
310    if(IS_DIRECTORY "${_sdk_path}")
311      set(_CMAKE_OSX_SYSROOT_PATH "${_sdk_path}")
312      # For non-Xcode generators use the path.
313      if(NOT "${CMAKE_GENERATOR}" MATCHES "Xcode")
314        set(CMAKE_OSX_SYSROOT "${_CMAKE_OSX_SYSROOT_PATH}")
315      endif()
316    endif()
317  endif()
318endif()
319