Yocto 빌드 시스템 이해하기
1. 서론
이 문서에서는 임베디드 리눅스 시스템을 구축하고 배포하기 위한 Yocto Project의 빌드 시스템을 공부하면서 학습한 핵심 개념들을 정리하겠습니다.
Yocto Project는 홈페이지에 “Mega Manual”을 자랑스럽게 게시하고 있습니다 - 그리고 이유가 있어서 메가입니다! 문서의 방대한 양을 고려할 때, 제 수준에서 모든 것을 마스터하려고 하는 것은 의미가 없습니다. 그래서 중요한 부분에 집중하고 도움이 되는 다이어그램과 함께 설명하겠습니다.
친절하게 링크를 제공하니 자유롭게 들어가서 살펴보세요 → mega-manual
이 글에는 구글링을 통한 저의 주관적인 분석이 많이 포함되어 있으니, 더 객관적인 분석을 원하신다면 위 링크를 참조해주세요.
2. 재미있는 역사 살펴보기
Yocto Project는 OpenEmbedded Project에 뿌리를 두고 있습니다.
OpenEmbedded Project는 Sharp(전자사전으로 친숙한)가 자사의 ROM 이미지를 오픈소스 라이선스로 공개하면서 시작되었다고 합니다.
(좋은 시절이었죠…)
이를 기반으로 2002년 OpenZaurus 프로젝트가 시작되었습니다. (Zaurus는 PDA였습니다)

