CMake多工程最小实现

背景:

最近团队的新项目开始基于CMake作为工程管理,结合VSCode作为IDE进行开发,一个原因当然是为了可支持跨平台。原来我们的开发环境是使用VS系列IDE进行开发,在底层框架完全改为CMake支持后,后续的项目开发也开始完全用CMake组织工程,虽然说的是使用VSCode开发,不过对于今天要总结的内容暂时不必要,所以,这次介绍使用CMake生成VS2015的工程,重点在CMakeLists.txt怎么写

由于真正用于项目开发的CMakeLists.txt已经非常庞大和复杂,所以这里我将简化一版自己的练习工程作为总结。


需要环境:

  • 演示操作系统:Windows
  • CMake工具:这里我用的是cmake-3.19.8-win64-x64
  • VS系列IDE:作为代码开发
  • VSCode:可选,如果有CMake-GUI,完全可以替代,无非就是配置后Configuration和Generation,只是我最近发现VSCode安装CMake相关插件后,可以修改后直接在根CMakeLists.txt里保存一下,就能生成工程了,非常方便。
  • 开发语言:C++
  • 引入三方框架:Qt (这里只介绍Qt,其它三方库同理)

达到结果:

既然是CMake多工程的最小实现,那么,一是多个工程,或者说必要的几类工程例子:动态库、静态库、可执行程序、Qt界面库,这些如何组织起来;二是最小实现,在满足如上库的工程能够组件起来的情况下,能够将CMake精简,首先理解最基础的命令即可。
所以,例子工程将实现在main中,调用简单动态库导出的一个接口并打印内容、调用静态库中一个计算函数并打印结果,调用界面库中的一个Qt界面类,并show出来。

在这里插入图片描述


实现步骤:

  1. 目录构建
  • 目录树:
Training
└─SourceCode
    ├─Product
    │  ├─Bin
    │  ├─Config
    │  └─Data
    └─Source
        ├─SampleLib
        │  ├─Include
        │  └─Src
        ├─SampleWidget
        │  ├─Include
        │  └─Src
        ├─TrainingApp
        │  ├─Include
        │  └─Src
        └─TrainingCore
            ├─Include
            └─Src
  • 以上是我所习惯的工程项目组织路径,具体介绍如下
  1. Training代码工程名;
  2. Training下的SourceCode则包含了工程代码(Source)和产物(Product)
  3. 产物Product内Bin是产物生成路径,所以工程编译生成的exe、dll等,会有一个拷贝的动作,拷贝到Bin下面的Release(或者Debug、MinSizeRel、RelWithDebInfo)子目录内;
  4. Product内的Config是软件启动所需要的如网络、数据库等的配置文件存放目录;
  5. Product内的Data是软件启动所需要的必要数据初始化,比如地图预加载数据之类的;
  6. Source内就是各自的工程了:
  • TrainingApp是main.cpp所在工程,是可执行程序例子工程,会依赖下面的三个例子工程,调用里面的接口或类;
  • SampleLib是静态库例子工程;
  • TrainingCore是动态库例子工程;
  • SampleWidget是带Qt界面的动态库例子工程;

SourceCode下
在这里插入图片描述
Source下
在这里插入图片描述
一个工程下,以TrainingApp为例
在这里插入图片描述


  1. CMakeLists.txt的组织
    接下来主要关心Source下的工程结构,首先是根CMakeLists.txt
    在这里插入图片描述
cmake_minimum_required(VERSION 3.9)

project(AlgorithmTraining VERSION 1.0.0)

# 避免find_package 设置_ROOT的警告
cmake_policy(SET CMP0074 OLD)

#--------------------------------------------------------------------
# 系统变量初始化
#--------------------------------------------------------------------
# 支持的Configuration
message("Generated with config types: ${CMAKE_CONFIGURATION_TYPES}")

#设置Product生成目录
SET(PRODUCT_EXECUTABLE_DIR ${CMAKE_SOURCE_DIR}/../Product/Bin/
                            ${PRODUCT_EXECUTABLE_DIR})
message(STATUS "PRODUCT_EXECUTABLE_DIR: " ${PRODUCT_EXECUTABLE_DIR})

# 设置Qt环境变量路径 or $ENV{QTDIR}
SET(CMAKE_PREFIX_PATH D:\\Qt\\Qt5.10.1\\5.10.1\\msvc2015_64)
message(STATUS "CMAKE_PREFIX_PATH: " ${CMAKE_PREFIX_PATH})

#--------------------------------------------------------------------
# 添加需要包含的模块
#--------------------------------------------------------------------
# install时需要的目录变量的module
include(GNUInstallDirs)
# 打包导出配置所需要的module
include(CMakePackageConfigHelpers)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

