AEGLOSLAB ROS 2 Jazzy 대전

제4부 빌드·패키징·배포

4.5 colcon 심층 — 그래프·mixin·확장

4.5.1 이 장에서 배우는 것 / 선수 지식

colcon build는 ROS 2 개발자가 하루에 수십 번 치는 명령이다. 그런데 대부분은 옵션 없이 치고, 빌드가 느려지거나 비정상이 되면 rm -rf build install로 대응한다. 이 장은 그 도구를 제대로 쓰는 법이다.

colcon은 ROS 전용 도구가 아니다. 임의의 패키지 묶음을 의존 순서대로 빌드하는 범용 도구이며, ROS 지원은 확장(extension)으로 추가돼 있다. 이 사실이 확장 구조와 옵션 체계를 설명한다.

배우는 것:

  • 의존 그래프와 빌드 순서 — colcon이 무엇을 읽고 어떻게 정렬하는가.
  • 패키지 선택 옵션 — 대규모 워크스페이스에서 필요한 것만 빌드하기.
  • 병렬 실행 제어 — 빌드가 메모리를 모두 소모할 때.
  • mixin — 자주 쓰는 옵션 묶음.
  • defaults.yaml — 팀 표준 설정.
  • 디렉터리 구조 — build·install·log에 무엇이 있는가.
  • 테스트 실행과 결과 확인.
  • 증분 빌드의 함정 — 언제 정말 지워야 하는가.

선수 지식: 4.1(ament·설치 레이아웃), 4.4(package.xml 의존), 0.5(워크스페이스).

정직: colcon 확장은 별도 패키지로 설치해야 하는 것과 기본으로 포함되는 것이 갈린다. python3-colcon-common-extensions가 실제로 무엇을 끌어오는지 Jazzy에서 확인해 4.5.15에 정리했다(실행 확인) — graph는 기본 포함, mixin·clean은 별도다. 옵션 이름도 실물과 대조했다(목차 3).

4.5.2 colcon의 구조

colcon build 패키지 탐색 src/ 하위 재귀 package.xml 파싱 이름 · 의존 · build_type 의존 그래프 구성 위상 정렬 빌드 태스크 실행 (병렬, 의존 순서 준수) 패키지별 빌드 도구 호출 ament_cmake → cmake ament_python → setup.py install/ 에 설치 setup.bash 생성 4.1.5

colcon이 하는 일과 안 하는 일:

하는 일안 하는 일
패키지 탐색·의존 정렬컴파일(CMake·setuptools가 함)
병렬 실행·진행 표시의존 설치(rosdep이 함 — 4.6)
환경 스크립트 생성소스 내려받기(vcstool — 4.7)
테스트 실행·결과 수집릴리스(bloom — 4.9)

"의존을 설치하지 않는다"가 중요하다. colcon build가 "패키지를 못 찾겠다"고 하면 rosdep으로 설치해야 한다.

rosdep install --from-paths src -y --ignore-src
colcon build

4.5.3 의존 그래프와 빌드 순서

colcon은 package.xml만 읽는다(4.4). CMakeLists.txt의 find_package는 보지 않는다.

# 위상 순서 확인
colcon list --topological-order

# 의존 관계 포함
colcon list --topological-graph

# 그래프 시각화 (common-extensions 에 이미 포함 — 별도 설치 불필요, 4.5.15)
colcon graph --dot | dot -Tpng -o graph.png

빌드 순서에 영향을 주는 태그:

태그순서 영향
build_depend·depend·buildtool_depend있음 — 먼저 빌드됨
exec_depend없음(확인 필요: 도구 버전 의존)
test_depend테스트 시에만

견해: <depend>를 남발하면 빌드 그래프가 불필요하게 직렬화된다(4.4.3). 병렬 빌드의 효율이 떨어지고, 한 패키지를 고쳤을 때 재빌드되는 범위가 넓어진다. 대규모 워크스페이스에서 이것이 체감된다.

순환 의존은 오류다. colcon이 위상 정렬에 실패하고 멈춘다. 인터페이스 패키지를 분리하라는 조언(1.2.5)이 이 문제를 구조적으로 예방한다.

4.5.4 패키지 선택 옵션

대규모 워크스페이스에서 필수다. 200개 패키지를 매번 전부 빌드할 수는 없다.

# 이것만
colcon build --packages-select my_pkg

