|
1 | 1 | ## Swift Driver Integration |
2 | 2 |
|
3 | | -### Pre Swift Driver Integration |
| 3 | +### Pre Swift Driver Integration |
4 | 4 |
|
5 | 5 | Historically (prior to Xcode 14), Swift compilation step was invoked by Xcode as a single external process. Xcode was calling `swiftc` and passing all required parameters (like all input files, output destinations, header paths etc.), and reading its standard output to recognize the status/state of a compilation. Essentially, there were two build systems: "the big one" from Xcode and "small one" by Swift. |
6 | 6 |
|
7 | 7 | That design was easy to mock in the XCRemoteCache, where the `xcswiftc` wrapper was first inspecting if the cached artifact can be reused (e.g. no new input `.swift` files were added to the list of compilation files) and based on that either continuing with the local compilation (cache miss) or mocking the compilation and existing early (cache hit). |
8 | 8 |
|
9 | | - |
10 | | - |
| 9 | +<p> |
| 10 | + <img src="docs/img/pre-driver.png#gh-light-mode-only"> |
| 11 | + <img src="docs/img/pre-driver-dark.png#gh-dark-mode-only"> |
| 12 | +</p> |
11 | 13 |
|
12 | 14 | ### Swift Driver Integration Design |
13 | 15 |
|
14 | 16 | With the upgraded design (aka Swift Driver Integration), Xcode splits the work into `n` subprocesses (when `n` is ~CPU), each responsible to compile a subset of files/actions. To align with that, XCRemoteCache meeds to specify a single place to identify if the cached artifact is applicable. `swift-frontend` has been picked for that - process responsible for module emitting. By reviewing Xcode's behavior, it has been found that this process is scheduled very early in the workflow timeline (with some approximation, we could say it is scheduled as a first step) so it seems as best candidate for the "pre-work". |
15 | 17 |
|
16 | | -As the same executable `swift-frontend` is invoked multiple times for the same target (e.g. to emit module, multiple batches of compilation etc.), XCRemoteCaches uses a file lock-based synchronization. Each `xcswift-frontend` (the wrapper for `swift-frontend`) tries to acquire a unique lock file. The lock has a name `$LLBUILD_BUILD_ID.lock`, which is unique for each build, placed in the `Intermediate` directory. `xcswift-frontend` process reads its content to find if the "pre-work" from the emit-module has already been done - if not, it releases a lock a gives a way to other processes (presumably the "emit-module") to do the required work. As a lock file is unique per target and a build (it is actually unique per target compilation, placed in `TARGET_TEMP_DIR`), initially the file is empty. |
| 18 | +As the same executable `swift-frontend` is invoked multiple times for the same target (e.g. to emit module, multiple batches of compilation etc.), XCRemoteCaches uses a file lock-based synchronization. Each `xcswift-frontend` (the wrapper for `swift-frontend`) tries to acquire a unique lock file. The lock has a name `$LLBUILD_BUILD_ID.lock`, which is unique for each build, placed in the `Intermediate` directory. `xcswift-frontend` process reads its content to find if the "pre-work" from the emit-module has already been done - if not, it releases a lock a gives a way to other processes (presumably the "emit-module") to do the required work. As a lock file is unique per target and a build (it is actually unique per target compilation, placed in `TARGET_TEMP_DIR`), initially the file is empty. |
17 | 19 |
|
18 | 20 | Note the emit module step holds a shared lock for the time of the entire process lifetime, so only once the "pre-work" is finished, all other `xcswift-frontend` processes can continue their job (with either noop or fallbacking to the `swift-frontend` in case a cache miss). Non emit-module steps (compilation steps) acquire a lock only for a very short period - to read the content of that file, thus multiple batches of compilation can run in parallel. |
19 | 21 |
|
20 | | - |
| 22 | +<p> |
| 23 | + <img src="docs/img/driver.png#gh-light-mode-only"> |
| 24 | + <img src="docs/img/driver-dark.png#gh-dark-mode-only"> |
| 25 | +</p> |
21 | 26 |
|
22 | 27 | <img src="./../img/sample-driver-timeline.png" width="600px"> |
23 | 28 |
|
24 | 29 | ### Sample timelines |
25 | 30 |
|
26 | | -#### Emit Module acquires a lock first (common) |
| 31 | +#### Emit Module acquires a lock first (common) |
27 | 32 |
|
28 | | - |
| 33 | +<p> |
| 34 | + <img src="docs/img/driver-scenario1.png#gh-light-mode-only"> |
| 35 | + <img src="docs/img/driver-scenario1-dark.png#gh-dark-mode-only"> |
| 36 | +</p> |
29 | 37 |
|
30 | | -#### A compilation step acquires a lock first (uncommon but possible) |
| 38 | +#### A compilation step acquires a lock first (uncommon but possible) |
31 | 39 |
|
32 | | - |
| 40 | +<p> |
| 41 | + <img src="docs/img/driver-scenario2.png#gh-light-mode-only"> |
| 42 | + <img src="docs/img/driver-scenario2-dark.png#gh-dark-mode-only"> |
| 43 | +</p> |
33 | 44 |
|
34 | 45 | ### Other considerations/open questions |
35 | 46 |
|
|
0 commit comments