(미완성)평범한 개발자의 C 프로그래밍 이야기

make - cmake 소개

 

마지막으로 cmake를 소개하면서 빌드 관련 내용을 마무리하겠습니다. make는 사실 과도하게 복잡합니다. 아마 이전 장들을 보시면서 공감하셨을 겁니다. 그런데 과도하게 복잡한 만큼 많이 편리한 것도 아닙니다. 생성될 타겟에 의존적인 파일들을 자동으로 찾아주는 것도 아니고, 빌드 옵션을 자동으로 생성해주는 것도 없고 거의 모든 것을 직접 작성해야합니다. 사실 너무나 오래전에 만들어진 툴이다보니 편의성면에서는 많이 부족한게 사실입니다. 이런 단점들을 극복하고자 make을 자동으로 생성해주는 툴이 많이 나오고 있습니다. 리눅스/유닉스 환경에서는 autotools가 가장 많이 쓰인다고 하지만 autotools자체도 make보다 많이 편리하지는 않고 배우는 것조차 일입니다. 최근에는 autotools의 대안으로 파이썬기반의 SCons나 Waf도 많이 사용되고 있고, 멀티플랫폼을 지원하며 KDE에서 사용되는 것으로 유명해진 cmake도 많이 사용되고 있습니다. 이번 장에서는 configure나 make는 전혀 사용하지 않고 cmake만으로 빌드하고 테스트를 실행해보겠습니다.

cmake를 사용하기 위해서는 CMakeLists.txt라는 파일을 만들어서 cmake에게 명령을 내려야합니다. 우리가 빌드할 파일은 크게 라이브러리 파일과 유닛테스트 파일로 나눌 수 있으므로 CMakeLists.txt src/CMakeLists.txt test/CMakeLists.txt 등으로 3개의 CMakeLists.txt파일을 만들겠습니다.

cmake는 기본적으로 변수를 설정하고 cmake가 제공하는 함수에 설정된 변수를 전달하는 형태가 많이 사용됩니다. 라이브러리에 들어갈 소스 파일들의 이름을 변수에 저장하고 라이브러리를 빌드하는 함수에 이 변수를 전달하면, 자동으로 해당 파일들을 찾아서 라이브러리로 빌드합니다. 실행 파일을 만들 때도 실행 파일에 들어갈 소스 파일 이름과 실행 파일의 이름을 빌드 함수에 전달하면 실행 파일을 빌드합니다. 기본적인 사용법은 아주 간단하므로 예제 파일을 보면서 자세한 설명을 하겠습니다.

가장 먼저 만들 파일은 소스에서 최상위 디렉토리에 있는 CMakeLists.txt입니다. 이 파일은 다른 모든 빌드 과정에서 사용될 변수들을 설정하고 하위 디렉토리에있는 CMakeLists.txt파일을 읽어오는 일을 합니다.

CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
cmake 버전을 2.6이상만 사용합니다. 제가 테스트한 버전은 2.8입니다.

PROJECT (calib_project)
프로젝트 이름입니다. 원하는 이름 아무거나 할 수 있습니다.

SET(CMAKE_VERBOSE_MAKEFILE ON)
SET함수는 변수를 만들거나 변수의 값을 설정하는 일을 합니다. cmake로 빌드를 하면 기본적으로 컴파일 명령이 출력되지 않고 빌드가 완료되었는지만 알려줍니다. 그래서 CMAKE_VERBOSE_MAKEFILE 변수를 ON으로 설정해서 어떤 옵션으로 컴파일되고 있는지 확인할 수 있게합니다.

if ("${build}" MATCHES "debug")
 SET (CMAKE_BUILD_TYPE "debug")
else ("${build}" MATCHES "debug")
 SET (CMAKE_BUILD_TYPE "release")
