티스토리 뷰
[WWDC18] Behind the Scenes of the Xcode Build Process (1) Build System
Hani_Levenshtein 2022. 2. 15. 04:59안녕하세요 Hani입니다.
이번에는 WWDC18 Behind the Scenes of the Xcode Build Process를 정리해볼 거예요.
목차 🧐
1. Build System
2. Clang Build
3. Swift Build
4. Linker
이 세션에서는 PetWall이라는 앱을 가지고 빌드 과정을 알아볼 겁니당. ☺️
먼저 구현 코드가 들어가있는 소스 파일가 컴파일러에 의해 컴파일되고
컴파일된 코드와 라이브러리가 링커에 의해 링크가 됩니다.
다음엔 리소스들이 복사되고
Code Sign을 하며 shell script나 makefile에서 사용자 정의 작업을 할 수도 있습니다.
빌드 과정에서 이러한 작업들은 Command Line Tool을 동작시켜 수행되는데
Xcode Project Configuration에 기반한 특정 순서에 따라 실행돼야 합니다.
직접 적어서 작업할 필요는 없고 Xcode Build System한테 PetWall 앱 실행시켜 달라하면 알아서 해줍니다. ☺️
Xcode Project Configuration에 기반한 특정 순서..!
빌드 작업 순서는 의존성 정보(Dependency Information)에 따라 결정되는데
의존성 정보는 작업, 작업의 입력, 작업의 출력에 따라 결정됩니다.
컴파일 작업을 예로 들면
구현 파일(.m)을 입력으로 받고, 목적 파일(.o)을 출력으로 내놓는 것이에욥.
링크 작업을 예로 들면
하나 이상의 오브젝트 파일로 실행가능한 파일이나 라이브러리를 내놓는 것이져
여기서 링크 작업을 통해 실행 파일을 만들기 위해 이전 작업들에 의존하는 관계가 형성이 됩니다.
(실행파일을 만들기 위해 링크할 목적파일을 만들기 위해선 컴파일이 선행되어야 하니까)
Build System은 이런 의존성 정보를 통해 어떤 작업을 먼저 실행할지 결정하고, 그 중에 어떤 작업을 병렬로 처리할지 결정하며
이를 의존성 순서(Dependency Order)라고 합니다.
그럼 빌드를 누르면 무슨 일이 일어나는지 함보까
첫 번째 작업은 Build System이 Xcode Project 파일을 가져와서
프로젝트를 구성하는 모든 파일의 의존성 관계를 트리로 나타냅니다.
이 그래프는 프로젝트의 입력 및 출력 파일의 모든 의존성을 나타냅니다.
다음은 저수준 빌드 실행 엔진(Low-level Build Execution Engine)인 llbuild가 의존성 그래프를 처리하여
Dependency Specifications을 보고 어느 작업을 병렬로 실행할지, 작업 순서는 어떻게 할지 파악하고 실행합니다.
예를 들면 Clang이 Objective-C 소스 파일(.m)을 컴파일할 때 예상대로라면 오브젝트 파일(.o)이 생성되지만
의존성 파일(.d) 또한 생성될 수 있습니다.
이 파일은 소스 파일에 의해 포함된 헤더 파일 정보를 가지고 있어요.
다음 번에 빌드하게 되면 빌드 시스템은 의존성 파일의 정보를 보고
소스 파일이 포함하고 있는 헤더 파일이 바뀌었는지 확인하여 소스파이리을 재컴파일해야 하는지 결정합니다.
빌드 시스템의 주요 역할은 작업을 실행하는 거예요.
하지만 프로젝트가 커질수록 빌드 프로세스가 길어지기 때문에 모든 빌드때마다 이 과정을 겪는 것은 .. 🥺
그래서 빌드 시스템은 그래프 상의 작업 중 일부만, 이전 빌드와 비교하여 변경된 사항만 다시 빌드하도록 만듭니다. 👍
이를 증분 빌드(Incremental Build)라고 하며
증분 빌드가 제대로 동작하기 위해 정확한 의존성 정보를 가지고 있는 것이 중요합니다.
그럼 어떻게 빌드 시스템이 의존성 정보의 변화를 추적할까욥?
빌드 그래프에서의 각 작업은 Signature라는 Hash를 가지고 있습니다.
Signature는 파일 경로와 수정 시간같은 작업의 입력에 대한 통계 정보와 컴파일러 버전 등의 작업별 메타 데이터를 포함합니다.
빌드 시스템은 현재 빌드와 이전 빌드의 Signature를 추적하여 빌드가 수행될 때마다 작업을 다시 할지 결정합니다.
현 빌드의 작업 Signature가 이전 빌드의 Signature와 다르면 빌드 시스템은 해당 작업을 다시 실행합니다.
동일하다면 건너뛰게 됩니다. 👍
빌드 프로세스는 특정 순서대로 작업이 실행되는 것이긴 하지만
이렇게 순차적으로 기억하지 말구
빌드는 유향 그래프로 나타낼 수 있음을 기억해야 합니다.
개발자가 작업의 순서에 대하여 기억하기보단 작업의 의존성에 대하여 생각할 필요가 있고
빌드 시스템이 그래프의 구조를 통해 작업을 최적으로 실행할 수 있도록 만들어야 해요.
이렇게 하면 빌드 시스템이 올바르게 작업의 순서를 결정하고
멀티코어 하드웨어의 장점을 최대한 살려 작업을 병렬화 할 수 있습니다.
특정 작업의 경우, 의존성 정보는 빌드 시스템에 built-in된 것들로부터 옵니다.
빌드 시스템은 Compiler, Linker, Asset Catalogue 및 Storyboard Processor에 대한 규칙을 가지고 있는데
이 규칙은 어떤 종류의 파일이 입력으로 허용되고 어떤 출력이 생성되는지 정의합니다.
Project - Target - Build Rules에서 확인할 수 있어요. ☺️
타겟 의존성은 빌드의 순서를 결정합니다.
특정 경우에는 빌드 시스템이 다른 타겟과 병렬 소스를 컴파일 할 수 있는데
이는 컴파일 페이즈에서 추가 비용없이 병렬화를 더 일찍 시작할 수 있음을 의미합니다.
Project - Target - Build Phase
하지만 run script 페이즈를 사용하는 경우
병렬화가 진행되기 전에 script를 완전히 끝내야 합니다.
암시적 의존성은 타겟을 성공적으로 빌드하기 위해 필수적이지만
구체적으로 정의되어 있지 않은 의존성입니다.
(타겟 의존성과 관련이 있습니다.)
예를 들면
Binary Build Phase의 링크 라이브러리에 타겟을 배열하고
(Project - Target - Build Phase - Link Binary With Library)
Scheme 편집기에서 암시적 의존성이 enable 되어있다면
(Xcode - Product - Edit Scheme - Build - Build Options - Find Implicit Dependencies - Enable)
기본적으로 켜져있지만
빌드 시스템은 타겟 의존성에 나열되어 있지 않아도 타겟에 대한 암시적 의존성을 설정합니다.
빌드 페이즈 의존성에는 여러 단계가 존재합니다.
헤더 복사, 소스 코드 컴파일, 번들 리소스 복사 등등..
각 페이즈의 작업은 페이즈가 나열된 순서대로 실행되지만
빌드시스템은 더 좋은 방법을 알고 있다면 이를 무시합니다.
ex)
링크 라이브러리가 있는 경우, Compile Sources 앞에 Link Binary With Library를 먼저 실행
이런 페이즈들의 순서가 잘못되면 빌드가 실패하기 때문에 올바른 순서로 되어있는지 확인할 필요가 있습니당.
(아니 더 좋은 방법을 알고있다메)
마지막으로 스킴 순서 의존성입니다.
Edit Scheme - Build에서 Parallelize Build를 Enable 하면 더 나은 성능을 얻을 수 있습니다.
(Xcode 13부터 Dependency Order 체크박스를 enable하는 것으로 변경됨)
Parallelize Build를 끄면 Xcode는 Scheme의 Build Action에 있는 타겟을 순서대로 실행하게 되는데
타겟 의존성은 빌드 순서를 결정할 때 더 높은 우선순위를 가지고 있습니다.
의존성을 올바르게 설정하지 않아도 예측가능한 빌드 순서를 가질 수 있게되지만
대신 병렬화가 희생되어 빌드 속도가 느려집니다.
따라서 Dependency Order 체크박스를 enable하고
타겟 의존성을 올바르게 설정하여
순서에 의존하지 않도록 하는 편이 좋습니다.
사용자 정의 Shell Script Build Phase나 Build Rule을 만든다면
빌드 시스템이 불필요하게 스크립트 작업을 다시할 필요가 없도록 만들고 올바른 순서대로 수행했는지 판단하기 위해
빌드 시스템에게 입력과 출력이 무엇인지 확실히 알려줘야 합니다.
이 파일들의 경로는 스크립트에서 환경 변수로서 사용할 수 있습니다.
프로젝트에서 타겟 의존성을 위한 auto-link에 의존하지 말아야 합니다.
(Project - Target - Build Settings - Apple Clang - Language - Modules)
이를 enable시키면 링크 라이브러리의 빌드 페이즈에서
명시적으로 링크하지 않고 import한 모듈에 해당하는 프레임워크를
컴파일러가 자동적으로 링크하게 됩니다.
auto-link는 빌드 시스템 수준에서 프레임워크에 대한 의존성을 설정하지 않는다는 것이 중요하며
링크를 시도하기 전에 의존하는 타겟이 실제로 빌드된다는 보장이 없습니다.
따라서 Platform System Tool Kit 의 프레임워크에 대해서만 auto-link에 의존해야 합니다.
Foundation이나 UIKit 같은 경우 빌드가 시작되기 전에 이미 존재한다는 것을 알고있기 때문에 ㄱㅊ.
자체 프로젝트의 타겟에 대하여 명시적 의존성을 추가해야 합니다.
프로젝트의 File Navigator에 다른 프로젝트를 드래그 앤 드롭으로 프로젝트 참조를 생성할 수 있습니다.
수미상관으로 첫 슬라이드로 돌아왔습니당..
정확한 의존성 정보를 이용하면
빌드 시스템이 최대한 병렬화를 하여 빌드 순서를 정하여 빌드 시간을 벌어줄 수 있습니다.
암튼 첫 목차부터 너덜너덜해진 포스팅..
References
https://developer.apple.com/videos/play/wwdc2018/415/?time=263
'WWDC' 카테고리의 다른 글
[WWDC18] iOS Memory Deep Dive 정리 (0) | 2022.02.15 |
---|---|
[WWDC19] LLDB: Beyond "po" (2) | 2022.02.12 |
[WWDC17] Engineering for Testability 정리 (2) Scalable Test Code (0) | 2021.12.21 |
[WWDC17] Engineering for Testability 정리 (1) Testable App Code (0) | 2021.12.21 |
[WWDC16] Understanding Swift Performance 정리 (4) Protocol Types (0) | 2021.12.16 |
- Total
- Today
- Yesterday
- MeTal
- rxswift
- WWDC21
- 포드 풀커슨 알고리즘
- WWDC19
- WWDC17
- 부스트캠프 6기
- CompositionalLayout
- 최단경로문제
- Testable
- 최단경로 알고리즘
- WWDC16
- 코딩대회
- HIG
- 에드몬드 카프 알고리즘
- 벨만포드 알고리즘
- test coverage
- State Restoration
- 다익스트라 시간복잡도
- 최대 매칭
- 네트워크 유량
- IOS
- 벨만포드 시간복잡도
- CPU와 Memory
- 강한 순환 참조
- mach-o
- 네트워크 플로우
- 최단경로 문제
- 컴퓨터 추상화
- observeOn
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |