Eth Dev Post

Bitcode 도입하기

Bitcode 도입하기 feature image

목차

  1. About bitcode
    1. bitcode 업로드 프로세스
    2. bitcode가 활성화된 앱의 Symbolication
    3. bcsymbolmap
  2. Xcode Build Setting for bitcode
    1. ENABLE_BITCODE
    2. BITCODE_GENERATION_MODE
  3. Library의 bitcode 지원
    1. 바이너리의 bitcode 지원 확인하기
    2. CocoaPods을 통해 배포되는 라이브러리의 bitcode 지원

이번 글에서는 bitcode 도입을 위해 필요한 부분을 정리해보았습니다.

About bitcode

bitcode 업로드 프로세스

Bitcode is an intermediate representation of a compiled program
  • bitcode는 앱스토어에서 App Thining을 위해 바이너리 빌드시 포함하는 IR입니다. 앱스토어에서는 업로드된 바이너리에 bitcode가 포함되어 있을 경우 앱의 compile과 linking을 다시 수행하여, 디바이스의 아키텍쳐별로 최적화된 바이너리를 새롭게 생성하고 이를 배포합니다.
  • 앱 사용자는 자신의 디바이스에 맞는 아키텍처에 대한 바이너리만 다운로드 받으면 되므로 bitcode 활성화시 사용자가 다운로드 받는 앱의 용량이 줄어들게 됩니다.
  • 정리하면 앱 스토어에 바이너리 업로드시 다음의 과정이 수행됩니다.
  1. bitcode가 적용된 하나의 바이너리를 앱스토어에 업로드합니다.
  2. 앱 스토어에서 이 바이너리를 다시 컴파일하여 아키텍처별로 바이너리를 생성합니다.(arm64용 바이너리, armv7용 바이너리 등)
  3. 앱 사용자는 각 디바이스의 아키텍처에 맞는 바이너리만 다운로드 합니다.(bitcode 적용 이전에는 바이너리가 나뉘어지지 않고, 한 개(fat binary)만 사용합니다.)

bitcode가 활성화된 앱의 Symbolication

  • 앱 스토어에서 다시 컴파일이 발생하는 것은 앱 개발자가 스토어 업로드를 위해 진행하는 아카이빙의 결과물로 나오는 dSYM을 사용할 수 없게 만듭니다. 앱 바이너리와 dSYM은 바이너리에 포함되는 UUID를 기반으로 매칭이 됩니다. 그래서 빌드가 다시 발생하면(설령 수정 사항이 없는 코드라도) 이전 빌드 결과물의 바이너리/dSYM은 이후의 것과 매칭될 수 없습니다. 그래서 아카이빙 과정에서 생성된 dSYM은 스토어 버전의 앱에서 발생하는 Crash Report를 Symbolicate할 수 없습니다.
  • 따라서, bitcode 활성화 이후에 Third Party Crash 분석 플랫폼에 dSYM 업로드시에는 반드시 Xcode Organizer나 iTunes Connect에서 파일을 다운로드 받아야 합니다.

bcsymbolmap

  • 아카이빙 진행시 iTunes Connect에 앱의 Symbol을 업로드할지 결정하는 체크 박스가 존재합니다. 이를 비활성화 할 경우 Xcode는 바이너리 업로드 이전에 앱의 dSYM 파일에 포함된 Symbol을 난독화합니다.("__hidden#109_"와 같은 형태) 이렇게 난독화된 Symbol은 .bcsymbolmap이라는 파일을 통해 복호화됩니다. 그래서 bitcode를 지원하는 바이너리의 dSYM 파일은 그에 대응하는 .bcsymbolmap을 항상 함께 가지고 있습니다.
  • Third Party Crash 분석 플랫폼에 dSYM을 업로드할 때, Xcode에서 다운로드 받는 dSYM은 복호화가 되어 있어서 바로 업로드해도 무방합니다. 하지만 iTunes Connect에서 직접 다운로드 받는 dSYM은 복호화를 직접해주어야 합니다.
xcrun dsymutil -symbol-map <xcarchive 내의 BCSymbolMaps 경로> <다운로드 받은 dSYM 디렉토리 경로>

Xcode Build Setting for bitcode

  • Xcode는 바이너리 빌드시 full bitcode를 포함하는 것과 bitcode가 있다는 것을 표시하는 marker를 포함하는 옵션이 존재합니다.

ENABLE_BITCODE