# 이것과 그 의존 전부 (처음 빌드할 때)
colcon build --packages-up-to my_pkg

# 이것에 의존하는 것들 (API를 바꿨을 때)
colcon build --packages-above my_pkg

# 이것에 의존하는 것들 + 자신 (변경 후 영향 범위 검증)
colcon build --packages-above-and-dependencies my_pkg

# 제외
colcon build --packages-skip heavy_pkg another_pkg

# 이미 빌드된 것 건너뛰기
colcon build --packages-skip-build-finished

# 정규식으로 선택 (실행 확인 — select·skip·ignore·up-to 각각 -regex 판이 있다)
colcon build --packages-select-regex '^my_.*'
옵션언제
--packages-select한 패키지만 고쳤고 의존이 이미 빌드됨
--packages-up-to새 워크스페이스에서 특정 패키지까지만
--packages-above공개 API·메시지를 바꿨을 때
--packages-skip규모가 크거나 깨진 패키지 제외

견해: --packages-select를 습관적으로 쓰다가 의존 패키지 변경을 놓치는 실수가 흔하다. 메시지 정의(1.2)나 공개 헤더를 바꿨다면 --packages-above로 영향 범위를 다시 빌드해야 한다. 애매하면 전체 빌드가 안전하다.

4.5.5 병렬 실행과 자원 통제

기본은 CPU 코어 수만큼 병렬 빌드한다. 이것이 메모리를 모두 소모하는 원인이다 — 특히 C++ 템플릿이 규모가 큰 패키지(Nav2·MoveIt 2)에서.

# 동시 패키지 수 제한
colcon build --parallel-workers 2

# 각 패키지 내부의 컴파일 병렬도 (make -j 에 해당)
colcon build --parallel-workers 2 \
  --cmake-args -DCMAKE_BUILD_TYPE=Release \
  --executor sequential

# make 병렬도 직접 지정
MAKEFLAGS="-j2" colcon build --parallel-workers 2
옵션효과
--parallel-workers N동시에 빌드하는 패키지 수
--executor sequential한 번에 하나씩(디버깅·저메모리)
MAKEFLAGS="-jN"패키지 안의 컴파일 병렬도

메모리 소요는 대략 두 값의 곱에 비례한다:

$$M \approx N_{\text{workers}} \times N_{\text{make jobs}} \times M_{\text{컴파일 단위}}$$

C++ 컴파일 단위 하나가 1~2 GB를 쓰는 경우가 있으므로, 8코어에서 기본 설정이면 10 GB 이상을 쓸 수 있다.

함정: 빌드가 갑자기 종료되거나 머신이 멈추면 OOM(메모리 부족)을 의심하라. 오류 메시지가 c++: fatal error: Killed signal terminated program cc1plus 같은 형태로 나온다.

# 확인
dmesg | grep -i 'out of memory'
journalctl -k | grep -i oom

# 대처: 병렬도 축소
colcon build --parallel-workers 1 --executor sequential

임베디드 보드(라즈베리파이·Jetson)에서는 거의 항상 제한이 필요하다. 그리고 그런 환경에서는 크로스 컴파일을 검토하는 편이 낫다(4.8).

4.5.6 주요 빌드 옵션

# 최적화 (지정 안 하면 최적화 없음 — 4.2.9)
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release

# 디버그 심볼 포함 최적화
colcon build --cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo

# 심볼릭 링크 설치 (4.3.5)
colcon build --symlink-install

# 통합 설치 레이아웃 (4.1.7)
colcon build --merge-install

# compile_commands.json 생성 (IDE·clangd용)
colcon build --cmake-args -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

# 출력을 화면에 그대로
colcon build --event-handlers console_direct+

# 실패 시 즉시 중단
colcon build --continue-on-error   # (기본은 계속)

compile_commands.json이 개발 경험을 크게 바꾼다 — clangd·VS Code가 정확한 자동완성과 오류 표시를 제공한다.

# 워크스페이스 루트에 통합 (패키지별로 흩어진 것을 모음)
colcon build --cmake-args -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
# build/*/compile_commands.json 들을 병합하는 도구가 필요할 수 있다 (확인 필요)

출력 제어:

# 기본: 요약만
colcon build

# 경고·오류를 화면에
colcon build --event-handlers console_cohesion+

# 전부 실시간 출력 (병렬이면 섞인다)
colcon build --event-handlers console_direct+

