cmake_logo-main

CMake 是一套 open source 的建制專案工具, 利用寫好的 CMakeLists 可以產生出不同平台上的建構檔, 在 Linux 是 Makefile, Windows 則是 Visual Studio, 或者是 Mac 上的 XCode. CMake 除了跨平台外的優點外就是使用起來很彈性, 已經有很多知名的專案都是透過 CMake 來做建置.

與一般的自動化系統不同, CMake 為了不讓編譯的過程污染到原始程式碼的目錄, 會將 build 的目錄跟 source code 的位置分開來, 通常會是在 project 中寫好 CMakeLists.txt, 然後在名為 build 的目錄底下利用 cmake command 來產生建構檔, 之後再根據各平台所使用的編譯工具來編譯目標檔案, 例如 linux 下通常會是 make.

接下來以一個很簡單的例子 MyProject 來開始, 在這目錄底會下包含 include, src 這兩個子資料夾以及 main.cpp, 在這裡我們希望可以產生兩個含式庫: libio.a, libhello.a, 並且將這兩個 library 跟目標執行檔 main.o 連結在一起, 那該怎麼用 CMake 來做到這個簡單的任務呢?

CMakeLists.txt

example1

CMakeLists.txt:

cmake_minimum_required(VERSION 2.6)
project(MyProject)

include_directories(include)
set(IO_SOURCES src/input.cpp src/print.cpp)
set(HELLO_SOURCES src/hello.cpp)
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

add_library(io ${IO_SOURCES})
add_library(hello ${HELLO_SOURCES})
add_executable(main main.cpp)
target_link_libraries(main io hello)

這個範例的 CMakeLists.txt 乍看之下很複雜, 但其實只有三個主要的部份, 接下來就從這三個部份開始來介紹 CMake 的寫法

1. Set environment

cmake_minimum_required(VERSION 2.6): 檢查cmake版本, 當發現版本不對時還是可以執行, 只是會發出警告來提醒你要確認版本

project(MyProject): 用來指名 project 名稱, 作為之後參考的變數

2. Set include directory, source path and output path

include_directories(include): 指定 include header path

set(IO_SOURCES src/input.cpp src/print.cpp):
set(variable values)
CMake 中 set command 是用來設定變數, 將第二個所給予的內容指定到第一個變數名稱當中(CMake的指令沒有大小寫之分)
在這裡則是 IO_SOURCES 這個變數來代替 src/input.cpp src/print.cpp 這兩隻檔案
set(HELLO_SOURCES src/hello.cpp): 而這個則是用 HELLO_SOURCES 來代表 hello.cpp

set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
這兩行指令用來指名編譯好的含式庫和執行檔之後要放的位置
PROJECT_BINARY_DIR: 建置檔放的位置, 通常是執行 cmake 指令的位置, 假設在 build 目錄下執行 cmake ../MyProject 那麼 PROJECT_BINARY_DIR 就會是 build/
LIBRARY_OUTPUT_PATH: 這個變數告訴 library 要放在哪裡
EXECUTABLE_OUTPUT_PATH: 執行檔要放在什麼位置

3. Set target library or executable

add_library(io ${IO_SOURCES}): 將目標編譯成指定的 library 名稱, 在這裡預設會產生出 libio.a
如果不用變數的話就寫成這樣子 add_library(io src/input.cpp src/print.cpp)
在這裡也可以指名要產生靜態或是動態含式庫:
Static library: add_library(io STATIC src/input.cpp src/print.cpp)
Shared library: add_library(io SHARED src/input.cpp src/print.cpp)

add_executable(main main.cpp): 將 main.cpp 編譯成執行檔, 檔名就是 main

target_link_libraries(main io hello): 將執行檔與指定的這些含式庫連結起來

來看一下剛剛這個例子的實際操作, 利用 CMakeLists.txt 來產生出 Makefile 後, 接著我們就可以用 make 指令來編譯這個專案了!

output_YBMDVm


Install

當編譯好之後可能會有需要 install 的需求, 這時候就可以利用 install 這個指令, 繼續上面的CMakeLists.txt 加上下面幾行之後, 就可以在 make 後利用 make install 了

file(GLOB HEADER_PATH include/*.h)
install(FILES ${HEADER_PATH} DESTINATION include)
install(TARGETS io hello DESTINATION lib)
install(TARGETS main DESTINATION bin)

file(GLOB HEADER_PATH include/*.h):
file(GLOB variable globbing-expressions) CMake 中設定通則表示示, 符合此規則的檔案會被指定到 variable
在這裡則是將 include/input.h hello.h print.h 指定到變數 HEADER_PATH

install(FILES ${HEADER_PATH} DESTINATION include):
install(FILES files DESTINATION dir)
install(TARGETS targets DESTINATION dir)
CMake 會根據預設的 CMAKE_INSTALL_PREFIX 來將指定的目標檔案安裝或複製到目的地
在 linux 底下預設的 CMAKE_INSTALL_PREFIX = /usr/local
windows 則為 CMAKE_INSTALL_PREFIX = C:/Program Files

有些時候可能會需要安裝到一些特定的位置去, 這時候就可以透過修改 CMAKE_INSTALL_PREFIX , 將目標檔改安裝到指定的資料夾底下, 例如:
set(CMAKE_INSTALL_PREFIX ${PROJECT_BINARY_DIR}/test_install)
就會將原本安裝到 /usr/local/bin 改到 build/test_install/bin 底下

Multiple projects

CMake 也能處理一個專案中包含多個子專案的狀況, 藉由分開的 CMakeListst.txt 可以將專案分開來管理. 下面這個例子 MyProject 包含了兩個子專案 SubProject1, SubProject2 以及 CMakeListst.txt. 而這兩個子專案也各有一個屬於子專案的 CMakeListst.txt, 這時候我們只要在 MyProject/CMakeLists.txt 中利用 add_subdirectory 這個指令就可以將這兩個子專案加入到 MyProject 中.

example2

MyProject/CMakerLists.txt:

cmake_minimum_required(VERSION 2.6)
project(MyProject)
add_subdirectory(SubProject1)
add_subdirectory(SubProject2)

MyProject/SubProject1/CMakerLists.txt:

cmake_minimum_required(VERSION 2.6)
project(SubProject1)
....

到這邊為止就是基本 cmake 的介紹, 藉由這些的指令其實我們就可以完成許多專案的需求了!
如果還想了解更多 cmake command 可以參考官網, 有更詳細的說明, Enjoy ~

References

  1. https://cmake.org/cmake/help/cmake2.6docs.html
  2. https://cmake.org/cmake-tutorial/
  3. https://zh.wikibooks.org/zh-tw/CMake_%E5%85%A5%E9%96%80
  4. http://mirkokiefer.com/blog/2013/03/cmake-by-example/
廣告