1. 程式人生 > >在 linux 下使用 CMake 構建應用程式

在 linux 下使用 CMake 構建應用程式

CMake 簡介

CMake 是一個跨平臺的自動化建構系統,它使用一個名為 CMakeLists.txt 的檔案來描述構建過程,可以產生標準的構建檔案,如 Unix 的 Makefile 或Windows Visual C++ 的 projects/workspaces 。檔案 CMakeLists.txt 需要手工編寫,也可以通過編寫指令碼進行半自動的生成。CMake 提供了比 autoconfig 更簡潔的語法。在 linux 平臺下使用 CMake 生成 Makefile 並編譯的流程如下:

  1. 編寫 CmakeLists.txt
  2. 執行命令cmake PATH或者ccmake PATH
    生成 Makefile ( PATH CMakeLists.txt 所在的目錄 )
  3. 使用 make 命令進行編譯。

第一個工程

現假設我們的專案中只有一個原始檔 main.cpp

清單 1 原始檔 main.cpp

1

2

3

4

5

6

7

1 #include<iostream>

2

3 int main()

4 {

5     std::cout<<"Hello word!"<<std::endl;

6     return 0;

7 }

為了構建該專案,我們需要編寫檔案 CMakeLists.txt 並將其與 main.cpp 放在 同一個目錄下:

清單 2 CMakeLists.txt

1

2

3

4

1 PROJECT(main)

2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

3 AUX_SOURCE_DIRECTORY(. DIR_SRCS)

4 ADD_EXECUTABLE(main ${DIR_SRCS})

CMakeLists.txt 的語法比較簡單,由命令、註釋和空格組成,其中命令是不區分大小寫的,符號"#"後面的內容被認為是註釋。命令由命令名稱、小括號和引數組成,引數之間使用空格進行間隔。例如對於清單2的 CMakeLists.txt 檔案:第一行是一條命令,名稱是 PROJECT ,引數是 main ,該命令表示專案的名稱是 main 。第二行的命令限定了 CMake 的版本。第三行使用命令 AUX_SOURCE_DIRECTORY 將當前目錄中的原始檔名稱賦值給變數 DIR_SRCS 。 CMake 手冊中對命令 AUX_SOURCE_DIRECTORY 的描述如下:

1

aux_source_directory(<dir> <variable>)

該命令會把引數 <dir> 中所有的原始檔名稱賦值給引數 <variable> 。 第四行使用命令 ADD_EXECUTABLE 指示變數 DIR_SRCS 中的原始檔需要編譯 成一個名稱為 main 的可執行檔案。

完成了檔案 CMakeLists.txt 的編寫後需要使用 cmake 或 ccmake 命令生成Makefile 。 ccmake 與命令 cmake 的不同之處在於 ccmake 提供了一個圖形化的操作介面。cmake 命令的執行方式如下:

1

cmake [options] <path-to-source>

這裡我們進入了 main.cpp 所在的目錄後執行 “cmake .” 後就可以得到 Makefile 並使用 make 進行編譯,如下圖所示。

圖 1. camke 的執行結果

camke 的執行結果

處理多原始檔目錄的方法

CMake 處理原始碼分布在不同目錄中的情況也十分簡單。現假設我們的原始碼分佈情況如下:

圖 2. 原始碼分佈情況

原始碼分佈情況

其中 src 目錄下的檔案要編譯成一個連結庫。

第一步,專案主目錄中的 CMakeLists.txt

在目錄 step2 中建立檔案 CMakeLists.txt 。檔案內容如下:

清單 3 目錄 step2 中的 CMakeLists.txt

1

2

3

4

5

6

1 PROJECT(main)

2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

3 ADD_SUBDIRECTORY( src )

4 AUX_SOURCE_DIRECTORY(. DIR_SRCS)

5 ADD_EXECUTABLE(main ${DIR_SRCS}  )

6 TARGET_LINK_LIBRARIES( main Test )

相對於清單 2,該檔案添加了下面的內容: 第三行,使用命令 ADD_SUBDIRECTORY 指明本專案包含一個子目錄 src 。第六行,使用命令 TARGET_LINK_LIBRARIES 指明可執行檔案 main 需要連線一個名為Test的連結庫 。

第二步,子目錄中的 CmakeLists.txt

在子目錄 src 中建立 CmakeLists.txt。檔案內容如下:

清單 4. 目錄 src 中的 CmakeLists.txt

1

2

1 AUX_SOURCE_DIRECTORY(. DIR_TEST1_SRCS)

2 ADD_LIBRARY ( Test ${DIR_TEST1_SRCS})

在該檔案中使用命令 ADD_LIBRARY 將 src 目錄中的原始檔編譯為共享庫。

第三步,執行 cmake

