轉:CMake快速入門教程-實戰

CMake快速入門教程:實戰
收藏人:londonKu    
2012-05-07 | 閱:10128  轉:34  
 |    來源
  |  分享 
 
 
 
  
 
 

0. 前言
一個多月前,因爲工程項目的須要,匆匆的學習了一下cmake的使用方法,如今有時間拿出來整理一下。本文假設你已經學會了cmake的使用方法,若是你還不會使用cmake,請參考相關資料以後再繼續向下看。
本文中介紹的是生成可執行程序的方法和步驟,生成動態庫和靜態庫的方法與此有所不一樣,隨後會介紹動態庫和靜態庫項目中cmake的編寫方法。
本文參考《CMake Practice》這篇文章完成,旨在指導用戶快速使用CMake,若是須要更詳細的內容,請通讀《CMake Practice》這篇文章。下載路徑:http://sewm.pku.edu.cn/src/paradise/reference/CMake%20Practice.pdf
1. 項目目錄結構
咱們項目的名稱爲CRNode,假設咱們項目的全部文件存放再~/workspace/CRNode,以後沒有特殊說明的話,咱們所指的目錄都以此目錄爲相對路徑。
咱們的目錄結構以下:css

~/workspace/CRNode
  ├─ src
  │  ├─ rpc
  │  │  ├─ CRMasterCaller.h
  │  │  ├─ CRMasterCaller.cc
  │  │  ├─ CRNode.h
  │  │  ├─ CRNode.cc
  │  │  ├─ Schd_constants.h
  │  │  ├─ Schd_constants.cc
  │  │  ├─ CRMaster.h
  │  │  ├─ CRMaster.cc
  │  │  ├─ CRNode_server.skeleton.h
  │  │  ├─ CRNode_server.skeleton.cc
  │  │  ├─ Schd_types.h
  │  │  └─ Schd_types.cc
  │  ├─ task
  │  │  ├─ TaskExecutor.h
  │  │  ├─ TaskExecutor.cc
  │  │  ├─ TaskMonitor.h
  │  │  └─ TaskMonitor.cc
  │  ├─ util
  │  │  ├─ Const.h
  │  │  ├─ Const.cc
  │  │  ├─ Globals.h
  │  │  ├─ Globals.cc
  │  │  ├─ Properties.h
  │  │  ├─ Properties.cc
  │  │  ├─ utils.h
  │  │  └─ utils.cc
  │  ├─ main.cc
  │  └─ CMakeLists.txt
  ├─ doc
  │  └─ crnode.txt
  ├─ COPYRIGHT
  ├─ README
  ├─ crnode.sh
  └─ CMakeLists.txt

其中,src存放源代碼文件和一個CMakeLists.txt文件,CMakeLists文件的編寫咱們稍候介紹;doc目錄中存放項目 的幫助文檔,該文檔以及COPYRIGHT和README一塊兒安裝到/usr/share/doc/crnode目錄中;COPYRIGHT文件存放項目 的版權信息,README存放一些說明性文字;crnode.sh存放CRNode的啓動命令;CMakeLists.txt文件稍候介紹。
除此以外,項目還依賴兩個外部庫:Facebook開發的thrift庫,其頭文件存放在/usr/include/thrift目錄中;log4cpp庫,其頭文件存放再/usr/include下。
2. CMakeLists.txt文件
本工程中使用了兩個CMakeLists.txt文件,分別項目的根目錄(即~/workspace/CRNode目錄,下同)和src目錄中 (參考以上目錄結構)。咱們先給出兩個CMakeLists.txt的內容,在下一節中再對兩個CMakeLists.txt進行詳細介紹。兩個 CMakeLists.txt文件的內容分別以下:
2.1 根目錄中CMakeLists內容html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cmake_minimum_required (VERSION 2.6)

project (CRNode)

ADD_SUBDIRECTORY(src bin)

#SET(CMAKE_INSTALL_PREFIX ${PROJECT_BINARY_DIR})
SET(CMAKE_INSTALL_PREFIX /usr/local)

INSTALL(PROGRAMS crnode.sh DESTINATION bin)

INSTALL(FILES COPYRIGHT README DESTINATION share/doc/crnode)

INSTALL(DIRECTORY doc/ DESTINATION share/doc/crnode)

2.2 src/CMakeLists.txt內容node

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
INCLUDE_DIRECTORIES(/usr/include/thrift)

