Bug 1803775 - Update macOS supported SDKs documentation. r=spohl DONTBUILD
[gecko.git] / widget / cocoa / docs / sdks.md
blob9cebcb9683c5a83a01215c20e61921a7b35418f4
1 # A primer on macOS SDKs\r
2 \r
3 ## Overview\r
4 \r
5 A macOS SDK is an on-disk directory that contains header files and meta information for macOS APIs.\r
6 Apple distributes SDKs as part of the Xcode app bundle. Each Xcode version comes with one macOS SDK,\r
7 the SDK for the most recent released version of macOS at the time of the Xcode release.\r
8 The SDK is located at `/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk`.\r
9 \r
10 Compiling Firefox for macOS requires a macOS SDK. The build system uses the SDK from Xcode.app by\r
11 default, and you can select a different SDK using the `mozconfig` option `--with-macos-sdk`:\r
13 ```text\r
14 ac_add_options --with-macos-sdk=/Users/username/SDKs/MacOSX11.3.sdk\r
15 ```\r
17 ## Supported SDKs\r
19 First off, Firefox runs on 10.12 and above. This is called the "minimum deployment target" and is\r
20 independent of the SDK version.\r
22 Our official Firefox builds compiled in CI (continuous integration) currently use the 11.3 SDK (last updated in [bug 1788854](https://bugzilla.mozilla.org/show_bug.cgi?id=1788854)). This is also the minimum supported SDK version for local builds.\r
24 Compiling with different SDKs breaks from time to time. Such breakages should be [reported in Bugzilla](https://bugzilla.mozilla.org/enter_bug.cgi?blocked=mach-busted&bug_type=defect&cc=:spohl,:mstange&component=General&form_name=enter_bug&keywords=regression&op_sys=macOS&product=Firefox%20Build%20System&rep_platform=All) and fixed quickly.\r
26 ## Obtaining SDKs\r
28 Sometimes you need an SDK that's different from the one in your Xcode.app, for example\r
29 to check whether your code change breaks building with other SDKs, or to verify the\r
30 runtime behavior with the SDK used for CI builds.\r
32 The easy but slightly questionable way to obtain an SDK is to download it from a public github repo.\r
34 Here's another option:\r
36  1. Have your Apple ID login details ready, and bring enough time and patience for a 5GB download.\r
37  2. Check [these tables in the Xcode wikipedia article](https://en.wikipedia.org/wiki/Xcode#Xcode_7.0_-_10.x_(since_Free_On-Device_Development))\r
38     and find an Xcode version that contains the SDK you need.\r
39  3. Look up the Xcode version number on [xcodereleases.com](https://xcodereleases.com/) and click the Download link for it.\r
40  4. Log in with your Apple ID. Then the download should start.\r
41  5. Wait for the 5GB Xcode_*.xip download to finish.\r
42  6. Open the downloaded xip file. This will extract the Xcode.app bundle.\r
43  7. Inside the app bundle, the SDK is at `Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk`.\r
45 ## Effects of the SDK version\r
47 An SDK only contains declarations of APIs. It does not contain the implementations for these APIs.\r
49 The implementation of an API is provided by the OS that the app runs on. It is supplied at runtime,\r
50 when your app starts up, by the dynamic linker. For example, the AppKit implementation comes\r
51 from `/System/Library/Frameworks/AppKit.framework` from the OS that the app is run on, regardless\r
52 of what SDK was used when compiling the app.\r
54 In other words, building with a macOS SDK of a higher version doesn't magically make new APIs available\r
55 when running on older versions of macOS. And, conversely, building with a lower macOS SDK doesn't limit\r
56 which APIs you can use if your app is run on a newer version of macOS, assuming you manage to convince the\r
57 compiler to accept your code.\r
59 The SDK used for building an app determines three things:\r
61  1. Whether your code compiles at all,\r
62  2. which range of macOS versions your app can run on (available deployment targets), and\r
63  3. certain aspects of runtime behavior.\r
65 The first is straightforward: An SDK contains header files. If you call an API that's not declared\r
66 anywhere - neither in a header file nor in your own code - then your compiler will emit an error.\r
67 (Special case: Calling an unknown Objective-C method usually only emits a warning, not an error.)\r
69 The second aspect, available deployment targets, is usually not worth worrying about:\r
70 SDKs have large ranges of supported macOS deployment targets.\r
71 For example, the 10.15 SDK supports running your app on macOS versions all the way back to 10.6.\r
72 This information is written down in the SDK's `SDKSettings.plist`.\r
74 The third aspect, varying runtime behavior, is perhaps the most insidious and surprising aspect, and is described\r
75 in the next section.\r
77 ## Runtime differences based on macOS SDK version\r
79 When a new version of macOS is released, existing APIs can change their behavior.\r
80 These changes are usually described in the AppKit release notes:\r
82  - [macOS 10.15 release notes](https://developer.apple.com/documentation/macos_release_notes/macos_catalina_10_15_release_notes?language=objc)\r
83  - [macOS 10.14 AppKit release notes](https://developer.apple.com/documentation/macos_release_notes/macos_mojave_10_14_release_notes/appkit_release_notes_for_macos_10_14?language=objc)\r
84  - [macOS 10.13 AppKit release notes](https://developer.apple.com/library/archive/releasenotes/AppKit/RN-AppKit/)\r
85  - [macOS 10.12 and older AppKit release notes](https://developer.apple.com/library/archive/releasenotes/AppKit/RN-AppKitOlderNotes/)\r
87 Sometimes, these differences in behavior have the potential to break existing apps. In those instances,\r
88 Apple often provides the old (compatible) behavior until the app is re-built with the new SDK, expecting\r
89 developers to update their apps so that they work with the new behavior, at the same time as\r
90 they update to the new SDK.\r
92 Here's an [example from the 10.13 release notes](https://developer.apple.com/library/archive/releasenotes/AppKit/RN-AppKit/#10_13NSCollectionView%20Responsive%20Scrolling):\r
94 > Responsive Scrolling in NSCollectionViews is enabled only for apps linked on or after macOS 10.13.\r
96 Here, "linked on or after macOS 10.13" means "linked against the macOS 10.13 SDK or newer".\r
98 Apple's expectation is that you upgrade to the new macOS version when it is released, download a new\r
99 Xcode version when it is released, synchronize these updates across the machines of all developers\r
100 that work on your app, use the SDK in the newest Xcode to compile your app, and make changes to your\r
101 app to be compatible with any behavior changes whenever you update Xcode.\r
102 This expectation does not always match reality. It definitely doesn't match what we're doing with Firefox.\r
104 For Firefox, SDK-dependent compatibility behaviors mean that developers who build Firefox locally\r
105 can see different runtime behavior than the users of our CI builds, if they use a different SDK than\r
106 the SDK used in CI.\r
107 That is, unless we change the Firefox code so that it has the same behavior regardless of SDK version.\r
108 Often this can be achieved by using APIs in a way that's more in line with the API's recommended use.\r
110 For example, we've had cases of\r
111 [broken placeholder text in search fields](https://bugzilla.mozilla.org/show_bug.cgi?id=1273106),\r
112 [missing](https://bugzilla.mozilla.org/show_bug.cgi?id=941325) or [double-drawn focus rings](https://searchfox.org/mozilla-central/rev/9ad88f80aeedcd3cd7d7f63be07f577861727054/widget/cocoa/nsNativeThemeCocoa.mm#149-169),\r
113 [a startup crash](https://bugzilla.mozilla.org/show_bug.cgi?id=1516437),\r
114 [fully black windows](https://bugzilla.mozilla.org/show_bug.cgi?id=1494022),\r
115 [fully gray windows](https://bugzilla.mozilla.org/show_bug.cgi?id=1576113#c4),\r
116 [broken vibrancy](https://bugzilla.mozilla.org/show_bug.cgi?id=1475694), and\r
117 [broken colors in dark mode](https://bugzilla.mozilla.org/show_bug.cgi?id=1578917).\r
119 In most of these cases, the breakage was either very minor, or it was caused by Firefox doing things\r
120 that were explicitly discouraged, like creating unexpected NSView hierarchies, or relying on unspecified\r
121 implementation details. (With one exception: In 10.14, HiDPI-aware `NSOpenGLContext` rendering in\r
122 layer-backed windows simply broke.)\r
124 And in all of these cases, it was the SDK-dependent compatibility behavior that protected our users from being\r
125 exposed to the breakage. Our CI builds continued to work because they were built with an older SDK.\r
127 We have addressed all known cases of breakage when building Firefox with newer SDKs.\r
128 I am not aware of any current instances of this problem as of this writing (June 2020).\r
130 For more information about how these compatibility tricks work,\r
131 read the [Overriding SDK-dependent runtime behavior](#overriding-sdk-dependent-runtime-behavior) section.\r
133 ## Supporting multiple SDKs\r
135 As described under [Supported SDKs](#supported-sdks), Firefox can be built with a wide variety of SDK versions.\r
137 This ability comes at the cost of some manual labor; it requires some well-placed `#ifdefs` and\r
138 copying of header definitions.\r
140 Every SDK defines the macro `MAC_OS_X_VERSION_MAX_ALLOWED` with a value that matches the SDK version,\r
141 in the SDK's `AvailabilityMacros.h` header. This header also defines version constants like `MAC_OS_X_VERSION_10_12`.\r
142 For example, I have a version of the 10.12 SDK which contains the line\r
144 ```cpp\r
145 #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_12_4\r
146 ```\r
148 The name `MAC_OS_X_VERSION_MAX_ALLOWED` is rather misleading; a better name would be\r
149 `MAC_OS_X_VERSION_MAX_KNOWN_BY_SDK`. Compiling with an old SDK *does not* prevent apps from running\r
150 on newer versions of macOS.\r
152 With the help of the `MAC_OS_X_VERSION_MAX_ALLOWED` macro, we can make our code adapt to the SDK that's\r
153 being used. Here's [an example](https://searchfox.org/mozilla-central/rev/9ad88f80aeedcd3cd7d7f63be07f577861727054/toolkit/xre/MacApplicationDelegate.mm#345-351) where the 10.14 SDK changed the signature of\r
154 [an `NSApplicationDelegate` method](https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428471-application?language=objc):\r
156 ```objc++\r
157 - (BOOL)application:(NSApplication*)application\r
158     continueUserActivity:(NSUserActivity*)userActivity\r
159 #if defined(MAC_OS_X_VERSION_10_14) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14\r
160       restorationHandler:(void (^)(NSArray<id<NSUserActivityRestoring>>*))restorationHandler {\r
161 #else\r
162       restorationHandler:(void (^)(NSArray*))restorationHandler {\r
163 #endif\r
164   ...\r
166 ```\r
168 We can also use this macro to supply missing API definitions in such a way that\r
169 they don't conflict with the definitions from the SDK.\r
170 This is described in the "Using macOS APIs" document, under [Using new APIs with old SDKs](./macos-apis.html#using-new-apis-with-old-sdks).\r
172 ## Overriding SDK-dependent runtime behavior\r
174 This section contains some more details on the compatibility tricks that cause different runtime\r
175 behavior dependent on the SDK, as described in\r
176 [Runtime differences based on macOS SDK version](#runtime-differences-based-on-macos-sdk-version).\r
178 ### How it works\r
180 AppKit is the one system framework I know of that employs these tricks. Let's explore how AppKit makes this work,\r
181 by going back to the [NSCollectionView example](https://developer.apple.com/library/archive/releasenotes/AppKit/RN-AppKit/#10_13NSCollectionView%20Responsive%20Scrolling) from above:\r
183 > Responsive Scrolling in NSCollectionViews is enabled only for apps linked on or after macOS 10.13.\r
185 For each of these SDK-dependent behavior differences, both the old and the new behavior are implemented\r
186 in the version of AppKit that ships with the new macOS version.\r
187 At runtime, AppKit selects one of the behaviors based on the SDK version, with a call to\r
188 `_CFExecutableLinkedOnOrAfter()`. This call checks the SDK version of the main executable of the\r
189 process that's running AppKit code; in our case that's the `firefox` or `plugin-container` executable.\r
190 The SDK version is stored in the mach-o headers of the executable by the linker.\r
192 One interesting design aspect of AppKit's compatibility tricks is the fact that most of these behavior differences\r
193 can be toggled with a "user default" preference.\r
194 For example, the "responsive scrolling in NSCollectionViews" behavior change can be controlled with\r
195 a user default with the name "NSCollectionViewPrefetchingEnabled".\r
196 The SDK check only happens if "NSCollectionViewPrefetchingEnabled" is not set to either YES or NO.\r
198 More precisely, this example works as follows:\r
200  - `-[NSCollectionView prepareContentInRect:]` is the function that supports both the old and the new behavior.\r
201  - It calls `_NSGetBoolAppConfig` for the value "NSCollectionViewPrefetchingEnabled", and also supplies a "default\r
202    value function".\r
203  - If the user default is not set, the default value function is called. This function has the name\r
204    `NSCollectionViewPrefetchingEnabledDefaultValueFunction`.\r
205  - `NSCollectionViewPrefetchingEnabledDefaultValueFunction` calls `_CFExecutableLinkedOnOrAfter(13)`.\r
207 You can find many similar toggles if you list the AppKit symbols that end in `DefaultValueFunction`,\r
208 for example by executing `nm /System/Library/Frameworks/AppKit.framework/AppKit | grep DefaultValueFunction`.\r
210 ### Overriding SDK-dependent runtime behavior\r
212 You can set these preferences programmatically, in a way that `_NSGetBoolAppConfig()` can pick them up,\r
213 for example with [`registerDefaults`](https://developer.apple.com/documentation/foundation/nsuserdefaults/1417065-registerdefaults?language=objc)\r
214 or like this:\r
216 ```objc++\r
217 [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NSViewAllowsRootLayerBacking"];\r
218 ```\r
220 The AppKit release notes mention this ability but ask for it to only be used for debugging purposes:\r
222 > In some cases, we provide defaults (preferences) settings which can be used to get the old or new behavior,\r
223 > independent of what system an application was built against. Often these preferences are provided for\r
224 > debugging purposes only; in some cases the preferences can be used to globally modify the behavior\r
225 > of an application by registering the values (do it somewhere very early, with `-[NSUserDefaults registerDefaults:]`).\r
227 It's interesting that they mention this at all because, as far as I can tell, none of these values are documented.\r