CMAKE入門實戰
- 2019 年 10 月 6 日
- 筆記
0.導語
最近做的項目使用CLION構建,而這個採用CMakeLists.txt管理,因此為了更好的學習,故找到了一篇大牛級別的入門文章,有文章有程式碼,本文是花了一點時間把這篇文章學習後的重要點記錄吧,原作者github地址:https://github.com/wzpan/cmake-demo。
1.單個源文件
CMakeLists.txt 的語法比較簡單,由命令、注釋和空格組成,其中命令是不區分大小寫的。符號 # 後面的內容被認為是注釋。命令由命令名稱、小括弧和參數組成,參數之間使用空格進行間隔。
首先創建一個main.cpp
#include <iostream> int main() { std::cout << "Hello, World!" << std::endl; return 0; }
編寫第一個CMakeLists.txt:
cmake_minimum_required(VERSION 3.10) project(cmakeLearn) add_executable(main main.cpp)
cmake_minimum_required:指定運行此配置文件所需的 CMake 的最低版本;project:參數值是cmakeLearn,該命令表示項目的名稱是cmakeLearn。add_executable:將名為main.cpp的源文件編譯成一個名稱為cmakeLearn的可執行文件。
現在查看一下路徑:
light@city:~/CLionProjects/cmakeLearn$ ls CMakeLists.txt main.cpp
輸入下列命令進行cmake
cmake .
cmake過程
light@city:~/CLionProjects/cmakeLearn$ cmake . -- The C compiler identification is GNU 5.5.0 -- The CXX compiler identification is GNU 5.5.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - done -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: /home/light/CLionProjects/cmakeLearn
cmake 後 生成了Makefile
light@city:~/CLionProjects/cmakeLearn$ ls CMakeCache.txt cmake_install.cmake main.cpp CMakeFiles CMakeLists.txt Makefile
然後執行make
light@city:~/CLionProjects/cmakeLearn$ make Scanning dependencies of target main [ 50%] Building CXX object CMakeFiles/main.dir/main.cpp.o [100%] Linking CXX executable main [100%] Built target main
執行
light@city:~/CLionProjects/cmakeLearn$ ./main Hello, World!
2.多個源文件
2.1 同一目錄,多個源文件
在1中的cmake添加下面這行:
# 查找當前目錄下的所有源文件 # 並將名稱保存到 DIR_SRCS 變數 aux_source_directory(. DIR_SRCS)
當本地源文件很多,如果將源文件都加到裡面就很煩,所以這裡採用aux_source_directory。CMake 會將當前目錄所有源文件的文件名賦值給變數 DIR_SRCS ,再指示變數 DIR_SRCS 中的源文件需要編譯成一個名稱為 Demo 的可執行文件。
這樣就避免出現下面這種情況:
# 指定生成目標 add_executable(Demo main.cc MathFunctions.cc xx.cc ....cc)
小結:
aux_source_directory(. DIR_SRCS)
查找當前目錄下的所有源文件,並將名稱保存到 DIR_SRCS 變數。
2.2 多個目錄,多個源文件
此時目錄架構:
./Demo3 | +--- main.cc | +--- math/ | +--- MathFunctions.cc | +--- MathFunctions.h
對於這種情況,需要分別在項目根目錄 Demo3 和 math 目錄里各編寫一個 CMakeLists.txt 文件。
為了方便,我們可以先將 math 目錄里的文件編譯成靜態庫再由 main 函數調用。
這裡與上述2.1CMakeLists不同之處是在上面基礎上加上了:
# 添加 math 子目錄 add_subdirectory(math) # 添加鏈接庫 target_link_libraries(Demo MathFunctions)
以此完成:
- 子目錄添加
- 鏈接庫添加
最後,在子目錄下指定鏈接庫名字:
子目錄中的 CMakeLists.txt:
# 查找當前目錄下的所有源文件 # 並將名稱保存到 DIR_LIB_SRCS 變數 aux_source_directory(. DIR_LIB_SRCS) # 生成鏈接庫 add_library (MathFunctions ${DIR_LIB_SRCS})
小結:
add_library(MathFunctions ${DIR_LIB_SRCS})
將 src 目錄中的源文件編譯為靜態鏈接庫。
3.自定義編譯選項
CMake 允許為項目增加編譯選項,從而可以根據用戶的環境和需求選擇最合適的編譯方案。
例如,可以將 MathFunctions 庫設為一個可選的庫,如果該選項為 ON ,就使用該庫定義的數學函數來進行運算。否則就調用標準庫中的數學函數庫。
本節CMake與2不同如下三塊:
(1)加入一個配置頭文件,用於處理 CMake 對源碼的設置
# 加入一個配置頭文件,用於處理 CMake 對源碼的設置 configure_file ( "${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_BINARY_DIR}/config.h" )
configure_file 命令用於加入一個配置頭文件 config.h ,這個文件由 CMake 從 config.h.in生成,通過這樣的機制,將可以通過預定義一些參數和變數來控制程式碼的生成。
例如:修改main.cc:
#include "config.h" ... ... ... #ifdef USE_MYMATH #include "math/MathFunctions.h" #else #include #endif
根據 USE_MYMATH 的預定義值來決定是否調用標準庫還是 MathFunctions 庫。
上修改main.cc中引用了一個 config.h 文件,這個文件預定義了 USE_MYMATH 的值。但我們並不直接編寫這個文件,為了方便從 CMakeLists.txt 中導入配置,我們編寫一個 config.h.in 文件:
#cmakedefine USE_MYMATH
這樣 CMake 會自動根據 CMakeLists 配置文件中的設置自動生成 config.h 文件。
這裡使用了ccmake進行可視化編譯選擇,Ubuntu上安裝:
sudo apt-get install cmake-curses-gui
運行ccmake .後:

hjkl控制方向移動,enter 鍵可以修改該選項。修改完成後可以按下 c 選項完成配置,之後再按 g 鍵確認生成 Makefile 。ccmake 的其他操作可以參考窗口下方給出的指令提示。
下面來看一下config.h文件內容:
USE_MYMATH 為 ON
#define USE_MYMATH
USE_MYMATH 為 OFF
/* #undef USE_MYMATH */
(2)是否加入 MathFunctions 庫
# 是否加入 MathFunctions 庫 if (USE_MYMATH) include_directories ("${PROJECT_SOURCE_DIR}/math") add_subdirectory (math) set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions) endif (USE_MYMATH)
option 命令添加了一個 USE_MYMATH 選項,並且默認值為 ON 。
(3)是否使用自己的 MathFunctions 庫
# 是否使用自己的 MathFunctions 庫 option (USE_MYMATH "Use provided math implementation" ON)
根據 USE_MYMATH 變數的值來決定是否使用我們自己編寫的 MathFunctions 庫。
4.安裝和測試
4.1 安裝
之前在編譯一些源程式碼程式的時候,先make後make install,這樣會把一些頭文件與靜態/動態庫安裝到指定的目錄下。
那在CMAKE中同樣可以這麼做,如下:
首先先在 math/CMakeLists.txt 文件里添加下面兩行:
指定 MathFunctions 庫的安裝路徑 install (TARGETS MathFunctions DESTINATION bin) install (FILES MathFunctions.h DESTINATION include)
指明 MathFunctions 庫的安裝路徑。之後同樣修改根目錄的 CMakeLists 文件,在末尾添加下面幾行:
# 指定安裝路徑 install (TARGETS Demo DESTINATION bin) install (FILES "${PROJECT_BINARY_DIR}/config.h" DESTINATION include)
通過上面訂製,後安裝如下:
light@city:~/cmake-demo/Demo5$ sudo make install [ 50%] Built target MathFunctions [100%] Built target Demo Install the project... -- Install configuration: "" -- Installing: /usr/local/bin/Demo -- Installing: /usr/local/include/config.h -- Installing: /usr/local/lib/libMathFunctions.a -- Installing: /usr/local/include/MathFunctions.h
據此完成了安裝工作。
4.2 測試
CMake 提供了一個稱為 CTest 的測試工具。我們要做的只是在項目根目錄的 CMakeLists 文件中調用一系列的 add_test 命令。
啟動測試:
# 啟用測試 enable_testing()
(1)無幫助資訊測試
# 測試程式是否成功運行 add_test (test_run Demo 5 2)
(2)有幫助資訊測試
# 測試 2 的 10 次方 add_test (test_2_10 Demo 2 10) set_tests_properties (test_2_10) PROPERTIES PASS_REGULAR_EXPRESSION "is 1024")
其中 PASS_REGULAR_EXPRESSION 用來測試輸出是否包含後面跟著的字元串。
5.支援gdb
讓 CMake 支援 gdb 的設置也很容易,只需要指定 Debug 模式下開啟 -g 選項:
set(CMAKE_BUILD_TYPE "Debug") set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb") set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
6.添加版本號
在編寫項目中給項目添加版本號:
set (Demo_VERSION_MAJOR 1) set (Demo_VERSION_MINOR 0)
分別指定當前的項目的主版本號和副版本號。
如果想在程式碼中獲取資訊,可以修改config.h.in文件,添加兩個預定義變數:
// the configured options and settings for Tutorial #define Demo_VERSION_MAJOR @Demo_VERSION_MAJOR@ #define Demo_VERSION_MINOR @Demo_VERSION_MINOR@
主程式調用,列印:
printf("%s Version %d.%dn", argv[0], Demo_VERSION_MAJOR, Demo_VERSION_MINOR);
7.生成安裝包
如何配置生成各種平台上的安裝包,包括二進位安裝包和源碼安裝包。為了完成這個任務,我們需要用到 CPack ,它同樣也是由 CMake 提供的一個工具,專門用於打包。
我們做如下三個工作:
- 導入 InstallRequiredSystemLibraries 模組,以便之後導入 CPack 模組;
- 設置一些 CPack 相關變數,包括版權資訊和版本資訊,其中版本資訊用了上一節定義的版本號;
- 導入 CPack 模組。
對應CMakeLists.txt如下:
# 構建一個 CPack 安裝包 include (InstallRequiredSystemLibraries) set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") set (CPACK_PACKAGE_VERSION_MAJOR "${Demo_VERSION_MAJOR}") set (CPACK_PACKAGE_VERSION_MINOR "${Demo_VERSION_MINOR}") include (CPack)
下面就是如何使用:
輸入cpack .,也可以指定二進位與源碼安裝包:
- 生成二進位安裝包:
cpack -C CPackConfig.cmake
- 生成源碼安裝包
cpack -C CPackSourceConfig.cmake
cpack安裝:
light@city:~/cmake-demo/Demo8$ cpack -CPackConfig.cmake CPack: Create package using STGZ CPack: Install projects CPack: - Run preinstall target for: Demo8 CPack: - Install project: Demo8 CPack: Create package CPack: - package: /home/light/cmake-demo/Demo8/Demo8-1.0.1-Linux.sh generated. CPack: Create package using TGZ CPack: Install projects CPack: - Run preinstall target for: Demo8 CPack: - Install project: Demo8 CPack: Create package CPack: - package: /home/light/cmake-demo/Demo8/Demo8-1.0.1-Linux.tar.gz generated. CPack: Create package using TZ CPack: Install projects CPack: - Run preinstall target for: Demo8 CPack: - Install project: Demo8 CPack: Create package CPack: - package: /home/light/cmake-demo/Demo8/Demo8-1.0.1-Linux.tar.Z generated.
此時會在本地目錄下創建3個不同格式的二進位包文件:
light@city:~/cmake-demo/Demo8$ ls Demo8* Demo8-1.0.1-Linux.sh Demo8-1.0.1-Linux.tar.gz Demo8-1.0.1-Linux.tar.Z
隨便選擇一個安裝,例如sh:
sh Demo8-1.0.1-Linux.sh

使用:
light@city:~/cmake-demo/Demo8$ ./Demo8-1.0.1-Linux/bin/Demo 5 2 Now we use our own Math library. 5 ^ 2 is 25