SET(SRC_LIST main.cc
        rpc/CRMasterCaller.cpp
        rpc/CRNode_server.skeleton.cpp
        rpc/Schd_constants.cpp
        rpc/CRMaster.cpp
        rpc/CRNode.cpp
        rpc/Schd_types.cpp
        task/TaskExecutor.cpp
        task/TaskMoniter.cpp
        util/Const.cpp
        util/Globals.cc
        util/utils.cc
        util/Properties.cpp
        )

ADD_EXECUTABLE(crnode ${SRC_LIST})

TARGET_LINK_LIBRARIES(crnode log4cpp thrift)

INSTALL(TARGETS crnode
        RUNTIME DESTINATION bin
)

3. CMake語法
A. 變量使用${}方式取值,可是在 IF 控制語句中是直接使用變量名;
B. 指令(參數 1 參數 2…),參數使用括弧括起,參數之間使用空格或分號分開;
C. 指令是大小寫無關的,參數和變量是大小寫相關的。但,推薦你所有使用大寫指令。學習

4. CMakeLists.txt剖析
4.1 cmake_minimum_required命令ui

1
cmake_minimum_required (VERSION 2.6)

規定cmake程序的最低版本。這行命令是可選的,咱們能夠不寫這句話,但在有些狀況下,若是CMakeLists.txt文件中使用了一些高版本cmake特有的一些命令的時候,就須要加上這樣一行,提醒用戶升級到該版本以後再執行cmake。spa

4.2 project命令component

3
project (CRNode)

指定項目的名稱。項目最終編譯生成的可執行文件並不必定是這個項目名稱,而是由另外一條命令肯定的,稍候咱們再介紹。
可是這個項目名稱仍是必要的,在cmake中有兩個預約義變量:< projectname >_BINARY_DIR以及< projectname >_SOURCE_DIR,在咱們的項目中,兩個變量分別爲:CRNode_BINARY_DIR和CRNode_SOURCE_DIR。內部編譯 狀況下二者相同,後面咱們會講到外部編譯,二者所指代的內容會有所不一樣。要理解這兩個變量的定義,咱們首先須要瞭解什麼是「外部構建(out-of- source build)」,咱們將在下一小節中介紹「外部構建」。
同時cmake還預約義了PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR變量。在咱們的項目 中,PROJECT_BINARY_DIR等同於CRNode_BINARY_DIR,PROJECT_SOURCE_DIR等同於 CRNode_SOURCE_DIR。在實際的應用用,我強烈推薦使用PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR變 量,這樣即便項目名稱發生變化也不會影響CMakeLists.txt文件。server

4.3 外部構建
假設咱們此時已經完成了兩個CMakeLists.txt文件的編寫,能夠執行cmake命令生成Makefile文件了。此時咱們由兩種方法能夠執行cmake、編譯和安裝:htm

1
2
cmake .
make

或者blog

1
2
3
4
mkdir build
cd build
cmake ..
make

兩種方法最大的不一樣在於執行cmake和make的工做路徑不一樣。第一種方法中,cmake生成的全部中間文件和可執行文件都會存放在項目 目錄中;而第二種方法中,中間文件和可執行文件都將存放再build目錄中。第二種方法的優勢顯而易見,它最大限度的保持了代碼目錄的整潔。同時因爲第二 種方法的生成、編譯和安裝是發生在不一樣於項目目錄的其餘目錄中,因此第二種方法就叫作「外部構建」。
回到以前的疑問,再外部構建的狀況下,PROJECT_SOURCE_DIR指向的目錄同內部構建相同,仍然爲~/workspace /CRNode,而PROJECT_BINARY_DIR則有所不一樣,指向~/workspace/CRNode/build目錄。
固然,cmake強烈推薦使用外部構建的方法。

4.4 ADD_SUBDIRECTORY命令

5
ADD_SUBDIRECTORY(src bin)

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])這個指令用於向當前工程添加存放源文件的子目錄,並能夠指定中間二進制和目標二進制存放的位置。 EXCLUDE_FROM_ALL 參數的含義是將這個目錄從編譯過程當中排除。好比,工程的 example,可能就須要工程構建完成後,再進入 example 目錄單獨進行構建。
在咱們的項目中,咱們添加了src目錄到項目中,而把對應於src目錄生成的中間文件和目標文件存放到bin目錄下,在上一節舉例中「外部構建」的狀況下,中間文件和目標文件將存放在build/srcobj目錄下。