#--------------------------------------------------------------------
# 添加子目录
#--------------------------------------------------------------------
add_subdirectory(TrainingApp)
add_subdirectory(TrainingCore)
add_subdirectory(SampleLib)
add_subdirectory(SampleWidget)

#--------------------------------------------------------------------
# 添加自定义配置
#--------------------------------------------------------------------
# 生成后事件:创建<Configuration>目录
add_custom_target(TARGET ALL
    COMMAND ${CMAKE_COMMAND} -E make_directory ${PRODUCT_EXECUTABLE_DIR}/$<CONFIGURATION>)

接下来依次是子CMakeLists.txt:
可执行程序TrainingApp
在这里插入图片描述

# 工程名称
set(PROJECT_NAME TrainingApp)
project(${PROJECT_NAME}
    VERSION ${CMAKE_PROJECT_VERSION})

#------------------------------------------------
# 依赖库/框架
#------------------------------------------------

#--------------------------------------------------------------------
# 源代码文件
#--------------------------------------------------------------------
set(_Enter
    "${CMAKE_CURRENT_LIST_DIR}/Src/main.cpp"
 )
source_group(入口 FILES ${_Enter})

# 生成target
add_executable(${PROJECT_NAME}
    ${_Enter}
) 

#--------------------------------------------------------------------
# 工程依赖
#--------------------------------------------------------------------

# 路径寻址
target_include_directories(${PROJECT_NAME}
    PRIVATE
        ./Include
)

# 依赖库
target_link_libraries(${PROJECT_NAME} 
    PUBLIC
        TrainingCore
        SampleLib
        SampleWidget
)

#--------------------------------------------------------------------
# 事件
#--------------------------------------------------------------------

# 拷贝
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 
  COMMAND "${CMAKE_COMMAND}" -E copy 
     "$<TARGET_FILE:${PROJECT_NAME}>"
     "${PRODUCT_EXECUTABLE_DIR}/$<CONFIGURATION>/$<TARGET_FILE_NAME:${PROJECT_NAME}>" 
  COMMENT "Copying to product directory")

动态库TrainingCore
在这里插入图片描述

# 工程名称
set(PROJECT_NAME TrainingCore)
project(${PROJECT_NAME}
    VERSION ${CMAKE_PROJECT_VERSION})

#------------------------------------------------
# 依赖库/框架
#------------------------------------------------

#--------------------------------------------------------------------
# 源代码文件
#--------------------------------------------------------------------
set(_Export
    "${CMAKE_CURRENT_LIST_DIR}/Include/TrainingCoreExport.h"
)
source_group(导出 FILES ${_Export})

set(_Public
   "${CMAKE_CURRENT_LIST_DIR}/Include/TrainingCoreApplication.h"
   "${CMAKE_CURRENT_LIST_DIR}/Src/TrainingCoreApplication.cpp"
 )
source_group(公用方法 FILES ${_Public})

add_library(${PROJECT_NAME} SHARED 
    ${_Export}
    ${_Public}
) 

#--------------------------------------------------------------------
# 工程依赖
#--------------------------------------------------------------------

# 导出宏
target_compile_definitions(${PROJECT_NAME} PRIVATE TRAININGCORE_EXPORTS)

# 路径寻址
target_include_directories(${PROJECT_NAME}
    PRIVATE
        ./Include
)

#--------------------------------------------------------------------
# 事件
#--------------------------------------------------------------------

# 拷贝
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 
  COMMAND "${CMAKE_COMMAND}" -E copy 
     "$<TARGET_FILE:${PROJECT_NAME}>"
     "${PRODUCT_EXECUTABLE_DIR}/$<CONFIGURATION>/$<TARGET_FILE_NAME:${PROJECT_NAME}>" 
  COMMENT "Copying to product directory")

静态库SampleLib
在这里插入图片描述

# 工程名称
set(PROJECT_NAME SampleLib)
project(${PROJECT_NAME}
    VERSION ${CMAKE_PROJECT_VERSION})

#------------------------------------------------
# 依赖库/框架
#------------------------------------------------

#--------------------------------------------------------------------
# 源代码文件
#--------------------------------------------------------------------

# 生成target
add_library(${PROJECT_NAME} STATIC)