ENABLE_BITCODE을 YES로 설정하면 소스코드 빌드/아카이브시 bitcode와 관련된 Flag가 추가됩니다. 이 때, Build Action에서는 -embed-bitcode-marker가 추가되고, Archive Action에서는 -embed-bitcode가 추가됩니다. 관련 정보는 실제 빌드/아카이빙 수행시 로그를 확인해보면 알 수 있습니다.

  • 빌드 수행시 로그
// 빌드 로그 발췌
CompileSwift normal arm64 TestObj.swift (in target 'SomProject' from project 'SomProject')
...
/Debug-iphoneos/SomProject.build/Objects-normal/arm64/TestObj.o -embed-bitcode-marker 
...
  • 아카이빙 수행시 로그
// 아카이빙 로그 발췌
SwiftCodeGeneration normal arm64 (in target 'SomProject' from project 'SomProject')
...
/Release-iphoneos/SomProject.build/Objects-normal/arm64/TestObj.bc -embed-bitcode -target arm64-apple-ios11.0 -Xllvm -aarch64-use-tbi -O -disable-llvm-optzns -module-name
...

BITCODE_GENERATION_MODE

User Defined Settings에 BITCODE_GENERATION_MODE을 통해서도 ENABLED_BITCODE와 동일한 효과를 만들어 낼 수 있습니다. 이 때 value로 maker를 설정할 경우 embed-bitcode-marker가 컴파일 Flag로 전달되고, bitcode를 설정할 경우 embed-bitcode가 전달됩니다. 경우에 따라서 Build Action에서 full bitcode를 사용해야 하는 경우에 해당 설정 값을 변경하여 Build를 진행할 수 있습니다.

스크린샷 2020-05-22 오전 12 59 59

Note: Other C Flag에 직접 -fembed-bitcode를 추가하여 bitcode를 활성화할 수도 있습니다.

Library의 bitcode 지원

  • bitcode의 가장 큰 단점은 사용하고 있는 라이브러리가 하나라도 bitcode를 지원하지 않을 경우, 앱에서는 bitcode를 활성화할 수 없다는 점입니다.
  • 앱에서 사용하고 있는 라이브러리의 bitcode 관련하여 참고하면 좋은 내용을 정리하면 다음과 같습니다.

바이너리의 bitcode 지원 확인하기

  • 특정 라이브러리가 bitcode를 지원하는지 확인하기 위해서는 해당 라이브러리의 바이너리가 LLVM Symbol을 포함하고 있는지를 확인하면 됩니다. 다만, 어떤 Symbol을 확인하면 되는지에 대한 여러 이슈가 존재하여 확인에 혼돈이 오는 경우가 있습니다.
  • 결론적으로 아래의 명령어를 통해 출력되는 Symbol이 존재한다면 해당 바이너리는 bitcode를 지원하는 것이라고 볼 수 있습니다.
$ otool -arch arm64 -l MyFramework/MyFramework | grep __LLVM
$ otool -arch armv7 -l  myLib.a | grep __LLVM

CocoaPods을 통해 배포되는 라이브러리의 bitcode 지원

  • bitcode는 link 과정에서 처리되는 것이 아니라, compile 과정에서 처리됩니다. 그래서 라이브러리의 빌드를 수행할 때, 해당 바이너리가 bitcode 지원을 하는지 안 하는지에 따라 해당 라이브러리의 bitcode 지원 여부가 결정됩니다. 따라서, 빌드된 바이너리로 배포되는 framework/library의 경우, 이전에 bitcode를 지원하지 않았다면, bitcode를 활성화하여 다시 빌드 후 재배포가 필요합니다.
  • CocoaPods의 경우 vendored_framework, 혹은 vendored_libraries 으로 배포되는 것들이 이에 해당합니다. 이 중 하나의 라이브러리라도 bitcode를 지원하지 않을 경우 라이브러리를 사용하는 앱에서는 bitcode를 사용할 수 없습니다.
  • 반면, CocoaPods에서 빌드 바이너리 형태로 배포되는 Pod이 아닌 것들은 앱을 빌드할 경우 직접 빌드를 수행합니다. 그래서 앱의 PodFile 혹은 PodSpec에서 ENABLED_BITCODE를 명시적으로 NO로 설정하지 않으면 bitcode를 지원합니다.

참고자료

< 홈으로

잘못된 정보나 궁금하신 점은 hcn1519@gmail.com으로 알려주시면 감사하겠습니다.