CMake系列:问题解决点滴记录

文章目录

  • 1. CMake点滴记录
    • 1.1. 如何理解、使用CMake多配置编译系统
    • 1.2. 判断环境变量是否存在
    • 1.3. VS调试不用windeployqt
    • 1.4. Debug与Release输出不同名称的编译结果
    • 1.5. CMake不用手动添加源代码
    • 1.6. CMake在Windows上生成没有控制台的程序
    • 1.7. CMake添加编译生成事件
    • 1.8. VS项目设置UNICODE
    • 1.9. 一个子项目的属性如何传递到另一个子项目
    • 1.10. CMake使用Qt MOC、界面和资源文件
    • 1.11. CMake多编译器支持
    • 1.12 关闭链接警告
    • 1.13 有空格的宏定义不是字符串类型
    • 1.14 版本号转十六进制整数
    • 1.15 CMake文件读写操作

1. CMake点滴记录

1.1. 如何理解、使用CMake多配置编译系统

  1. 常见错误

    以Debug和Release配置有不同的宏定义为例,如下所示:

    if(NOT DEFINED CMAKE_BUILD_TYPE)add_definitions(-DDEBUG)
    else()add_definitions(-DRELEASE)
    endif()
    
  2. 问题现象

    1. 按Debug运行Configure后Release配置没有 RELEASE宏定义,按Release运行Configure后Debug配置没有 DEBUG宏定义。
    2. 每次运行 cmake -B命令只能看到一个宏定义
  3. 正确做法

    add_definitions($<$:-DDEBUG> $<$:-DRELEASE>)
    

    或者

    add_definitions($,-DDEBUG,-DRELEASE>)
    
  4. CMake的最少Configure原则

    以下典型的情况不运行Configure:

    1. 项目配置没有改变
    2. CMake运行环境没有改变:操作系统版本、编译器版本等等
    3. 不是准备版本发布
  5. CMake统一原则

    同一个项目只有同一步CMakeLists.txt,应对不同的编译器、不同的操作系统。

  6. cmake if指令应该什么时候使用

    1. 不同的编译器有不同的编译选项,比如MSVC和GCC
    2. 不同的目标平台有不同的编译选项,比如Windows和Linux
  7. 活学活用

    CI只需要验证Debug配置环境可运行:cmake -B build -DCMAKE_BUILD_TYPE=Debug,Release同理。
    CI只需要验证Debug编译结果可运行:cmake --build build --config Debug -j 16,Release同理。

  8. CMake淘汰的用法

    项目属性+配置名的做法已被逐步淘汰。举例如下:

    set(COMPILE_DEFINITIONS_DEBUG   DEBUG)   # 3.23版本已不起作用
    set(COMPILE_DEFINITIONS_RELEASE RELEASE) # 3.23版本已不起作用
    set_target_properties(${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_NAME_DEBUG   testdLIBRARY_OUTPUT_NAME_RELEASE test
    )
    

1.2. 判断环境变量是否存在

方法1:

if(NOT DEFINED ENV{Qt5_DIR})message(FATAL_ERROR "environment variable Qt5_DIR is required.")
else()message("---- Qt5_DIR is set to " $ENV{Qt5_DIR})
endif()

方法2:

if("$ENV{Qt5_DIR}" STREQUAL "")message(FATAL_ERROR "environment variable Qt5_DIR is required.")
else()message("---- Qt5_DIR is set to " $ENV{Qt5_DIR})
endif()

1.3. VS调试不用windeployqt

像Qt Creator一样调试

set_property(TARGET LTFocus PROPERTY VS_DEBUGGER_ENVIRONMENT "PATH=$ENV{Qt5_DIR}/bin")
set_property(TARGET LTFocus PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${EXECUTABLE_OUTPUT_PATH}")

1.4. Debug与Release输出不同名称的编译结果

set_property(TARGET LTFocus PROPERTY DEBUG_POSTFIX "d")

1.5. CMake不用手动添加源代码

举例:

file(GLOB SRCS *.h *.cpp *.ui *.rc *.qrc)
add_executable(${PROJECT_NAME} ${SRCS})

GLOB换成 GLOB_RECURSE,结果包含子目录下的代码文件。

1.6. CMake在Windows上生成没有控制台的程序

方法1:

if(MSVC)add_executable(${PROJECT_NAME} WIN32 ${SRCS})
endif()

方法2:

if(MSVC)add_link_options(/SUBSYSTEM:WINDOWS)
endif()

1.7. CMake添加编译生成事件

set(EXE_DIR ${CMAKE_SOURCE_DIR}/${CMAKE_BUILD_TYPE}/)
cmake_path(NATIVE_PATH EXE_DIR EXE_DIR)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND copy test.* ${EXE_DIR} /YWORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE})
  1. POST_BUILD指编译成功后,PRE_BUILD指编译成功前。
  2. EXE_DIR最后面的杠是必须的,因为copy命令不判断复制来源是目录还是文件。如果末尾没有杠且复制目的地不存在同名目录,copy就会在目的地创建与目录同名的文件,然后复制失败。
  3. cmake_path的作用是转换路径分隔符,copy命令不支持Linux风格的路径。

1.8. VS项目设置UNICODE

这里有个坑,常见错误的做法

add_compile_definitions(UNICODE _UNICODE)

正确的做法

add_definitions(-DUNICODE -D_UNICODE)

