1. 程式人生 > >【使用CMake組織C++工程】2:CMake 常用命令和變數

【使用CMake組織C++工程】2:CMake 常用命令和變數

前言

前面的文章介紹了一個最簡單的CMake工程,這篇文章將介紹一個稍微複雜一些的CMake工程,結合這個工程總結一下在組織一個C/C++工程時最為常用的一些CMake命令和變數。對於涉及到的命令和變數,介紹的原則是點到即止,先僅需掌握基本用法即可,讓工程跑起來。

上一篇文章中那個最簡單的CMake Hello World工程,在其CMake指令碼檔案CMakeLists.txt中,僅有一句話:

add_executable(hello hello.cpp)

這裡面的add_executable就是一個CMake命令,它的作用是新增一個可執行檔案構建目標。

下面從一個C++應用程式的編譯過程為脈絡對涉及到的命令和變數進行說明。

為了讓下面的說明舉例更加容易理解,先給出本文的示例工程目錄結構:

➜  /Users/sunyongjian1/codes/local_codes/cmake_test tree
.
├── CMakeLists.txt
├── include
│   └── util.h
├── lib
│   └── libutil.a
└── src
    └── main.cpp

三個資料夾: include, lib, src分別存放包含檔案,庫檔案,原始檔;一個CMakeLists.txt指令碼。下面我的任務是編寫這個指令碼,使得工程包含util.h標頭檔案,編譯main.cpp, 連結libutil.a, 最終生成一個可執行檔案hello.

給工程起個名字

加上這句:project(hello)

解釋

命令:project(<PROJECT-NAME> [LANGUAGES] [<language-name>...])

作用:定義工程名稱, 設定幾個變數的名字: PROJECT_NAME, PROJECT_SOURCE_DIR, <PROJECT-NAME>_SOURCE_DIR, PROJECT_BINARY_DIR, <PROJECT-NAME>_BINARY_DIR, 高階用法請見參考連結2:CMake命令

讓CMake找到我的標頭檔案

加上這句:include_directories(./include)

作用:把當前目錄(CMakeLists.txt所在目錄)下的include資料夾加入到包含路徑

我習慣這樣寫:include_directories(${CMAKE_CURRENT_LIST_DIR}/include)

解釋

命令: include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])

作用:

  • 把dir1, [dir2 …]這(些)個路徑新增到當前CMakeLists及其子CMakeLists的標頭檔案包含路徑中;

  • AFTER 或者 BEFORE 指定了要新增的路徑是新增到原有包含列表之前或之後

  • 若指定 SYSTEM 引數,則把被包含的路徑當做系統包含路徑來處理

第二種寫法裡用到了CMAKE_CURRENT_LIST_DIR這個變數,它表示當前CMakeLists所在的路徑.

讓CMake找到我的原始檔

加上: aux_source_directory(./src ${hello_src})

作用: 把當前路徑下src目錄下的所有原始檔路徑放到變數hello_src

解釋

命令:aux_source_directory(<dir> <variable>)

作用:查詢dir路徑下的所有原始檔,儲存到variable變數中.

上面的例子中,hello_src是一個自定義變數,在執行了aux_source_directory(./src ${hello_src})之後,我就可以像這樣來新增一個可執行檔案:add_executable(hello ${hello_src}), 意思是用hello_src裡面的所有原始檔來構建hello可執行程式, 不用手動列出src目錄下的所有原始檔了。

注意:

  • aux_source_directory 不會遞迴包含子目錄,僅包含指定的dir目錄

  • CMake官方不推薦使用aux_source_directory及其類似命令(file(GLOB_RECURSE …))來搜尋原始檔,原因是這樣包含的話,如果我再在被搜尋的路徑下新增原始檔,我不需要修改CMakeLists指令碼,也就是說,原始檔多了,而CMakeLists並不需要(沒有)變化,也就使得構建系統不能察覺到新加的檔案,除非手動重新執行cmake,否則新新增的檔案就不會被編譯到專案結果中。

  • 類似include_directories()CMAKE_CURRENT_LIST_DIR的用法,也可以寫成:aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/src ${hello_src})

