由於工作需要,最近打算統一將所有C/C++項目都改成使用cmake編譯。傳統后台業務問題不大,但是有些牽涉到跨平台的Qt項目還是折騰了一陣。下面對這段時間的收獲做一個總結,也希望幫助看到本文的朋友少走彎路。特此聲明,以下配置均為Qt5.6.3 MinGW 4.9.2 32位版本。
1. 基本配置項
1.1 moc ui和rcc編譯開關
SET(CMAKE_AUTOMOC ON) SET(CMAKE_AUTOUIC ON) SET(CMAKE_AUTORCC ON)
1.2 啟用C++11標准
* 如果在.h文件中直接對參數初始化或使用了nullptr等,請務必配置這一項
SET(CMAKE_CXX_STANDARD 11)
1.3 包含所有.h文件
* 有些只編寫了.h文件,例如常量聲明,結構體聲明等,請務必配置這一項
SET(CMAKE_INCLUDE_CURRENT_DIR ON)
1.4 查找Qt模塊
* 首先是CMAKE_PREFIX_PATH,對應各個模塊的cmake文件路徑,其次FIND_PACKAGE才能生效
SET(CMAKE_PREFIX_PATH <PREFIX_PATH>/lib/cmake)
FIND_PACKAGE(Qt5 COMPONENTS Core Xml Sql Gui Widgets REQUIRED)
1.5 引入外部頭文件和動態鏈接庫
* 我習慣在src的同級目錄使用include和lib路徑來保存依賴,這樣讓整個系統看起來更整潔
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/../include)
LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/../lib)
1.6 統一配置各目錄層級的.cpp
* 網上有很多做法是每一個目錄編寫獨立的CMakeLists.txt,但是個人感覺沒有單一CMakeLists.txt文件配置方便,特別是如果各個目錄間存在依賴的情況下更容易出錯
AUX_SOURCE_DIRECTORY(./<mod_1> mod_1_src_list) AUX_SOURCE_DIRECTORY(./<mod_2> mod_2_src_list) AUX_SOURCE_DIRECTORY(. src_list)
1.7 指定最終編譯產物的輸出路徑
* 和使用include和lib作為外部依賴路徑類似,我也習慣在src的同級目錄分別創建bin和out用來存放最終的編譯產物
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../out)
1.8 區分release和debug生成的動態庫
* 有時候為了方便調試,我們需要讓debug版本的動態以d結尾
SET(CMAKE_DEBUG_POSTFIX d)
1.9 QDebug在release下依然可以輸出函數名和行號
* 主要是為了保證日志數據有效
ADD_DEFINITIONS(-DQT_MESSAGELOGCONTEXT)
1.10 添加鏈接庫
TARGET_LINK_LIBRARIES(${target} Qt5::Sql Qt5::Gui <lib>)
1.11 輸出
ADD_EXECUTABLE(${target} ${SRC_LIST})
ADD_LIBRARY(${target} SHARED ${SRC_LIST})
1.12 根據release和debug分目錄數據產物
* 主要是針對動態庫產物的輸出,分不同的目錄更適合大型項目的統編
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/../debug)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/../release)
1.13 添加自定義的宏
* 可以在項目中通過條件編譯的方式選擇不同的配置
OPTION(C_MODE "Use Customize Mode" ON) IF(C_MODE) # 其他指令 ENDIF()
改變代碼的編譯過程
// 在cmakelists.txt中定義 ADD_DEFINITIONS(-DTEST_DEBUG) // 配置源碼中的條件編譯 #ifdef TEST_DEBUG ... ... #else ... #endif
debug調試
ADD_DEFINITIONS("-Wall -g")
1.14 添加指定文件
* 一般來說,一個.cpp文件都會有一個.h來對應。編譯的時候我們只需要告訴編譯器所有的.cpp文件即可。例如:ADD_EXECUTABLE(sth ${cpp})。但是有時候,我們會定義一些結構體或常量,並將他們集中聲明在一個.h文件里。
FILE(GLOB HEADER_FILES "*.h") FILE(GLOB SOURCE_FILES "*.cpp")
1.15 安裝與復制
* 當我們需要在編譯完成以后執行copy或install的時候
FILE(COPY ${HEADER_FILES} DESTINATION ${PROJECT_SOURCE_DIR}/../include/${target})
INSTALL(TARGETS mylib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic)
2. 注意事項
2.1 如果是使用MinGW編譯windows下的動態庫不需要添加導出類的宏
2.2 LINK_DIRECTORIES 指令必須放在ADD_指令前
2.3 對多級目錄的項目使用cmake做統編,每一個層級的編譯應該使用動態庫的方式
2.4 如果你使用的是QtCreator,自定義宏的方式可能不生效,但這並不是cmake的問題
3. Windows下如何使用cmake和gcc
3.1 安裝MinGW Installation Manager和CMake的windows安裝包,安裝gcc編譯工具鏈(mingw32-gcc, mingw32-gcc-g++, mingw32-make ...缺少的依賴可以從MinGW Installation Manager里面安裝)
3.2 配置環境變量
*3.3 mingw32-make.exe 復制后重命名為 cmake.exe
3.4 指定編譯方案:cmake -G "MinGW Makefiles" . (如果編譯器為vs的話使用"NMake Makefiles")
3.5 make 完成編譯

*3.6 因為mingw已經不再更新,建議使用mingw-w64代替
