제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이 하는 일과 안 하는 일:
| 하는 일 | 안 하는 일 |
|---|---|
| 패키지 탐색·의존 정렬 | 컴파일(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-info | RelWithDebInfo |
compile-commands | compile_commands.json 생성 |
ccache | ccache 사용(재빌드 가속) |
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과의 차이:
| 축 | mixin | defaults.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-information | colcon graph·colcon info·colcon list | colcon-graph라는 패키지를 찾게 되는데 그런 패키지는 없다. 기본으로 이미 있다 |
package-selection | --packages-select·--packages-up-to 계열 전부 | 이것이 없으면 부분 빌드 자체가 안 된다 |
defaults | defaults.yaml(4.5.11) | |
metadata | colcon metadata |
포함되지 않는 것 — 따로 설치해야 한다:
| 확장 | 패키지 | 증상 |
|---|---|---|
mixin | python3-colcon-mixin | colcon build --mixin release가 unrecognized arguments로 실패 |
clean | python3-colcon-clean | colcon 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 |
| 2 | colcon test-result 생략 | 실패를 통과로 오인 | CI에 반드시 포함 |
| 3 | 병렬도 미제한 | OOM으로 빌드 실패 | --parallel-workers |
| 4 | CMAKE_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) | 사용 패키지 재빌드 |
| 7 | install만 삭제 | 어중간한 상태 | build도 함께 |
| 8 | 삭제한 패키지가 남아 있음 | 유령 패키지 | 해당 디렉터리 제거 |
| 9 | merge/isolated 혼용 | 예측 불가 | 전체 삭제 후 재빌드 |
| 10 | defaults.yaml을 잊음 | 설정이 왜 다른지 모름 | 워크스페이스에 두고 버전 관리 |
| 11 | rosdep 없이 빌드 시도 | 의존 없음 오류 | 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 패키지로 변환되는 메커니즘과, 사내 패키지·에어갭 환경에서의 운용.