endif ("${build}" MATCHES "debug")
cmake를 실행할 때 -D옵션으로 cmake에게 변수 값을 전달할 수 있습니다. cmake -D build=debug 형태로 cmake를 실행하면 build라는 이름의 변수를 만들어서 cmake에게 전달할 수 있습니다. CMAKE_BUILD_TYPE 변수는 cmake에 내장된 변수입니다. cmake는 이 변수의 값을 확인해서 디버그 모드로 빌드할 것인지 릴리즈 모드로 빌드할 것인지를 판단합니다.

if가 처음 사용되었으므로 간단하게 설명하겠습니다. cmake에서는 문자열의 비교를 MATCHES로 처리합니다. 그리고 else와 endif에 if에서 사용된 표현식을 다시한번 써줍니다. 그 외에는 기본적인 if-else와 같습니다.

ADD_DEFINITIONS(-DCALIB_CFG_BUILD_MODE="${CMAKE_BUILD_TYPE}")
컴파일 옵션에 추가될 매크로 정의는 ADD_DEFINITIONS함수로 지정합니다. src/build_info.c에 CALIB_CFG_BUILD_MODE값이 필요하므로 매크로 상수를 만들었습니다.

if ("${bit}" MATCHES "32")
 ADD_DEFINITIONS(-DCALIB_CFG_COMPILE_BIT=32 -m32)
 SET (CMAKE_EXE_LINKER_FLAGS -m32)
else ("${bit}" MATCHES "32")
 ADD_DEFINITIONS(-DCALIB_CFG_COMPILE_BIT=64 -m64)
 SET (CMAKE_EXE_LINKER_FLAGS -m64)
endif ("${bit}" MATCHES "32")
빌드 비트를 확인하는 부분입니다. 빌드 모드와 마찬가지로 cmake의 실행 옵션으로 bit변수에 값을 설정합니다. 이 값에 따라 컴파일 비트와 링크 비트를 설정합니다. ADD_DEFINITIONS 함수는 원래 매크로 상수를 추가하는 함수인데 편의상 컴파일 옵션도 이 함수를 이용해서 추가했습니다. CMAKE_EXE_LINKER_FLAGS는 실행 파일을 만들 때 링커가 사용할 옵션이 저장된 변수입니다. SET 함수를 사용해서 -m32나 -m64 옵션을 추가합니다.

ADD_DEFINITIONS(-Wall -DCALIB_CFG_OS="${CMAKE_SYSTEM_NAME}" -DCALIB_CFG_CPU="${CMAKE_SYSTEM_PROCESSOR}")
CMAKE_SYSTEM_NAME과 CMAKE_SYS_PROCESSORS은 각각 운영체제 이름과 프로세서 정보를 가지고있는 내장 변수입니다. configure를 사용하지 않는 대신에 cmake가 가진 정보를 사용합니다.

ADD_SUBDIRECTORY(src)
src/CMakeLists.txt를 처리합니다.

ADD_SUBDIRECTORY(test)
test/CMakeLists.txt를 처리합니다.


이제 소스를 빌드해서 라이브러리를 만드는 src/CMakeListst.txt를 분석하겠습니다.

SET (LIBRARY_OUTPUT_PATH ../lib)
빌드된 라이브러리가 저장될 디렉토리입니다.

INCLUDE_DIRECTORIES (../include)
소스 빌드에 필요한 헤더 파일이 저장된 디렉토리입니다.

SET (LIBSRCS sys_info.c build_info.c)
소스 파일의 리스트입니다.

CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
PROJECT (calib_project)

# set dir to store library
SET (LIBRARY_OUTPUT_PATH ../lib)

# set dir for header
INCLUDE_DIRECTORIES (../include)

# sources
SET (LIBSRCS sys_info.c build_info.c)

# build libcalib.a with LIBSRCS sources
ADD_LIBRARY (calib STATIC ${LIBSRCS})

 

라이브러리를 빌드하는 함수 ADD_LIBRARY를 호출합니다. calib은 라이브러리의 이름이고 STATIC은 정적 라이브러리로 빌드하라는 의미입니다. SHARED로 지정하면 공유라이브러리를 빌드합니다.


