[Documentation] [TitleIndex] [WordIndex

개요

CMakeLists.txt 파일은 소프트웨어 패키지 빌드를 위한 CMake 빌드 시스템의 입력 파일이다. CMake와 호환되는 패키지는 하나 이상의 CMakeLists.txt 파일을 가지며, 이 파일을 통해 어떻게 패키지 내의 코드를 빌드하고 어디에 설치할 지를 기술하게 된다. catkin 프로젝트를 위한 CMakeLists.txt 파일은 표준적인 CMakeLists.txt 파일 형식에 몇가지 추가적인 제약사항을 더한 것이다.

전체적인 구조와 순서

모든 CMakeLists.txt 파일은 반드시 아래의 형식을 따라 작성하여야 하며, 그렇지 않을 경우 정상적인 패키지 빌드가 되지 않을 수 있다.

  1. 최소 CMake 버전 요구사항 (cmake_minimum_required)

  2. 패키지 이름 (project())

  3. 빌드를 위해 필요한 다른 CMake/Catkin 패키지 (find_package())

  4. 파이썬 모듈 지원 여부 (catkin_python_setup())

  5. 메시지/서비스/액션 생성자 (add_message_files(), add_service_files(), add_action_files())

  6. 메시지/서비스/액션 생성 실행 (generate_messages())

  7. 패키지 빌드 정보 (catkin_package())

  8. 빌드할 라이브러리와 실행파일 (add_library()/add_executable()/target_link_libraries())

  9. 테스트 (catkin_add_gtest())

  10. 설치 룰 (install())

CMake 버전

모든 catkin CMakeLists.txt 파일은 반드시 최소로 필요한 CMake 버전을 기술하는 것으로 시작한다. Catkin은 CMake 버전 2.8.3 이상을 필요로 한다.

cmake_minimum_required(VERSION 2.8.3)

패키지 명

그 다음 항목은 퍄키지 명이며, CMake project 함수로 작성한다. robot_brain 패키지를 만들 경우 아래와 같이 작성하면 된다.

project(robot_brain)

참고로 CMake에서는 프로잭트 명을 ${PROJECT_NAME} 변수로 정의하여 사용할 수도 있다.

빌드에 필요한 CMake 패키지 찾기

그 다음에는 CMake find_package 함수를 이용하여 패키지 빌드를 위해 사전에 필요한 다른 의존 패키지를 기술한다. catkin 패키지를 위해서는 항상 catkin 패키지가 필요하므로 기본적으로 아래 문장이 들어가야 한다.

find_package(catkin REQUIRED)

프로젝트에 다른 의존성을 가진 패키지가 필요할 경우, 그러한 패키지들은 자동적으로 CMake 컴포넌트로 변회된다. 그런 패키지를 위해 find_package 함수를 쓰는 것 대신에 컴포넌트로 정의하는 것이 좀더 편할 수 있다. 예를 들어 nodelet 패키지를 사용하는 경우 다음과 같이 할 수 있다.

find_package(catkin REQUIRED COMPONENTS nodelet)

NB: find_package components 는 빌드 의존성에만 사용한다. 즉 런타임 의존성을 위해서 사용하지 않는다.

또는 아래와 같이 할 수도 있다.

find_package(catkin REQUIRED)
find_package(nodelet REQUIRED)

그러나 곧 이와 같은 방식이 불편하다는 사실을 알게 될 것이다.

find_package()의 동작 방식

CMake가 find_package 함수를 통해 의존 패키지를 찾으면, CMake는 찾은 패키지에 대한 정보를 담은 몇 가지 환경 변수를 생성하고, 이를 이후 CMake 스크립트에서 사용하게 된다. 패키지의 헤더 파일과 소스 코드의 위치, 패키지가 사용하는 라이브러리 정보와 경로가 환경 변수에 저장된다. 환경 변수는 아래와 같으며, <NAME>은 <PACKAGE NAME>_<PROPERTY>의 형식을 따른다.

Catkin 패키지는 왜 Component로 기술되는가?

Catkin 패키지는 실제로는 catkin 자체의 컴포넌트는 아니다. 그보다는 CMake의 component 기능을 활용하여 catkin이 타이핑의 수고를 덜어주는 설계를 택했다고 볼 수 있다. catkin 패키지를 find_package 함수를 이용하여 컴포넌트 만들면, 패키지의 환경 변수가 catkin_ 접두사를 가진 하나의 세트로 관리되어 편리할 수 있다. 코드에서 nodelet 패키지를 사용하며, 아래와 같이 패키지를 찾는다고 생각해보자.

find_package(catkin REQUIRED COMPONENTS nodelet)

이렇게 하면, nodelet에 대한 헤더 파일 경로, 라이브러리 정보 등이 catkin_ 환경 변수에 적용될 것이다. 즉 catkin_INCLUDE_DIRS이 catkin 자체에 대한 헤더 파일 경로 뿐 아니라, nodelet을 위한 것도 함께 갖게 된다는 뜻이다. 이렇게 해두면 나중에 편리할 수 있다.

물론 위와 같이 하지 않고, nodelet 패키지 자체만으로 find_package 함수를 사용할 수도 있다.

find_package(nodelet)

위와 같이 하면 nodelet_INCLUDE_DIRS, nodelet_LIBRARIES 등과 같이 별도의 환경 변수 세트가 생성될 것이다.

Boost

C++과 Boost를 사용하고 있다면, Boost를 위한 find_package() 구문을 추가하여야 하며, Boost 중 어떤 부분을 사용할지를 아래 예시와 같이 기술하여야 한다. 아래는 Boost의 thread를 사용하는 경우이다.

find_package(Boost REQUIRED COMPONENTS thread)

catkin_package()

catkin_package() 는 catkin이 제공하는 CMake 매크로 함수로 catkin 관련 정보를 빌드 시스템에 전달하여 pkg-config와 CMake 파일을 생성하기 위해 필요하다. 이 함수는 반드시 add_library() or add_executable()로 빌드 타겟을 선언하기 전에 호출하여야 하며, 5 개의 선택가능한 인자를 가진다.

전체 매크로에 대한 상세 설명은 이 곳을 참조 바람

아래는 이해를 돕기 위한 예시이다.

catkin_package(
   INCLUDE_DIRS include
   LIBRARIES ${PROJECT_NAME}
   CATKIN_DEPENDS roscpp nodelet
   DEPENDS eigen opencv)

위 예시는 패키지 폴더 내 "include" 폴더에 헤더를 위치시킨다. 라이브러리 이름은 ${PROJECT_NAME} 환경 변수를 따르는데, 이는 앞에서 project() 함수로 지정해 둔 것이다. 또한 이 패키지를 빌드하고 실행하기 위해서는 "roscpp"와 "nodelet" 패키지가 필요하며, 외부 의존성으로 "eigen"과 "opencv"를 사용하는 것을 볼 수 있다.

빌드 타겟 정의하기

여러 가지 형태로 빌드 타겟을 정의할 수 있으나, 아래 두 가지 방법 중 하나를 사용한다.

타겟 명명

우선 catkin에서 빌드 타겟의 이름은 어느 폴더에 빌드/설치되느냐와 관계 없이 중복되지 않는 유일한 것이어야 한다. 이는 CMake의 규칙이다. 타겟의 이름을 변경하고자 할때에는 아래와 같이 set_target_properties() 함수를 사용하면 된다.

set_target_properties(rviz_image_view
                      PROPERTIES OUTPUT_NAME image_view
                      PREFIX "")

위와 같이 하면 타겟의 이름이 rviz_image_view에서 image_view로 변경되어 빌드/설치 결과물에 반영된다.

출력 디렉토리 설정

실행 파일과 라이브러리를 위한 기본 출력 디렉토리가 정해져 있으나 특별한 경우 이를 원하는 대로 수정할 수 있다. 예로 파이썬 바인딩이 필요한 라이브러리의 경우 파이선 import가 가능한 폴더에 위치해야 할 수 있다.

set_target_properties(python_module_library
  PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION})

