# Binaries 배포와 Install Rule 설정

ROS2 Humble 환경에서 패키지를 배포하려면, 소스 코드 형태를 배포하는 방식과 바이너리 형태로 패키징하여 배포하는 방식을 모두 고려해야 한다. 특히 다양한 운영체제(Ubuntu, Windows, macOS 등)를 대상으로 개발된 경우에는, 바이너리를 통해 간단하게 설치할 수 있도록 제공하는 것이 편의성 측면에서 유리하다. ROS2는 ament, colcon 같은 툴체인을 이용하여 소스 코드를 빌드하고 설치 규칙을 정의함으로써 바이너리를 생성하고 배포하도록 지원한다.

하지만 바이너리를 생성한다는 것은 단순히 빌드가 완료된 실행 파일과 라이브러리를 모아두는 것뿐만 아니라, 올바른 설치 경로 설정, 패키지 메타정보(패키지명, 버전, 종속성) 반영, 그리고 배포 후 설치 시 환경 변수나 실행 경로가 문제없이 동작하도록 해야 함을 의미한다. 본 장에서는 ROS2 Humble에서 제공되는 기본적인 도구를 활용하여 바이너리를 생성하고 배포하는 과정과, CMakeLists.txt를 통해 설정하는 Install Rule에 대해 자세히 살펴본다.

#### ROS2 바이너리 배포 개념

* **소스 코드 배포**: GitHub 등 저장소에 공개하여 사용자가 직접 빌드하도록 하는 방식. 빌드 환경을 구성해야 하므로 초심자나 환경이 다른 사용자는 어려움을 겪을 수 있다.
* **바이너리 배포**: 운영체제별로 빌드된 결과물을 패키지로 제공하므로, 설치 시점에 빌드 과정을 거치지 않아도 된다. 특히 리눅스에서는 .deb나 .rpm, 윈도우즈에서는 .msi, macOS에서는 .pkg 형태로 패키징하여 제공하기도 한다. ROS2 Humble 버전에서는 colcon 툴과 ament 빌드시스템 기반으로 바이너리 생성 및 설치 스크립트를 자동화할 수 있다.

#### 패키징 개념과 colcon의 역할

colcon은 ROS2에서 사용되는 빌드 툴로, 여러 개의 패키지를 동시에 빌드 및 설치할 수 있도록 관리한다. 예컨대 여러 패키지를 포함한 멀티 패키지 환경에서 다음과 같이 빌드와 설치를 진행할 수 있다:

```
colcon build --symlink-install
```

위 명령어는 소스 코드 디렉터리 구조를 심볼릭 링크로 연결하여 빌드 결과물을 즉시 반영할 수 있게 해준다. 실제 바이너리를 배포하기 위해서는 `--symlink-install` 옵션을 제거한 뒤, 명령어를 실행해 빌드 산출물이 최종 설치 위치에 복사되도록 하는 것이 일반적이다:

```
colcon build
```

colcon 빌드 과정이 끝나면, 해당 빌드 결과물이 `install/` 디렉터리 아래에 구조화되어 저장된다. 이 설치 결과물을 이용해 `.deb` 등 배포 패키지를 만드는 과정을 자동화할 수도 있다.

#### ament와 CMake 설정

ROS2 패키지는 보통 `ament_cmake` 빌드시스템을 사용한다. CMakeLists.txt에서 ament 모듈을 불러오고, 헤더/라이브러리/실행 파일 등 다양한 리소스를 설치하고 싶을 때 Install Rule을 설정한다. 예를 들어 다음과 같은 코드 조각을 보자:

```cmake
cmake_minimum_required(VERSION 3.8)
project(my_package)

find_package(ament_cmake REQUIRED)

# 실행 파일 빌드
add_executable(my_node src/my_node.cpp)
ament_target_dependencies(my_node
  rclcpp
  std_msgs
)

# 라이브러리 빌드
add_library(my_lib SHARED src/my_lib.cpp)
ament_target_dependencies(my_lib
  rclcpp
  some_other_dependency
)

# 실행 파일 설치 규칙
install(
  TARGETS my_node
  DESTINATION lib/${PROJECT_NAME}
)

# 라이브러리 설치 규칙
install(
  TARGETS my_lib
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin
)

ament_package()
```

