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)
  1. cmake_minimum_required:指定運行此配置文件所需的 CMake 的最低版本;
  2. project:參數值是 cmakeLearn,該命令表示項目的名稱是 cmakeLearn
  3. 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 提供的一個工具,專門用於打包。

我們做如下三個工作:

  1. 導入 InstallRequiredSystemLibraries 模組,以便之後導入 CPack 模組;
  2. 設置一些 CPack 相關變數,包括版權資訊和版本資訊,其中版本資訊用了上一節定義的版本號;
  3. 導入 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