Include 경로와 Library 경로

타겟을 정의하기 전에 빌드에 필요한 헤더 파일이나 라이브러리 등의 경로에 대한 정보를 기술해 두어야 한다.

include_directories()

include_directories()의 인자는 앞서 find_package() 호출 시 생성된 *_INCLUDE_DIRS 환경 변수와 추가로 지정한 디렉토리 경로가 반영된다. catkin과 Boost를 사용하는 경우, 아래와 같이 기술하면 된다.

include_directories(include ${Boost_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS})

첫번째 인자인 "include"는 패키지 폴더내 include/ 디렉토리 또한 경로로 추가됨을 의미한다.

라이브러리 경로를 추가하기 위해 CMake link_directories() 함수를 사용할 수 있으나, 추천되는 방법은 아니다. 왜냐하면 find_package() 실행 시 모든 catkin과 CMake 패키지를 위한 link 정보를 얻게 되기 때문이다. target_link_libraries()를 이용하여 라이브러리를 링크하면 된다.

link_directories(~/my_libs)

target_link_libraries()link_directories()의 상세한 차이에 대해서는 이 글을 참조할 수있다.

실행 파일 타겟

빌드할 실행 파일 타겟을 기술하기 위해 add_executable() CMake 함수를 사용한다.

add_executable(myProgram src/main.cpp src/some_file.cpp src/another_file.cpp)