讓CMake找到我的庫檔案

加上:link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)

解釋

命令:link_directories(directory1 directory2 ...)

作用:不必細說,與include_directories()類似,這個命令添加了庫包含路徑。

告訴CMake我的構建目標

加上:add_executable(${PROJECT_NAME} ${hello_src})

解釋

命令:add_executable(<name> [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] source1 [source2 ...])

作用:目前僅需知道,其作用是使用${hello_src}裡面的原始檔來生成一個可執行檔案,起名叫${PROJECT_NAME}, 即hello. 在一開始定義的那個project(hello)中的hello。

告訴CMake我要連結哪個庫檔案

加上:target_link_libraries(${PROJECT_NAME} util)

解釋

命令:target_link_libraries(<target> [item1 [item2 [...]]] [[debug|optimized|general] <item>] ...)

作用:僅需知道,名字叫${PROJECT_NAME}這個target需要連結util這個庫,會優先搜尋libutil.a(windows上就是util.lib), 如果沒有就搜尋libutil.so(util.dll, util.dylib)’

上面的例子意思是,讓hello去連結util這個庫。

傳遞FLAGS給C++編譯器

如果我的main.cpp裡面用到了C++11,那麼我需要告訴CMake在生成的Makefile裡告訴編譯器啟用C++11。與此類似,我可能也要傳遞其他FLAGS給編譯器,怎麼辦?

答案是:設定CMAKE_CXX_FLAGS變數

加上:

set(CMAKE_CXX_COMPILER      "clang++" )         # 顯示指定使用的C++編譯器

set(CMAKE_CXX_FLAGS   "-std=c++11")             # c++11
set(CMAKE_CXX_FLAGS   "-g")                     # 除錯資訊
set(CMAKE_CXX_FLAGS   "-Wall")                  # 開啟所有警告

set(CMAKE_CXX_FLAGS_DEBUG   "-O0" )             # 除錯包不優化
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG " )   # release包優化

解釋

  • CMAKE_CXX_FLAGS 是CMake傳給C++編譯器的編譯選項,通過設定這個值就好比 g++ -std=c++11 -g -Wall

  • CMAKE_CXX_FLAGS_DEBUG 是除了CMAKE_CXX_FLAGS外,在Debug配置下,額外的引數

  • CMAKE_CXX_FLAGS_RELEASE 同理,是除了CMAKE_CXX_FLAGS外,在Release配置下,額外的引數

開始構建

通過以上步驟, 最後,在檔案頭部新增CMake版本檢查,以我的電腦上的環境為例,我的CMake版本是3.0,那麼我在指令碼最開始加上:

cmake_minimum_required ( VERSION 3.0)

完整的CMakeLists.txt如下所示:

cmake_minimum_required ( VERSION 3.0)

project(hello)

include_directories(${CMAKE_CURRENT_LIST_DIR}/include)

link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)

aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/src ${hello_src})

add_executable(${PROJECT_NAME} ${hello_src})

target_link_libraries(${PROJECT_NAME} util)

set(CMAKE_CXX_COMPILER      "clang++" )         # 顯示指定使用的C++編譯器

set(CMAKE_CXX_FLAGS   "-std=c++11")             # c++11
set(CMAKE_CXX_FLAGS   "-g")                     # 除錯資訊
set(CMAKE_CXX_FLAGS   "-Wall")                  # 開啟所有警告

set(CMAKE_CXX_FLAGS_DEBUG   "-O0" )             # 除錯包不優化
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG " )   # release包優化

在CMakeLists.txt所在目錄,新建build目錄,並切換進build進行構建即可. 具體構建方法參見上一篇CMake Hello World的構建。

注意:生成的可執行檔案路徑會在build/src目錄下,如需修改生成位置,請參考CMake變數EXECUTABLE_OUTPUT_PATH

總結

本文通過一個C++工程例項,介紹了構建過程中用到的一些CMake命令和變數.

後面的文章將會講解如何構建更加複雜的C++工程,會用到CMake裡的function和其他命令和變數。

參考連結

作者水平有限,對相關知識的理解和總結難免有錯誤,還望給予指正,非常感謝!