Skip to content

Commit 63ce243

Browse files
committed
[CMake] Add initial build system support for macCatalyst
This commit adds initial build system support for macCatalyst, an Apple technology that enables code targeting iOS to be recompiled so that it can be executed on macOS while still using iOS APIs. This is the first in a series of commits building out support for macCatalyst in the compiler, runtime, standard library, and overlays. Swift for macCatalyst represents the work of multiple people, including Devin Coughlin, Ross Bayer, and Brent Royal-Gordon. Under macCatalyst, compiler-provided shared libraries (including overlays) are built as one of four kinds (or "flavors") of libraries, each with different install names and Mach-O load commands. This commit adds the build system infrastructure to produce these different library flavors. **macOS-like Libraries** A "macOS-like" library (such as the GLKit overlay) is a plain-old macOS library that can only be loaded into regular macOS processes. It has a macOS slice with a single load command allowing it to be loaded into normal macOS processes. **iOS-like Libraries** An "iOS-like" library, such as the UIKit overlay, is a library with a macOS slice but with a load command that only allows it be loaded into macCatalyst processes. iOS-like libraries are produced by passing a new target tuple to the compiler: swiftc ... -target x86_64-apple-ios13.0-macabi ... Here 'ios' (and an iOS version number) is used for OS portion of the triple, but the 'macabi' environment tells the compiler that the library is intended for macCatalyst. **Zippered Libraries** A "zippered" library can be loaded into either a macCatalyst process or a standard macOS process. Since macCatalyst does not introduce a new Mach-O slice, the same code is shared between both processes. Zippered libraries are usually relatively low level and with an API surface that is similar between macOS and iOS (for example, both the Foundation overlay and the Swift Standard Library/Runtime itself are zippered). Zippered libraries are created by passing both the usual `-target` flag to the compiler and an additional `-target-variant` flag: swiftc ... -target x86_64-apple-macos10.15 \ -target-variant x86_64-apple-ios13.0-macabi Just like the -target flag, -target-variant takes a target tuple. This tells the compiler to compile the library for the -target tuple but to add an extra load command, allowing the library to be loaded into processes of the -target-variant flavor as well. While a single zippered library and slice is shared between macOS and macCatalyst, zippered libraries require two separate .swiftinterface/.swiftmodule files, one for macOS and one for macCatalyst. When a macOS or macCatalyst client imports the library, it will use module file for its flavor to determine what symbols are present. This enables a zippered library to expose a subset of its target APIs to its target-variant. **Unzippered-Twin Libraries** "Unzippered Twins" are pairs of libraries with the same name but different contents and install locations, one for use from macOS processes and one for use from macCatalyst processes. Unzippered twins are usually libraries that depend on AppKit on macOS and UIKit on iOS (for example, the MapKit overlay) and so do not share a common implementation between macOS and macCatalyst. The macCatalyst version of an unzippered twin is installed in a parallel directory hierarchy rooted at /System/iOSSupport/. So, for example, while macOS and zippered Swift overlays are installed in /usr/lib/swift/, iOS-like and the macCatalyst side of unzippered twins are installed in /System/iOSSupport/usr/lib/swift. When building for macCatalyst, the build system passes additional search paths so that the macCatalyst version of libraries is found before macOS versions. The add_swift_target_library() funciton now take an optional MACCATALYST_BUILD_FLAVOR, which enables swift libraries to indicate which flavor of library they are.
1 parent 91fe7db commit 63ce243

File tree

11 files changed

+601
-54
lines changed

11 files changed

+601
-54
lines changed

cmake/modules/AddSwift.cmake

Lines changed: 300 additions & 43 deletions
Large diffs are not rendered by default.

cmake/modules/StandaloneOverlay.cmake

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ set(SWIFT_BUILD_STANDALONE_OVERLAY TRUE)
9393
set(SWIFT_STDLIB_LIBRARY_BUILD_TYPES "SHARED")
9494
set(SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES "SHARED")
9595

96+
option(SWIFT_ENABLE_MACCATALYST
97+
"Build the overlays with macCatalyst support"
98+
FALSE)
99+
100+
set(SWIFT_DARWIN_DEPLOYMENT_VERSION_MACCATALYST "13.0" CACHE STRING
101+
"Minimum deployment target version for macCatalyst")
96102

97103
# -----------------------------------------------------------------------------
98104