위 예제는 3 개의 소스 파일(src/main.cpp, src/some_file.cpp, src/another_file.cpp)을 빌드하여 myProgram이라는 이름의 실행 파일을 만들어낸다.

라이브러리 타겟

add_library() CMake 함수를 이용하여 빌드할 라이브러리를 기술한다. 기본적으로 catkin은 공유 라이브러리로 빌드한다.

add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SRCS})

target_link_libraries() 함수로 실행 파일 타겟과 링크될 라이브러리를 지정한다. 일반적으로 add_executable() 호출 이후에 위치하며, 만약 ros is not found라는 오류가 확인되면${catkin_LIBRARIES}를 추가한다. .

문법:

target_link_libraries(<executableTargetName>, <lib1>, <lib2>, ... <libN>)

예시:

add_executable(foo src/foo.cpp)
add_library(moo src/moo.cpp)
target_link_libraries(foo moo)

메시지, 서비스, 액션 타겟

ROS의 메시지 파일(.msg), 서비스 파일(.srv), 액션 파일(.action)을 사용하려면 패키지 빌드 전에 이들 파일을 위한 특별한 전처리 빌드 단계가 필요하다. 매크로를 통해 사용하는 프로그래밍 언어에 적합한 형태로 메시지, 서비스, 액션을 전처리하게 된다. 빌드 시스템은 gencpp, genpy, genlisp 등의 생성 도구를 이용하여 바인딩을 위한 전처리를 수행한다.

아래와 같이 매시지, 서비스, 액션 각각을 위한 세 개의 매크로가 제공된다.

이 매크로 뒤에 아래와 같이 생성을 시작하는 함수를 사용한다.

 generate_messages()

주의사항/제약사항

 find_package(catkin REQUIRED COMPONENTS ...)
 add_message_files(...)
 add_service_files(...)
 add_action_files(...)
 generate_messages(...)
 catkin_package(...)
 ...

catkin_package(
 ...
 CATKIN_DEPENDS message_runtime ...
 ...)

find_package(catkin REQUIRED COMPONENTS message_generation)

  add_dependencies(some_target ${catkin_EXPORTED_TARGETS})

그렇게 해야 필요한 순서대로 의존성이 만족되는 결과를 얻을 수 있다. (아래에서 some_target은 add_executable()로 설정한 실행 파일 타겟 이름이다:

  add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS})

  add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

예시

