Xcode build process
[LLVM], Clang LLVM, Swift LLVM
Xcode uses xcodebuild
internally[Example]
Objective-C language
1. Preprocessing
[Driver][Preprocessing][Parsing and Semantic Analysis] Parser `.m -> AST`
2.Compiling by compiler(Clang)
[Code Generation and Optimization](GCC_OPTIMIZATION_LEVEL) LLVM IR Generation `AST -> LLVM IR`.
3. Assembling
[LLVM backend] LLVM `LLVM IR -> .o`
4. Static Linking(ld)
5. Output binary
Xcode and Objective-C exposes some of these steps:
Preprocessing
:
Replace macros
Splits .h
and .m
.
In Xcode, you can look at the preprocessor output of .m
file by selecting
select .m file -> Product -> Perform Action -> Preprocess
Compiling
- translates a low-level intermediate code.
Often you can see this file when debug a code that you are not owned. Xcode allows you to review the output.
select .m file -> Product -> Perform Action -> Assemble
Assembling
(produce .o
) - translates code into object file (.o
file)[Mach-O] In Xcode, you’ll find these object files inside the <product_name>.build/Objects-normal
folder inside the derived data directory.
Static Linking
(produce .app
, .a
, .framework
...)[About] - It is a part of static linker that has to resolve symbols between object files and libraries/frameworks. This process produce a merged executable file which can contain additional resources and dynamic binary
Output binary
Swift language
LVVM Frontend
1. Preprocessing
[Parse module] Parser `.m -> AST`
[Sema module] Type checking > type-checks AST and annotates it with type information
2.Compiling by compiler(Swiftc)
[SILGen module] SIL Generator `AST -> raw SIL` > optimizations
`Guaranteed Optimization Passes`, `Diagnostic Passes` `raw SIL -> canonical SIL. This optimization is applied in any case
`Optimization Passes`(SWIFT_OPTIMIZATION_LEVEL)
[IRGen] LLVM IR Generation
LVVM Backend
3. Assembling
[LLVM backend] LLVM `LLVM IR -> .o`
4. Static Linking(ld)
5. Output binary
Swift Example
import Foundation
class ClassA {
func foo(param: String) -> Int {
return 1
}
}
AST
-dump-parse Parse input file(s) and dump AST(s)
-dump-ast Parse and type-check input file(s) and dump AST(s)
-print-ast Parse and type-check input file(s) and pretty print AST(s)
# xcrun swiftc -dump-parse "ClassA.swift"
(source_file "ClassA.swift"
(import_decl range=[ClassA.swift:8:1 - line:8:8] 'Foundation')
(class_decl range=[ClassA.swift:10:1 - line:14:1] "ClassA"
(func_decl range=[ClassA.swift:11:5 - line:13:5] "foo(param:)"
(parameter "self")
(parameter_list range=[ClassA.swift:11:13 - line:11:27]
...
#xcrun swiftc -dump-ast "ClassA.swift"
(source_file "ClassA.swift"
(import_decl range=[ClassA.swift:8:1 - line:8:8] 'Foundation')
(class_decl range=[ClassA.swift:10:1 - line:14:1] "ClassA" interface type='ClassA.Type' access=internal non-resilient
(func_decl range=[ClassA.swift:11:5 - line:13:5] "foo(param:)" interface type='(ClassA) -> (String) -> Int' access=internal
(parameter "self")
...
# xcrun swiftc -print-ast "ClassA.swift"
import Foundation
internal class ClassA {
internal func foo(param: String) -> Int
@objc deinit
internal init()
}
#xcrun swiftc -frontend -emit-syntax "ClassA.swift" | python -m json.tool
{
"kind": "SourceFile",
"layout": [
{
"kind": "CodeBlockItemList",
"layout": [
{
"kind": "CodeBlockItem",
"layout": [
{
"kind": "ImportDecl",
"layout": [
{
...
SIL
-emit-silgen Emit raw SIL file(s)
-emit-sil Emit canonical SIL file(s)
# xcrun swiftc -emit-silgen "ClassA.swift"
sil_stage raw
import Builtin
import Swift
import SwiftShims
import Foundation
class ClassA {
func foo(param: String) -> Int
@objc deinit
init()
}
// ClassA.foo(param:)
sil hidden [ossa] @$s6ClassAAAC3foo5paramSiSS_tF : $@convention(method) (@guaranteed String, @guaranteed ClassA) -> Int {
// %0 "param" // user: %2
sil_vtable ClassA {
#ClassA.foo: (ClassA) -> (String) -> Int : @$s6ClassAAAC3foo5paramSiSS_tF // ClassA.foo(param:)
#ClassA.init!allocator: (ClassA.Type) -> () -> ClassA : @$s6ClassAAACABycfC // ClassA.__allocating_init()
#ClassA.deinit!deallocator: @$s6ClassAAACfD // ClassA.__deallocating_deinit
}
// Mappings from '#fileID' to '#filePath':
// 'ClassA/ClassA.swift' => 'ClassA.swift'
...
LLVM IR
-emit-ir Emit LLVM IR file(s)
xcrun swiftc -emit-ir "ClassA.swift"
; ModuleID = '<swift-imported-modules>'
source_filename = "<swift-imported-modules>"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx12.0.0"
%objc_class = type { %objc_class*, %objc_class*, %swift.opaque*, %swift.opaque*, %swift.opaque* }
define i32 @main(i32 %0, i8** %1) #0 {
entry:
%2 = bitcast i8** %1 to i8*
ret i32 0
}
...
-emit-object Emit object file(s) (-c)
-emit-executable Emit a linked executable
[Build With Timing Summary]
Also you can use Xcode Report Navigator
to learn more about build process. Moreover Xcode v14
includes a great feature to visualisation and analyzation of build process
Show the Report navigator -> <select build> -> <right_click> -> Show in Timeline
//or
Show the Report navigator -> <select build> -> Editor -> Open Timeline