빌드 시스템 (1)

🏁 학습할 내용
- 미리보기
- 빌드 프로세스
- 작업 tool
- 순서 결정
- 동작 과정
- 증분 빌드
- 우리가 신경써야할 것은 무엇인가?
- 사용자 스크립트
👀 미리보기
자세히 들여다보기전에, 먼저 간단하게 알고가면 좋은 배경지식을 정리해보자.
🐝 빌드 프로세스




- 소스코드 컴파일 및 링크 (.m, .swift, exec .)
- 리소스 복사 (헤더, asset카탈로그, 스토리보드)
- 코드 사이닝 및 스크립트 실행
🔨 작업 tool

1️⃣ Clang
소스 코드 컴파일러
2️⃣ LD
여러 .o 파일과 라이브러리를 하나의 실행파일 또는 프레임워크로 결합
- 심볼 해석
- 주소 재배치
- 정적/동적 라이브러리 연결
- Mach-O 바이너리 생성
- 입력: .o, .a, .dylib
- 출력: 실행 파일, .app, .framework, .dylib
3️⃣ AC Tool (Asset Catalog Tool)
Asset.xcassets -> 바이너리 리소스로 컴파일
- 입력: 이미지, Color Set, App Thinning 정보등
- 출력: Assets.car(compiled asset catalog)
💡App Thinning이란
스토어와 운영체제가 사용자의 디바이스(기기) 특성과 OS 버전에 맞춰 필요한 부분만 다운로드하여 설치를 최적화하는 기술
4️⃣ IB Tool (Interface Builder Tool)
Stroyboard / XIB -> 컴파일된 리소스로 변환
- 입력: .storyboard, .xib
- 출력: 컴파일된 nib 파일, 런타임에 바로 로딩 가능한 형태
수천 개 이상의 실행들을 특정, arguments와 configuration에 지정된 순서로 실행됨
🛤️ 순서 결정
💡 종속성 정보
작업이 소비하는 입력 및 생성되는 출력


Complie 과정에서 .m파일 입력으로 받고, .o 출력하고
링커 작업은, 작업은 .o파일을 입력으로 받고, 실행파일 또는 라이브러리를 출력한다.
이런 특징을 정리하면 다음과 같다.
- 종속성 그래프를 통해 의존성 정보가 어떻게 흐르는 지 알 수 있으며
- 결과적으로 실행 순서를 파악할 수 있음
- 만약 컴파일 작업 자체 lane이 완전히 독립적일 경우, 병렬로 실행할 수 잇음
- 링커 작업은 이전 컴파일 작업 결과물을 입력으로 이용하니, 마지막에 실행되야 함
❓어떻게 동작할까?
🌳 종속성 그래프 (트리) 만들기


- build description 정보인 프로젝트 파일 가져옴
- 프로젝트 파일을 모든 파일을 parse 후, target과 의존 관계를 파악
- build setting을 적용 후, directed graph(유향 그래프)인 트리형 구조로 변경
- 트리를 통해, 실행되는 작업간의 모든 종속성을 파악할 수 있음
⤵️ 실행할 순서와 병렬 실행 확정 짓기

- low-level execution engine(libuild)가 위에서 만들어진 트리를 받아, 종속성 스펙을 읽음
- 실행되야하는 순서와 병렬로 실행할 수 있는 작업을 실행