至此我們完成了專案中所有 CMakeLists.txt 檔案的編寫,進入目錄 step2 中依次執行命令 “cmake .” 和 “make” 得到結果如下:

圖3. 處理多原始檔目錄時 cmake 的執行結果

處理多原始檔目錄時 cmake 的執行結果

在執行 cmake 的過程中,首先解析目錄 step2 中的 CMakeLists.txt ,當程式執行命令 ADD_SUBDIRECTORY( src ) 時進入目錄 src 對其中的 CMakeLists.txt 進行解析。

在工程中查詢並使用其他程式庫的方法

在開發軟體的時候我們會用到一些函式庫,這些函式庫在不同的系統中安裝的位置可能不同,編譯的時候需要首先找到這些軟體包的標頭檔案以及連結庫所在的目錄以便生成編譯選項。例如一個需要使用博克利資料庫專案,需要標頭檔案db_cxx.h 和連結庫 libdb_cxx.so ,現在該專案中有一個原始碼檔案 main.cpp ,放在專案的根目錄中。

第一步,程式庫說明檔案

在專案的根目錄中建立目錄 cmake/modules/ ,在 cmake/modules/ 下建立檔案 Findlibdb_cxx.cmake ,內容如下:

清單 5. 檔案 Findlibdb_cxx.cmake

1

2

3

4

5

6

7

8

9

10

11

12

01 MESSAGE(STATUS "Using bundled Findlibdb.cmake...")

0203 FIND_PATH(

04   LIBDB_CXX_INCLUDE_DIR

05   db_cxx.h

06   /usr/include/

07   /usr/local/include/

08   )

09

10 FIND_LIBRARY(

11   LIBDB_CXX_LIBRARIES NAMES  db_cxx

12   PATHS /usr/lib/ /usr/local/lib/

13   )

檔案 Findlibdb_cxx.cmake 的命名要符合規範: FindlibNAME.cmake ,其中NAME 是函式庫的名稱。Findlibdb_cxx.cmake 的語法與 CMakeLists.txt 相同。這裡使用了三個命令: MESSAGE , FIND_PATH 和 FIND_LIBRARY 。

  • 命令 MESSAGE 將引數的內容輸出到終端
  • 命令 FIND_PATH 指明標頭檔案查詢的路徑,原型如下
    find_path(<VAR> name1 [path1 path2 ...]) 該命令在引數 path* 指示的目錄中查詢檔案 name1 並將查詢到的路徑儲存在變數 VAR 中。清單538行的意思是在 /usr/include/ /usr/local/include/ 中查詢檔案db_cxx.h ,並將db_cxx.h 所在的路徑儲存在 LIBDB_CXX_INCLUDE_DIR中。
  • 命令 FIND_LIBRARY FIND_PATH 類似,用於查詢連結庫並將結果儲存在變數中。清單51013行的意思是在目錄 /usr/lib/ /usr/local/lib/ 中尋找名稱為 db_cxx 的連結庫,並將結果儲存在 LIBDB_CXX_LIBRARIES

第二步, 專案的根目錄中的 CmakeList.txt

在專案的根目錄中建立 CmakeList.txt :

清單 6. 可以查詢連結庫的 CMakeList.txt

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

01 PROJECT(main)

02 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

03 SET(CMAKE_SOURCE_DIR .)

04 SET(CMAKE_MODULE_PATH ${CMAKE_ROOT}/Modules ${CMAKE_SOURCE_DIR}/cmake/modules)

05 AUX_SOURCE_DIRECTORY(. DIR_SRCS)

06 ADD_EXECUTABLE(main ${DIR_SRCS})

0708 FIND_PACKAGE( libdb_cxx REQUIRED)

09 MARK_AS_ADVANCED(

10 LIBDB_CXX_INCLUDE_DIR

11 LIBDB_CXX_LIBRARIES

12 )

13 IF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)

14 MESSAGE(STATUS "Found libdb libraries")

15    INCLUDE_DIRECTORIES(${LIBDB_CXX_INCLUDE_DIR})

16     MESSAGE( ${LIBDB_CXX_LIBRARIES} )

17     TARGET_LINK_LIBRARIES(main ${LIBDB_CXX_LIBRARIES}18 )

19 ENDIF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)

在該檔案中第4行表示到目錄 ./cmake/modules 中查詢 Findlibdb_cxx.cmake ,8-19 行表示查詢連結庫和標頭檔案的過程。第8行使用命令 FIND_PACKAGE 進行查詢,這條命令執行後 CMake 會到變數 CMAKE_MODULE_PATH 指示的目錄中查詢檔案 Findlibdb_cxx.cmake 並執行。第13-19行是條件判斷語句,表示如果 LIBDB_CXX_INCLUDE_DIR 和 LIBDB_CXX_LIBRARIES 都已經被賦值,則設定編譯時到 LIBDB_CXX_INCLUDE_DIR 尋找標頭檔案並且設定可執行檔案 main 需要與連結庫 LIBDB_CXX_LIBRARIES 進行連線。

