【使用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和其他命令和變數。
參考連結
作者水平有限,對相關知識的理解和總結難免有錯誤,還望給予指正,非常感謝!