summaryrefslogtreecommitdiff
path: root/Modules/Platform/Darwin-Initialize.cmake
blob: 80e9a40195ddc0dedde676239448853316751b4e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# Ask xcode-select where to find /Developer or fall back to ancient location.
execute_process(COMMAND xcode-select -print-path
  OUTPUT_VARIABLE _stdout
  OUTPUT_STRIP_TRAILING_WHITESPACE
  ERROR_VARIABLE _stderr
  RESULT_VARIABLE _failed)
if(NOT _failed AND IS_DIRECTORY ${_stdout})
  set(OSX_DEVELOPER_ROOT ${_stdout})
elseif(IS_DIRECTORY "/Developer")
  set(OSX_DEVELOPER_ROOT "/Developer")
else()
  set(OSX_DEVELOPER_ROOT "")
endif()

execute_process(COMMAND sw_vers -productVersion
  OUTPUT_VARIABLE CURRENT_OSX_VERSION
  OUTPUT_STRIP_TRAILING_WHITESPACE)

# Save CMAKE_OSX_ARCHITECTURES from the environment.
set(CMAKE_OSX_ARCHITECTURES "$ENV{CMAKE_OSX_ARCHITECTURES}" CACHE STRING
  "Build architectures for OSX")

# macOS, iOS, tvOS, and watchOS should lookup compilers from
# Platform/Apple-${CMAKE_CXX_COMPILER_ID}-<LANG>
set(CMAKE_EFFECTIVE_SYSTEM_NAME "Apple")

#----------------------------------------------------------------------------
# _CURRENT_OSX_VERSION - as a two-component string: 10.5, 10.6, ...
#
string(REGEX REPLACE "^([0-9]+\\.[0-9]+).*$" "\\1"
  _CURRENT_OSX_VERSION "${CURRENT_OSX_VERSION}")

#----------------------------------------------------------------------------
# CMAKE_OSX_DEPLOYMENT_TARGET

# Set cache variable - end user may change this during ccmake or cmake-gui configure.
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND _CURRENT_OSX_VERSION VERSION_GREATER 10.3)
  set(CMAKE_OSX_DEPLOYMENT_TARGET "$ENV{MACOSX_DEPLOYMENT_TARGET}" CACHE STRING
    "Minimum OS X version to target for deployment (at runtime); newer APIs weak linked. Set to empty string for default value.")
endif()

#----------------------------------------------------------------------------
# CMAKE_OSX_SYSROOT

if(CMAKE_OSX_SYSROOT)
  # Use the existing value without further computation to choose a default.
  set(_CMAKE_OSX_SYSROOT_DEFAULT "${CMAKE_OSX_SYSROOT}")
elseif(NOT "x$ENV{SDKROOT}" STREQUAL "x" AND
        (NOT "x$ENV{SDKROOT}" MATCHES "/" OR IS_DIRECTORY "$ENV{SDKROOT}"))
  # Use the value of SDKROOT from the environment.
  set(_CMAKE_OSX_SYSROOT_DEFAULT "$ENV{SDKROOT}")
elseif(CMAKE_SYSTEM_NAME STREQUAL iOS)
  set(_CMAKE_OSX_SYSROOT_DEFAULT "iphoneos")
elseif(CMAKE_SYSTEM_NAME STREQUAL tvOS)
  set(_CMAKE_OSX_SYSROOT_DEFAULT "appletvos")
elseif(CMAKE_SYSTEM_NAME STREQUAL watchOS)
  set(_CMAKE_OSX_SYSROOT_DEFAULT "watchos")