第三步,執行 cmake

完成 Findlibdb_cxx.cmake 和 CMakeList.txt 的編寫後在專案的根目錄依次執行 “cmake . ” 和 “make ” 可以進行編譯,結果如下圖所示:

圖 4. 使用其他程式庫時 cmake 的執行結果

使用其他程式庫時 cmake 的執行結果

使用 cmake 生成 debug 版和 release 版的程式

在 Visual Studio 中我們可以生成 debug 版和 release 版的程式,使用 CMake 我們也可以達到上述效果。debug 版的專案生成的可執行檔案需要有除錯資訊並且不需要進行優化,而 release 版的不需要除錯資訊但需要優化。這些特性在 gcc/g++ 中是通過編譯時的引數來決定的,如果將優化程度調到最高需要設定引數-O3,最低是 -O0 即不做優化;新增除錯資訊的引數是 -g -ggdb ,如果不新增這個引數,除錯資訊就不會被包含在生成的二進位制檔案中。

CMake 中有一個變數 CMAKE_BUILD_TYPE ,可以的取值是 Debug Release RelWithDebInfo 和 MinSizeRel。當這個變數值為 Debug 的時候,CMake 會使用變數 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_C_FLAGS_DEBUG 中的字串作為編譯選項生成 Makefile ,當這個變數值為 Release 的時候,工程會使用變數 CMAKE_CXX_FLAGS_RELEASE 和 CMAKE_C_FLAGS_RELEASE 選項生成 Makefile。

現假設專案中只有一個檔案 main.cpp ,下面是一個可以選擇生成 debug 版和 release 版的程式的 CMakeList.txt :

清單 7

1

2

3

4

5

6

7

1 PROJECT(main)

2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

3 SET(CMAKE_SOURCE_DIR .)

45 SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")

6 SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

78 AUX_SOURCE_DIRECTORY(. DIR_SRCS)

9 ADD_EXECUTABLE(main ${DIR_SRCS})

第 5 和 6 行設定了兩個變數 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_CXX_FLAGS_RELEASE, 這兩個變數是分別用於 debug 和 release 的編譯選項。 編輯 CMakeList.txt 後需要執行 ccmake 命令生成 Makefile 。在進入專案的根目錄,輸入 "ccmake ." 進入一個圖形化介面,如下圖所示:

圖 5. ccmake 的介面

ccmake 的介面

按照介面中的提示進行操作,按 "c" 進行 configure ,這時介面中顯示出了配置變數 CMAKE_BUILD_TYPE 的條目。如下圖所示:

圖 6. 執行了 configure 以後 ccmake 的介面

執行了 configure 以後 ccmake 的介面

下面我們首先生成 Debug 版的 Makefile :將變數 CMAKE_BUILD_TYPE 設定為 Debug ,按 "c" 進行 configure ,按 "g" 生成 Makefile 並退出。這時執行命令 find * | xargs grep "O0" 後結果如下:

清單 8 find * | xargs grep "O0"的執行結果

1

2

3

4

CMakeFiles/main.dir/flags.make:CXX_FLAGS = -O0 -Wall -g -ggdb

CMakeFiles/main.dir/link.txt:/usr/bin/c++ -O0 -Wall -g -ggdb

CMakeFiles/main.dir/main.cpp.o -o main -rdynamic

CMakeLists.txt:SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")

這個結果說明生成的 Makefile 中使用了變數 CMAKE_CXX_FLAGS_DEBUG 作為編譯時的引數。

下面我們將生成 Release 版的 Makefile :再次執行命令 "ccmake ." 將變數CMAKE_BUILD_TYPE 設定為 Release ,生成 Makefile 並退出。執行命令 find * | xargs grep "O0" 後結果如下:

清單 9 find * | xargs grep "O0"的執行結果

1

CMakeLists.txt:SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")

而執行命令 find * | xargs grep "O3" 後結果如下:

清單 10. find * | xargs grep "O3"的執行結果

1

2

3

4

5

6

CMakeCache.txt:CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG

CMakeCache.txt:CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG

CMakeFiles/main.dir/flags.make:CXX_FLAGS = -O3 -Wall

CMakeFiles/main.dir/link.txt:/usr/bin/c++ -O3 -Wall

CMakeFiles/main.dir/main.cpp.o -o main -rdynamic

CMakeLists.txt:SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

這兩個結果說明生成的 Makefile 中使用了變數 CMAKE_CXX_FLAGS_RELEASE 作為編譯時的引數。

下載資源

相關主題