본문 바로가기
WWDC/2024

Demystify explicitly built modules (추천세션!)

by 토끼찌짐 2024. 8. 8.

https://developer.apple.com/videos/play/wwdc2024/10171/

 

Demystify explicitly built modules - WWDC24 - Videos - Apple Developer

Explore how builds are changing in Xcode 16 with explicitly built modules. Discover how modules are used to build your code, how...

developer.apple.com

해당 포스트는 위 영상을 기반으로 개인적으로 메모/정리한 글입니다.

개요

Xcode 16 에서 도입되는 explicitly built modules 라는 새로운 빌드 방법을 소개합니다. Swift 와 Clang 모듈의 빌드에 어떤 변화가 생겼는지 알아보세요.


What are modules?

Modules are units of code distribution describing the interface of a library or framework. (모듈은 라이브러리/프레임워크의 인터페이스를 표현하는 코드 배포 묶음입니다)

Swift 모듈(Swift Code)과 Clang 모듈(Objective-C)를 import 할 수 있다.

<1> Swift

Swift 타겟은 많은 Swift 파일을 포함하고 있으며, 이것들이 결국 하나의 모듈로 표현된다.

모듈은 다른 모듈을 import 할 수 있는데, 이 때문에 전체 프로젝트에 대한 비순환적 모듈 그래프가 형성된다.

Swift compiler 는 외부 인터페이스만 가져와서 이것을 `.swiftinterface` 텍스트 파일로 요약한다.

(ex) 외부에 공개되는 public class, public var 등만 명시되고 구현은 숨겨짐

<2> Objective-C

C 언어 계열에서는 모듈의 인터페이스가 직접 작성된다(hand-authored)

(좌) Header 예시 (우) Module map - UIKit module 예시

Module map: 헤더가 모듈을 구성하는 방법을 컴파일러에게 설명하는 파일.

위 예시로 알아보면: 이 모듈은 framework 이며 이름은 ‘UIKit’이다. “UIKit.h” 에 이 모듈에 포함된 모든 헤더가 포함되어 있으며, “import UIKit” 코드를 통해 이 모듈을 사용할 수 있다.

Modules share the parsing of interfaces between source files.

컴파일러는 각 모듈을 바이너리 파일로 분리해 컴파일하고, 각 모듈이 참조될 때마다 해당 모듈의 Public interface 를 import 한다. 그렇기 때문에 소스 파일들 사이에서 모듈을 통해 인터페이스들의 파싱(parsing)이 공유될 수 있다.

컴파일 되는 형식

- Swift: `*.swiftmodule`
- Clang(Objective-C): `*.pcm` 또는 precompiled module file

 

Using modules

컴파일러가 모듈을 처리하는 원리를 알아보자. 기본적으로 import 구문을 만나면, 컴파일러는 해당 모듈을 찾아서 컴파일 해야한다. 어떤 식으로 이루어질까?

예시를 들어보자.

1. 컴파일러가 `@import UIKit` 구문과 만난다
2. 컴파일러는 UIKit의 Module map 을 SDK에서 찾는다.
3. 컴파일러는 UIKit의 .pcm 파일(컴파일 된 파일)을 찾아본다. 만약 이 때 존재하지 않는다면? 그때가 첫 번째 이 모듈을 빌드할 첫 번째 시점이다. 모듈을 빌드할 방법으로는 2가지가 있다. Implicitly built modules & explicitly built modules. 이 중 후자가 Xcode 16에서 도입된 방법이고 이번 세션에서 다루는 주제다.

 

먼저 기존의 Implicitly built modules 에 대해 알아보자.

Swift & Clang 모듈을 빌드하는 기존 방식.

하나의 컴파일 Task에서 특정 모듈을 빌드할 동안, 그 모듈이 필요한 다른 컴파일 Task는 Block 상태로 계속 기다리고 있어야 한다. 즉, 빌드 시스템 전체로 봤을 때 모듈에 대한 전반적인 인식이 부족하다.

 

이제 새로운 Explicitly built modules 를 알아보자.

Xcode 16 에서는 컴파일러가 내부에서 암묵적으로 모듈들을 빌드하던 과정을 명시적인 빌드 시스템 테스크로 끌어올린다. 모듈들을 먼저 빌드하는 것이 기존과의 차이를 만드는 핵심이다.

원리: 각 소스 파일의 컴파일이 3단계로 이루어진다 (아래 일러스트 참고)

1. Scanning

각 소스 파일을 스캔해서 전체 프로젝트에 대한 모듈 그래프(Module graph)를 구축. 타겟 간에 같은 모듈을 공유하는 것도 가능.

2. Building modules

모듈들의 컴파일이 이루어진다. 빌드 로그에서 이것들을 명시적인 테스크로 확인 가능하며, 소스 파일들이 의존하고 있는 모듈들이 컴파일 된다.

3. Building the original code

마지막으로 각 소스 파일이 컴파일된다. 의존하고 있는 모듈들은 2번 단계에서 이미 컴파일 되어있다.

 

Explicitly built modules의 장점

빌드 시스템에서 모듈에 대한 인식이 높아짐에 따라 상당히 시간이 단축된다. execution lane 을 효율적으로 채울 수 있기 때문이다.

Block되어 기다리던 시간들이 다 사라졌다!

좀 더 신뢰할 수 있다(안정적이다). 컴파일러는 매번 동일한 방식으로 컴파일을 실행하며, 정확한 종속성과 빌드 그래프를 제공해준다.

Xcode 상에서 디버깅을 시작할 때 빌드 시스템이 Swift 모듈을 디버거에 전달해 줄 수 있다. 기존에는 디버거가 완전히 별도의 모듈 그래프를 가지고 있었기 때문에  “po” 등을 계산 시 Swift Type 에 대해 알아야 할 때 등의 케이스에서 모듈을 다시 빌드하기도 했다. Explicitly built modules 와 함께라면 이럴 필요 없이 이미 빌드 된 모듈을 공유받을 수 있다.

디버깅 시작 시점이 좀 더 빨라질 것이다!

 

실제 적용 예시

적용 방법: Xcode 16 에서 Build Settings > Explicitly Built Modules 를 YES로 설정하기.

1단계: Scan
2단계: Compile module task. 이렇게 컴파일 된 모듈은 타겟 간 공유된다

실제로 적용해보면 같은 모듈이 여러 번 컴파일 될 때도 있다. 예를 들어 UIKit 모듈이 몇 번씩 컴파일 될 수 있음. 이것은 소스 코드 구조 상 같은 UIKit 모듈의 다른 버전/변형(variants)이 필요해져서이다. 타겟의 빌드 설정(macro 등) 때문에 발생할 수 있는데, 이것은 빌드 로그에서 ‘modules report’를 필터링해보면 확인 가능하며, 암묵적이지 않고 명시적인 Explicitly built modules 에서는 이런 정보를 바로 확인할 수 있으므로 빌드 최적화의 힌트를 알아낼 수 있다.

UIKit을 포함한 여러 모듈이 2 variants 필요하다는 것이 빌드 로그에 바로 찍힌다. 이는 같은 모듈을 여러 번 컴파일하게 만든다.

'WWDC > 2024' 카테고리의 다른 글

Analyze heap memory  (0) 2024.08.08
Explore Swift performance  (0) 2024.08.08
Migrate your app to Swift 6 (추천세션!)  (0) 2024.08.08
Consume noncopyable types in Swift  (0) 2024.08.08
A Swift Tour: Explore Swift’s features and design  (0) 2024.08.07