아래 예시의 CMakeLists.txt는 패키지 내 "msg" 디렉토리 안에 "MyMessage1.msg"와 "MyMessage2.msg" 두 개의 메시지 파일이 있고, 이 메시지가 std_msgssensor_msgs에 의존성을 갖고 있으며, 또한 패키지가 "srv" 디렉토리 안에 "MyService.srv"라는 서비스 파일을 가지고 있고, 위 메시지와 서비스를 사용하는 message_program이라는 실행 파일 타겟을 가지고 있고, 동시에 does_not_use_local_messages_program이라는 일부 ROS 기능을 사용하지만, 메시지나 서비스를 정의하여 사용하지 않는 실행 파일 타겟도 가지는 경우의 것이다.

   1   # Get the information about this package's buildtime dependencies
   2   find_package(catkin REQUIRED
   3     COMPONENTS message_generation std_msgs sensor_msgs)
   4 
   5   # Declare the message files to be built
   6   add_message_files(FILES
   7     MyMessage1.msg
   8     MyMessage2.msg
   9   )
  10 
  11   # Declare the service files to be built
  12   add_service_files(FILES
  13     MyService.srv
  14   )
  15 
  16   # Actually generate the language-specific message and service files
  17   generate_messages(DEPENDENCIES std_msgs sensor_msgs)
  18 
  19   # Declare that this catkin package's runtime dependencies
  20   catkin_package(
  21    CATKIN_DEPENDS message_runtime std_msgs sensor_msgs
  22   )
  23 
  24   # define executable using MyMessage1 etc.
  25   add_executable(message_program src/main.cpp)
  26   add_dependencies(message_program ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
  27 
  28   # define executable not using any messages/services provided by this package
  29   add_executable(does_not_use_local_messages_program src/main.cpp)
  30   add_dependencies(does_not_use_local_messages_program ${catkin_EXPORTED_TARGETS})

여기에 추가로 만약 "action" 디렉토리에 "MyAction.action" 파일을 두고 actionlib 액션을 빌드하려면, actionlib_msgs를 넣고자 한다면 generate_messages(...) 전에 아래를 호출해 주어야 한다.

add_action_files(FILES
  MyAction.action
)

물론 패키지는 actionlib_msgs에 대한 빌드 의존성을 가져야 한다.

파이썬 모듈 지원 활성화

ROS 패키지에 파이썬 모듈이 포함되는 경우, setup.py 파일이 필요하며, 아래와 같은 호출이 CMakeLists.txt에 추가되어야 하며, 그 위치는 generate_messages()catkin_package() 호출 전이어야 한다.

catkin_python_setup()

단위 시험

gtest 기반의 단위시험을 지원하기 위한 catkin_add_gtest()라는 catkin에만 존재하는 매크로가 있다.

if(CATKIN_ENABLE_TESTING)
  catkin_add_gtest(myUnitTest test/utest.cpp)
endif()

추가적으로 선택 가능한 단계: 설치 타겟 정의하기

빌드가 완료되면, 빌드 타겟이 workspace 내의 build 영역에 생성된다. 그러나 이 빌드된 결과를 편리하게 사용하려면 workspace가 아닌 시스템 레벨의 설치 경로로 설치해주는 것이 필요하다. (설치 경로에 대한 정보는 REP 122에서 확인할 수 있다.) 다시 말하면 "make install" 과정을 위해 설치 타겟의 경로 위치를 지정해 주어야 한다는 의미이다.

설치 타겟을 정의하는 작업은 CMake install() 함수로 하면 되며, 아래와 같은 인자를 사용할 수 있다.

  • TARGETS - 설치할 타겟

  • ARCHIVE DESTINATION - 정적 라이브러리와 DLL 경로

  • LIBRARY DESTINATION - Non-DLL 공유 라이브러리와 모듈 경로

  • RUNTIME DESTINATION - 실행 파일 타겟과 윈도우 DLL 형식의 공유 라이브러리 경로

아래 예시들을 참조한다.

install(TARGETS ${PROJECT_NAME}
  ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
)

install(TARGETS ${PROJECT_NAME}_node
  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

위와 같은 표준 설치 경로 외에 몇몇의 파일은 특수한 폴더에 설치되어야 하는 경우도 있을 수 있다. 예를 들어 파이썬 바인딩을 포함하는 라이브러리는 아래와 같이 파이썬 환경에서 import 가능한 위치에 설치되어야 한다.

install(TARGETS python_module_library
  ARCHIVE DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
  LIBRARY DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
)

파이썬 실행 스크립트 설치하기

파이썬 코드용 설치 룰은 add_library()add_executable() 함수를 사용하지 않으므로 다소 다르며, 아래와 같이 하면 된다.

catkin_install_python(PROGRAMS scripts/myscript
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})

파이썬 스크립트와 모듈을 설치하는 방법과 폴더 구조에 대한 상세한 정보 예시는 catkin manual을 참조하기 바란다.

단지 파이썬 스크립트만 설치하고 모듈을 제공하지는 않는다면, 위에서 언급된 setup.py 파일을 만들거나 catkin_python_setup()을 호출할 필요는 없다.

헤더파일 설치하기

헤더 파일 또한 "include" 폴더에 설치되어야 한다. 이를 위해 대개 폴더 전체를 설치하는 방법을 사용한다. (폴더 내에 포함된 SVN 폴더처럼 설치가 필요하지 않은 파일 패턴을 필터링하여 설치할 수도 있다. ) 아래와 같은 설치 룰을 참조하기 바란다.

install(DIRECTORY include/${PROJECT_NAME}/
  DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
  PATTERN ".svn" EXCLUDE
)

패키지 명으로 헤더 파일을 구분할 필요가 없다면 아래와 같이 할 수 있다.

install(DIRECTORY include/
  DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION}
  PATTERN ".svn" EXCLUDE
)

roslaunch 파일 등 설치하기

${CATKIN_PACKAGE_SHARE_DESTINATION}을 이용하여 launch 파일 등 기타 리소스 파일의 위치를 정한다.

install(DIRECTORY launch/
  DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
  PATTERN ".svn" EXCLUDE)

2024-09-28 14:18