시간이 지나면서 OpenEmbedded Project로 진화했고, Debian 기반 패키지 관리 및 빌드 방법을 채택했습니다 (우리에게 친숙한 Ubuntu도 Debian 기반입니다).
OpenEmbedded는 셸과 파이썬 스크립트로 작성된 빌드 도구인 Bitbake와 무엇을 빌드할지 지정하는 **메타데이터(레시피)**로 구성됩니다.
(Bitbake도 너무 커져서 2004년에 make처럼 별도 프로젝트로 분리되었습니다)
OpenEmbedded Project는 전 세계 개발자들 사이에서 거의 10,000개의 레시피와 수백 개의 머신을 지원하며 큰 인기를 얻었습니다. 하지만 이러한 성장으로 관리가 점점 어려워져 다양한 개선 시도가 있었습니다.
그중 하나가 2003년 임베디드 스타트업 OpenedHand가 시작한 Poky Linux Project였습니다. 이 프로젝트는 필수적인 수백 개의 레시피만 선택하고 QEMU를 통한 가상 환경 빌드와 SDK 빌드를 지원하여 빠르게 인기를 얻었습니다. (OpenedHand는 2008년 인텔에 인수되었습니다. 아무리 찾아봐도 인수 가격은 찾을 수 없었습니다…)
시간이 흘러 2010년, Linux Foundation WG가 Yocto Project를 발표했습니다. 프로젝트의 핵심은 Poky Linux를 기반으로 임베디드 리눅스 배포판을 만드는 것이었습니다.
2011년부터 너무 커진 OpenEmbedded Project는 Poky Linux에서 **OpenEmbedded-Core (OE-Core)**로 분리되었습니다. (이전 OpenEmbedded는 OE-Classic이라고 부릅니다)
OE-Core는 ARM과 x86 같은 주요 아키텍처 지원, QEMU 지원에 집중하며, X 윈도우에서 실행되는 Sato 기반 GUI 테스트 도구도 포함합니다.
이러한 긴 크기 축소와 분할 과정을 통해 위에 표시된 현재의 계층 아키텍처를 달성했고, 이전의 Push 모델 대신 Pull 모델을 채택하여 프로젝트 분기 가능성을 제거했습니다.
3. 크로스 빌드 개요
이제 역사를 이해했으니, 간단한 임베디드 개발이나 크로스 빌드를 해본 독자들은 3장을 쉽게 따라갈 수 있을 것입니다.
역사에서 언급했듯이 Yocto Project는 Poky Linux를 기반으로 Bitbake를 빌드 도구로 사용하고, 메타데이터(레시피)를 사양으로 사용하며, Bitbake는 OpenEmbedded 프로젝트의 핵심 기능입니다.
그래서 우리는 이 Bitbake를 분석하는 데 집중해야 합니다.
하지만…
긴 역사를 가진 Bitbake에 대해 모든 것을 아는 것은 너무 많습니다… 간단한 다이어그램을 통해 빌드에 필요한 중요한 기능을 간략히 이해해봅시다.
3.1 사용자 구성
먼저 해야 할 일 - 빌드 환경 설정입니다.
크로스 빌드를 해본 사람이라면 이것이 가장 짜증나고 시간이 많이 걸리는 부분이지만, 가장 중요한 영역이라는 것을 알고 있습니다.
아래 다이어그램을 살펴봅시다:
Poky Project는 이 부분을 자동화하는 스크립트를 제공합니다. 바로 oe-init-build-env 스크립트입니다.
스크립트를 실행하면 모든 빌드 관련 작업이 이루어지는 Build Directory가 생성됩니다.
생성된 Build 디렉토리의 conf 디렉토리에서는 사용자 구성을 수정할 수 있으며, bitbake 명령줄을 통해서도 변경할 수 있습니다.
수정할 수 있는 정보에는 빌드 시스템에서 방대한 메타데이터(레시피) 중 어떤 것을 사용할지, 대상 머신 설정, 빌드에 필요한 패키지 다운로드 경로, 캐시 경로 등이 포함됩니다.
3.2 소스 준비
[출처: https://www.yoctoproject.org/docs/3.1/overview-manual]
기반이 마련되면 우리의 소프트웨어를 가져와서 Yocto Project 구조에 연결해야 합니다.
이 부분을 처리하는 레시피 함수는 do_fetch와 do_unpack입니다. 이 두 함수는 Build Directory 내에 Working Directory를 만들고 실제 소스 코드를 복사합니다.
이 구조는 동일한 소스에서 다양한 아키텍처와 OS를 지원하도록 설계되었습니다.
3.3 구성 & 컴파일 & 스테이징

[출처: https://www.yoctoproject.org/docs/3.1/overview-manual]
원본 소프트웨어 소스를 가져온 후 다음 과정은 컴파일과 설치입니다.
이 과정은 CMake나 Autotool 같은 빌드 도구를 알고 있다면 더 친숙할 것입니다.
먼저 do_prepare_recipe_sysroot 함수가 크로스 빌드를 위한 두 개의 sysroot를 Working Directory에 배치합니다 (타겟 sysroot와 sysroot-native).
do_configure를 통해 컴파일에 필요한 빌드 구성 파일을 **원본 소스(S)**에서 **빌드 디렉토리(B)**로 추출합니다 (일반적인 cmake 과정과 동일).
do_compile 과정은 빌드 디렉토리에서 컴파일을 진행합니다 (일반적인 make 과정과 동일).
do_install 과정은 컴파일된 파일들을 설치 **대상 목적지(D)**에 배치합니다.
3.4 패키지 분류

[출처: https://www.yoctoproject.org/docs/3.1/overview-manual]
소스 컴파일과 설치가 완료되면 패키징하고 배포하는 방법을 결정할 차례입니다.
Yocto Project는 세 가지 배포 형식을 지원합니다: rpm, deb, ipk.
do_package, do_packagedata 함수는 설치 대상 D의 파일을 패키징을 위해 분할하고 분류합니다.
패키지 분할은 Ubuntu에서 python을 apt 패키지로 설치할 때 python, python-dev, python-3.6 등 다양한 형태로 존재하는 것과 비교하면 이해할 수 있습니다.
3.5 이미지 생성

[출처: https://www.yoctoproject.org/docs/3.1/overview-manual]
패키지가 잘 만들어지면 이제 Bitbake를 사용하여 이미지의 rootfs(루트 파일 시스템)에 넣을 수 있습니다.
먼저 do_rootfs 함수는 위 과정에서 생성된 패키지가 설치된 이미지의 Rootfs를 만듭니다.
이 과정에서 매우 중요한 변수들이 있으므로 살펴보겠습니다:
- IMAGE_INSTALL: 생성된 패키지 컬렉션(Package Feeds 영역)에서 이미지에 포함할 패키지를 나열
- PACKAGE_EXCLUDE: 설치하지 않을 항목을 나열
- PACKAGE_CLASSES: 사용할 패키지 유형 선택 (rpm, deb, ipk)
- PACKAGE_INSTALL: 이미지에 설치될 최종 패키지 목록
패키지 설치를 완료한 후 후처리 작업을 수행할 수 있습니다.
후처리에서는 매니페스트 파일이 생성되고 특정 스크립트가 실행되며, 대부분 테스트 목적입니다.
매니페스트 파일은 Poky가 지원하는 Qemu 같은 가상 환경뿐만 아니라 실제 대상 환경에서도 테스트 자동화에 사용됩니다. (자세한 내용은 testimage*.bbclass 또는 testsdk.class 참조)
이는 배포를 위한 V-사이클에서 통합 테스트나 시스템 테스트를 구축하는 데 유용할 것입니다.
후처리가 완료되면 마침내 대상에 업로드할 이미지를 만들 준비가 됩니다.
do_image 함수가 이 역할을 처리하며, 내부적으로 do_image_*를 제공하여 다른 파일 시스템(ext4, fat32 등)을 다르게 처리합니다.
3.6 SDK 생성
대부분의 사람들은 이게 당연히 작동해야 한다고 생각하며, 실제로 위에서 언급한 대부분의 프로세스는 DevOps 영역에 있습니다.
기능 개발자에게 크로스 컴파일을 위해 이미지 생성을 거치는 것은 시간과 리소스의 큰 낭비이므로, DevOps는 SDK 형태로 쉽게 컴파일할 수 있는 환경을 만들어야 합니다.
이를 구축하는 것은 매우 지루하지만 생산성 관점에서 필수적입니다. 그래서 역사에서 언급했듯이 Poky Linux 프로젝트도 이를 고려하여 기능으로 제공했습니다.

[출처: https://www.yoctoproject.org/docs/3.1/overview-manual]
위 과정에서 패키징이 잘 되었다고 가정해봅시다.
Yocto Project를 사용하면 do_populate_sdk 또는 do_populate_sdk_ext를 사용하여 이러한 패키지에서 SDK를 쉽게 만들 수 있습니다.
SDK 설치 파일은 일반적으로 위 다이어그램과 같이 /build/tmp/deploy/*.sh로 생성됩니다.
그 파일만 있으면 개발자들은 크로스 빌드 환경을 쉽게 설정할 수 있습니다.

4. 결론
2020년 말 현재, 자동차 시스템 플랫폼은 자동차 OEM뿐만 아니라 글로벌 IT 거대 기업, 전자 및 반도체 회사들이 서둘러 차지하려는 뜨거운 감자입니다.
차량뿐만 아니라 IoT 세계도 Linux 기반 커널을 가지고 있다는 점을 고려하면, Yocto와 같은 오픈소스 프로젝트를 기반으로 통합 빌드 환경을 제공하는 생태계는 더욱 인기를 얻을 것입니다.
실제로 LinkedIn을 보면 VW Group, BMW Group, 현대자동차 그룹, 토요타 그룹뿐만 아니라 자동차 반도체 칩 제조업체, 아마존, 페이스북을 포함한 IT 기업들이 Yocto Project에 익숙한 시니어 레벨의 Build Architect나 Build Engineer를 채용하고 있습니다.
Yocto 프로젝트는 커널부터 애플리케이션까지 모든 것을 다루므로 최소한 3개의 컴퓨터 언어에 능숙해야 하며, 엄청난 양의 학습이 필요합니다.
저도 Yocto 빌드 시스템을 공부하면서 꽤 어려웠고, 이 글이 저처럼 처음부터 공부하는 분들에게 도움이 되길 바랍니다. 이것으로 이 글을 마칩니다.