file(GLOB INC_FILES Include/*.h)
file(GLOB INC_FILES1 Src/*.h)
set(${PROJECT_NAME}_Includes
   ${INC_FILES}
   ${INC_FILES1}
)

aux_source_directory(Src CPP_FILES)
set(${PROJECT_NAME}_SRCS 
    ${CPP_FILES}
)

target_sources(${PROJECT_NAME}
    PRIVATE 
		${${PROJECT_NAME}_Includes}
        ${${PROJECT_NAME}_SRCS}
)

#--------------------------------------------------------------------
# 工程依赖
#--------------------------------------------------------------------

# 路径寻址
target_include_directories(${PROJECT_NAME}
    PRIVATE
        ./Include
)

#--------------------------------------------------------------------
# 事件
#--------------------------------------------------------------------

Qt界面库SampleWidget
在这里插入图片描述

# 工程名称
set(PROJECT_NAME SampleWidget)
project(${PROJECT_NAME}
    VERSION ${CMAKE_PROJECT_VERSION})

#------------------------------------------------
# 依赖库/框架
#------------------------------------------------

# Qt
find_package(Qt5
    COMPONENTS
        Core
        Gui
        Widgets
        LinguistTools
    REQUIRED
)
set(CMAKE_AUTOMOC ON)   # 自动处理moc文件
set(CMAKE_AUTORCC ON)   # 自动处理资源文件
set(CMAKE_AUTOUIC ON)   # 自动处理UI文件

#--------------------------------------------------------------------
# 源代码文件
#--------------------------------------------------------------------
set(_Export
    "${CMAKE_CURRENT_LIST_DIR}/Include/SampleWidgetExport.h"
)
source_group(导出 FILES ${_Export})

set(_UI
    "${CMAKE_CURRENT_LIST_DIR}/Include/SampleWidget.h"
    "${CMAKE_CURRENT_LIST_DIR}/Src/SampleWidget.cpp"
    "${CMAKE_CURRENT_LIST_DIR}/Src/SampleWidget.ui"
 )
source_group(界面 FILES ${_UI})

# 生成target
add_library(${PROJECT_NAME} SHARED 
    ${_Export}
    ${_UI}
) 

#--------------------------------------------------------------------
# 工程依赖
#--------------------------------------------------------------------

# 导出宏
target_compile_definitions(${PROJECT_NAME} PRIVATE SAMPLEWIDGET_EXPORTS)

# 路径寻址
target_include_directories(${PROJECT_NAME}
    PRIVATE
        ./Include
)

# 链接库
target_link_libraries(${PROJECT_NAME}  Qt5::Core Qt5::Gui Qt5::Widgets)

#--------------------------------------------------------------------
# 事件
#--------------------------------------------------------------------

# 拷贝
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 
  COMMAND "${CMAKE_COMMAND}" -E copy 
     "$<TARGET_FILE:${PROJECT_NAME}>"
     "${PRODUCT_EXECUTABLE_DIR}/$<CONFIGURATION>/$<TARGET_FILE_NAME:${PROJECT_NAME}>" 
  COMMENT "Copying to product directory")

  1. 通过VSCode执行CMake,build出最终的VS2015工程
  • 刚才说VSCode可选,是因为CMake的GUI完全可以满足,把根CMakeLists.txt拖进去,然后设置必要参数,Configure->Generate后,就在指定的build目录下生成了。
    在这里插入图片描述
  • 然后说下VSCode如何“爽快”地生成:
    需要安装CMake需要的插件:CMake、CMake Tools
  • 在VSCode中点击“打开文件夹”,选到Source目录(即根CMakeLists.txt的目录)
    在这里插入图片描述
    这个时候VSCode把目录树都加载好了
  • 需要选择指定生成的工程
    在这里插入图片描述
  • VSCode会弹出这个,选择VS14.0(或者按需选择)即可
    在这里插入图片描述
  • 然后每次修改了任意CMakeLists.txt后,只需在根的CMakeLists.txt内Ctrl+s保存下,它就自动给生成build目录,生成工程了:
    在这里插入图片描述
  • 然后查看目录就会发现Source下有“build”文件夹,里面就是生成的VS工程了:
    在这里插入图片描述

这里是用VS2015作为例子,现在就可以打开sln,进入熟悉的VS-IDE开发了


  1. 配置到直接F5启动调试
  • 进入熟悉的界面,先编译一下,看生成的产物有没有拷贝到指定目录。
    在这里插入图片描述
    在Product/Bin下面确实生成了Debug目录,里面有2个库和1个exe
    在这里插入图片描述
  • 这时候双击exe会提示缺Qt库,这很正常,使用Qt的Bin内的windeployqt.exe执行一下,就会获得所需要的Qt基础依赖库
    在这里插入图片描述
  • 如果想F5直接启动,那么就把TrainingApp设置为启动项目,然后配置下属性内的调试
    在这里插入图片描述
    然后“应用”,就可以愉快地F5启动了。

分享

最后分享这个练习工程的CMakeLists.txt必要的演示源码,放在网盘链接内,目录组织形式根据例子介绍的放入了,还包括一个CMake 3.19的Window安装包
大家可以基于这个安装包,结合上面的步骤,自己跑一遍,就基本熟悉CMake如何组建工程的了,然后就可以用这样的方法写自己的工程了,进而学习更复杂的CMake指令,以及更多的工程构建,包括跨平台如何支持的配置。

链接:https://pan.baidu.com/s/1Tta_s__yCCfGmeefMALzZA 提取码:lcc6

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值