4.5 SET命令

8
SET(CMAKE_INSTALL_PREFIX /usr/local)

現階段,只須要了解SET命令能夠用來顯式的定義變量便可。在以上的例子中,咱們顯式的將CMAKE_INSTALL_PREFIX的值定 義爲/usr/local,如此在外部構建狀況下執行make install命令時,make會將生成的可執行文件拷貝到/usr/local/bin目錄下。
固然,可執行文件的安裝路徑CMAKE_INSTALL_PREFIX也能夠在執行cmake命令的時候指定,cmake參數以下:

cmake -DCMAKE_INSTALL_PREFIX=/usr ..

若是cmake參數和CMakeLists.txt文件中都不指定該值的話,則該值爲默認的/usr/local。

4.6 INCLUDE_DIRECTORIES命令

1
INCLUDE_DIRECTORIES(/usr/include/thrift)

INCLUDE_DIRECTORIES相似gcc中的編譯參數「-I」,指定編譯過程當中編譯器搜索頭文件的路徑。當項目須要的頭文件不在 系統默認的搜索路徑時,須要指定該路徑。在咱們的項目中,log4cpp所需的頭文件都存放在/usr/include下,不須要指定;但thrift的 頭文件沒有存放在系統路徑下,須要指定搜索其路徑。

4.7 ADD_EXECUTABLE和ADD_LIBRARY

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SET(SRC_LIST main.cc
        rpc/CRMasterCaller.cpp
        rpc/CRNode_server.skeleton.cpp
        rpc/Schd_constants.cpp
        rpc/CRMaster.cpp
        rpc/CRNode.cpp
        rpc/Schd_types.cpp
        task/TaskExecutor.cpp
        task/TaskMoniter.cpp
        util/Const.cpp
        util/Globals.cc
        util/utils.cc
        util/Properties.cpp
        )

ADD_EXECUTABLE(CRNode ${SRC_LIST})

ADD_EXECUTABLE定義了這個工程會生成一個文件名爲 CRNode 的可執行文件,相關的源文件是 SRC_LIST 中定義的源文件列表。須要注意的是,這裏的CRNode和以前的項目名稱沒有任何關係,能夠任意定義。

4.8 EXECUTABLE_OUTPUT_PATH和LIBRARY_OUTPUT_PATH
咱們能夠經過 SET 指令從新定義 EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH 變量來指定最終的目標二進制的位置(指最終生成的CRNode可執行文件或者最終的共享庫,而不包含編譯生成的中間文件)。
命令以下:

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

須要注意的是,在哪裏 ADD_EXECUTABLE 或 ADD_LIBRARY,若是須要改變目標存放路徑,就在哪裏加入上述的定義。

4.9 TARGET_LINK_LIBRARIES命令

20
TARGET_LINK_LIBRARIES(CRNode log4cpp thrift)

這句話指定在連接目標文件的時候須要連接的外部庫,其效果相似gcc的編譯參數「-l」,能夠解決外部庫的依賴問題。

4.10 INSTALL命令
在執行INSTALL命令的時候須要注意CMAKE_INSTALL_PREFIX參數的值。該參數在3.5中已經有所介紹。其命令形式以下:

INSTALL(TARGETS targets...
	[[ARCHIVE|LIBRARY|RUNTIME]
	[DESTINATION < dir >]
	[PERMISSIONS permissions...]
	[CONFIGURATIONS
	[Debug|Release|...]]
	[COMPONENT < component >]
	[OPTIONAL]
	] [...])

參數中的 TARGETS 後面跟的就是咱們經過 ADD_EXECUTABLE 或者 ADD_LIBRARY 定義的目標文件,多是可執行二進制、動態庫、靜態庫。
DESTINATION 定義了安裝的路徑,若是路徑以/開頭,那麼指的是絕對路徑,這時候CMAKE_INSTALL_PREFIX 其實就無效了。若是你但願使用 CMAKE_INSTALL_PREFIX 來定義安裝路徑,就要寫成相對路徑,即不要以/開頭,那麼安裝後的路徑就是${CMAKE_INSTALL_PREFIX} /< destination 定義的路徑>
你不須要關心 TARGETS 具體生成的路徑,只須要寫上 TARGETS 名稱就能夠了。
非目標文件的可執行程序安裝(好比腳本之類):

