大部分情况下我们都要使用CMake生成的IDE工程进行相关的开发工作,所以本节重点分享一些优化类型的CMake命令和使用技巧,使CMake生成的IDE工程更加规范清晰。
app、lib项目结构优化
一般lib和app的源码我们是分开,并且这是2个工程,在app中要引入lib的CMakeLists.txt,可以这么做:
- 方式1
include(lib/CMakeLists.txt)
- 方式2
add_subdirectory(./lib )
> 注意:如果lib目录不是app目录的子目录,必须指定第二个参数,也就是bin目录
两种方式的异同:
- 相同点:
- 在上层的CMakeLists.txt中可以直接使用其中的变量。
- 区别:
-
include的scope还是上层的CMakeLists.txt -
add_subdirectory的scope是当前CMakeLists.txt
也就是
CMAKE_CURRENT_SOURCE_DIR发生了改变 -
设置lib输出目录
set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin)
将生成的lib放在bin目录下,我们可以指定输出到其他目录,方便后期打包exe。
设置app输出目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin)
将生成的可执行程序放在bin目录下,一般我们都会指定这个目录,方便后期打包exe。
EXECUTABLE_OUTPUT_PATH在Windows上的一个坑:
Visual Studio生成器(VS工程)会在EXECUTABLE_OUTPUT_PATH后面自动加一个${CMAKE_BUILD_TYPE},这与其他生成器不同!
解决办法:
if(${CMAKE_GENERATOR} MATCHES "Visual Studio*")
set(APP_REAL_PATH $(outdir))
else()
set(APP_REAL_PATH ${EXECUTABLE_OUTPUT_PATH})
endif()
使用vs中的宏$(outdir)正确获取到对应的目录即可,巧妙的规避了目录的问题,在复制文件的时候,我们也可以采取这种方式。
自动收集目录下的源代码
- 方式1:
aux_source_directory(./ SRC)
自动将指定目录下的源文件路径,以list的形式存放在指定的变量SRC中。
缺点: 无法递归,但可以配合
foreach实现、只能检索*.cpp文件。
- 方式2(推荐):
# 将所有的头文件收集起来放在src变量里面,无法递归
file(GLOB src ${CMAKE_CURRENT_LIST_DIR}/*.h)
# 将所有的头文件、源文件收集起来放在src变量里面,无法递归
file(GLOB src ${CMAKE_CURRENT_LIST_DIR}/*.h ${CMAKE_CURRENT_LIST_DIR}/*.cpp)
# 将所有的头文件收集起来放在src变量里面,可以递归文件
file(GLOB_RECURSE src ${CMAKE_CURRENT_LIST_DIR}/*.h)
给target设置源文件
- 方式1
set(LIB_NAME "lib")
aux_source_directory(./ SRC)
# 在add的同时添加source
add_library(${LIB_NAME} ${SRC})
- 方式2
set(LIB_NAME "lib")
# 先不着急指定源代码
add_library(${LIB_NAME})
# 收集源代码
aux_source_directory(./ SRC)
# 使用单独的api设置
target_sources(${LIB_NAME} PRIVATE ${SRC})
对于方式2,实际项目中也经常使用,举个例子:
set(src)
# 因为src变量的作用域,在a/CMakeLists.txt中也能访问到
include(a/CMakeLists.txt)
target_sources(${app} PRIVATE ${src})
这种方式适合源代码比较分散,分布在不同的CMakeLists.txt中,跨平台的游戏引擎代码就存在这种需求,所以在add_library、add_executeable先不设置源文件,外部定义src变量,交给某个CMakeLists.txe进行变量src的赋值修改,一般 a/CMakeLists.txt里面都会有如下的逻辑:
list(APPEND src others.cpp) # 追加源文件
对项目工程文件组进行重命名
将包含src的源码工程目录命名为src
# 语法:source_group(<name> [FILES <src>...] [REGULAR_EXPRESSION <regex>])
source_group("src" FILES ${src})
工程目录结构按照文件夹目录结构组织
# 语法:source_group(TREE <root> [PREFIX <prefix>] [FILES <src>...])
# TREE是在3.8支持的
source_group(TREE ./ FILES ${src}
项目工程中的文件结构会以源文件目录的结构进行组织,方便我们观察源码在磁盘上的位置。
${src}里面的所有文件必须是<root>子目录文件,否则会报错:
source_group ROOT is not a prefix of file
将多个Target放到一个目录下
CMake生成的IDE项目工程结构,项目默认都是平级的,但是有时我们希望将某些项目归类,集中放到一个目录下。
使用set_target_properties的FOLDER属性可以将生成的多个target放在一个目录下:
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set_target_properties(
cocos2d luacocos2d cocos2dInternal
PROPERTIES
FOLDER "cocos"
)
既然有set,就有get,对应的命令为:get_property、get_target_properties。
路径分隔符转换
set(QtHeaderFiles)
foreach(dir Widgets WebSockets WebEngineWidgets)
set(tmp ${env_qtdir}/include/Qt${dir})
include_directories(tmp)
message("qt header dir: ${tmp}")
file(GLOB head ${tmp}/*.h)
list(APPEND QtHeaderFiles ${head})
unset(tmp)
endforeach()
# 路径分隔符转换
file(TO_CMAKE_PATH "${QtHeaderFiles}" QtHeaderFiles)
如上所示,QtHeaderFiles里面的路径在Windows下是\
这个路径分隔符来自环境变量,而Windows的路径分隔符都是\
要转换为/,通过file(TO_CMAKE_PATH)转换下即可