다음은 유닛테스트를 빌드하는 test/CMakeLists.txt입니다.

ENABLE_TESTING ()
cmake는 유닛테스트를 위해 ctest라는 독립적인 툴을 가지고있습니다. cmake로 실행파일을 빌드하면 ctest로 실행과 각종 테스트를 진행하게됩니다. ctest를 사용하겠다는 의미로 ENABLE_TESTING 함수를 호출합니다.

INCLUDE_DIRECTORIES (../include)
헤더 파일의 디렉토리 설정입니다.

ADD_EXECUTABLE (test_build_info test_build_info.c)
ADD_EXECUTABLE은 어떤 소스를 가지고 어떤 실행 파일을 만들지를 지정합니다. test_build_info.c를 가지고 test_build_info 실행파일을 빌드합니다. 

TARGET_LINK_LIBRARIES (test_build_info calib)
test_build_info를 빌드할 때 calib라이브러리를 링크합니다.

ADD_EXECUTABLE (test_build_info test_build_info.c)
TARGET_LINK_LIBRARIES (test_build_info calib)
test_build_info의 빌드와 동일합니다. test_sys_info.c 소스와 calib 라이브러리를 가지고 test_sys_info라는 실행 파일을 빌드합니다.

ADD_TEST (unittest1 test_build_info)
ADD_TEST (unittest2 test_sys_info)
ADD_TEST함수는 ctest가 실행할 유닛테스트 파일을 지정합니다. 첫번째 인자는 테스트의 이름이고 두번째 인자는 실행 파일입니다.


모든 CMakeLists.txt파일을 작성했으면 다음 명령을 실행해서 Makefile을 만들고 빌드를 실행합니다.

$ cmake -D build=debug -D bit=64 CMakeLists.txt 
$ make

유닛테스트를 실행하기 위해서는 cmake test 명령을 내리거나 ctest를 실행하면 됩니다.

$ cmake test
$ ctest

cmake test를 실행하면 유닛테스트가 정상적으로 실행되었다는 메시지만 출력됩니다. 유닛테스트에서 출력한 메시지를 확인하고 싶다면 ctest -V나 ctest --debug 명령을 실행하면 됩니다. ctest는 실행 파일의 최종 반환값이 0이면 테스트 성공으로 인식하고 그 외의 값을 반환하면 실패로 인식합니다.

CMakeLists.txt를 다시 만들었을때는 CMakeCache.txt를 지우고 cmake를 다시 실행합니다.

$ rm CMakeCache.txt

댓글

댓글 본문
작성자
비밀번호
  1. ryan
    좋은글 감사합니다.
  2. 미지
    이전에 이클립스 이용해서 ndk개발하다가 안드로이드 스튜디오에 새롭게 접하면서 cmake간단히 개념잡자 하고 뒤적여 보던 중 제일 좋은 글입니다. 감사합니다. 봤어요 찍어주려고 회원가입도 했고요~~
  3. gurugio
    저도 안쓴지 너무 오래돼서 기억이 안나네요. 강좌에 써진대로 CMakeCache.txt를 지우고 cmake를 해보시면 어떻게 되나요?
    대화보기
    • python
      cmake 했던것을 초기화 하려면 어떻게 해야 하나요?
      cmake 해서 make했다가 에러가 나서 cmake의 옵션을 다르게 해서 다시 make 하려고 합니다.
    • 길똥고
      존 자료 감사합니다.

      좀 퍼가도 될런지요?

      http://cafe.naver.com......diy

      미리 감사 드리며~
    • 지니
      else ("${build}" MATCHES "debug")가 아니라 else ("${build}" MATCHES "release") 아닌가요?
    • gurugio
      https://github.com......3.5
      근데 cmake도 이제 흘러가고 요즘엔 scons가 대세인듯 합니다.
    graphittie 자세히 보기