最近团队的新项目开始基于CMake作为工程管理,结合VSCode作为IDE进行开发,一个原因当然是为了可支持跨平台。原来我们的开发环境是使用VS系列IDE进行开发,在底层框架完全改为CMake支持后,后续的项目开发也开始完全用CMake组织工程,虽然说的是使用VSCode开发,不过对于今天要总结的内容暂时不必要,所以,这次介绍使用CMake生成VS2015的工程,重点在CMakeLists.txt怎么写。
由于真正用于项目开发的CMakeLists.txt已经非常庞大和复杂,所以这里我将简化一版自己的练习工程作为总结。
既然是CMake多工程的最小实现,那么,一是多个工程,或者说必要的几类工程例子:动态库、静态库、可执行程序、Qt界面库,这些如何组织起来;二是最小实现,在满足如上库的工程能够组件起来的情况下,能够将CMake精简,首先理解最基础的命令即可。 所以,例子工程将实现在main中,调用简单动态库导出的一个接口并打印内容、调用静态库中一个计算函数并打印结果,调用界面库中的一个Qt界面类,并show出来。
Training └─SourceCode ├─Product │ ├─Bin │ ├─Config │ └─Data └─Source ├─SampleLib │ ├─Include │ └─Src ├─SampleWidget │ ├─Include │ └─Src ├─TrainingApp │ ├─Include │ └─Src └─TrainingCore ├─Include └─Src
Training代码工程名;Training下的SourceCode则包含了工程代码(Source)和产物(Product)产物Product内Bin是产物生成路径,所以工程编译生成的exe、dll等,会有一个拷贝的动作,拷贝到Bin下面的Release(或者Debug、MinSizeRel、RelWithDebInfo)子目录内;Product内的Config是软件启动所需要的如网络、数据库等的配置文件存放目录;Product内的Data是软件启动所需要的必要数据初始化,比如地图预加载数据之类的;Source内就是各自的工程了: TrainingApp是main.cpp所在工程,是可执行程序例子工程,会依赖下面的三个例子工程,调用里面的接口或类;SampleLib是静态库例子工程;TrainingCore是动态库例子工程;SampleWidget是带Qt界面的动态库例子工程;
TrainingApp是main.cpp所在工程,是可执行程序例子工程,会依赖下面的三个例子工程,调用里面的接口或类;SampleLib是静态库例子工程;TrainingCore是动态库例子工程;SampleWidget是带Qt界面的动态库例子工程;
SourceCode下 Source下 一个工程下,以TrainingApp为例
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")
这里是用VS2015作为例子,现在就可以打开sln,进入熟悉的VS-IDE开发了
最后分享这个练习工程的CMakeLists.txt和必要的演示源码,放在网盘链接内,目录组织形式根据例子介绍的放入了,还包括一个CMake 3.19的Window安装包。 大家可以基于这个安装包,结合上面的步骤,自己跑一遍,就基本熟悉CMake如何组建工程的了,然后就可以用这样的方法写自己的工程了,进而学习更复杂的CMake指令,以及更多的工程构建,包括跨平台如何支持的配置。
链接:https://pan.baidu.com/s/1Tta_s__yCCfGmeefMALzZA 提取码:lcc6