Skip to content

CMake:Example

CMake 사용 예제를 정리한다.

Install preefix

make install단계에 적용할, 프로그램 또는 라이브러리를 설치할 디렉토리를 적용할 수 있다.

cmake -DCMAKE_INSTALL_PREFIX=. -G "Unix Makefiles"

빌드 중 상세정보(명령행 등)를 보고싶을 경우 아래와 같이 VERBOSE옵션을 추가하면 된다.

$ make VERBOSE=1

중요한 점은 cmake가 아닌, make단계에서 적용하는 것이다.

아래와 같이 사용하면 CMakeLists.txt에서 사용할 수 있는 모든 변수 목록이 출력된다.

$ cmake -LAH

CMake Script Mode

CMake 빌드가 아닌, 스크립트 단위로 사용하고자 할 경우 아래와 같이 -P옵션을 적용하면 된다.

$ cmake -P {script_file}

Check directory exists

if(EXISTS "${maybedir}/")
# ...
endif()

Global property

You can 'simulate' GLOBAL variable behavior, by using properties with GLOBAL scope :

SET_PROPERTY(GLOBAL PROPERTY MyGlobalProperty "MyGlobalPropertyValue")

Then you can extract your global property by using

GET_PROPERTY(MyLocalVariable GLOBAL PROPERTY MyGlobalProperty)

Target property

I've tried GET_TARGET_PROPERTY but it does not work. The SUFFIX property returns NOTFOUND unless we explicitly set it by calling SET_TARGET_PROPERTIES. Take the following CMakeLists.txt as an example:

PROJECT(hello)

ADD_LIBRARY(hello SHARED hello.c)

GET_TARGET_PROPERTY(suffix hello SUFFIX)
MESSAGE("suffix = ${suffix}")

SET_TARGET_PROPERTIES(hello PROPERTIES SUFFIX .so)
GET_TARGET_PROPERTY(suffix hello SUFFIX)
MESSAGE("suffix = ${suffix}")

Library version

set_property(TARGET bar PROPERTY VERSION “0.0.0”)
set_property(TARGET bar PROPERTY SOVERSION 0 ) 

Custom Command

cmake_minimum_required(VERSION 2.8 )
project(part)
# ...
add_custom_command(OUTPUT part.out
               COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/part.src part.out
               DEPENDS part.src)
add_custom_target(part_out
              DEPENDS part.out)

Check machine

머신 아키텍처(32bit or 64bit)는 아래와 같이 확인할 수 있다.

if (CMAKE_SIZEOF_VOID_P EQUAL 8)
    set (BOOST_LIBRARY "/boost/win64/lib")
else ()
    set (BOOST_LIBRARY "/boost/win32/lib")
endif ()
set (CMAKE_EXE_LINKER_FLAGS ${BOOST_LIBRARY})

Linker flags

whole-archive

if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
    set (MAIN_LINK -Wl,-force_load ${MAIN_STATIC})
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
    set (MAIN_LINK -Wl,--whole-archive ${MAIN_STATIC} -Wl,--no-whole-archive)
endif ()
# ...
target_link_libraries (${MAIN_SHARED} ${MAIN_LINK})

CMake RPATH handling

아래와 같이 적용하면 된다.

set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

add_library(heart SHARED ${HEART_FILES})
add_executable(run ${RUN_FILES})
target_link_libraries(run heart)

install(
  TARGETS heart run
  RUNTIME DESTINATION bin
  LIBRARY DESTINATION lib
)

단, add_library, add_executable 과 같이 타겟을 생성하는 타이밍 보다 먼저 호출되어야 한다. (target_link_libraries같은 함수도 포함되는지 확인 필요)

Static & Shared library

add_library(MyLib SHARED source1.c source2.c)
add_library(MyLibStatic STATIC source1.c source2.c)
set_target_properties(MyLibStatic PROPERTIES OUTPUT_NAME MyLib)
# On Linux, it will build MyLib.a and MyLib.so

CUDA Example

google-protocol-buffers custom target example

Protobuf를 사용하기 위한 사용자정의 빌드방법.

## CMake google-protocol-buffers build script.

