1include(CMakeParseArguments)
2include(CompilerRTUtils)
3
4set(CMAKE_LIPO "lipo" CACHE PATH "path to the lipo tool")
5
6# On OS X SDKs can be installed anywhere on the base system and xcode-select can
7# set the default Xcode to use. This function finds the SDKs that are present in
8# the current Xcode.
9function(find_darwin_sdk_dir var sdk_name)
10  set(DARWIN_${sdk_name}_CACHED_SYSROOT "" CACHE STRING "Darwin SDK path for SDK ${sdk_name}.")
11  set(DARWIN_PREFER_PUBLIC_SDK OFF CACHE BOOL "Prefer Darwin public SDK, even when an internal SDK is present.")
12
13  if(DARWIN_${sdk_name}_CACHED_SYSROOT)
14    set(${var} ${DARWIN_${sdk_name}_CACHED_SYSROOT} PARENT_SCOPE)
15    return()
16  endif()
17  if(NOT DARWIN_PREFER_PUBLIC_SDK)
18    # Let's first try the internal SDK, otherwise use the public SDK.
19    execute_process(
20      COMMAND xcodebuild -version -sdk ${sdk_name}.internal Path
21      RESULT_VARIABLE result_process
22      OUTPUT_VARIABLE var_internal
23      OUTPUT_STRIP_TRAILING_WHITESPACE
24      ERROR_FILE /dev/null
25    )
26  endif()
27  if((NOT result_process EQUAL 0) OR "" STREQUAL "${var_internal}")
28    execute_process(
29      COMMAND xcodebuild -version -sdk ${sdk_name} Path
30      RESULT_VARIABLE result_process
31      OUTPUT_VARIABLE var_internal
32      OUTPUT_STRIP_TRAILING_WHITESPACE
33      ERROR_FILE /dev/null
34    )
35  else()
36    set(${var}_INTERNAL ${var_internal} PARENT_SCOPE)
37  endif()
38  if(result_process EQUAL 0)
39    set(${var} ${var_internal} PARENT_SCOPE)
40  endif()
41  set(DARWIN_${sdk_name}_CACHED_SYSROOT ${var_internal} CACHE STRING "Darwin SDK path for SDK ${sdk_name}." FORCE)
42endfunction()
43
44# There isn't a clear mapping of what architectures are supported with a given
45# target platform, but ld's version output does list the architectures it can
46# link for.
47function(darwin_get_toolchain_supported_archs output_var)
48  execute_process(
49    COMMAND "${CMAKE_LINKER}" -v
50    ERROR_VARIABLE LINKER_VERSION)
51
52  string(REGEX MATCH "configured to support archs: ([^\n]+)"
53         ARCHES_MATCHED "${LINKER_VERSION}")
54  if(ARCHES_MATCHED)
55    set(ARCHES "${CMAKE_MATCH_1}")
56    message(STATUS "Got ld supported ARCHES: ${ARCHES}")
57    string(REPLACE " " ";" ARCHES ${ARCHES})
58  else()
59    # If auto-detecting fails, fall back to a default set
60    message(WARNING "Detecting supported architectures from 'ld -v' failed. Returning default set.")
61    set(ARCHES "i386;x86_64;armv7;armv7s;arm64")
62  endif()
63
64  set(${output_var} ${ARCHES} PARENT_SCOPE)
65endfunction()
66
67# This function takes an OS and a list of architectures and identifies the
68# subset of the architectures list that the installed toolchain can target.
69function(darwin_test_archs os valid_archs)
70  if(${valid_archs})
71    message(STATUS "Using cached valid architectures for ${os}.")
72    return()
73  endif()
74
75  set(archs ${ARGN})
76  if(NOT TEST_COMPILE_ONLY)
77    message(STATUS "Finding valid architectures for ${os}...")
78    set(SIMPLE_C ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/src.c)
79    file(WRITE ${SIMPLE_C} "#include <stdio.h>\nint main() { printf(__FILE__); return 0; }\n")
80
81    set(os_linker_flags)
82    foreach(flag ${DARWIN_${os}_LINK_FLAGS})
83      set(os_linker_flags "${os_linker_flags} ${flag}")
84    endforeach()
85  endif()
86
87  # The simple program will build for x86_64h on the simulator because it is
88  # compatible with x86_64 libraries (mostly), but since x86_64h isn't actually
89  # a valid or useful architecture for the iOS simulator we should drop it.
90  if(${os} MATCHES "^(iossim|tvossim|watchossim)$")
91    list(REMOVE_ITEM archs "x86_64h")
92  endif()
93
94  set(working_archs)
95  foreach(arch ${archs})
96
97    set(arch_linker_flags "-arch ${arch} ${os_linker_flags}")
98    if(TEST_COMPILE_ONLY)
99      # `-w` is used to surpress compiler warnings which `try_compile_only()` treats as an error.
100      try_compile_only(CAN_TARGET_${os}_${arch} FLAGS -v -arch ${arch} ${DARWIN_${os}_CFLAGS} -w)
101    else()
102      set(SAVED_CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS})
103      set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${arch_linker_flags}")
104      try_compile(CAN_TARGET_${os}_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_C}
105                  COMPILE_DEFINITIONS "-v -arch ${arch}" ${DARWIN_${os}_CFLAGS}
106                  OUTPUT_VARIABLE TEST_OUTPUT)
107      set(CMAKE_EXE_LINKER_FLAGS ${SAVED_CMAKE_EXE_LINKER_FLAGS})
108    endif()
109    if(${CAN_TARGET_${os}_${arch}})
110      list(APPEND working_archs ${arch})
111    else()
112      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
113        "Testing compiler for supporting ${os}-${arch}:\n"
114        "${TEST_OUTPUT}\n")
115    endif()
116  endforeach()
117  set(${valid_archs} ${working_archs}
118    CACHE STRING "List of valid architectures for platform ${os}." FORCE)
119endfunction()
120
121# This function checks the host cpusubtype to see if it is post-haswell. Haswell
122# and later machines can run x86_64h binaries. Haswell is cpusubtype 8.
123function(darwin_filter_host_archs input output)
124  list_intersect(tmp_var DARWIN_osx_ARCHS ${input})
125  execute_process(
126    COMMAND sysctl hw.cpusubtype
127    OUTPUT_VARIABLE SUBTYPE)
128
129  string(REGEX MATCH "hw.cpusubtype: ([0-9]*)"
130         SUBTYPE_MATCHED "${SUBTYPE}")
131  set(HASWELL_SUPPORTED Off)
132  if(SUBTYPE_MATCHED)
133    if(${CMAKE_MATCH_1} GREATER 7)
134      set(HASWELL_SUPPORTED On)
135    endif()
136  endif()
137  if(NOT HASWELL_SUPPORTED)
138    list(REMOVE_ITEM tmp_var x86_64h)
139  endif()
140  set(${output} ${tmp_var} PARENT_SCOPE)
141endfunction()
142
143# Read and process the exclude file into a list of symbols
144function(darwin_read_list_from_file output_var file)
145  if(EXISTS ${file})
146    file(READ ${file} EXCLUDES)
147    string(REPLACE "\n" ";" EXCLUDES ${EXCLUDES})
148    set(${output_var} ${EXCLUDES} PARENT_SCOPE)
149  endif()
150endfunction()
151
152# this function takes an OS, architecture and minimum version and provides a
153# list of builtin functions to exclude
154function(darwin_find_excluded_builtins_list output_var)
155  cmake_parse_arguments(LIB
156    ""
157    "OS;ARCH;MIN_VERSION"
158    ""
159    ${ARGN})
160
161  if(NOT LIB_OS OR NOT LIB_ARCH)
162    message(FATAL_ERROR "Must specify OS and ARCH to darwin_find_excluded_builtins_list!")
163  endif()
164
165  darwin_read_list_from_file(${LIB_OS}_BUILTINS
166    ${DARWIN_EXCLUDE_DIR}/${LIB_OS}.txt)
167  darwin_read_list_from_file(${LIB_OS}_${LIB_ARCH}_BASE_BUILTINS
168    ${DARWIN_EXCLUDE_DIR}/${LIB_OS}-${LIB_ARCH}.txt)
169
170  if(LIB_MIN_VERSION)
171    file(GLOB builtin_lists ${DARWIN_EXCLUDE_DIR}/${LIB_OS}*-${LIB_ARCH}.txt)
172    foreach(builtin_list ${builtin_lists})
173      string(REGEX MATCH "${LIB_OS}([0-9\\.]*)-${LIB_ARCH}.txt" VERSION_MATCHED "${builtin_list}")
174      if (VERSION_MATCHED AND NOT CMAKE_MATCH_1 VERSION_LESS LIB_MIN_VERSION)
175        if(NOT smallest_version)
176          set(smallest_version ${CMAKE_MATCH_1})
177        elseif(CMAKE_MATCH_1 VERSION_LESS smallest_version)
178          set(smallest_version ${CMAKE_MATCH_1})
179        endif()
180      endif()
181    endforeach()
182
183    if(smallest_version)
184      darwin_read_list_from_file(${LIB_ARCH}_${LIB_OS}_BUILTINS
185        ${DARWIN_EXCLUDE_DIR}/${LIB_OS}${smallest_version}-${LIB_ARCH}.txt)
186    endif()
187  endif()
188
189  set(${output_var}
190      ${${LIB_ARCH}_${LIB_OS}_BUILTINS}
191      ${${LIB_OS}_${LIB_ARCH}_BASE_BUILTINS}
192      ${${LIB_OS}_BUILTINS} PARENT_SCOPE)
193endfunction()
194
195# adds a single builtin library for a single OS & ARCH
196macro(darwin_add_builtin_library name suffix)
197  cmake_parse_arguments(LIB
198    ""
199    "PARENT_TARGET;OS;ARCH"
200    "SOURCES;CFLAGS;DEFS"
201    ${ARGN})
202  set(libname "${name}.${suffix}_${LIB_ARCH}_${LIB_OS}")
203  add_library(${libname} STATIC ${LIB_SOURCES})
204  if(DARWIN_${LIB_OS}_SYSROOT)
205    set(sysroot_flag -isysroot ${DARWIN_${LIB_OS}_SYSROOT})
206  endif()
207
208  # Make a copy of the compilation flags.
209  set(builtin_cflags ${LIB_CFLAGS})
210
211  # Strip out any inappropriate flags for the target.
212  if("${LIB_ARCH}" MATCHES "^(armv7|armv7k|armv7s)$")
213    set(builtin_cflags "")
214    foreach(cflag "${LIB_CFLAGS}")
215      string(REPLACE "-fomit-frame-pointer" "" cflag "${cflag}")
216      list(APPEND builtin_cflags ${cflag})
217    endforeach(cflag)
218  endif()
219
220  set_target_compile_flags(${libname}
221    ${sysroot_flag}
222    ${DARWIN_${LIB_OS}_BUILTIN_MIN_VER_FLAG}
223    ${builtin_cflags})
224  set_property(TARGET ${libname} APPEND PROPERTY
225      COMPILE_DEFINITIONS ${LIB_DEFS})
226  set_target_properties(${libname} PROPERTIES
227      OUTPUT_NAME ${libname}${COMPILER_RT_OS_SUFFIX})
228  set_target_properties(${libname} PROPERTIES
229    OSX_ARCHITECTURES ${LIB_ARCH})
230
231  if(LIB_PARENT_TARGET)
232    add_dependencies(${LIB_PARENT_TARGET} ${libname})
233  endif()
234
235  list(APPEND ${LIB_OS}_${suffix}_libs ${libname})
236  list(APPEND ${LIB_OS}_${suffix}_lipo_flags -arch ${arch} $<TARGET_FILE:${libname}>)
237  set_target_properties(${libname} PROPERTIES FOLDER "Compiler-RT Libraries")
238endmacro()
239
240function(darwin_lipo_libs name)
241  cmake_parse_arguments(LIB
242    ""
243    "PARENT_TARGET;OUTPUT_DIR;INSTALL_DIR"
244    "LIPO_FLAGS;DEPENDS"
245    ${ARGN})
246  if(LIB_DEPENDS AND LIB_LIPO_FLAGS)
247    add_custom_command(OUTPUT ${LIB_OUTPUT_DIR}/lib${name}.a
248      COMMAND ${CMAKE_COMMAND} -E make_directory ${LIB_OUTPUT_DIR}
249      COMMAND ${CMAKE_LIPO} -output
250              ${LIB_OUTPUT_DIR}/lib${name}.a
251              -create ${LIB_LIPO_FLAGS}
252      DEPENDS ${LIB_DEPENDS}
253      )
254    add_custom_target(${name}
255      DEPENDS ${LIB_OUTPUT_DIR}/lib${name}.a)
256    set_target_properties(${name} PROPERTIES FOLDER "Compiler-RT Misc")
257    add_dependencies(${LIB_PARENT_TARGET} ${name})
258
259    if(CMAKE_CONFIGURATION_TYPES)
260      set(install_component ${LIB_PARENT_TARGET})
261    else()
262      set(install_component ${name})
263    endif()
264    install(FILES ${LIB_OUTPUT_DIR}/lib${name}.a
265      DESTINATION ${LIB_INSTALL_DIR}
266      COMPONENT ${install_component})
267    add_compiler_rt_install_targets(${name} PARENT_TARGET ${LIB_PARENT_TARGET})
268  else()
269    message(WARNING "Not generating lipo target for ${name} because no input libraries exist.")
270  endif()
271endfunction()
272
273# Generates builtin libraries for all operating systems specified in ARGN. Each
274# OS library is constructed by lipo-ing together single-architecture libraries.
275macro(darwin_add_builtin_libraries)
276  set(DARWIN_EXCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Darwin-excludes)
277
278  set(CFLAGS "-fPIC -O3 -fvisibility=hidden -DVISIBILITY_HIDDEN -Wall -fomit-frame-pointer")
279  set(CMAKE_C_FLAGS "")
280  set(CMAKE_CXX_FLAGS "")
281  set(CMAKE_ASM_FLAGS "")
282
283  set(PROFILE_SOURCES ../profile/InstrProfiling
284                      ../profile/InstrProfilingBuffer
285                      ../profile/InstrProfilingPlatformDarwin
286                      ../profile/InstrProfilingWriter)
287  foreach (os ${ARGN})
288    list_intersect(DARWIN_BUILTIN_ARCHS DARWIN_${os}_BUILTIN_ARCHS BUILTIN_SUPPORTED_ARCH)
289    foreach (arch ${DARWIN_BUILTIN_ARCHS})
290      darwin_find_excluded_builtins_list(${arch}_${os}_EXCLUDED_BUILTINS
291                              OS ${os}
292                              ARCH ${arch}
293                              MIN_VERSION ${DARWIN_${os}_BUILTIN_MIN_VER})
294
295      filter_builtin_sources(filtered_sources
296        EXCLUDE ${arch}_${os}_EXCLUDED_BUILTINS
297        ${${arch}_SOURCES})
298
299      darwin_add_builtin_library(clang_rt builtins
300                              OS ${os}
301                              ARCH ${arch}
302                              SOURCES ${filtered_sources}
303                              CFLAGS ${CFLAGS} -arch ${arch}
304                              PARENT_TARGET builtins)
305    endforeach()
306
307    # Don't build cc_kext libraries for simulator platforms
308    if(NOT DARWIN_${os}_SKIP_CC_KEXT)
309      foreach (arch ${DARWIN_BUILTIN_ARCHS})
310        # By not specifying MIN_VERSION this only reads the OS and OS-arch lists.
311        # We don't want to filter out the builtins that are present in libSystem
312        # because kexts can't link libSystem.
313        darwin_find_excluded_builtins_list(${arch}_${os}_EXCLUDED_BUILTINS
314                              OS ${os}
315                              ARCH ${arch})
316
317        filter_builtin_sources(filtered_sources
318          EXCLUDE ${arch}_${os}_EXCLUDED_BUILTINS
319          ${${arch}_SOURCES})
320
321        # In addition to the builtins cc_kext includes some profile sources
322        darwin_add_builtin_library(clang_rt cc_kext
323                                OS ${os}
324                                ARCH ${arch}
325                                SOURCES ${filtered_sources} ${PROFILE_SOURCES}
326                                CFLAGS ${CFLAGS} -arch ${arch} -mkernel
327                                DEFS KERNEL_USE
328                                PARENT_TARGET builtins)
329      endforeach()
330      set(archive_name clang_rt.cc_kext_${os})
331      if(${os} STREQUAL "osx")
332        set(archive_name clang_rt.cc_kext)
333      endif()
334      darwin_lipo_libs(${archive_name}
335                      PARENT_TARGET builtins
336                      LIPO_FLAGS ${${os}_cc_kext_lipo_flags}
337                      DEPENDS ${${os}_cc_kext_libs}
338                      OUTPUT_DIR ${COMPILER_RT_LIBRARY_OUTPUT_DIR}
339                      INSTALL_DIR ${COMPILER_RT_LIBRARY_INSTALL_DIR})
340    endif()
341  endforeach()
342
343  # We put the x86 sim slices into the archives for their base OS
344  foreach (os ${ARGN})
345    if(NOT ${os} MATCHES ".*sim$")
346      darwin_lipo_libs(clang_rt.${os}
347                        PARENT_TARGET builtins
348                        LIPO_FLAGS ${${os}_builtins_lipo_flags} ${${os}sim_builtins_lipo_flags}
349                        DEPENDS ${${os}_builtins_libs} ${${os}sim_builtins_libs}
350                        OUTPUT_DIR ${COMPILER_RT_LIBRARY_OUTPUT_DIR}
351                        INSTALL_DIR ${COMPILER_RT_LIBRARY_INSTALL_DIR})
352    endif()
353  endforeach()
354  darwin_add_embedded_builtin_libraries()
355endmacro()
356
357macro(darwin_add_embedded_builtin_libraries)
358  # this is a hacky opt-out. If you can't target both intel and arm
359  # architectures we bail here.
360  set(DARWIN_SOFT_FLOAT_ARCHS armv6m armv7m armv7em armv7)
361  set(DARWIN_HARD_FLOAT_ARCHS armv7em armv7)
362  if(COMPILER_RT_SUPPORTED_ARCH MATCHES ".*armv.*")
363    list(FIND COMPILER_RT_SUPPORTED_ARCH i386 i386_idx)
364    if(i386_idx GREATER -1)
365      list(APPEND DARWIN_HARD_FLOAT_ARCHS i386)
366    endif()
367
368    list(FIND COMPILER_RT_SUPPORTED_ARCH x86_64 x86_64_idx)
369    if(x86_64_idx GREATER -1)
370      list(APPEND DARWIN_HARD_FLOAT_ARCHS x86_64)
371    endif()
372
373    set(MACHO_SYM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/macho_embedded)
374
375    set(CFLAGS "-Oz -Wall -fomit-frame-pointer -ffreestanding")
376    set(CMAKE_C_FLAGS "")
377    set(CMAKE_CXX_FLAGS "")
378    set(CMAKE_ASM_FLAGS "")
379
380    set(SOFT_FLOAT_FLAG -mfloat-abi=soft)
381    set(HARD_FLOAT_FLAG -mfloat-abi=hard)
382
383    set(ENABLE_PIC Off)
384    set(PIC_FLAG -fPIC)
385    set(STATIC_FLAG -static)
386
387    set(DARWIN_macho_embedded_ARCHS armv6m armv7m armv7em armv7 i386 x86_64)
388
389    set(DARWIN_macho_embedded_LIBRARY_OUTPUT_DIR
390      ${COMPILER_RT_OUTPUT_DIR}/lib/macho_embedded)
391    set(DARWIN_macho_embedded_LIBRARY_INSTALL_DIR
392      ${COMPILER_RT_INSTALL_PATH}/lib/macho_embedded)
393
394    set(CFLAGS_armv7 "-target thumbv7-apple-darwin-eabi")
395    set(CFLAGS_i386 "-march=pentium")
396
397    darwin_read_list_from_file(common_FUNCTIONS ${MACHO_SYM_DIR}/common.txt)
398    darwin_read_list_from_file(thumb2_FUNCTIONS ${MACHO_SYM_DIR}/thumb2.txt)
399    darwin_read_list_from_file(thumb2_64_FUNCTIONS ${MACHO_SYM_DIR}/thumb2-64.txt)
400    darwin_read_list_from_file(arm_FUNCTIONS ${MACHO_SYM_DIR}/arm.txt)
401    darwin_read_list_from_file(i386_FUNCTIONS ${MACHO_SYM_DIR}/i386.txt)
402
403
404    set(armv6m_FUNCTIONS ${common_FUNCTIONS} ${arm_FUNCTIONS})
405    set(armv7m_FUNCTIONS ${common_FUNCTIONS} ${arm_FUNCTIONS} ${thumb2_FUNCTIONS})
406    set(armv7em_FUNCTIONS ${common_FUNCTIONS} ${arm_FUNCTIONS} ${thumb2_FUNCTIONS})
407    set(armv7_FUNCTIONS ${common_FUNCTIONS} ${arm_FUNCTIONS} ${thumb2_FUNCTIONS} ${thumb2_64_FUNCTIONS})
408    set(i386_FUNCTIONS ${common_FUNCTIONS} ${i386_FUNCTIONS})
409    set(x86_64_FUNCTIONS ${common_FUNCTIONS})
410
411    foreach(arch ${DARWIN_macho_embedded_ARCHS})
412      filter_builtin_sources(${arch}_filtered_sources
413        INCLUDE ${arch}_FUNCTIONS
414        ${${arch}_SOURCES})
415      if(NOT ${arch}_filtered_sources)
416        message(WARNING "${arch}_SOURCES: ${${arch}_SOURCES}")
417        message(WARNING "${arch}_FUNCTIONS: ${${arch}_FUNCTIONS}")
418        message(FATAL_ERROR "Empty filtered sources!")
419      endif()
420    endforeach()
421
422    foreach(float_type SOFT HARD)
423      foreach(type PIC STATIC)
424        string(TOLOWER "${float_type}_${type}" lib_suffix)
425        foreach(arch ${DARWIN_${float_type}_FLOAT_ARCHS})
426          set(DARWIN_macho_embedded_SYSROOT ${DARWIN_osx_SYSROOT})
427          set(float_flag)
428          if(${arch} MATCHES "^arm")
429            # x86 targets are hard float by default, but the complain about the
430            # float ABI flag, so don't pass it unless we're targeting arm.
431            set(float_flag ${${float_type}_FLOAT_FLAG})
432          endif()
433          darwin_add_builtin_library(clang_rt ${lib_suffix}
434                                OS macho_embedded
435                                ARCH ${arch}
436                                SOURCES ${${arch}_filtered_sources}
437                                CFLAGS ${CFLAGS} -arch ${arch} ${${type}_FLAG} ${float_flag} ${CFLAGS_${arch}}
438                                PARENT_TARGET builtins)
439        endforeach()
440        foreach(lib ${macho_embedded_${lib_suffix}_libs})
441          set_target_properties(${lib} PROPERTIES LINKER_LANGUAGE C)
442        endforeach()
443        darwin_lipo_libs(clang_rt.${lib_suffix}
444                      PARENT_TARGET builtins
445                      LIPO_FLAGS ${macho_embedded_${lib_suffix}_lipo_flags}
446                      DEPENDS ${macho_embedded_${lib_suffix}_libs}
447                      OUTPUT_DIR ${DARWIN_macho_embedded_LIBRARY_OUTPUT_DIR}
448                      INSTALL_DIR ${DARWIN_macho_embedded_LIBRARY_INSTALL_DIR})
449      endforeach()
450    endforeach()
451  endif()
452endmacro()
453