cmake/modules/SwiftConfigureSDK.cmake

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ macro(configure_sdk_darwin
133133
endif()
134134

135135
if(NOT EXISTS "${SWIFT_SDK_${prefix}_PATH}/System/Library/Frameworks/module.map")
136-
message(FATAL_ERROR "${name} SDK not found at ${SWIFT_SDK_${prefix}_PATH}.")
136+
message(FATAL_ERROR "${name} SDK not found at SWIFT_SDK_${prefix}_PATH.")
137137
endif()
138138

139139
# Determine the SDK version we found.
@@ -183,6 +183,20 @@ macro(configure_sdk_darwin
183183

184184
set(SWIFT_SDK_${prefix}_ARCH_${arch}_TRIPLE
185185
"${arch}-apple-${SWIFT_SDK_${prefix}_TRIPLE_NAME}")
186+
187+
if(SWIFT_ENABLE_MACCATALYST AND "${prefix}" STREQUAL "OSX")
188+
# For macCatalyst append the '-macabi' environment to the target triple.
189+
set(SWIFT_SDK_MACCATALYST_ARCH_${arch}_TRIPLE
190+
"${SWIFT_SDK_${prefix}_ARCH_${arch}_TRIPLE}-macabi")
191+
192+
# macCatalyst triple
193+
set(SWIFT_MACCATALYST_TRIPLE
194+
"x86_64-apple-ios${SWIFT_DARWIN_DEPLOYMENT_VERSION_MACCATALYST}-macabi")
195+
196+
# For macCatalyst, the xcrun_name is "macosx" since it uses that sdk.
197+
# Hard code the library subdirectory to "maccatalyst" in that case.
198+
set(SWIFT_SDK_MACCATALYST_LIB_SUBDIR "maccatalyst")
199+
endif()
186200
endforeach()
187201

188202
# Add this to the list of known SDKs.

cmake/modules/SwiftSource.cmake

Lines changed: 165 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
include(macCatalystUtils)
12
include(SwiftUtils)
23

34
# Process the sources within the given variable, pulling out any Swift
@@ -7,6 +8,9 @@ include(SwiftUtils)
78
#
89
# Usage:
910
# handle_swift_sources(sourcesvar externalvar)
11+
#
12+
# MACCATALYST_BUILD_FLAVOR
13+
# Possible values are 'ios-like', 'macos-like', 'zippered', 'unzippered-twin'
1014
function(handle_swift_sources
1115
dependency_target_out_var_name
1216
dependency_module_target_out_var_name
@@ -16,7 +20,7 @@ function(handle_swift_sources
1620
sourcesvar externalvar name)
1721
cmake_parse_arguments(SWIFTSOURCES
1822
"IS_MAIN;IS_STDLIB;IS_STDLIB_CORE;IS_SDK_OVERLAY;EMBED_BITCODE"
19-
"SDK;ARCHITECTURE;INSTALL_IN_COMPONENT"
23+
"SDK;ARCHITECTURE;INSTALL_IN_COMPONENT;MACCATALYST_BUILD_FLAVOR"
2024
"DEPENDS;COMPILE_FLAGS;MODULE_NAME"
2125
${ARGN})
2226
translate_flag(${SWIFTSOURCES_IS_MAIN} "IS_MAIN" IS_MAIN_arg)
@@ -63,6 +67,12 @@ function(handle_swift_sources
6367
if(swift_sources)
6468
set(objsubdir "/${SWIFTSOURCES_SDK}/${SWIFTSOURCES_ARCHITECTURE}")
6569

70+
get_maccatalyst_build_flavor(maccatalyst_build_flavor
71+
"${SWIFTSOURCES_SDK}" "${SWIFTSOURCES_MACCATALYST_BUILD_FLAVOR}")
72+
if(maccatalyst_build_flavor STREQUAL "ios-like")
73+
set(objsubdir "/MACCATALYST/${SWIFTSOURCES_ARCHITECTURE}")
74+
endif()
75+
6676
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}${objsubdir}")
6777

6878
set(swift_obj
@@ -91,7 +101,8 @@ function(handle_swift_sources
91101
${IS_SDK_OVERLAY_arg}
92102
${EMBED_BITCODE_arg}
93103
${STATIC_arg}
94-
INSTALL_IN_COMPONENT "${SWIFTSOURCES_INSTALL_IN_COMPONENT}")
104+
INSTALL_IN_COMPONENT "${SWIFTSOURCES_INSTALL_IN_COMPONENT}"
105+
MACCATALYST_BUILD_FLAVOR "${SWIFTSOURCES_MACCATALYST_BUILD_FLAVOR}")
95106
set("${dependency_target_out_var_name}" "${dependency_target}" PARENT_SCOPE)
96107
set("${dependency_module_target_out_var_name}" "${module_dependency_target}" PARENT_SCOPE)
97108
set("${dependency_sib_target_out_var_name}" "${sib_dependency_target}" PARENT_SCOPE)
@@ -131,6 +142,7 @@ endfunction()
131142
# OUTPUT objfile # Name of the resulting object file
132143
# SOURCES swift_src [swift_src...] # Swift source files to compile
133144
# FLAGS -module-name foo # Flags to add to the compilation
145+
# MACCATALYST_BUILD_FLAVOR flavor # macCatalyst flavor
134146
# [SDK sdk] # SDK to build for
135147
# [ARCHITECTURE architecture] # Architecture to build for
136148
# [DEPENDS cmake_target...] # CMake targets on which the object
@@ -151,7 +163,7 @@ function(_compile_swift_files
151163
dependency_sibgen_target_out_var_name)
152164
cmake_parse_arguments(SWIFTFILE
153165
"IS_MAIN;IS_STDLIB;IS_STDLIB_CORE;IS_SDK_OVERLAY;EMBED_BITCODE"
154-
"OUTPUT;MODULE_NAME;INSTALL_IN_COMPONENT"
166+
"OUTPUT;MODULE_NAME;INSTALL_IN_COMPONENT;MACCATALYST_BUILD_FLAVOR"
155167
"SOURCES;FLAGS;DEPENDS;SDK;ARCHITECTURE;OPT_FLAGS;MODULE_DIR"
156168
${ARGN})
157169

@@ -175,6 +187,27 @@ function(_compile_swift_files
175187
precondition(SWIFTFILE_ARCHITECTURE MESSAGE "Should specify an architecture")
176188
precondition(SWIFTFILE_INSTALL_IN_COMPONENT MESSAGE "INSTALL_IN_COMPONENT is required")
177189

190+
# Determine if/what macCatalyst build variant we are
191+
get_maccatalyst_build_flavor(maccatalyst_build_flavor
192+
"${SWIFTFILE_SDK}" "${SWIFTFILE_MACCATALYST_BUILD_FLAVOR}")
193+
194+
# Determine target triples
195+
get_target_triple(target_triple ignored_target_variant_triple
196+
"${SWIFTFILE_SDK}"
197+
"${SWIFTFILE_ARCHITECTURE}"
198+
DEPLOYMENT_VERSION "${SWIFT_SDK_${SWIFTFILE_SDK}_DEPLOYMENT_VERSION}")
199+
200+
get_target_triple(maccatalyst_target_triple ignored_target_variant_triple
201+
"${SWIFTFILE_SDK}"
202+
"${SWIFTFILE_ARCHITECTURE}"
203+
DEPLOYMENT_VERSION "${SWIFT_SDK_${SWIFTFILE_SDK}_DEPLOYMENT_VERSION}"
204+
MACCATALYST_BUILD_FLAVOR "${maccatalyst_build_flavor}")
205+
206+
# macCatalyst ios-like target triple
207+
if(maccatalyst_build_flavor STREQUAL "ios-like")
208+
set(target_triple "${maccatalyst_target_triple}")
209+
endif()
210+
178211
if ("${SWIFTFILE_MODULE_NAME}" STREQUAL "")
179212
get_filename_component(SWIFTFILE_MODULE_NAME "${first_output}" NAME_WE)
180213
message(SEND_ERROR
@@ -202,17 +235,18 @@ function(_compile_swift_files
202235
"${SWIFTFILE_ARCHITECTURE}"
203236
"${SWIFT_STDLIB_BUILD_TYPE}"
204237
"${SWIFT_STDLIB_ASSERTIONS}"
205-
swift_flags)
238+
swift_flags
239+
MACCATALYST_BUILD_FLAVOR "${maccatalyst_build_flavor}"
240+
)
206241

207242
# Determine the subdirectory where the binary should be placed.
208243
compute_library_subdir(library_subdir
209244
"${SWIFTFILE_SDK}" "${SWIFTFILE_ARCHITECTURE}")
210245

211-
# Allow import of other Swift modules we just built.
212-
list(APPEND swift_flags
213-
"-I" "${SWIFTLIB_DIR}/${library_subdir}")
214-
# FIXME: should we use '-resource-dir' here? Seems like it has no advantage
215-
# over '-I' in this case.
246+
if(maccatalyst_build_flavor STREQUAL "ios-like")
247+
compute_library_subdir(library_subdir
248+
"MACCATALYST" "${SWIFTFILE_ARCHITECTURE}")
249+
endif()
216250

217251
# If we have a custom module cache path, use it.
218252
if (SWIFT_MODULE_CACHE_PATH)
@@ -308,7 +342,8 @@ function(_compile_swift_files
308342
list(APPEND swift_flags "-parse-as-library")
309343

310344
set(module_base "${module_dir}/${SWIFTFILE_MODULE_NAME}")
311-
if(SWIFTFILE_SDK IN_LIST SWIFT_APPLE_PLATFORMS)
345+
if(SWIFTFILE_SDK IN_LIST SWIFT_APPLE_PLATFORMS OR
346+
SWIFTFILE_SDK STREQUAL "MACCATALYST")
312347
set(specific_module_dir "${module_base}.swiftmodule")
313348
set(specific_module_project_dir "${specific_module_dir}/Project")
314349
set(source_info_file "${specific_module_project_dir}/${SWIFTFILE_ARCHITECTURE}.swiftsourceinfo")
@@ -338,6 +373,57 @@ function(_compile_swift_files
338373
"-Xfrontend" "-experimental-skip-non-inlinable-function-bodies")
339374
endif()
340375

376+
set(module_outputs "${module_file}" "${module_doc_file}")
377+
378+
if(interface_file)
379+
list(APPEND module_outputs "${interface_file}")
380+
endif()
381+
382+
set(optional_arg)
383+
if(SWIFTFILE_SDK IN_LIST SWIFT_APPLE_PLATFORMS OR
384+
SWIFTFILE_SDK STREQUAL "MACCATALYST")
385+
# Allow installation of stdlib without building all variants on Darwin.
386+
set(optional_arg "OPTIONAL")
387+
endif()
388+
389+
if(SWIFTFILE_SDK IN_LIST SWIFT_APPLE_PLATFORMS OR
390+
SWIFTFILE_SDK STREQUAL "MACCATALYST")
391+
swift_install_in_component(DIRECTORY "${specific_module_dir}"
392+
DESTINATION "lib${LLVM_LIBDIR_SUFFIX}/swift/${library_subdir}"
393+
COMPONENT "${SWIFTFILE_INSTALL_IN_COMPONENT}")
394+
else()
395+
swift_install_in_component(FILES ${module_outputs}
396+
DESTINATION "lib${LLVM_LIBDIR_SUFFIX}/swift/${library_subdir}"
397+
COMPONENT "${SWIFTFILE_INSTALL_IN_COMPONENT}")
398+
endif()
399+
400+
# macCatalyst zippered module setup
401+
if(maccatalyst_build_flavor STREQUAL "zippered")
402+
compute_library_subdir(maccatalyst_library_subdir
403+
"MACCATALYST" "${SWIFTFILE_ARCHITECTURE}")
404+
405+
if(SWIFTFILE_MODULE_DIR)
406+
set(maccatalyst_module_dir "${SWIFTFILE_MODULE_DIR}")
407+
elseif(SWIFTFILE_IS_STDLIB)
408+
set(maccatalyst_module_dir "${SWIFTLIB_DIR}/${maccatalyst_library_subdir}")
409+
else()
410+
message(FATAL_ERROR "Don't know where to put the module files")
411+
endif()
412+
413+
set(maccatalyst_specific_module_dir
414+
"${maccatalyst_module_dir}/${SWIFTFILE_MODULE_NAME}.swiftmodule")
415+
set(maccatalyst_module_base "${maccatalyst_specific_module_dir}/${SWIFTFILE_ARCHITECTURE}")
416+
set(maccatalyst_module_file "${maccatalyst_module_base}.swiftmodule")
417+
set(maccatalyst_module_doc_file "${maccatalyst_module_base}.swiftdoc")
418+
419+
set(maccatalyst_module_outputs "${maccatalyst_module_file}" "${maccatalyst_module_doc_file}")
420+
421+
swift_install_in_component(DIRECTORY ${maccatalyst_specific_module_dir}
422+
DESTINATION "lib${LLVM_LIBDIR_SUFFIX}/swift/${maccatalyst_library_subdir}"
423+
COMPONENT "${SWIFTFILE_INSTALL_IN_COMPONENT}"
424+
"${optional_arg}")
425+
endif()
426+
341427
# If we have extra regexp flags, check if we match any of the regexps. If so
342428
# add the relevant flags to our swift_flags.
343429
if (SWIFT_EXPERIMENTAL_EXTRA_REGEXP_FLAGS OR SWIFT_EXPERIMENTAL_EXTRA_NEGATIVE_REGEXP_FLAGS)
@@ -401,6 +487,26 @@ function(_compile_swift_files
401487
set(sibopt_outputs "${sibopt_file}")
402488
set(sibgen_outputs "${sibgen_file}")
403489

490+
# macCatalyst zippered swiftmodule
491+
if(maccatalyst_build_flavor STREQUAL "zippered")
492+
set(maccatalyst_swift_flags "${swift_flags}")
493+
list(APPEND maccatalyst_swift_flags
494+
"-I" "${SWIFTLIB_DIR}/${maccatalyst_library_subdir}")
495+
set(maccatalyst_swift_module_flags ${swift_module_flags})
496+
elseif(maccatalyst_build_flavor STREQUAL "ios-like")
497+
compute_library_subdir(maccatalyst_library_subdir
498+
"MACCATALYST" "${SWIFTFILE_ARCHITECTURE}")
499+
list(APPEND swift_flags
500+
"-I" "${SWIFTLIB_DIR}/${maccatalyst_library_subdir}")
501+
else()
502+
# Allow import of other Swift modules we just built.
503+
list(APPEND swift_flags
504+
"-I" "${SWIFTLIB_DIR}/${library_subdir}")
505+
506+
# FIXME: should we use '-resource-dir' here? Seems like it has no advantage
507+
# over '-I' in this case.
508+
endif()
509+
404510
if(XCODE)
405511
# HACK: work around an issue with CMake Xcode generator and the Swift
406512
# driver.
@@ -424,6 +530,12 @@ function(_compile_swift_files
424530
COMMAND "${CMAKE_COMMAND}" -E touch ${sibopt_outputs})
425531
set(command_touch_sibgen_outputs
426532
COMMAND "${CMAKE_COMMAND}" -E touch ${sibgen_outputs})
533+
534+
# macCatalyst zippered outputs
535+
if(maccatalyst_build_flavor STREQUAL "zippered")
536+
set(command_touch_maccatalyst_module_outputs
537+
COMMAND "${CMAKE_COMMAND}" -E touch ${maccatalyst_module_outputs})
538+
endif()
427539
endif()
428540

429541
# First generate the obj dirs
@@ -516,6 +628,49 @@ function(_compile_swift_files
516628
COMMENT "Generating ${module_file}")
517629
set("${dependency_module_target_out_var_name}" "${module_dependency_target}" PARENT_SCOPE)
518630

631+
# macCatalyst zippered swiftmodule
632+
if(maccatalyst_build_flavor STREQUAL "zippered")
633+
get_target_triple(ios_like_target_triple ignored_target_variant
634+
"${SWIFTFILE_SDK}"
635+
"${SWIFTFILE_ARCHITECTURE}"
636+
MACCATALYST_BUILD_FLAVOR "ios-like")
637+
638+
# Remove previous -target <triple> and -target-variant flags from
639+
# the zippered Swift flags and add an ios-like target.
640+
remove_given_flag(maccatalyst_swift_flags "target")
641+
remove_given_flag(maccatalyst_swift_flags "target-variant")
642+
list(APPEND maccatalyst_swift_flags
643+
"-target" "${ios_like_target_triple}")
644+
645+
add_custom_command_target(
646+
maccatalyst_module_dependency_target
647+
COMMAND
648+
"${CMAKE_COMMAND}" "-E" "remove" "-f" ${maccatalyst_module_outputs}
649+
COMMAND
650+
"${CMAKE_COMMAND}" "-E" "make_directory" ${maccatalyst_specific_module_dir}
651+
COMMAND
652+
"${PYTHON_EXECUTABLE}" "${line_directive_tool}" "@${file_path}" --
653+
"${swift_compiler_tool}" "-emit-module" "-o" "${maccatalyst_module_file}"
654+
${maccatalyst_swift_flags} ${maccatalyst_swift_module_flags} "@${file_path}"
655+
${command_touch_maccatalyst_module_outputs}
656+
OUTPUT
657+
${maccatalyst_module_outputs}
658+
DEPENDS
659+
${swift_compiler_tool_dep}
660+
${source_files}
661+
${SWIFTFILE_DEPENDS}
662+
${swift_ide_test_dependency}
663+
${obj_dirs_dependency_target}
664+
COMMENT
665+
"Generating ${maccatalyst_module_file}")
666+
667+
# Piggy-back on the same out-var as the regular swiftmodule
668+
set("${dependency_module_target_out_var_name}"
669+
"${module_dependency_target}"
670+
"${maccatalyst_module_dependency_target}"
671+
PARENT_SCOPE)
672+
endif()
673+
519674
# This is the target to generate the .sib files. It is not built by default.
520675
add_custom_command_target(
521676
sib_dependency_target

0 commit comments

Comments
 (0)