exists_define_or_die (THIRD_PREFIX)

set (PROTOBUF_TARGET_NAME "protobuf")

project (${PROTOBUF_TARGET_NAME})

set  (PARENT_DIR        "${CMAKE_CURRENT_SOURCE_DIR}")
set  (PROTOBUF_DIR      "${PARENT_DIR}/protobuf")
#set (PROTOBUF_AUTOGEN  "${PROTOBUF_DIR}/autogen.sh")
set  (PROTOBUF_CONFIG   "${PROTOBUF_DIR}/configure")
set  (PROTOBUF_MAKE     "${PROTOBUF_DIR}/Makefile")

set (PROTOBUF_LIB_TARGET "${THIRD_PREFIX}/lib/libprotobuf.a")
set (PROTOBUF_EXE_TARGET "${THIRD_PREFIX}/bin/protoc")

add_custom_command (OUTPUT ${PROTOBUF_DIR}
    COMMAND git clone --depth=1 https://github.com/google/protobuf.git
    WORKING_DIRECTORY ${PARENT_DIR}
)

add_custom_command (OUTPUT ${PROTOBUF_CONFIG}
    COMMAND ./autogen.sh
    DEPENDS ${PROTOBUF_DIR}
    WORKING_DIRECTORY ${PROTOBUF_DIR}
)

add_custom_command (OUTPUT ${PROTOBUF_MAKE}
    COMMAND ./configure --prefix=${THIRD_PREFIX}
    DEPENDS ${PROTOBUF_CONFIG}
    WORKING_DIRECTORY ${PROTOBUF_DIR}
)

add_custom_command (OUTPUT ${PROTOBUF_LIB_TARGET} ${PROTOBUF_EXE_TARGET}
    COMMAND make && make install
    DEPENDS ${PROTOBUF_MAKE}
    WORKING_DIRECTORY ${PROTOBUF_DIR}
)

add_custom_target (${PROTOBUF_TARGET_NAME}
    DEPENDS ${PROTOBUF_LIB_TARGET} ${PROTOBUF_EXE_TARGET}
)
# ...
add_dependencies (MAIN_PROJ protobuf)

Script mode example

스크립트 모드 예제:

## CMake third-party library generator.

cmake_minimum_required (VERSION 2.8.8)

set (THIRD_ARCHIVE  "${CMAKE_CURRENT_SOURCE_DIR}/libthird.a")
set (THIRD_OBJS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/objs")

if (EXISTS ${THIRD_OBJS_DIR})
    file (REMOVE_RECURSE ${THIRD_OBJS_DIR})
endif ()

if (NOT EXISTS ${THIRD_OBJS_DIR})
    file (MAKE_DIRECTORY ${THIRD_OBJS_DIR})
endif ()

# Extract object in the archive.
foreach (LIB_CURSOR ${THIRD_LIBS})
    execute_process (
        COMMAND ar xv "${LIB_CURSOR}"
        WORKING_DIRECTORY "${THIRD_OBJS_DIR}"
    )
endforeach ()

execute_process (
    COMMAND ar cr "${THIRD_ARCHIVE}" *.o
    WORKING_DIRECTORY "${THIRD_OBJS_DIR}"
)

아래와 같이 실행하면 된다.

$ cmake -P script.cmake

I have had limited success with the following work-around to the apparent lack of ability to dynamically query for the properties of a target.

I invoke the cmake command to list all properties, and then try each one on the target.

# Get all propreties that cmake supports
execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)

# Convert command output into a CMake list
STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")

function(print_properties)
    message ("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}")
endfunction(print_properties)

function(print_target_properties tgt)
    if(NOT TARGET ${tgt})
      message("There is no target named '${tgt}'")
      return()
    endif()

    foreach (prop ${CMAKE_PROPERTY_LIST})
        string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" prop ${prop})
        # message ("Checking ${prop}")
        get_property(propval TARGET ${tgt} PROPERTY ${prop} SET)
        if (propval)
            get_target_property(propval ${tgt} ${prop})
            message ("${tgt} ${prop} = ${propval}")
        endif()
    endforeach(prop)
endfunction(print_target_properties)