INSTALL(PROGRAMS files... DESTINATION < dir >
	[PERMISSIONS permissions...]
	[CONFIGURATIONS [Debug|Release|...]]
	[COMPONENT < component >]
	[RENAME < name >] [OPTIONAL])

跟上面的 FILES 指令使用方法同樣,惟一的不一樣是安裝後權限爲OWNER_EXECUTE, GROUP_EXECUTE, 和 WORLD_EXECUTE,即 755 權限目錄的安裝。
安裝一個目錄的命令以下:

INSTALL(DIRECTORY dirs... DESTINATION < dir >
	[FILE_PERMISSIONS permissions...]
	[DIRECTORY_PERMISSIONS permissions...]
	[USE_SOURCE_PERMISSIONS]
	[CONFIGURATIONS [Debug|Release|...]]
	[COMPONENT < component >]
	[[PATTERN < pattern > | REGEX < regex >]
	[EXCLUDE] [PERMISSIONS permissions...]] [...])

DIRECTORY 後面鏈接的是所在 Source 目錄的相對路徑,但務必注意:abc 和 abc/有很大的區別。若是目錄名不以/結尾,那麼這個目錄將被安裝爲目標路徑下的 abc,若是目錄名以/結尾,表明將這個目錄中的內容安裝到目標路徑,但不包括這個目錄自己。咱們來看一個例子:

1
2
3
4
5
INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
	PATTERN "CVS" EXCLUDE
	PATTERN "scripts/*"
	PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
	GROUP_EXECUTE GROUP_READ)

這條指令的執行結果是:
將 icons 目錄安裝到 < prefix >/share/myproj,將 scripts/中的內容安裝到< prefix >/share/myproj,不包含目錄名爲 CVS 的目錄,對於 scripts/*文件指定權限爲 OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ。
由於crnode.txt 要安裝到/< prefix >/share/doc/crnode,因此咱們不能直接安裝整個 doc 目錄,這裏採用的方式是安裝 doc 目錄中的內容,也就是使用」doc/」在工程文件中添加:

1
INSTALL(DIRECTORY doc/ DESTINATION share/doc/crnode)

5. 編譯安裝
編譯安裝結果以下:

[root@sim91 build]# cmake ..
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fify/workspace/CRNode/build

[root@sim91 build]# make
Scanning dependencies of target crnode
[  7%] Building CXX object srcobj/CMakeFiles/crnode.dir/main.cc.o
[ 15%] Building CXX object srcobj/CMakeFiles/crnode.dir/rpc/CRMasterCaller.cpp.o
[ 23%] Building CXX object srcobj/CMakeFiles/crnode.dir/rpc/CRNode_server.skeleton.cpp.o
[ 30%] Building CXX object srcobj/CMakeFiles/crnode.dir/rpc/Schd_constants.cpp.o
[ 38%] Building CXX object srcobj/CMakeFiles/crnode.dir/rpc/CRMaster.cpp.o
[ 46%] Building CXX object srcobj/CMakeFiles/crnode.dir/rpc/CRNode.cpp.o
[ 53%] Building CXX object srcobj/CMakeFiles/crnode.dir/rpc/Schd_types.cpp.o
[ 61%] Building CXX object srcobj/CMakeFiles/crnode.dir/task/TaskExecutor.cpp.o
[ 69%] Building CXX object srcobj/CMakeFiles/crnode.dir/task/TaskMoniter.cpp.o
[ 76%] Building CXX object srcobj/CMakeFiles/crnode.dir/util/Const.cpp.o
[ 84%] Building CXX object srcobj/CMakeFiles/crnode.dir/util/Globals.cc.o
[ 92%] Building CXX object srcobj/CMakeFiles/crnode.dir/util/utils.cc.o
[100%] Building CXX object srcobj/CMakeFiles/crnode.dir/util/Properties.cpp.o
Linking CXX executable crnode

[root@sim91 build]# make install
[100%] Built target crnode
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/bin/crnode.sh
-- Installing: /usr/local/share/doc/crnode/COPYRIGHT
-- Installing: /usr/local/share/doc/crnode/README
-- Installing: /usr/local/share/doc/crnode
-- Installing: /usr/local/share/doc/crnode/crnode.txt
-- Installing: /usr/local/bin/crnode

大功告成!更多內容請參考《CMake Practice》,再次對《CMake Practice》的做者表示感謝!

相關文章
相關標籤/搜索