elseif("${CMAKE_GENERATOR}" MATCHES Xcode
       OR CMAKE_OSX_DEPLOYMENT_TARGET
       OR CMAKE_OSX_ARCHITECTURES MATCHES "[^;]"
       OR NOT EXISTS "/usr/include/sys/types.h")
  # Find installed SDKs in either Xcode-4.3+ or pre-4.3 SDKs directory.
  set(_CMAKE_OSX_SDKS_DIR "")
  if(OSX_DEVELOPER_ROOT)
    foreach(d Platforms/MacOSX.platform/Developer/SDKs SDKs)
      file(GLOB _CMAKE_OSX_SDKS ${OSX_DEVELOPER_ROOT}/${d}/*)
      if(_CMAKE_OSX_SDKS)
        set(_CMAKE_OSX_SDKS_DIR ${OSX_DEVELOPER_ROOT}/${d})
        break()
      endif()
    endforeach()
  endif()

  if(_CMAKE_OSX_SDKS_DIR)
    # Select SDK for current OSX version accounting for the known
    # specially named SDKs.
    set(_CMAKE_OSX_SDKS_VER_SUFFIX_10.4 "u")
    set(_CMAKE_OSX_SDKS_VER_SUFFIX_10.3 ".9")

    # find the latest SDK
    set(_CMAKE_OSX_LATEST_SDK_VERSION "0.0")
    file(GLOB _CMAKE_OSX_SDKS RELATIVE "${_CMAKE_OSX_SDKS_DIR}" "${_CMAKE_OSX_SDKS_DIR}/MacOSX*.sdk")
    foreach(_SDK ${_CMAKE_OSX_SDKS})
      if(_SDK MATCHES "MacOSX([0-9]+\\.[0-9]+)[^/]*\\.sdk" AND CMAKE_MATCH_1 VERSION_GREATER ${_CMAKE_OSX_LATEST_SDK_VERSION})
        set(_CMAKE_OSX_LATEST_SDK_VERSION "${CMAKE_MATCH_1}")
      endif()
    endforeach()

    # pick an SDK that works
    set(_CMAKE_OSX_SYSROOT_DEFAULT)
    foreach(ver ${CMAKE_OSX_DEPLOYMENT_TARGET}
                ${_CURRENT_OSX_VERSION}
                ${_CMAKE_OSX_LATEST_SDK_VERSION})
      set(_CMAKE_OSX_DEPLOYMENT_TARGET ${ver})
      set(_CMAKE_OSX_SDKS_VER ${_CMAKE_OSX_DEPLOYMENT_TARGET}${_CMAKE_OSX_SDKS_VER_SUFFIX_${_CMAKE_OSX_DEPLOYMENT_TARGET}})
      set(_CMAKE_OSX_SYSROOT_CHECK "${_CMAKE_OSX_SDKS_DIR}/MacOSX${_CMAKE_OSX_SDKS_VER}.sdk")
      if(IS_DIRECTORY "${_CMAKE_OSX_SYSROOT_CHECK}")
        set(_CMAKE_OSX_SYSROOT_DEFAULT "${_CMAKE_OSX_SYSROOT_CHECK}")
        break()
      endif()
    endforeach()

    if(NOT CMAKE_CROSSCOMPILING AND NOT CMAKE_OSX_DEPLOYMENT_TARGET AND _CURRENT_OSX_VERSION VERSION_LESS _CMAKE_OSX_DEPLOYMENT_TARGET)
      set(CMAKE_OSX_DEPLOYMENT_TARGET ${_CURRENT_OSX_VERSION} CACHE STRING
        "Minimum OS X version to target for deployment (at runtime); newer APIs weak linked. Set to empty string for default value." FORCE)
    endif()
  else()
    # Assume developer files are in root (such as Xcode 4.5 command-line tools).
    set(_CMAKE_OSX_SYSROOT_DEFAULT "")
  endif()
endif()

# Set cache variable - end user may change this during ccmake or cmake-gui configure.
# Choose the type based on the current value.
set(_CMAKE_OSX_SYSROOT_TYPE STRING)
foreach(v CMAKE_OSX_SYSROOT _CMAKE_OSX_SYSROOT_DEFAULT)
  if("x${${v}}" MATCHES "/")
    set(_CMAKE_OSX_SYSROOT_TYPE PATH)
    break()
  endif()
endforeach()
set(CMAKE_OSX_SYSROOT "${_CMAKE_OSX_SYSROOT_DEFAULT}" CACHE ${_CMAKE_OSX_SYSROOT_TYPE}
  "The product will be built against the headers and libraries located inside the indicated SDK.")

# Resolves the SDK name into a path
function(_apple_resolve_sdk_path sdk_name ret)
  execute_process(
    COMMAND xcrun -sdk ${sdk_name} --show-sdk-path
    OUTPUT_VARIABLE _stdout
    OUTPUT_STRIP_TRAILING_WHITESPACE
    ERROR_VARIABLE _stderr
    RESULT_VARIABLE _failed
  )
  set(${ret} "${_stdout}" PARENT_SCOPE)
endfunction()

function(_apple_resolve_supported_archs_for_sdk_from_system_lib sdk_path ret ret_failed)
  # Detect the supported SDK architectures by inspecting the main libSystem library.
  set(common_lib_prefix "${sdk_path}/usr/lib/libSystem")
  set(system_lib_dylib_path "${common_lib_prefix}.dylib")
  set(system_lib_tbd_path "${common_lib_prefix}.tbd")

  # Newer SDKs ship text based dylib stub files which contain the architectures supported by the
  # library in text form.
  if(EXISTS "${system_lib_tbd_path}")
    file(STRINGS "${system_lib_tbd_path}" tbd_lines REGEX "^archs: +\\[.+\\]")
    if(NOT tbd_lines)
      set(${ret_failed} TRUE PARENT_SCOPE)
      return()
    endif()

    # The tbd architectures line looks like the following:
    # archs:           [ armv7, armv7s, arm64, arm64e ]
    list(GET tbd_lines 0 first_arch_line)
    string(REGEX REPLACE
           "archs: +\\[ (.+) \\]" "\\1" arches_comma_separated "${first_arch_line}")
    string(STRIP "${arches_comma_separated}" arches_comma_separated)
    string(REPLACE "," ";" arch_list "${arches_comma_separated}")
    string(REPLACE " " "" arch_list "${arch_list}")
    if(NOT arch_list)
      set(${ret_failed} TRUE PARENT_SCOPE)
      return()
    endif()
    set(${ret} "${arch_list}" PARENT_SCOPE)
  elseif(EXISTS "${system_lib_dylib_path}")
    # Old SDKs (Xcode < 7) ship dylib files, use lipo to inspect the supported architectures.
    # Can't use -archs because the option is not available in older Xcode versions.
    execute_process(
      COMMAND lipo -info ${system_lib_dylib_path}
      OUTPUT_VARIABLE lipo_output
      OUTPUT_STRIP_TRAILING_WHITESPACE
      ERROR_VARIABLE _stderr
      RESULT_VARIABLE _failed
    )
    if(_failed OR NOT lipo_output OR NOT lipo_output MATCHES "(Non-fat file:|Architectures in the fat file:)")
      set(${ret_failed} TRUE PARENT_SCOPE)
      return()
    endif()

    # The lipo output looks like the following:
    # Non-fat file: <path> is architecture: i386
    # Architectures in the fat file: <path> are: i386 x86_64
    string(REGEX REPLACE
           "^(.+)is architecture:(.+)" "\\2" arches_space_separated "${lipo_output}")
    string(REGEX REPLACE
            "^(.+)are:(.+)" "\\2" arches_space_separated "${arches_space_separated}")

    # Need to clean up the arches, with Xcode 4.6.3 the output of lipo -info contains some
    # additional info, e.g.
    # Architectures in the fat file: <path> are: armv7 (cputype (12) cpusubtype (11))
    string(REGEX REPLACE
            "\\(.+\\)" "" arches_space_separated "${arches_space_separated}")

    # The output is space separated.
    string(STRIP "${arches_space_separated}" arches_space_separated)
    string(REPLACE " " ";" arch_list "${arches_space_separated}")

    if(NOT arch_list)
      set(${ret_failed} TRUE PARENT_SCOPE)
      return()
    endif()
    set(${ret} "${arch_list}" PARENT_SCOPE)
  else()
    # This shouldn't happen, but keep it for safety.
    message(WARNING "No way to find architectures for given sdk_path '${sdk_path}'")
    set(${ret_failed} TRUE PARENT_SCOPE)
  endif()
endfunction()

# Handle multi-arch sysroots. Do this before CMAKE_OSX_SYSROOT is
# transformed into a path, so that we know the sysroot name.
function(_apple_resolve_multi_arch_sysroots)
  if(DEFINED CMAKE_APPLE_ARCH_SYSROOTS)
    return() # Already cached
  endif()

  list(LENGTH CMAKE_OSX_ARCHITECTURES _num_archs)
  if(NOT (_num_archs GREATER 1))
    return() # Only apply to multi-arch
  endif()

  if(CMAKE_OSX_SYSROOT STREQUAL "macosx")
    # macOS doesn't have a simulator sdk / sysroot, so there is no need to handle per-sdk arches.
    return()
  endif()

  if(IS_DIRECTORY "${CMAKE_OSX_SYSROOT}")
    if(NOT CMAKE_OSX_SYSROOT STREQUAL _CMAKE_OSX_SYSROOT_DEFAULT)
      message(WARNING "Can not resolve multi-arch sysroots with CMAKE_OSX_SYSROOT set to path (${CMAKE_OSX_SYSROOT})")
    endif()
    return()
  endif()

  string(REPLACE "os" "simulator" _simulator_sdk ${CMAKE_OSX_SYSROOT})
  set(_sdks "${CMAKE_OSX_SYSROOT};${_simulator_sdk}")
  foreach(sdk ${_sdks})
    _apple_resolve_sdk_path(${sdk} _sdk_path)
    if(NOT IS_DIRECTORY "${_sdk_path}")
      message(WARNING "Failed to resolve SDK path for '${sdk}'")
      continue()
    endif()

    _apple_resolve_supported_archs_for_sdk_from_system_lib(${_sdk_path} _sdk_archs _failed)

    if(_failed)
      # Failure to extract supported architectures for an SDK means that the installed SDK is old
      # and does not provide such information (SDKs that come with Xcode >= 10.x started providing
      # the information). In such a case, return early, and handle multi-arch builds the old way
      # (no per-sdk arches).
      return()
    endif()

    set(_sdk_archs_${sdk} ${_sdk_archs})
    set(_sdk_path_${sdk} ${_sdk_path})
  endforeach()

  foreach(arch ${CMAKE_OSX_ARCHITECTURES})
    set(_arch_sysroot "")
    foreach(sdk ${_sdks})
      list(FIND _sdk_archs_${sdk} ${arch} arch_index)
      if(NOT arch_index EQUAL -1)
        set(_arch_sysroot ${_sdk_path_${sdk}})
        break()
      endif()
    endforeach()
    if(_arch_sysroot)
      list(APPEND _arch_sysroots ${_arch_sysroot})
    else()
      message(WARNING "No SDK found for architecture '${arch}'")
      list(APPEND _arch_sysroots "${arch}-SDK-NOTFOUND")
    endif()
  endforeach()

  set(CMAKE_APPLE_ARCH_SYSROOTS "${_arch_sysroots}" CACHE INTERNAL
    "Architecture dependent sysroots, one per CMAKE_OSX_ARCHITECTURES")
endfunction()

_apple_resolve_multi_arch_sysroots()

# Transform CMAKE_OSX_SYSROOT to absolute path
set(_CMAKE_OSX_SYSROOT_PATH "")
if(CMAKE_OSX_SYSROOT)
  if("x${CMAKE_OSX_SYSROOT}" MATCHES "/")
    # This is a path to the SDK.  Make sure it exists.
    if(NOT IS_DIRECTORY "${CMAKE_OSX_SYSROOT}")
      message(WARNING "Ignoring CMAKE_OSX_SYSROOT value:\n ${CMAKE_OSX_SYSROOT}\n"
        "because the directory does not exist.")
      set(CMAKE_OSX_SYSROOT "")
    endif()
    set(_CMAKE_OSX_SYSROOT_PATH "${CMAKE_OSX_SYSROOT}")
  else()
    _apple_resolve_sdk_path(${CMAKE_OSX_SYSROOT} _sdk_path)
    if(IS_DIRECTORY "${_sdk_path}")
      set(_CMAKE_OSX_SYSROOT_PATH "${_sdk_path}")
      # For non-Xcode generators use the path.
      if(NOT "${CMAKE_GENERATOR}" MATCHES "Xcode")
        set(CMAKE_OSX_SYSROOT "${_CMAKE_OSX_SYSROOT_PATH}")
      endif()
    endif()
  endif()
endif()