CMAKE

Overview

linux 환경에서 빌드 환경을 만들 때 Makefile을 많이 사용하여 구성한다.
그런데 Makefile 문법 자체가 상당히 복잡하기 때문에 전문가가 아닌 이상 대규모 프로젝트의 빌드 환경을 구성하기 위해 직접 손으로 Makefile을 작성하는 것은 불가능하다. 

autotools vs cmake

autotools 도 위처럼 Makefile을 쉽게 작성하기 위해서 만들어진  빌드 환경 generator이다.
autotools는 autoscan을 이용하여 configure.ac  파일을 생성하여 수작업으로 수정이 필요하다.
그리고 Makefile.am 파일을 수작업으로 작성한 후 autoreconf을 이용하여 Makefile.in 및 configure 파일을 생성해야 한다. 그리고 최종적으로 configure을 실행함으로써 (아무런 에러가 없을 때) Makefile이 생성된다.
이 과정만 보아도 autotools을 이용하여 Makefile 파일을 생성과정이 복잡하다는 것을 알 수 있다. 더군다나 configure.ac 파일을 수정하는 것도 Makefile 파일을 직접 작성하는 것처럼 상당한 전문적인 지식이 필요하다.

반면에 cmake는 CMakeLists.txt 파일 안에 cmake script 작성하는 것으로 모든 일이 끝난다. cmake script 문법 자체가 심플하기 때문에 쉽게 익힐 수 있다. 

autotools vs cmake 사용추이


  • 파란색: autotools
  • 빨간색: cmake


hello world for cmake

hello 폴더를 만든 후 main.cpp 파일과 CMakeLists.txt 파일을 아래처럼 작성한다.
CMakeList.txt 파일은 cmake 가 읽어서 Makefile 파일을 생성할 때 사용되는 script 파일이다.

main.cpp

#include <stdio.h>
int main()
{
    printf("hellow world!\n");
    return 0;
}


 CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
PROJECT(HelloProject)
ADD_EXECUTABLE(hello main.cpp)

CMAKE_MINIMUM_REQUIRED


PROJECT


ADD_EXECUTABLE


Makefile 생성 

  • console에서 "cmake . "   실행하면 Makefile이 실행된다.
  • cmake 파라미터에는 CMakeLists.txt 파일이 있는 경로를 
cmake 실행 결과
main.cpp 파일과 CMakeLists.txt 파일 작성이 완료하였다면 "cmake ."  을 실행하여 Makefile을 생성할 수 있다. 그 이후 make 명령을 사용하여 main.cpp 소스파일을 컴파일&링크하여 Hello 실행파일을 생성하면 된다.
 위에서 자동으로 생성된 Makefile은 수정할 일이 없다.  이 파일을 열어보면 복잡한 내용때문에 다시 닫고 싶을 것이다. 아무튼 Hello 실행파일은 잘 실행된다!


Shared library 만들기

위 Hello world 예제에서는 cmake를 사용하여 실행파일을 만들어 보았다. 이번에는 share library(*.so)를 만들어보자.
library 폴더를 생성한 후 CMakeLists.txt, utility.cpp, utility.h 파일을 아래처럼 생성한다.

utility.cpp

#include "utility.h"
int g_Value = 0;
void setValue(int value)
{
    g_Value = value;
}
int getValue(void)
{
    return g_Value;
}

utility.h

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

void setValue(int value);
int getValue(void);

#ifdef __cplusplus
}
#endif

CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
PROJECT(utility_project)
ADD_LIBRARY(utility SHARED utility.cpp)
  • static library를 생성할 때는 SHARED 를 삭제하거나 STATIC 으로 변경한다.
  • SHARED, STATIC 을 명시하지 않으면 기본으로 STATIC 으로 설정된다.


Makefile 생성하기

Shared library의 output path 설정하기

위에서 사용된 CMakeLists.txt 파일에서 아래처럼 INSTALL 명령을 추가한다.
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
PROJECT(utility_project)
ADD_LIBRARY(utility SHARED utility.cpp)

INSTALL(TARGETS utility DESTINATION ${CMAKE_BINARY_DIR}/lib)
INSTALL(FILES utility.h DESTINATION ${CMAKE_BINARY_DIR}/include)
INSTALL

Shared library 이용하여 App 작성하기