정적 라이브러리의 메인 함수 제거 방법

dwm의 main을 제거하고 나만의 main함수를 추가하는 방법. strip 유틸리티를 사용하면 된다.

cmake_minimum_required (VERSION 3.3)

project (opwm)

set (CMAKE_EXPORT_COMPILE_COMMANDS ON)

set (DWM_NAME dwm)
set (DWM_VERSION 6.2)
set (DWM_SOURCE_FILES
    dwm/drw.c
    dwm/dwm.c
    dwm/util.c)

set (OPWM_NAME opwm)
set (OPWM_VERSION 6.2)
set (OPWM_SOURCE_FILES
    main.c)
set (DWM_NOMAIN_NAME dwm-nomain)
set (DWM_NOMAIN_PATH
    "${CMAKE_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}dwm-nomain${CMAKE_STATIC_LIBRARY_SUFFIX}")
set (COMMON_COMPILE_DEFINITIONS
    _DEFAULT_SOURCE
    _BSD_SOURCE
    _POSIX_C_SOURCE=2
    XINERAMA)
set (COMMON_INCLUDE_DIRECTORIES
    /usr/X11R6/include
    /usr/include/freetype2
    ${CMAKE_SOURCE_DIR}
    ${CMAKE_SOURCE_DIR}/dwm)
set (COMMON_COMPILE_OPTIONS
    -std=c99
    -pedantic
    -Wall
    -Wno-deprecated-declarations)
set (COMMON_LINK_LIBRARIES
    -L/usr/X11R6/lib
    -lX11
    -lXinerama
    -lfontconfig
    -lXft)

add_library (${DWM_NAME} STATIC ${DWM_SOURCE_FILES})
target_compile_definitions (${DWM_NAME} PRIVATE ${COMMON_COMPILE_DEFINITIONS} VERSION=\"${DWM_VERSION}\")
target_include_directories (${DWM_NAME} PRIVATE ${COMMON_INCLUDE_DIRECTORIES})
target_compile_options (${DWM_NAME} PRIVATE ${COMMON_COMPILE_OPTIONS})

add_custom_target (
    ${DWM_NOMAIN_NAME}
    ALL
    DEPENDS ${DWM_NOMAIN_PATH})
add_custom_command (
    OUTPUT ${DWM_NOMAIN_PATH}
    COMMAND strip --strip-symbol=main -o "${DWM_NOMAIN_PATH}" "$<TARGET_FILE:${DWM_NAME}>"
    DEPENDS ${DWM_NAME}
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

add_executable (${OPWM_NAME} ${OPWM_SOURCE_FILES})
add_dependencies (${OPWM_NAME} ${DWM_NOMAIN_NAME})
target_compile_definitions (${OPWM_NAME} PRIVATE ${COMMON_COMPILE_DEFINITIONS})
target_include_directories (${OPWM_NAME} PRIVATE ${COMMON_INCLUDE_DIRECTORIES})
target_compile_options (${OPWM_NAME} PRIVATE ${COMMON_COMPILE_OPTIONS})
target_link_libraries (${OPWM_NAME} PRIVATE ${DWM_NOMAIN_PATH} ${COMMON_LINK_LIBRARIES})

set (COMPILE_COMMANDS_NAME cc)
set (COMPILE_COMMANDS_SRC "${CMAKE_BINARY_DIR}/compile_commands.json")
set (COMPILE_COMMANDS_DEST "${CMAKE_SOURCE_DIR}/compile_commands.json")
add_custom_target (${COMPILE_COMMANDS_NAME} DEPENDS ${COMPILE_COMMANDS_DEST})
add_custom_command (
    OUTPUT ${COMPILE_COMMANDS_DEST}
    COMMAND ${CMAKE_COMMAND} -E copy "${COMPILE_COMMANDS_SRC}" "${COMPILE_COMMANDS_DEST}"
    DEPENDS ${OPWM_NAME} ${COMPILE_COMMANDS_SRC}
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

install (TARGETS ${OPWM_NAME})

References


  1. CMake_RPATH_handling_-_KitwarePublic.pdf 

  2. RPATH_handling_-Wiki-_CMake.pdf