參考資料地址:https://github.com/Akagi201/learning-cmake/blob/master/docs/cmake-practice.pdf
一、初識cmake
1. Cmake特點
- 開放源代碼
- 跨平台
- 能夠管理大型項目
- 簡化編譯構建和編譯過程(常用流程:cmake + make)
- 高效率
- 可擴展
- 每個目錄編寫一份CMakeLists.txt
二、初試cmake —— helloworld
1. 准備工作
mkdir -p /backup/cmake
cd /backup/cmake
mkdir t1
cd t1
在t1目錄建立main.c和CMakeLists.txt文件:
//main.c
1 #include<stdio.h> 2 int main() 3 { 4 printf("Hello World from t1 Main!\n"); 5 return 0; 6 }
//CMakeLists
1 PROJECT(HELLO) 2 SET(SRC_LIST main.c) 3 MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR} 4 MESSAGE(STATUS "This is SOURCE dir " ${HELLO_SOURCE_DIR}) 5 ADD_EXECTUABLE(hello SRC_LIST)
2. 開始構建
cmake . //生成Makefile、CMakeFiles、CMakeCache.txt等文件
make [VERBOSE=1] //實際構建工程,VERBOSE=1可查看make構建的詳細過程
./hello //運行目標文件
3. CMakeLists.txt代碼解釋
(1)PROJECT指令的語法
PROJECT(projectname [CXX] [C] [Java]) //定義工程名稱,並指定支持的語言(默認支持所有語言)
該指令隱式的定義了兩個cmake變量:<projectname>_BINARY_DIR以及<projectname>_SOURCE_DIR,內部編譯的情況下,兩個變量相同,如上述工程中均為/backup/cmake/t1,外部編譯則有所不同
同時cmake系統也自動預定義了PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR變量,他們的值分別跟HELLO_BINARY_DIR與HELLO_SOURCE_DIR一致;建議直接使用PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR變量,避免工程名稱的影響
(2)SET指令的語法
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]]) //用來顯式的定義變量
如:SET(SRC_LIST main.c),如果有多個源文件:SET(SRC_LIST main.c t1.c t2.c)
(3)MESSAGE指令的語法
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display"...) //用於向終端輸出用戶定義的信息
包含了三種類型:
- SEND_ERROR,產生錯誤,生成過程被跳過
- SATUS,輸出前綴為—的信息。
- FATAL_ERROR,立即終止所有cmake過程
(4)ADD_EXECUTABLE(hello ${SRC_LIST}) //生成名為hello的可執行文件,源文件列表由變量SRC_LIST定義,本例等同於ADD_EXECUTABLE(hello main.c)
4. 基本語法規則
(1)變量使用${var}方式取值,但是在IF控制語句中是直接使用變量名
(2)指令(參數1 參數2...) //參數使用括弧括起,參數之間使用空格或分號分開
(3)指令是大小寫無關的,參數和變量是大小寫相關的。建議全部使用大寫指令
5. 清理工程:make clean
6. 內部構建與外部構建(in-source build, out-of-source build)
(1)內部編譯不足:生成的中間臨時文件與代碼文件混在一起,且無法自動刪除
(2)外部編譯過程
- 清除上述t1目錄中的除main.c和CMakeLists.txt之外的所有中間文件,特別是CMakeCache.txt
- 在t1目錄中新建build目錄 //也可以建在其他位置
- 進入build目錄,運行cmake .. //..代表父目錄,也可以輸入工程代碼文件的全路徑;最終在build目錄中生成編譯需要的Makefile和其他中間文件
- 運行make //在當前目錄(build)中生成hello目標文件
//外部編譯優勢:對原有的工程沒有任何影響,所有的活動均發生在編譯目錄(build)
//注意:此時的HELLO_SOURCE_DIR仍然指代工程路徑,即/backup/cmake/t1;而HELLO_BINARY_DIR則指代編譯路徑,即/backup/cmake/t1/build
三、更好的Hello World
在采用外部構建的基礎上(構建目錄為build子目錄),修改上述Hello World使得更像一個工程,實現目標如下:
- 為工程添加一個子目錄src,用來放置工程源代碼
- 添加一個子目錄doc,用來放置這個工程的文檔hello.txt
- 在工程目錄添加文本文件COPYRIGHT, README
- 在工程目錄添加一個runhello.sh腳本,用來調用hello二進制
- 將構建后的目標文件放入構建目錄的bin子目錄
- 最終安裝這些文件:將hello二進制與runhello.sh安裝至/usr/bin,將doc目錄的內容以及COPYRIGHT/README安裝到/usr/share/doc/cmake/t2
1. 准備工作
mkdir -p /backup/cmake/t2
cd /back/cmake/t1 + cp main.c CMakeLists.txt ../t2
2. 添加子目錄src
cd /back/cmake/t2
mkdir src
mv main.c src
每個目錄的CMakeLists.txt如下:
// /backup/cmake/t2/CMakeLists.txt
1 PROJECT(HELLO) 2 ADD_SUBDIRECTORY(src bin) //bin目錄為編譯輸出(包含編譯中間結果)的目錄
// /backup/cmake/t2/src/CMakeLists.txt
1 ADD_EXECUTABLE(hello main.c)
mkdir build + cd build
cmake .. + make //構建生成的hello目標文件位於build/bin目錄中
語法解釋(ADD_SUBDIRECTORY指令):
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
用於向當前工程添加存放源文件的子目錄,並可以指定中間二進制和目標二進制文件存放的位置,EXCLUDE_FROM_ALL用於將指定目錄從編譯過程中排除,如工程中的example目錄可以在工程構建完成后進行單獨構建;如果未指定binary_dir則編譯輸出(包含中間結果)目錄為build/src(與源文件src目錄對應)
3. 指定目標二進制的保存位置
利用SET指令重新指定最終生成的目標二進制位置(指hello或者共享庫,不包含編譯生成的中間文件)
- SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) "build/bin"
- SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) "build/lib"
注:指令加到哪個CMakeLists.txt?添加原則:在哪里ADD_EXECUTABLE或ADD_LIBRARY,如果需要改變目標存放路徑,就在哪里加入上述的定義,本例中為src子目錄下的CMakeLists.txt
4. 如何安裝?
(1)直接make install //將hello安裝到/usr/bin目錄
(2)直接make install DESTDIR=/tmp/test //安裝到/tmp/test/usr/bin目錄(打包時常用)
5. 修改Helloworld支持安裝 (用到cmake的INSTALL指令和CMAKE_INSTALL_PREFIX變量)
(1)添加doc目錄及文件
cd /backup/cmake/t2
mkdir doc
vim doc/hello.txt //填寫任意內容並保存
(2)在工程目錄添加runhello.sh腳本、COPYRIGHT和README
cd /backup/cmake/t2
vim runhello.sh //內容為hello
touch COPYRIGHT
touch README
(3)改寫各目錄下的CMakeLists.txt文件
- 安裝COPYRIGHT和README,直接修改主工程目錄CMakeLists.txt文件,加入如下指令:INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)
- 安裝runhello.sh,直接修改主工程目錄CMakeLists.txt文件,加入如下指令:INSTALL(PROGRAMS runhello.sh DESTINATION bin)
- 安裝doc中的hello.txt,有兩種方式:一是通過在doc目錄建立CMakeLists.txt並將doc目錄通過ADD_SUBDIRECTORY加入工程來完成;另一種是直接在主工程目錄通過INSTALL(DIRECTORY ...)實現——INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/t2);其中“doc/”表明安裝doc目錄中的內容,而非整個目錄
注:DESTINATION均使用相對路徑,安裝后的路徑為${CMAKE_INSTALL_PREFIX}/<DESTINATION定義的路徑>,若采用絕對路徑則CMAKE_INSTALL_PREFIX其實就無效的
6. 運行修改內容
cd build
cmake -D CMAKE_INSTALL_PREFIX=/tmp/t2/usr .. //CMAKE_INSTALL_PREFIX的默認定義是/usr/local
make
make install
進入/tmp/t2目錄查看安裝結果:
./usr
./usr/share
./usr/share/doc
./usr/share/doc/cmake
./usr/share/doc/cmake/t2
./usr/share/doc/cmake/t2/hello.txt
./usr/share/doc/cmake/t2/README
./usr/share/doc/cmake/t2/COPYRIGHT
./usr/bin
./usr/bin/hello
./usr/bin/runhello.sh