美文网首页
CMake教程——QT项目使用CMake

CMake教程——QT项目使用CMake

作者: 生活简单些 | 来源:发表于2020-03-29 14:30 被阅读0次

    1. Basic Cmake Based Project

    # Qt对cmake版本的最小要求(但测试发现低一点的版本似乎也没问题)
    cmake_minimum_required(VERSION 3.16.0)
    
    # 项目命名
    # VERSION 1.0.0 LANGUAGES CXX: 是可选的
    project(helloworld VERSION 1.0.0 LANGUAGES CXX)
    
    # 如果采用非Qt Creator开发,需要通过告知Qt的安装路径,建议把Qt的安装路径设置到环境变量
    # 例如:QT_DIR=D:\Qt\6.1.2\msvc2019_64
    set(CMAKE_PREFIX_PATH $ENV{QT_DIR})
    
    # 有些项目会动态生成头文件,项目中需要引入它,因此需要将output目录也include进来
    # 等同于INCLUDE_DIRECTORY(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
    set(CMAKE_INCLUDE_CURRENT_DIR ON)
    
    # Qt6 对C++版本推荐至少17
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    
    # Qt特有的编译器需要打开,默认是关闭的
    set(CMAKE_AUTOMOC ON) # Meta-Object Compiler
    set(CMAKE_AUTORCC ON) # Resource Compiler
    set(CMAKE_AUTOUIC ON) # User Interface Compiler
    
    # 寻找Qt的库
    # Qt6 COMPONENTS Widgets:寻找Qt库中的Widget模块
    # REQUIRED: 意味着找不到报错并不会继续下去
    find_package(Qt6 COMPONENTS Core Qml Quick LinguistTools REQUIRED)
    
    # 集成源码以及资源并打包
    set(TS_FILES TestApp_zh_CN.ts TestApp_en_US.ts)
    aux_source_directory(src SRC_LIST)
    qt6_add_resources(QML_QRC qml_module_a.qrc qml_module_b.qrc)
    set(PROJECT_SOURCES ${SRC_LIST} ${TS_FILES} ${QML_QRC})
    
    # 这里如果不加WIN32,会导致编译的可执行文件运行时候会同时弹出一个命令行终端
    # 这是Windows的特性,对于其它平台得去掉WIN32
    add_executable(${CMAKE_PROJECT_NAME} WIN32 ${PROJECT_SOURCES})
    
    qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
    
    # cmake本身有四种编译模式:`Debug`, `Release`, `RelWithDebInfo`, `MinSizeRel`
    # 此操作将`Debug`和`RelWithDebInfo`归类于QML的debug,即这两种模式下QML运行会保留debug信息
    target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
    
    # 链接库到当前项目
    # PRIVATE:项目私有内部链接,只有在开发Library对外公开时候才会使用PUBLIC
    target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE 
        Qt6::Core
        Qt6::Qml
        Qt6::Quick)
    
    # 加入新qml文件能自动扫描到并集成到项目
    qt_import_qml_plugins(${CMAKE_PROJECT_NAME})
    

    2. Executable VS Library

    # 将${SRC_LIST}将编译成可执行文件,如果没有main函数会报错
    add_executable(${CMAKE_PROJECT_NAME} ${SRC_LIST})
    
    # 将${SRC_LIST}编译为library,library的类型可选择,默认是静态库
    add_library(${CMAKE_PROJECT_NAME} [STATIC|SHARED|MODULE] ${SRC_LIST})
    

    3. Every module has its own CMakeList.txt in its folder

    3.1 不推荐的做法:

    aux_source_directory(. DIR_SRCS1)
    aux_source_directory(. DIR_SRCS2)
    aux_source_directory(. DIR_SRCS3)
    
    add_executable(${CMAKE_PROJECT_NAME} ${DIR_SRCS1} ${DIR_SRCS2} ${DIR_SRCS3})
    

    这种做法会导致项目里即便改了一处代码,也会编译所有代码,导致编译时间较长,不能很好利用增量编译,再说C/C++本身编译就很慢

    3.2 推荐的做法

    以下以一个开源项目live555改版成为cmake项目作为推荐的项目代码组织结构的案例,虽然最终目标是编译成Library给外部使用,但内部同时也包含了打包成可执行文件的模块(mediaServer):

    ├── CMakeLists.txt
    ├── BasicUsageEnvironment
    │   └── CMakeLists.txt
    │   └── BasicHashTable.cpp
    │   └── xxx.cpp
    │   └── include
    │       └── BasicHashTable.hh
    │       └── xxx.hh
    ├── groupsock
    │   └── CMakeLists.txt
    │   └── GroupEId.cpp
    │   └── xxx.cpp
    │   └── include
    │       └── GroupEId.hh
    │       └── xxx.hh
    ├── liveMedia
    │   └── CMakeLists.txt
    │   └── AC3AudioRTPSink.cpp
    │   └── xxx.cpp
    │   └── include
    │       └── GroupEId.hh
    │       └── xxx.hh
    ├── mediaServer
    │   └── CMakeLists.txt
    │   └── DynamicRTSPServer.cpp
    │   └── xxx.cpp
    └── UsageEnvironment
        └── CMakeLists.txt
        └── HashTable.cpp
        └── xxx.cpp
        └── include
            └── Boolean.hh
            └── xxx.hh
    

    其中,BasicUsageEnvironment, groupsock, liveMedia, UsageEnvironment都是live555项目的子模块,mediaServer是集成所有子模块打包成为可执行文件的部分。

    作为项目入口,推荐的CMakeList.txt可以如下:

    cmake_minimum_required(VERSION 3.15)
    project(live555 LANGUAGES CXX)
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    
    # add all sub folders as modules
    ADD_SUBDIRECTORY(UsageEnvironment)
    ADD_SUBDIRECTORY(BasicUsageEnvironment)
    ADD_SUBDIRECTORY(groupsock)
    ADD_SUBDIRECTORY(liveMedia)
    ADD_SUBDIRECTORY(mediaServer)
    

    模块内部CMakeList.txt推荐如下:

    # 这里指定当前模块名,这里推荐用文件名作为模块名
    project(BasicUsageEnvironment)
    
    # 因为当前模块cpp里使用里其他模块的头文件,因此需要把它们include进来
    include_directories(../UsageEnvironment/include)
    include_directories(../groupsock/include)
    
    # 当前模块的头文件肯定要include进来
    include_directories(./include)
    
    # 当前模块下的cpp跟CMakeList.txt在同级目录
    aux_source_directory(./ SRC_LIST)
    
    # 当前只是模块,最终需要把所有的模块合并构建,因此当前需要指定编译对象为STATIC
    # ${PROJECT_NAME}的值即为当前模块名,需要注意的是不能用${CMAKE_PROJECT_NAME},因为那是跟目录下CMakeList.txt指定的名字,那是整个项目的名字,即:live555
    add_library(${PROJECT_NAME} STATIC ${SRC_LIST})
    

    作为目标是编译为可执行文件的CMakeList.txt如下:

    project(mediaServer)
    
    include_directories(../UsageEnvironment/include)
    include_directories(../groupsock/include)
    include_directories(../liveMedia/include)
    include_directories(../BasicUsageEnvironment/include)
    
    # 当前目录下也有代码
    aux_source_directory(. SRC_LIST)
    
    # 当前模块是main入口,最终目标是编译出live555的可执行文件,因此不再用add_library()
    add_executable(${PROJECT_NAME} ${SRC_LIST})
    
    # 链接所有其他模块到当前模块
    target_link_libraries(${PROJECT_NAME} PRIVATE 
        liveMedia 
        groupsock 
        BasicUsageEnvironment 
        UsageEnvironment)   
    

    4. 强制以Debug, Release, RelWithDebInfo, MinSizeRel其中一种作为编译方式

    方法一:

    在工程build目录下执行 cmake .. -DCMAKE_BUILD_TYPE=Debug|Release|MinSizeRel|RelWithDebInfo
    

    方法二:

    或者在顶级CMakeList.txt里加入: set(CMAKE_BUILD_TYPE Debug|Release|MinSizeRel|RelWithDebInfo)
    

    5. CMake高频常用变量

    变量的引用方式是使用${},在IF中,不需要使用这种方式,直接使用变量名亦可
    自定义变量:SET(OBJ_NAME xxxx),调用自定义变量: ${OBJ_NAME}
    设置环境变量: SET(ENV{NAME} value), 需要注意的是这里ENV没有$; 调用环境变量: $ENV{NAME}
    CMAKE的常用变量:

    变量 意义
    CMAKE_SOURCE_DIR 工程项目跟目录
    CMAKE_CURRENT_SOURCE_DIR CMakeList.txt所在的目录
    CMAKE_MODULE_PATH 如果工程复杂,可能需要编写一些cmake模块,这里通过SET指定这个变量
    LIBRARY_OUTPUT_DIR 库最终存放目录
    BINARY_OUTPUT_DIR 可执行的最终存放目录
    PROJECT_NAME 当前CMakeList.txt里设置的project_name
    CMAKE_PROJECT_NAME 项目跟目录配置的project_name

    6. 在CMake里区分操作系统

    if(CMAKE_SYSTEM_NAME MATCHES "Linux")  // 注意区分大写
      message(STATUS "Linux platorm!")
    elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
    if(CMAKE_CL_64)
      message(STATUS "Windows Win64 platform!")
      else()
        message(STATUS "Windows Win32 platform!")
      endif(CMAKE_CL_64)
    elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
      message(STATUS "FreeBSD platform!")
    else()
      message(STATUS "other platform!")
    endif(CMAKE_SYSTEM_NAME MATCHES "Linux")
    

    简化版亦可:

    if (WIN32)
        message(STATUS "Now is windows")
    elseif (APPLE)
        message(STATUS "Now is Apple systens.")
    elseif (UNIX)
        message(STATUS "Now is UNIX-like OS's.")
    endif ()
    

    7. 在CMake里打印信息

    message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
    
    (无) = 重要消息
    STATUS = 非重要消息
    WARNING = CMake 警告, 会继续执行
    AUTHOR_WARNING = CMake 警告 (dev), 会继续执行
    SEND_ERROR = CMake 错误, 继续执行,但是会跳过生成的步骤
    FATAL_ERROR = CMake 错误, 终止所有处理过程
    

    一般用于排错打印日志,或者打印编译过程信息及步骤

    相关文章

      网友评论

          本文标题:CMake教程——QT项目使用CMake

          本文链接:https://www.haomeiwen.com/subject/esnyuhtx.html