# 로그 파일 확인
less log/latest_build/my_pkg/stdout_stderr.log

견해: 빌드 실패 시 가장 먼저 볼 곳은 log/latest_build/<패키지>/stdout_stderr.log다. 화면 출력은 병렬 실행 때문에 잘리거나 섞이지만, 로그 파일은 온전하다. 이것을 모르면 "오류 메시지가 안 보인다"며 원인을 찾지 못한다.

4.5.7 mixin — 옵션 묶음

긴 옵션을 매번 치지 않기 위한 확장이다.

# 설치 — ros:jazzy-* 도커 이미지에는 이미 있다 (python3-colcon-mixin 0.2.3, 89차 실행 확인)
sudo apt install python3-colcon-mixin
colcon mixin add default \
  https://raw.githubusercontent.com/colcon/colcon-mixin-repository/master/index.yaml
colcon mixin update default

# 사용
colcon build --mixin release
colcon build --mixin debug compile-commands
colcon build --mixin release ccache
mixin효과
release-DCMAKE_BUILD_TYPE=Release
debug-DCMAKE_BUILD_TYPE=Debug
rel-with-deb-infoRelWithDebInfo
compile-commandscompile_commands.json 생성
ccacheccache 사용(재빌드 가속)
lld·mold빠른 링커
asan·tsan새니타이저(2.9.6)

직접 정의할 수도 있다:

# ~/.colcon/mixin/my_mixins.mixin
{
  "build": {
    "robot-release": {
      "cmake-args": ["-DCMAKE_BUILD_TYPE=Release"],
      "merge-install": true,
      "parallel-workers": 4
    },
    "robot-dev": {
      "cmake-args": ["-DCMAKE_BUILD_TYPE=RelWithDebInfo",
                     "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"],
      "symlink-install": true
    }
  }
}
colcon mixin add my file://$HOME/.colcon/mixin/my_mixins.mixin
colcon mixin update my
colcon build --mixin robot-dev

견해: 팀 표준 빌드 설정을 mixin으로 배포하는 것이 좋다. "릴리스는 이 옵션으로 빌드한다"를 문서 대신 실행 가능한 형태로 공유할 수 있고, 개발자마다 다른 옵션으로 빌드해 생기는 문제를 줄인다.

4.5.8 defaults.yaml — 항상 적용되는 설정

# ~/.colcon/defaults.yaml  또는  <워크스페이스>/colcon_defaults.yaml
build:
  symlink-install: true
  cmake-args:
    - -DCMAKE_BUILD_TYPE=RelWithDebInfo
    - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
  parallel-workers: 4
test:
  event-handlers:
    - console_direct+

mixin과의 차이:

축mixindefaults.yaml
적용명시적(--mixin 지정)자동(항상)
범위선택적전역 또는 워크스페이스
용도상황별 프로파일개인·팀 기본값

함정: defaults.yaml은 명령줄에 안 보이므로 혼란의 원인이 될 수 있다. "왜 내 빌드만 다르지"의 원인이 여기인 경우가 있다. 팀 공유 설정은 워크스페이스 안의 colcon_defaults.yaml 로 두고 버전 관리하는 편이 명확하다.

# 실제 적용된 설정 확인
colcon build --help | head    # (확인 필요: 적용된 defaults를 보여주는 방법)

4.5.9 디렉터리 구조

경로내용
ws/src/소스 — 사람이 관리
ws/build/중간 산출물
ws/install/설치본 — 이것을 소싱
ws/log/빌드·테스트 로그
ws/log/latest_build최근 빌드 로그로의 심링크
ws/log/latest_test최근 테스트 로그로의 심링크
ws/log/<빌드타임스탬프>/<패키지>/stdout_stderr.log가장 유용 — 빌드 실패 원인
ws/log/<빌드타임스탬프>/<패키지>/command.log실행된 명령
디렉터리지워도 되나영향
build/✅전체 재컴파일(느려짐)
install/✅재설치 필요
log/✅과거 로그 손실
src/❌소스 손실
# 특정 패키지만 정리
rm -rf build/my_pkg install/my_pkg
colcon build --packages-select my_pkg

# 전체 정리
rm -rf build install log

함정: install만 지우고 build를 남기면 어중간한 상태가 될 수 있다. CMake 캐시에 옛 설치 경로가 남아 있기 때문이다. 둘을 함께 지우는 것이 안전하다.

4.5.10 테스트