🚨 주의할 점
종속성 정보를 너무 많이 가질 수 없으므로, 빌드 시스템은 작업 실행중에 더 많은 정보를 발견할 수 있다.
그래서, 원래 그래프보다 더 많은 정보를 통해 더 짧은 경로를 이용할 수도 있다? 라는 의미인 듯
🏋️ 증분 빌드
변화가 영향을 미치는 범위만 찾아서 필요한 파일만 재컴파일하는 빌드
🧪 원리
- Swift는 모든 swift 파일 내부의 타입, 프로토콜, extension등을 분석해 파일 간의 의존성 그래프를 만듬
- 빌드 프로세스의 각 작업은 hash 형태로된 signature가 있음
- 해당 hash는 파일 경로, 수정 타임스탬프 등의 통계 데이터가 포함되어있음
- 빌드 시스템은 현재 빌드와 이전빌드의 signature를 비교하여 다시 실행할 지 여부를 결정함
🥳 증분 빌드가 가능할 떄
- swift 컴파일러는 각 파일을 인터페이스 파일을 생성
- 인터페이스 파일에는 Stored property, function signature, protocol등의 정보가 있음
- 만약 함수 내부(body)가 바뀌면, 인터페이스 파일은 그대로, 다른 파일 영향없음 증분 빌드 가능 ✅
- stored property, public API가 변경되면, 인터페이스가 바뀜, 증분 빌드 불가 ❌
😭 증분 빌드가 불가능할 떄
- 모듈 public interface 변경
- struct stored property 변경
- protocol 변경
- extension에 public method 추가
- Swift version 변경
- optimization level 변경
- Package / Pod 업데이트
- DerivedData 삭제
- Build setting 변경
- bridging header(ObjC) 변경 (objc를 최소화 해야하는 이유
🕵️♀️ 우리가 신경써야할 것
빌드 순서는 결국 종속성 그래프를 통해 결정됨
우리는 그래프 구조에 따라 작업을 잘 실행할 수 있도록 도와줘야함
- 작업을 순서대로 올바르게 수행되도록
- 가능한 멀티코어 하드웨어를 활용해 병렬화 할 수 있도록
🔴 우리는 종속성을 신경써줘야함
📩 종속성의 근원
1️⃣ Built -in
빌드 시스템에 내장된 지식 관련

- 컴파일러, 링커, resources(asset 카탈로그, ...) 등에 대한 규칙
- Build Rules: 특정 파일 종류별로 어떤 처리를 할 것인지를 정의
2️⃣ Target Dependencies
Target 빌드 순서를 결정

- 여기서는 SampleTests가 Sample을 의존하므로, Sample의 빌드 순서가 우선 (일방적으로)
- 병렬 실행 여부에, 큰 영향을 줌
- 커스텀 run script가 작성되면 병렬 실행 전에 우선적으로 run script가 실행되야함 (데이터 레이스 떄문에)
- 그래서 조금 느려질 수 있음
3️⃣ Implicit Dependencies
Link Binary With Libraries에 필요한 Target을 명시


- scheme editor에서 Find Implict dependencies가 활성화되어 있다면(기본적으로 켜져있음)
- 위에서 본 Target Dependenices에 의존 관계가 없더라도, 의존성 설정이 가능
4️⃣ Build phase Dependencies

- scheme editor > parallelize build가 해제되어있다면, Build Phase에 정해진 순서로 실행될 수 있음
- 만약 옵션이 켜져있다면, 빌드시스템이 더 나은 빌드 순서로 실행할 수 있음
5️⃣ Scheme order Dependencies
scheme editor에서 설정하는 빌드 관련 설정
🤖 사용자 스크립트
🔴 다른 빌드 단계와 다른 점


- Input과 output을 명시적으로 입력해줘야한다.
- 불필요한 스크립트 재실행하지 않아도됨
- 정확한 의존성 정보를 제공함으로써, 빌드 성능을 높힐 수 있다.
- 결과적으로 빌드 프로세스에서 데이터 경쟁이 발생하지 않도록 연속적으로 스크립트 단계를 한 번에 실행
그렇다면, 스크립트는 병렬 실행 및 증분 빌드가 불가능할까??
놉, 그렇지 않다.
🏖️ SandBoxing Script
❓스크립트를 병렬로 실행하려면?

- 만약 타겟의 스크립트가 종속성 분석을 기반으로 실행되고, 입력 및 출력의 전체목록을 지정했다면, 병렬실행이 충분히 가능하다.
- 하지만 스크립트 Phase를 병렬로 실행할 경우, 빌드 시스템은 입출력 목록에 의존하게된다.
- 불완전한 입출력 목록은 디버그가 매우 어려운 데이터 레이스를 유발 시킬 수 있다.
🧩 역할


샌드 박싱은 스크립트가 실수로
소스 파일 및 중간 빌드 객체에 엑세스를 차단하는 선택정 기능
🙋 어떤 방식으로 샌드박싱은 데이터 경쟁과 잘못된 엑세스를 방지할까?
먼저 샌드박싱일 없을 때는 어떤 상황인지, 실제 예제를 살펴보자.


첫번 째 스크립트는 raw.txt파일을 일고 내용의 체크섬을 계산 후, 결과를
DERIVED_FILE_DIR/checksum.txt에 write
두번 째 스크립트는 동일한 raw.txt과 생성된 체크섬을 읽어, html 파일에 삽입
🤬 만약 정확한 입출력에 대한 종속성 정보가 선언되지 않을경우,
빌드 시스템은 두 스크립트를 병렬로 실행
(FUSE_BUILD_SCRIPT_PHASES가 활성화 되어있을 경우)
이렇게되면 2개의 문제가 발생할 수 있다.


- 첫번쨰로, html 생성 스크립트에서 checksum 결과가 없기때문에 스크립트가 실패될 수 있다.
- 두번째는, 디스크에 있는 이전 결과의 checksum결과가 html 생성에 사용될 수 있다.ㅊㅊ

만약 샌드박싱이 되어있다면, 다음과 같이 HTML 생성 스크립트가 checksum.txt를
읽으려고 시도하자마다 실패하게되고, 깔끔한 가이드를 알려준다.

이후, 올바른 입출력에 대한 종속성 정보를 잘 선언할 경우, 위 문제는 깔끔히 해결되면,
여전히 관련 없는 빌드 단계는 병렬로 실행할 수 있다.

ENABLE_USER_SCRIPT_SANDBOXING을 YES로 설정하면 샌드박싱기능을 사용할 수 있다.
출처
https://zeddios.tistory.com/919
Behind the Scenes of the Xcode Build Process (1)
안녕하세요 :) Zedd입니다. 티스토리 앱이 완전히 리뉴얼 됐네요!! https://apps.apple.com/kr/app/%ED%8B%B0%EC%8A%A4%ED%86%A0%EB%A6%AC-tistory/id906304982 티스토리 - TISTORY 티스토리 블로거만을 위한 모바일 앱! 티
zeddios.tistory.com
https://vapor3965.tistory.com/40
WWDC 18 - Behind the Scenes of the Xcode Build Process
세션보면서 정리한 내용입니다. 해석이 잘못된 경우가 있을수있으니 발견하시면 댓글로 남겨주시면 감사하겠습니다🙏🏻 https://developer.apple.com/videos/play/wwdc2018/415/ Behind the Scenes of the Xcode Build P
vapor3965.tistory.com
https://ios-development.tistory.com/1828
[iOS - swift] 전체빌드와 증분빌드 차이점
전체빌드 개념단어 그대로 캐싱 없이, a부터 z까지 모든 파일을 빌드하는 것 cf) 빌드란?소스 코드들을 실행 가능한 형태로 메모리에 올리는 것증분빌드 개념증분 빌드(Incremental Build)는 “변화가
ios-development.tistory.com
https://www.youtube.com/watch?v=yo8_luxkSXY&t=40s