위에서 생성한 libutility.so 파일을 hello application에서 사용하는 방법에 대해서 설명한다.
위에서 사용한 예제를 사용하여 아래와 같은 directory구조로 만들었다.
hello application과 library 를 한번에 빌드하기 위해서 CMakeLists.txt (1) 파일이 신규로 필요하고 hello applicatrion에서 library를 link관련 설정을 추가하기 위해서 CMakeLists.txt (2) 파일 수정이 필요하다.
├── CMakeLists.txt (1)
├── [hello]
│           ├── CMakeLists.txt  (2)
│           └── main.cpp
└── [library]
    ├── CMakeLists.txt
    ├── [include]
    │           └── utility.h
    ├── [lib]
    │           └── libutility.so
    ├── utility.cpp
    └── utility.h

CMakeLists.txt (1)

빌드 트리 전체를 설정한다.
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
PROJECT(myproject)

ADD_SUBDIRECTORY(library)
ADD_SUBDIRECTORY(hello)
ADD_SUBDIRECTORY


CMakeLists.txt (2)


기존 아래 내용에 library관련 include 파일 path와 library link관련 cmake command를 추가해야 한다.
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
PROJECT(hello_project)
ADD_EXECUTABLE(hello main.cpp)
                                                                  +
INCLUDE_DIRECTORIES(../library/include)
FIND_LIBRARY(UTILITY_LIB NAMES utility PATHS ../library/lib)
TARGET_LINK_LIBRARIES(hello ${UTILITY_LIB})
INCLUDE_DIRECTORIES

FIND_LIBRARY

TARGET_LINK_LIBRARIES

32-bit build on 64-bit machine