# 빌드 후 테스트
colcon build --packages-select my_pkg
colcon test --packages-select my_pkg

# 결과 확인 (이 명령을 안 치면 실패를 못 본다)
colcon test-result --verbose

# 실패한 것만
colcon test-result --all | grep -i fail

# 특정 테스트만
colcon test --packages-select my_pkg --ctest-args -R test_my_lib
colcon test --packages-select my_py_pkg --pytest-args -k test_compute

함정: colcon test는 테스트가 실패해도 종료 코드가 0일 수 있다. 반드시 colcon test-result로 확인해야 한다. CI 스크립트에서 이것을 빠뜨리면 실패한 테스트를 통과로 처리하게 된다(4.10).

# CI에서의 올바른 형태
colcon test --packages-select my_pkg
colcon test-result --verbose    # 실패 시 비0 종료

4.5.11 증분 빌드의 함정

언제 정말 지워야 하는가.

변경증분 빌드로 충분?
.cpp 수정✅
헤더 수정✅(의존 추적됨)
CMakeLists.txt 수정대개 ✅
메시지 정의 변경(1.2)⚠️ 사용하는 패키지도 재빌드 필요
package.xml 의존 변경⚠️ 재빌드 권장
merge/isolated 전환❌ 반드시 삭제(4.1.7)
--symlink-install 켜고 끄기❌ 삭제 권장
컴파일러·배포판 변경❌ 삭제
패키지 이름 변경·삭제❌ install에 잔재가 남는다

"패키지를 삭제했는데 여전히 보인다" — install/에 남아 있기 때문이다. colcon은 지워진 패키지의 설치본을 자동으로 제거하지 않는다.

rm -rf build/old_pkg install/old_pkg

견해: 문제가 비정상이면 지우고 다시 빌드하라 — 다만 그 전에 이유를 한 번 생각하라. rm -rf build install은 확실하지만 대규모 워크스페이스에서 수십 분이 든다. 위 표에서 해당 항목을 찾으면 특정 패키지만 지우는 것으로 끝나는 경우가 많다.

4.5.15 common-extensions가 실제로 끌어오는 것 — 실측

"colcon 확장은 따로 설치해야 한다"는 말은 반만 맞다. Jazzy에서 python3-colcon-common-extensions의 의존을 직접 펼쳐 봤다(실행 확인).

포함되는 확장 21종:

argcomplete · bash · cd · cmake · core · defaults · devtools · library-path · metadata · notification · output · package-information · package-selection · parallel-executor · powershell · python-setup-py · recursive-crawl · ros · test-result · zsh

이 목록에서 읽어야 할 것:

확장무엇을 주나흔한 오해
package-informationcolcon graph·colcon info·colcon listcolcon-graph라는 패키지를 찾게 되는데 그런 패키지는 없다. 기본으로 이미 있다
package-selection--packages-select·--packages-up-to 계열 전부이것이 없으면 부분 빌드 자체가 안 된다
defaultsdefaults.yaml(4.5.11)
metadatacolcon metadata

포함되지 않는 것 — 따로 설치해야 한다:

확장패키지증상
mixinpython3-colcon-mixincolcon build --mixin release가 unrecognized arguments로 실패
cleanpython3-colcon-cleancolcon clean이 invalid choice

함정: --mixin은 공식 문서와 튜토리얼에 자주 나오는데 기본 설치에 없다. colcon-common-extensions를 설치했으니 다 있으리라 여기고 --mixin release를 쳤다가 인자 오류를 만나면, 옵션 철자를 의심하기 쉽다. 동사 목록을 먼저 보라 — colcon --help의 선택지에 mixin이 없으면 확장이 없는 것이다(실행 확인 — 설치 전 10종 → 설치 후 11종).

4.5.15.1 패키지 선택 옵션의 전체 형태

--packages-select 계열은 실제로 20종이다(실행 확인). 규칙이 보이면 외울 필요가 없다.

축값
동작select(이것만) · skip(이것 빼고) · ignore(전혀 못 본 척)
범위up-to(의존까지) · above(이것에 의존하는 것까지) · above-and-dependencies · above-depth
지정 방식이름 나열 · -regex(정규식) · -by-dep(의존 관계로)
상태select-build-failed · skip-build-finished · select-test-failures · skip-test-passed

상태 기반 넷이 실무에서 특히 값지다. 큰 워크스페이스에서 빌드가 중간에 깨졌을 때:

colcon build --packages-skip-build-finished   # 이미 된 것 건너뛰고 이어서
colcon test --packages-select-test-failures   # 실패한 테스트만 다시

--packages-start·--packages-end도 있어, 위상 정렬 순서에서 구간을 잘라 빌드할 수도 있다.

4.5.12 유용한 확장

확장기능설치
colcon-mixin옵션 묶음(4.5.7)python3-colcon-mixin
colcon-clean정리 명령python3-colcon-clean — 별도 설치(실행 확인)
colcon-graph의존 그래프 출력별도 패키지가 아니다 — colcon-package-information이 제공하며 common-extensions에 포함(실행 확인)
colcon-cd패키지 디렉터리로 이동python3-colcon-cd
colcon-argcomplete셸 자동완성python3-colcon-argcomplete
# colcon-cd 설정
echo "source /usr/share/colcon_cd/function/colcon_cd.sh" >> ~/.bashrc
echo "export _colcon_cd_root=/opt/ros/jazzy/" >> ~/.bashrc
# 사용
colcon_cd my_pkg

# 자동완성
echo "source /usr/share/colcon_argcomplete/hook/colcon-argcomplete.bash" >> ~/.bashrc

견해: colcon-argcomplete와 colcon-cd는 설치 비용 대비 효과가 크다. 패키지 이름을 탭으로 완성할 수 있고, 대규모 워크스페이스에서 디렉터리를 찾아다니지 않아도 된다.

4.5.13 함정 카탈로그

#함정증상대처
1빌드 로그를 안 봄오류 원인 못 찾음log/latest_build/<pkg>/stdout_stderr.log
2colcon test-result 생략실패를 통과로 오인CI에 반드시 포함
3병렬도 미제한OOM으로 빌드 실패--parallel-workers
4CMAKE_BUILD_TYPE 미지정최적화 없이 빌드--cmake-args -DCMAKE_BUILD_TYPE=Release. --mixin release는 python3-colcon-mixin 설치가 먼저다(4.5.15)
5--packages-select만 사용의존 패키지 미갱신--packages-above
6메시지 변경 후 부분 빌드타입 불일치(1.3.5)사용 패키지 재빌드
7install만 삭제어중간한 상태build도 함께
8삭제한 패키지가 남아 있음유령 패키지해당 디렉터리 제거
9merge/isolated 혼용예측 불가전체 삭제 후 재빌드
10defaults.yaml을 잊음설정이 왜 다른지 모름워크스페이스에 두고 버전 관리
11rosdep 없이 빌드 시도의존 없음 오류rosdep install 먼저(4.6)
12순환 의존위상 정렬 실패인터페이스 패키지 분리
13--mixin 이 인자 오류colcon-mixin은 common-extensions에 없다python3-colcon-mixin 설치(4.5.15)

4.5.14 체크포인트

  • colcon이 하는 일과 하지 않는 일을 각각 세 가지 들 수 있는가. 의존을 설치하는 것은 무엇인가.
  • colcon이 빌드 순서를 결정할 때 읽는 파일은 무엇인가. CMakeLists.txt를 읽는가.
  • 어떤 의존 태그가 빌드 순서에 영향을 주는가. <depend> 남발이 왜 문제인가.
  • 공개 API를 바꿨을 때 써야 할 패키지 선택 옵션은 무엇인가.
  • 빌드 중 OOM이 발생하는 이유를 식으로 설명하고, 두 가지 병렬도를 구분할 수 있는가.
  • 빌드 실패 시 가장 먼저 볼 파일 경로는?
  • mixin과 defaults.yaml의 차이와 각각의 용도를 말할 수 있는가.
  • colcon test만 실행하고 test-result를 생략하면 무슨 문제가 생기는가.
  • 증분 빌드로 충분한 변경과 반드시 삭제해야 하는 변경을 각각 세 가지씩 들 수 있는가.
  • 패키지를 삭제했는데 여전히 인식되는 이유는?
  • colcon-common-extensions에 graph는 포함되는가, mixin은? --mixin이 인자 오류를 낼 때의 원인은(4.5.15).

다음 장 4.6은 colcon이 하지 않는 일 중 하나 — rosdep을 다룬다. ROS 패키지 의존이 실제 OS 패키지로 변환되는 메커니즘과, 사내 패키지·에어갭 환경에서의 운용.