위 예시는 가장 기본적인 형태로, 빌드된 실행 파일(my\_node)과 라이브러리(my\_lib)에 대한 설치 규칙(install 명령어)을 명시한다. 이렇게 설정해두면 colcon 빌드 시 `install/` 경로에 자동으로 필요한 파일이 배치된다. 해당 경로 구조는 ROS2 설치 규칙을 따른 형태로 구성된다.

#### ROS2 바이너리 배포 형식

ROS2 Humble에서는 아래와 같은 형태로 바이너리가 구성될 수 있다:

* **라이브러리(lib/)**: ROS2 노드가 링크해야 할 동적/정적 라이브러리가 저장된다.
* **실행 파일(lib/\<package\_name>/)**: 노드 실행 파일이 `lib/<패키지명>/` 경로 아래에 위치하는 것이 권장된다.
* **include/**: 헤더 파일이 필요한 경우엔 여기에 배치하여 타 패키지에서 `#include <something>`로 활용하도록 할 수 있다.
* **share/**: 패키지 매니페스트(package.xml), launch 파일, URDF, 기타 설정 파일 등이 위치한다. 특히 ROS2에서 ament 패키지는 `share/<package_name>` 안에 패키지 정보를 가진다.

이러한 구조로 정리된 바이너리는 `.deb` 형태로 배포될 수 있으며, 이하의 명령어를 통해 설치할 수 있다:

```
sudo dpkg -i ros-humble-<package_name>_<version>_amd64.deb
```

(운영체제 환경에 따라 x86\_64, arm64 등 아키텍처 명칭은 달라질 수 있다.)

#### 바이너리 배포 자동화와 Deb 패키지 생성

colcon 빌드 후에는 `install/` 디렉터리 내에 모든 빌드 산출물이 적절한 구조로 배치된다. 이를 기반으로 Debian 패키지(.deb)를 생성하려면, ROS2 빌드시스템에서 제공하는 추가 확장도구나 스크립트를 사용할 수 있다. 대표적으로 다음과 같은 접근 방식이 있다:

1. **Bloom**을 이용하는 방식
   * ROS1에서 ROS-Buildfarm과 함께 사용되던 bloom 툴은 ROS2에서도 사용 가능하다.
   * 패키지 메타데이터(package.xml)를 읽고, 자동으로 .deb 패키지 생성 스크립트를 만들어준다.
   * GitHub 저장소에서 release 태그를 기반으로 메타정보를 구성하고, Ubuntu PPA 등을 통해 배포할 수 있다.
2. **수동 스크립트 기반 생성**
   * `dpkg-deb`와 같은 리눅스 표준 도구를 이용해 수동으로 디렉터리 구조를 준비하고 패키지를 생성할 수도 있다.
   * ROS2 빌드 산출물이 있는 `install/` 디렉터리를 기반으로, `DEBIAN/control` 파일을 비롯한 필요한 파일들을 준비한 뒤 `dpkg-deb --build <폴더>` 명령을 수행한다.
   * 이 경우 패키지 종속성 관계를 수동으로 설정해야 하므로, package.xml의 정보를 자동으로 반영하기가 다소 번거롭다.

**bloom을 이용한 예시**

아래 과정은 매우 간단한 예시에 불과하므로, 실제로는 ROS2 공식 문서를 참고하여 준비 과정을 진행하는 것이 권장된다:

package.xml에 패키지의 필수 정보(이름, 버전, 라이선스, 종속성 등)를 정확히 기입한다.

Git 저장소에서 릴리스 태그(예: v0.1.0)를 생성하고 커밋한다.

로컬 환경에서 bloom을 설치한 뒤, 아래와 같이 실행한다:

```
bloom-generate rosdebian --ros-distro humble
```

생성된 Debian control 파일 및 기타 스크립트를 확인한 다음, 빌드/배포를 진행한다:

```
fakeroot debian/rules binary
```

#### macOS, Windows 대상 패키징 고려

ROS2 Humble 환경에서 macOS와 Windows에 대한 바이너리 배포는 리눅스에 비해 다소 절차가 복잡할 수 있다.

* **macOS**: .pkg나 .dmg 파일로 배포가 일반적이며, Homebrew 탭을 등록하는 방식을 취하기도 한다.
* **Windows**: .msi 형태의 설치파일을 만들어 배포하거나, Zip 아카이브를 풀어서 사용할 수 있도록 제공하기도 한다.

이러한 각 플랫폼별로 패키징 도구가 상이하므로, 실제 현업에서는 Docker나 CI 파이프라인을 이용하여 여러 환경에서 자동 빌드하고, 산출물을 각각 올바른 형태로 패키징해 배포한다.

#### 추가 Install Rule 설정

기본적으로 `CMakeLists.txt`에서 `install(TARGETS ...)`를 통해 실행 파일과 라이브러리를 설치해왔다. 그런데 실제 패키지 배포를 위해서는 다음과 같은 다양한 리소스도 함께 설치해야 할 수 있다.

헤더 파일: 주로 C++ 라이브러리 제공 시 필요하다.

```cmake
install(
  DIRECTORY include/
  DESTINATION include
)
```

Python 스크립트: Python 노드를 제공하는 경우, `install(PROGRAMS ...)` 구문으로 실행 권한을 포함해 설치한다.

```cmake
install(
  PROGRAMS scripts/my_python_node
  DESTINATION lib/${PROJECT_NAME}
)
```

런치 파일(launch/)이나 설정 파일(config/): 예를 들어 launch 디렉터리에 있는 모든 `.launch.py` 파일을 설치하고 싶다면:

```cmake
install(
  DIRECTORY launch/
  DESTINATION share/${PROJECT_NAME}/launch
)
```

resource 폴더, msg/srv/action 인터페이스 등: ROS2에서 생성되는 메시지나 서비스 정의 파일, 또는 실행 중 필요한 리소스(이미지, URDF, YAML 구성 파일)도 배포할 수 있다.

```cmake
install(
  DIRECTORY resource/
DESTINATION share/${PROJECT_NAME}/resource
)
```

#### Cross-Compile 시 고려사항

만약 임베디드 장치나 다른 아키텍처(예: arm64)용 바이너리를 빌드하려면, `colcon`이 설치된 메인 PC(예: x86\_64 Ubuntu)에서 cross-compile 환경을 준비해야 한다. 이를 위해서는 다음과 같은 항목을 고려한다:

1. **toolchain 파일**: CMake Toolchain 파일을 작성하고, 툴체인 내에 컴파일러 경로, sysroot 경로 등을 설정한다.
2. ament\_cmake 환경 변수:

   `CMAKE_TOOLCHAIN_FILE` 을 colcon에 전달하여 빌드 시점에 크로스 컴파일러를 사용하도록 한다.

```
 colcon build \
     --merge-install \
     --cmake-args \
       -DCMAKE_TOOLCHAIN_FILE=/path/to/my_toolchain.cmake \
       -DCMAKE_BUILD_TYPE=Release
```

3. **다른 아키텍처용 종속성 해결**: 종속성이 되는 시스템 라이브러리나 ROS2 기반 라이브러리도 동일 아키텍처로 준비되어 있어야 한다. 이를 위해 Docker나 QEMU를 활용하기도 한다.

#### ament\_python을 사용하는 Python 패키지 배포

ROS2 패키지는 C++뿐 아니라 Python 기반 노드를 포함할 수도 있다. 이때는 `ament_cmake` 대신(또는 함께) `ament_python` 빌드시스템을 사용하여 Python 패키지를 빌드 및 배포할 수 있다. Python 스크립트를 단순 복사하는 방식과 달리, 표준 파이썬 패키지 구조(`setup.py` 등)를 따르도록 구성하면 배포 시 의존성 관리와 설치 프로세스를 한층 편리하게 만들 수 있다.

**기본 디렉터리 구조**

파이썬 ROS2 패키지를 구성할 때 일반적으로 권장되는 구조는 다음과 같다:

```
my_python_package/
├─ package.xml
├─ setup.py
├─ setup.cfg
├─ resource/
│   └─ my_python_package
├─ my_python_package/
│   ├─ __init__.py
│   ├─ my_node.py
│   └─ ...
└─ tests/
└─ test_my_node.py
```

* `my_python_package/` 내부에 실제 파이썬 모듈이 위치한다.
* `setup.py`와 `package.xml`에서 이 패키지의 이름, 버전, 의존성 등을 지정한다.
* `resource/` 디렉터리에는 ament가 필요로 하는 패키지 정보 파일이 생성될 수 있다.

**package.xml 예시**

아래는 Python 패키지 예시에 해당하는 package.xml 스니펫이다:

```xml
<package format="3">
  <name>my_python_package</name>
  <version>0.1.0</version>
  <description>Example Python package for ROS2 Humble</description>
  <maintainer email="user@example.com">User Name</maintainer>
  <license>Apache-2.0</license>

  <buildtool_depend>ament_python</buildtool_depend>
  <exec_depend>rclpy</exec_depend>
  <exec_depend>some_other_python_dep</exec_depend>

  <export>
    <build_type>ament_python</build_type>
  </export>
</package>
```

* `<buildtool_depend>`에 `ament_python`을 선언해야 한다.
* 실행 시 필요한 Python 라이브러리를 `<exec_depend>`로 추가한다(예: `rclpy`, `numpy` 등).

**setup.py 예시**

파이썬 표준 distutils/setuptools 방식에 맞추어 `setup.py`를 작성한다:

```python
from setuptools import setup

package_name = 'my_python_package'

setup(
    name=package_name,
    version='0.1.0',
    packages=[package_name],
    data_files=[
        ('share/' + package_name, ['package.xml']), 
    ],
    install_requires=['setuptools'],  
    zip_safe=True,
    maintainer='User Name',
    maintainer_email='user@example.com',
    description='Example Python package for ROS2 Humble',
    license='Apache-2.0',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'my_node = my_python_package.my_node:main'
        ],
    },
)
```

* `packages` 항목에 실제 파이썬 패키지 디렉터리를 명시한다.
* `data_files`로 `package.xml`을 공유 디렉터리(`share/<package_name>`)에 복사하도록 설정한다.
* `entry_points`를 통해 ROS2 실행 파일(즉, 노드)로 사용할 스크립트를 지정한다.

**빌드 및 설치**

위와 같은 구조와 설정을 마친 뒤, colcon 명령어로 빌드를 진행한다:

```
colcon build
```

* `ament_python`이 자동으로 setup.py를 읽고 설치 과정을 수행한다.
* 설치가 완료되면 `install/` 디렉터리에 파이썬 패키지와 콘솔 스크립트가 배치된다.
* `source install/setup.bash`를 실행 후 `my_node` 명령어를 통해 노드를 실행할 수 있다.

**Install Rule의 Python 관점**

C++ 프로젝트에서는 `CMakeLists.txt`에 install() 명령을 직접 명시했다. Python 프로젝트에서는 대부분 setup.py에서 필요한 항목들을 자동으로 처리한다. 그러나 다음과 같이 CMakeLists.txt를 추가로 사용하는 경우도 있다:

```cmake
cmake_minimum_required(VERSION 3.8)
project(my_python_package)

find_package(ament_python REQUIRED)
ament_python_install_package(${PROJECT_NAME})

# 스크립트나 리소스 폴더 등을 복사하고 싶다면:
install(
  DIRECTORY launch
  DESTINATION share/${PROJECT_NAME}
)

ament_package()
```

* `ament_python_install_package()`는 setup.py 기반의 설치를 수행한다.
* 별도의 launch, config, resource 디렉터리가 있다면 일반 CMake install 규칙으로 추가 설치 처리를 해준다.

#### 런타임 환경 구성

바이너리 배포 후, 사용자가 ROS2 노드를 쉽게 실행할 수 있으려면 다음 환경 구성이 잘 이뤄져야 한다:

* `setup.bash`(또는 setup.zsh, setup.ps1) 스크립트를 통해 `$PATH`, `$PYTHONPATH`, `$AMENT_PREFIX_PATH` 등이 설정되어야 한다.
* 각 노드(실행 파일, Python console script)가 올바른 경로에서 참조될 수 있도록 설치 대상 디렉터리 구조가 일관적으로 유지되어야 한다.
* 런치 파일을 통해 여러 노드를 동시에 실행해야 한다면, 설치 경로에 위치한 런치 파일이 `$AMENT_PREFIX_PATH`를 통해 검색 가능하도록 해야 한다.

\--- 전 점검 포인트

* **package.xml**: 버전, 이름, 종속성, 빌드 유형(`ament_cmake`, `ament_python`) 등의 필드가 정확한가?
* **setup.py**: 파이썬 패키지 정보와 entry\_points가 원하는 대로 정의되어 있는가?
* **Install Rule**: C++ 실행 파일, Python 스크립트, 메시지/서비스/액션 파일, 리소스, 런치 스크립트가 올바른 경로에 배포되는가?
* **배포 대상 OS**: 윈도우, macOS, 리눅스 등 플랫폼별 빌드/설치/실행 경로에 문제가 없는지?
* **버전 관리**: ROS-Buildfarm, GitHub Actions 등 CI/CD에서 자동 빌드-배포가 가능한가?