ADD_LIBRARY 또는 ADD_EXECUTABLE 호출한 후에 SET_TARGET_PROPERTIES 를 사용하여 "-m32" flag를 설정한다.
  • Creating libraries
    • ADD_LIBRARY(utility SHARED utility.cpp)
    • SET_TARGET_PROPERTIES(utility PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
  • Creating executables
    • ADD_EXECUTABLE(hello main.cpp)
    • SET_TARGET_PROPERTIES(hello PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")

사용자 정의 변수 추가하기

  • option
    • in CMakeLists.txt
      • option(MyOption, "This is my custom option", OFF)
    • in Console
      • cmake -DMyOption=ON  ....
  • cmake -D  <VAR>=Value
    • 예) cmake -D MODE=1234
  • CMakeLists.txt 안에 아래처럼 IF 조건을 사용한다.
if(DEFINED MODE)
MESSAGE(STATUS "The used mode: ${MODE}")
endif(DEFINED MODE)

Toolchain 변경하기

  • cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.arm.cmake .
  • toolchain.arm.cmake 파일내용
  • 주의사항
    • toolchain을 변경할 때 CMakeCache.txt 파일과 CMakeFiles 폴더를 삭제해야 반영됨
    • toolchain 종류에 따라서 "-m32" flag 설정이 에러가 발생하는 경우가 있음.
SET(TOOLCHAIN_PATH /opt/toolchains/stbgcc-4.8-1.0/bin)

SET(CMAKE_C_COMPILER ${TOOLCHAIN_PATH}/arm-linux-gcc)
SET(CMAKE_CXX_COMPILER ${TOOLCHAIN_PATH}/arm-linux-g++)
SET(CMAKE_LINKER ${TOOLCHAIN_PATH}/arm-linux-ld)
SET(CMAKE_NM ${TOOLCHAIN_PATH}/arm-linux-nm)
SET(CMAKE_OBJCOPY ${TOOLCHAIN_PATH}/arm-linux-objcopy)
SET(CMAKE_OBJDUMP ${TOOLCHAIN_PATH}/arm-linux-objdump)
SET(CMAKE_RANLIB ${TOOLCHAIN_PATH}/arm-linux-ranlib)

  • cross compile시 아래와 같은 옵션이 필요할 수 있다.
    • SET(CMAKE_FIND_ROOT_PATH /opt/toolchain/target/root)
    • SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
    • SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
    • SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 


Library version 설정하기

  • VERSION 과 SOVERSION 을 설정하면 아래처럼 soft link 파일이 자동 생성된다.
    • libutility.so.2.0.6
    • libutility.so.0.0.0 (soft link)
    • libutility.so (soft link)
SET_TARGET_PROPERTIES(
utility
PROPERTIES
VERSION 2.0.6
SOVERSION 0.0.0
)


Macro 만들기

  • Macro 함수를 만들어 호출할 수도 있다.
# DECLARE MACRO FUNCTION
MACRO (GET_BUILD_DATE RESULT)
EXECUTE_PROCESS(COMMAND date OUTPUT_VARIABLE ${RESULT})
ENDMACRO (GET_BUILD_DATE)

# Call Macro Function
GET_BUILD_DATE(RESULT)

STRING(REGEX REPLACE "\n$" "" RESULT "${RESULT}")
MESSAGE(STATUS "Result: ${RESULT}")

TimeStamp string 생성

  • $VERSION 변수에 date time format의 string을 생성함.
STRING(TIMESTAMP VERSION "%Y-%m-%d %H:%M:%S")
FILE (WRITE ./version.c "char *szVer=\"${VERSION}\";")
MESSAGE(STATUS, "Version: ${VERSION}")

debug/release 빌드하기

  • CMakeLists.txt 작성할 때 debug , release targe을 아래 처럼 추가한다.
  • make release 또는 make debug 사용하여 빌드한다.
ADD_CUSTOM_TARGET(debug
  COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${CMAKE_SOURCE_DIR}
  COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
  COMMENT "Switch CMAKE_BUILD_TYPE to Debug"
  )

ADD_CUSTOM_TARGET(release
  COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Release ${CMAKE_SOURCE_DIR}
  COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
  COMMENT "Switch CMAKE_BUILD_TYPE to Release"
  )

distclean target만들기

  • cmake는 기본적으로 clean target만 지원하고 distclean target은 제공하지 않는다. 따라서 ADD_CUSTOM_TARGET 을 사용하여 distclean target을 추가해야 한다.
  • make distclean 했을 때 삭제될 파일은 CMakeCache.txt와 CMakeFiles 폴더 이다.
ADD_CUSTOM_TARGET(distclean
COMMAND make clean
COMMAND rm CMakeCache.txt
COMMAND rm -rf CMakeFiles
COMMENT "clean-up completed!"
)

CMake Variables

CMAKE_BINARY_DIR

  • cmake 실행할 폴더 경로.
  • sub project에서 모두 동일한 값.
CMAKE_SOURCE_DIR

  • 최상위 CMakeLists.txt 파일이 위치한 폴더 경로.
  • sub project에서 모두 동일한 값.
CMAKE_CURRENT_SOURCE_DIR

  • CMakeLists.txt 파일이 위치한 폴더 경로.
  • sub project 마다 경로가 다름.
CMAKE_CURRENT_BINARY_DIR

  • cmake 실행할 폴더 하위에 생성되는 각 Project 폴더(CMakeLists.txt파일 존재)의 경로.
  • sub project 마다 경로가 다름.

[CMAKE 실행시 사용할 수 있는 definitions]

CMAKE_TOOLCHAINT_FILE

  • toolchain 변경할 때 사용함.

CMAKE_BUILD_TYPE

  • "Debug" or "Release" 용 빌드시 사용함.
  • default build type이 empty string이기 때문에 최상위 CMakeLists.txt 파일에 아래 코드를 추가하여 default로 debug 또는 release 용인지 명시하는 것이 좋다.
  • if (NOT CMAKE_BUILD_TYPE)
        message(STATUS "No build type selected, default to release")
        set(CMAKE_BUILD_TYPE "release")
    endif()

CMAKE_C_FLAGS

  • gcc 컴파일 옵션을 설정한다. -m32 을 사용하면 32bit용으로 빌드할 수 있다.

CMAKE_CXX_FLAGS

  • g++ 컴파일 옵션을 설정한다. -m32 을 사용하면 32bit용으로 빌드할 수 있다.

CMAKE_EXE_LINKER_FLAGS

  • linker관련 옵션 설정
  • static link 사용하는 예제
    • set(CMAKE_EXE_LINKER_FLAGS "-static")

CMAKE_SHARED_LINKER_FLAGS

  • shared library 만들때 사용할 link flag를 설정한다.
  • symbol table 제거할때 -s 사용한다.

BUILD_SHARED_LIBS

  • "ON" 을 설정하면 library 생성시 default으로 shared library 형태로 생성된다.
  • 예) cmake -DBUILD_SHARED_LIBS=ON .


References

댓글