add_definitions的每个宏定义须添加-D前缀,add_compile_definitions的每个宏定义不需要前缀。最终执行的编译命令,所有的宏都有 -D/D前缀。

1.9. 一个子项目的属性如何传递到另一个子项目

CMake中变量的作用域是自上而下的,一个下层项目的属性传递给另一个子项目的属性,如果没有上下级关系,则需要通过 set_propertyget_property指令实现。对于整个CMake项目树结构,需先添加 set_property的下级项目,后添加 get_property的下级项目。

1.10. CMake使用Qt MOC、界面和资源文件

方法1:打开CMake支持Qt的自动化设置,把Qt资源以代码形式直接添加到项目

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
add_executable(${PROJECT_NAME} *.qrc *.ui)

方法2:按Qt资源命名规则生成代码,把生成代码添加到项目。下面代码以UI文件为例。

file(GLOB UI_SOURCES *.ui)
qt5_wrap_ui(UI_SOURCES ${UI_GENERATED})
foreach(UI_SRC ${UI_GENERATED})cmake_path(GET UI_SRC PARENT_PATH UI_DIR)include_directories(${UI_DIR})
endforeach()

1.11. CMake多编译器支持

方法1:直接设置编译器路径

set(CMAKE_SYSTEM_NAME QNX)set(arch gcc_ntoarmv7le)set(CMAKE_C_COMPILER qcc)
set(CMAKE_C_COMPILER_TARGET ${arch})
set(CMAKE_CXX_COMPILER QCC)
set(CMAKE_CXX_COMPILER_TARGET ${arch})set(CMAKE_SYSROOT $ENV{QNX_TARGET}) 

方法2:利用CMake的探测机器。以VC为例,CMake运行环境存在 %VCINSTALLDIR%,则CMake能自动找到 cl编译器,进而确认编译器版本和特性。VCINSTALLDIR环境变量是Visual Studio提供的 vcvarsall.bat提供的。典型的环境初始化代码:

@echo off
title Visual Studio 2019 Development Kit
set WORKDIR=%cd%
set Qt5_DIR=C:\Qt\Qt5.14.0\5.14.0\msvc2017_64
set GENERATOR=Visual Studio 16 2019
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
call "%Qt5_DIR%\bin\qtenv2.bat"
set PATH=%PATH%;C:\Qt\qtcreator-7.0.2\bin;C:\Program Files\CMake\bin
cd /d "%WORKDIR%"
cmd /k echo OK!

CMake能探测以默认选项安装的开发工具,比如VS安装到C盘,则即使不设置环境变量,CMake也能找到VC编译器。

1.12 关闭链接警告

CMake v3.24.3

代码add_link_options(/IGNORE:4099,4098)不起作用,
代码set_target_properties(${PROJECT_NAME} PRIVATE LINK_FLAGS "/IGNORE:4099,4098")可正常关闭链接警告。

1.13 有空格的宏定义不是字符串类型

错误的做法:add_compile_definitions(GC_API="__host__ __device__")。实际结果是代码多了一个字符串类型的宏定义,不能作用于NVIDIA GPU函数修饰符。正确做法是使用转义字符把空格转义,即:add_compile_definitions(GC_API=__host__\ __device__)

1.14 版本号转十六进制整数

project(Test1DLL VERSION 2.1.3.1)
math(EXPR AOI_INT_VER "${PROJECT_VERSION_MAJOR}<<24|${PROJECT_VERSION_MINOR}<<16|${PROJECT_VERSION_PATCH}<<8|${PROJECT_VERSION_TWEAK}" OUTPUT_FORMAT HEXADECIMAL)
add_compile_definitions(Test1DLL_EXPORTS AOI_PF_VER="${PROJECT_VERSION}" AOI_INT_VER=${AOI_INT_VER})

1.15 CMake文件读写操作

    write_file(${LIBRARY_OUTPUT_DIRECTORY}/Test1/makelib.cmd "@echo off")write_file(${LIBRARY_OUTPUT_DIRECTORY}/Test1/makelib.cmd "title libTest1 release tool by zhtqs8@163.com" APPEND)write_file(${LIBRARY_OUTPUT_DIRECTORY}/Test1/makelib.cmd "set CA7ZIP=C:\\Program Files\\7-Zip\\7z.exe" APPEND)write_file(${LIBRARY_OUTPUT_DIRECTORY}/Test1/makelib.cmd "set CVER=${PROJECT_VERSION}" APPEND)write_file(${LIBRARY_OUTPUT_DIRECTORY}/Test1/makelib.cmd "if not exist \"%CA7ZIP%\" echo 7-zip is required. && goto end" APPEND)write_file(${LIBRARY_OUTPUT_DIRECTORY}/Test1/makelib.cmd "\"%CA7ZIP%\" a \"libTest1%CVER%.zip\" libTest12.0.dll libTest12.0.lib libTest12.0.pdb libTest12.0*.md ../../src/test1/Test1DevCommon.h ../../src/test2/Test1Dev.h ../../src/test3/Test1Common.h" APPEND)write_file(${LIBRARY_OUTPUT_DIRECTORY}/Test1/makelib.cmd ":end" APPEND)write_file(${LIBRARY_OUTPUT_DIRECTORY}/Test1/makelib.cmd "pause" APPEND)
作者:岬淢箫声
邮箱:zhtqs8@163.com
来源:https://caowei.blog.csdn.net/article/details/129088446


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部