使用cmake构建visual studio项目
之前在写c++项目的时候,一直使用的是宇宙第一IDE Visual Studio,vs确实默默在后台做了许多事情,你只用管写代码其他的都不用操心。但是当项目引入大量的第三方库的时候,许多构建相关的配置就会比较麻烦。考虑到Rigel(一个巨大的轮子)这个项目后续需要支持多平台构建,所以打算试试使用cmake组织整个工程。
对于cmake使用其实没有太多经验的,主要是之前在编译一些第三方库的时候,libpng freetype之类的。一行命令行脚本 cmake ./
然后自动生成了sln项目,后面就是熟悉的visual studio了。
cmake初体验
创建cmake工程
在项目目录下创建一个CMakeLists.txt
创建好了一个cmake工程。
1 | #指定cmake的最小版本 |
在一个大型的项目中可能会有很多子工程,就类似于vs中的解决方案和项目的关系。可以对每个子项目指定一个CMakeLists.txt
文件,子项目可以是一个静态库动态库或者是可执行程序等等。
在主的CMakeLists文件中添加add_subdirectory([sub folder path])
就可以引入子工程的配置。
配置生成的项目
接下来就要添加项目了add_executable([target name] [source files...])
add_executable可以为项目添加一个可执行工程add_library([target name] [source files...])
对于项目中静态库和动态库可以使用add_library进行添加
如果项目只有一个main.cpp
那么add_executable(testproj main.cpp)
,生成的sln中就会把main.cpp添加到testproj这个项目的源文件下面。
项目依赖配置
我们可以指定一个target依赖其他的库项目的targetadd_dependencies(RigelEditor rgcore)
多源文件的配置
如果有多个源文件可以直接在source files里面添加add_executable(testproj xxx1.cpp xxx2.cpp...)
。
当然可以使用aux_source_directory([Path] [Variable])
命令来获取一个目录下的的所有源文件 然后储存在变量Variable
中
还可以使用FILE()
方法来获取
几个简单的例子
1 | FILE(GLOB HEAD_RIGEL_EDITOR ./include/*.h) #GLOB 获取./include/目录下的*.h 储存在变量HEAD_RIGEL_EDITOR中 |
add_executable(testproj ${HEAD_RIGEL_EDITOR} ${SRCS_RIGEL_EDITOR})
这样我们就可以给项目添加多个源文件了
CMAKE生成的sln工程中不会把源文件拷贝到build的目录中,而是通过引用文件的方式
引用路径与库目录
我们添加了一个子项目后还需要设置include的路径,来添加对头文件的引用。target_include_directories(RigelEditor PUBLIC ./include/rgeditor)
这样就给RigelEditor这target添加了一个include的路径./include/rgeditor
。
添加link的库文件target_link_libraries(RigelEditor rgcore)
如果需要添加第三方的库文件,可以直接引用库文件的路径。
需要注意的是使用CMAKE_SOURCE_DIR可以获取到源文件的目录而不是生成的目录。
target_link_libraries(rgcore ${THIDPART_LIB_DIR}\\freetype\\libs\\x64\\freetype28.lib)
cmake其他配置
源文件分组
在visual studio中我们可以添加筛选器给源文件进行分组。cmake提供了source_group()
来支持这一特性。
source_group
需要写在add_library
或者add_executable
之后
下面是使用的示例:
1 | FILE(GLOB SRCS_RGGRAPHICS_CORE ./source/*.cpp) |
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
打开USE_FOLDERS的配置
判定编译的Architecture
有时候我们的配置需要区别64位与32位,如一些静态库动态库的版本不同,就需要在cmake中判断当前配置是否是64位。
CMAKE提供了CMAKE_CL_64
这个变量来判定
1 | if(CMAKE_CL_64) |
Configuration Debug/Release的判定
有时候我们需要区别Debug/Release build$<$CONFIG:DEBUG:${BUILD_DIR_RIGELEDITOR_DEBUG}>$<$CONFIG:RELEASE:${BUILD_DIR_RIGELEDITOR_RELEASE}>
这个值在Debug的配置下输出${BUILD_DIR_RIGELEDITOR_DEBUG}在Release下则是
${BUILD_DIR_RIGELEDITOR_RELEASE}
指定build输出目录
生成sln会给每个子项目生成自己的输出路径一般情况下是$(ProjectDir)$(PlatformTarget)$(Configuration)
如TestProj/x64/Debug
这样的路径
在cmake中我们可以制定一个target生成的路径便于我们执行post-build的脚本来做一些处理
1 | set_target_properties(RigelEditor PROPERTIES |
自定义命令处理post-build
项目生成之后如果有文件需要拷贝,在vs中可以使用生成事件-后期生成事件-命令行来添加命令。cmake中也可以指定这样的生成事件的配置。
add_custom_command(TARGET RigelEditor POST_BUILD COMMAND xcopy ARGS ${CMD_COPY_DATA})
这样指定了RigelEditor在Post Build阶段执行 xcopy的命令行 参数为 ${CMD_COPY_DATA}
生成sln之后再后期生成事件中就可以看到cmake添加的命令
1 | setlocal |
cmake杂项
###VC++ Unicode支持
在vc++中定义UNICODE
与_UNICODE
这两个宏,字符串就自动使用了宽字节版本的。所以我们需要在cmake中设定宏,否则编译会报错,使用visual stuido直接创建项目时会直接帮我们设置的好(再一次赞美下VisualStudio)add_definitions(-DUNICODE -D_UNICODE)
###Visual Studio中配置预编译头
vs中通过配置预编译头文件加快项目的编译速度。可以通过设置Compile Flags /Yc /Yu添加预编译头的配置
使用CMake进行构建时,set_source_files_properties
命令可以对源文件添加编译器参数
为了给多个子项目都配置预编译头文件,使用CMake的macro来批量处理。
定义两个macro set_pch
use_pch
1 | macro(set_pch VARCPP VARHEADER) |
使用方式
假设预编译头文件为stdafx.h则所有的.cpp文件除了stdafx.cpp需要设置 /Yustdafx.h,使用预编译头。
stdafx.cpp设置 /Ycstdafx.h,生成预编译头。
1 | use_pch("${SRCS_RGCORE}" "rgcore.h") |
一个常见的错误 调用cmake中的方法与宏时 如果需要传入的参数为数组 正确的写法应该是"${VARNAME}"
而不是${VARNAME}
总结
大概花了半天的时间将Rigel 从sln迁移到了cmake来构建。上文大概就是在配置过程中遇到的坑以及解决的方法的总结,提及到的cmake命令可以满足基本的项目构建了。
cmake使用起来感觉确实很方便,比起直接设置vs sln中的配置之类的,后续应该就会都用cmake了吧。
更新记录
- 2017-10-04 文章发布
- 2017-10-13 添加了Visual Stdudio 预编译头配置