由于工作需要,最近打算统一将所有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()
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的问题