【ToolChains】| CMake 技巧

文章详细介绍了如何在CMake中设置编译类型、系统环境、MSVC版本、C/C++标准、编译器标志,以及生成动态库的export头文件、程序版本信息和Windows文件版本信息的配置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

判断 CMake 编译环境

编译类型 CMAKE_BUILD_TYPE

可取值为:Debug, Release, RelWithDebInfo, MinSizeRel 等预设值

if (CMAKE_BUILD_TYPE MATCHES Debug)
    #do some thing
endif()

系统环境CMAKE_SYSTEM_NAME

代表当前系统的类型,

值有ANDROID, APPLE, IOS, UNIX, WIN32, WINCE, WINDOWS_PHONE等

可以直接对这些值进行条件判断来确定

if (UNIX)
    #cond1
elseif(WIN32)
    #cond2
endif()

编译工具环境

编译环境包括MSVC, MINGW, BORLAND, 等

可直接用if判断

msvc版本可通过MSVC10, MSVC11, MSVC12, MSVC14, MSVC60, MSVC70, MSVC71, MSVC80, MSVC90, MSVC_TOOLSET_VERSION, MSVC_VERSION等判断

编译器设置

C/C++标准

# 全局
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_C_STANDARD 11)

编译器flag

主要靠修改CMAKE_CXX_FLAG_<BUILD_TYPE>来进行修改, 也可以直接修改所有类型的, 或者通过判断编译类型来配置

set(DEFINES "-Wunused-parameter")
set(CMAKE_CXX_FLAGS_DEBUG "-pipe -O2 -std=gnu++11 -Wall -W -D_REENTRANT -fPIC ${DEFINES}")
set(CMAKE_C_FLAGS_DEBUG "-pipe -O2 -Wall -W -D_REENTRANT -fPIC ${DEFINES}")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-fno-pie")

set(CMAKE_CXX_FLAGS_RELEASE “-pipe -O2 -std=gnu++11 -Wall -W -D_REENTRANT -fPIC ${DEFINES}”)
set(CMAKE_C_FLAGS_RELEASE “-pipe -O2 -Wall -W -D_REENTRANT -fPIC ${DEFINES}”)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE “-fno-pie”)

特殊的编译器设置

  • sanitize (msvc目前只支持32位)

检查内存泄露和内存其他问题

list(APPEND CMAKE_CXX_FLAGS_DEBUG -fsanitize=address -g)

配置后运行程序时发生内存问题程序会立马中断退出, 并且打印内存问题

  • /utf-8 (msvc)

强制源码使用utf-8, 避免代码到了其他平台乱码

list(APPEND CMAKE_CXX_FLAGS_DEBUG /utf-8)
  • -DUNICODE (msvc)

决定windows下面使用标准字符api还是宽字符api

#ifdef UNICODE
#define ShellExecute  ShellExecuteW
#else
#define ShellExecute  ShellExecuteA
#endif // !UNICODE
  • /SUBSYSTEM:WINDOWS

更改软件启动入口, 默认是/SUBSYSTEM:CONSOLE, 会先启动命令行再启动其他ui, 而更改后命令行将不再启动, 所有打印信息也将会看不到, 只有通过x64dbg这种调试软件才能看到

  • /OPT:REF /OPT:ICF

添加这两个参数release模式下编译也会生成pdb文件

根据模板生成文件

生成动态库的export头文件

一般来说Windows平台生成动态库的头文件要区分__declspec(dllexport)__declspec(dllimport),会专门用一个头文件进行定义,同时区分动态链接库和静态链接库,如果项目较复杂可以用cmake生成到build目录进行包含,利用``函数,下面是其的定义

GENERATE_EXPORT_HEADER( LIBRARY_TARGET
          [BASE_NAME <base_name>]
          [EXPORT_MACRO_NAME <export_macro_name>]
          [EXPORT_FILE_NAME <export_file_name>]
          [DEPRECATED_MACRO_NAME <deprecated_macro_name>]
          [NO_EXPORT_MACRO_NAME <no_export_macro_name>]
          [INCLUDE_GUARD_NAME <include_guard_name>]
          [STATIC_DEFINE <static_define>]
          [NO_DEPRECATED_MACRO_NAME <no_deprecated_macro_name>]
          [DEFINE_NO_DEPRECATED]
          [PREFIX_NAME <prefix_name>]
          [CUSTOM_CONTENT_FROM_VARIABLE <variable>]
)

#例如

generate_export_header(${PROJECT_NAME}
BASE_NAME KaTeX parse error: Expected group after '_' at position 52: …RO_NAME PROJECT_̲{MODULE_NAME_UPPER}_API
EXPORT_FILE_NAME ${MODULE_NAME}_Export.hpp
STATIC_DEFINE ${MODULE_NAME_UPPER}_BUILT_AS_STATIC
)

在需要导出的类或函数加上EXPORT_MACRO_NAME就可以导出动态库了

生成程序版本信息

程序版本信息有许多,包括版本号、git分支、git提交号、sdk版本、构建日期等等,Windows下还有文件版本信息(属性里面的内容)

版本信息模板

编写模板,和生成代码

//Global.hpp.in
#include "PROJECT_global.hpp"
#include "BuildTime.hpp"

namespace PROJECT {

#define PROJECT_APP_NAME “@PROJECT_APP_NAME@”
#define PROJECT_APP_ORG “@PROJECT_APP_ORG@”
#define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define PROJECT_VERSION_TWEAK @PROJECT_VERSION_TWEAK@
#define PROJECT_VERSION_BETA @PROJECT_VERSION_BETA@
#define PROJECT_VERSION “@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.@PROJECT_VERSION_TWEAK@”
#define PROJECT_VERSION_STATUS “@PROJECT_VERSION_STATUS@”
#define PROJECT_VERSION_COMMIT “@PROJECT_VERSION_COMMIT@”
#define PROJECT_VERSION_BRANCH “@PROJECT_VERSION_BRANCH@”
#define PROJECT_VERSION_DATE BUILD_TIME
#define PROJECT_QT_VERSION “@PROJECT_QT_VERSION@”
#define PROJECT_QT_PLATFORM “@PROJECT_QT_PLATFORM@”
}

set(PROJECT_APP_NAME "Project")
set(PROJECT_APP_ORG "Company")
set(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK})
set(PROJECT_VERSION_BETA 1)
set(PROJECT_VERSION_STATUS ${GIT_STATUS})
set(PROJECT_VERSION_COMMIT ${GIT_COMMIT})
set(PROJECT_VERSION_BRANCH ${GIT_BRANCH})
set(PROJECT_VERSION_DATE ${GIT_DATE})
set(PROJECT_QT_VERSION ${QT_VERSION})
set(PROJECT_QT_PLATFORM ${QT_PLATFORM})

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Global.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/gen/Global.hpp @ONLY)
从文本文件获取版本号
file(READ ${CMAKE_SOURCE_DIR}/VERSION VERSION_STR)
string(REPLACE "." ";" VERSION_STR_LIST ${VERSION_STR})
list(LENGTH VERSION_STR_LIST LEN)
if (NOT LEN EQUAL 4)
    message(FATAL_ERROR "version number not in correct format")
endif ()
list(GET VERSION_STR_LIST 0 PROJECT_VERSION_MAJOR)
list(GET VERSION_STR_LIST 1 PROJECT_VERSION_MINOR)
list(GET VERSION_STR_LIST 2 PROJECT_VERSION_PATCH)
list(GET VERSION_STR_LIST 3 PROJECT_VERSION_TWEAK)
message(VERSION:${PROJECT_VERSION_MAJOR})

利用cmake读写文件可以实现buildVersion累加

获取git信息
include(FindGit)
if (${GIT_FOUND})
    unset(GIT_VERSION_NUM CACHE)
    unset(GIT_BRANCH CACHE)
    message("Found Git executable, version: ${GIT_VERSION_STRING}")
    execute_process(
            COMMAND ${GIT_EXECUTABLE} describe --tags --exact-match --abbrev=0
            WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
            OUTPUT_VARIABLE GIT_TAG
            OUTPUT_STRIP_TRAILING_WHITESPACE
            RESULT_VARIABLE GIT_VERSION_RESULT
    )
    if (NOT ${GIT_VERSION_RESULT} EQUAL 0)
        set(GIT_STATUS "u")
        set(GIT_TAG "notag")
    else ()
        set(GIT_VERSION_STATUS "")
    endif ()
execute_process(
        COMMAND ${GIT_EXECUTABLE} log -1 --format=%H
        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
        OUTPUT_VARIABLE GIT_COMMIT
        OUTPUT_STRIP_TRAILING_WHITESPACE
        RESULT_VARIABLE GIT_COMMIT_RESULT
)
if (NOT GIT_COMMIT_RESULT EQUAL 0)
    message(FATAL_ERROR "cannot find git commit number")
else ()
    message("git commit: ${GIT_COMMIT}")
endif ()

execute_process(
        COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
        OUTPUT_VARIABLE GIT_BRANCH
        OUTPUT_STRIP_TRAILING_WHITESPACE
        RESULT_VARIABLE GIT_BRANCH_RESULT
)
if (NOT GIT_BRANCH_RESULT EQUAL 0)
    message(WARNING "cannot find git branch name")
else ()
    message("git branch: ${GIT_BRANCH}")
endif ()

set(GIT_STATUS ${GIT_STATUS} CACHE INTERNAL "git version status")
set(GIT_COMMIT ${GIT_COMMIT} CACHE INTERNAL "git commit number")
set(GIT_BRANCH ${GIT_BRANCH} CACHE INTERNAL "git branch name")

else ()
MESSAGE(WARNING “cannot find Git executable, skip git version check”)
set(GIT_COMMIT “unknown”)
set(GIT_BRANCH “unknown”)
endif ()

构建日期

构建日期用cmake生成问题在于编译的时候不一定会重新跑cmake,所以需要用到cmake执行cmake脚本的命令

更新时间cmake:

message("start updating build time")
if (NOT DEFINED BUILD_TIME_HEADER)
    message(FATAL_ERROR "Need to pass `-DBUILD_TIME_HEADER:PATH=" /path/to/header"` to this script")
endif ()
unset(DATE CACHE)
string(TIMESTAMP DATE "%b %d %Y %a %H:%M:%S")
set(DATE "${DATE}")
message("compile date: ${DATE}")
file(WRITE ${BUILD_TIME_HEADER}
        "//this file is automatically generated by UpdateBuildTime.cmake\n"
        "#ifndef BUILD_TIME_HEADER\n"
        "#define BUILD_TIME_HEADER\n"
        "\n"
        "#define BUILD_TIME \"${DATE}\"\n"
        "\n"
        "#endif //BUILD_TIME_HEADER\n"
        )
message("Write build date to file ${BUILD_TIME_HEADER}")

在构建时增加BuildTime目标依赖

add_custom_target(BuildTime
        COMMAND ${CMAKE_COMMAND}
        -DBUILD_TIME_HEADER:PATH="${BUILD_TIME_HEADER}"
        -P "${CMAKE_SOURCE_DIR}/cmake/UpdateBuildTime.cmake")
if (CMAKE_BUILD_TYPE MATCHES Release)
    add_dependencies(${PROJECT_NAME} BuildTime)
endif ()

增加条件的原因是防止调试编译时过于频繁的更新编译时间

生成Windows下文件信息

将信息编译到exe或者dll中, 同样利用confugure_file

#if defined(UNDER_CE)
#include <winbase.h>
#else
#include <winver.h>
#endif
VS_VERSION_INFO VERSIONINFO
        FILEVERSION @RC_FILE_VERSION_NUM@
        PRODUCTVERSION @RC_PRODUCT_VERSION_NUM@
        FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0x0L
BEGIN
        BLOCK "StringFileInfo"
BEGIN
        BLOCK "040904b0"
BEGIN
        VALUE "Comments", @RC_COMMENTS@
        VALUE "CompanyName", @RC_COMPANY_NAME@
        VALUE "FileDescription", @RC_FILE_DESCRIPTION@
        VALUE "FileVersion", @RC_FILE_VERSION@
        VALUE "InternalName", @RC_INTERNAL_NAME@
        VALUE "LegalCopyright", @RC_LEGAL_COPYRIGHT@
        VALUE "OriginalFilename", @RC_ORIGINAL_FILE_NAME@
        VALUE "ProductName", @RC_PRODUCT_NAME@
        VALUE "ProductVersion", @RC_PRODUCT_VERSION@
        VALUE "PrivateBuild", @RC_PRIVATE_BUILD@
        VALUE "LegalTrademarks", @RC_LEGAL_TRADEMARKS@
        END
END
        BLOCK "VarFileInfo"
BEGIN
        VALUE "Translation", 0x804, 1300
END
        END

编译时和源代码一起编译就可以

导入第三方库

function(set_library_target NAMESPACE LIB_NAME DEBUG_LIB_FILE_NAME RELEASE_LIB_FILE_NAME INCLUDE_DIR)
    add_library(${NAMESPACE}::${LIB_NAME} STATIC IMPORTED)
    set_target_properties(${NAMESPACE}::${LIB_NAME} PROPERTIES
            IMPORTED_CONFIGURATIONS "RELEASE;DEBUG"
            IMPORTED_LOCATION_RELEASE "${RELEASE_LIB_FILE_NAME}"
            IMPORTED_LOCATION_DEBUG "${DEBUG_LIB_FILE_NAME}"
            INTERFACE_INCLUDE_DIRECTORIES "${INCLUDE_DIR}"
            )
    set(${NAMESPACE}_${LIB_NAME}_FOUND 1)
endfunction()
【ToolChains】| CMake 技巧
<think>我们正在处理一个使用CMake构建Qt项目时遇到的错误:'QApplication' file not found。根据引用内容,特别是引用[1]和引用[2],我们可以知道这个错误通常是由于CMake配置中未正确设置Qt模块导致的。 解决方案步骤: 1. 确保已安装正确版本的Qt,并且CMake能够找到它。 2. 在CMakeLists.txt中,我们需要使用`find_package`来查找Qt5,并指定需要的组件(如Widgets)。 3. 设置自动处理moc、uic和rcc的选项(这对于Qt的元对象系统、界面编译和资源编译是必要的)。 4. 将Qt模块链接到目标可执行文件。 具体配置如下(参考引用[2]): 首先,在CMakeLists.txt中设置最低CMake版本,并启用自动处理Qt相关文件的选项: ```cmake cmake_minimum_required(VERSION 3.1.0) # Qt5要求至少3.1,但建议使用更高版本 project(YourProjectName) # 设置自动处理moc、uic和rcc set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) ``` 然后,查找Qt5的Widgets模块(因为QApplication属于Widgets模块): ```cmake find_package(Qt5 COMPONENTS Widgets REQUIRED) ``` 接下来,添加可执行文件,并指定源文件(包括头文件、源文件、界面文件、资源文件等): ```cmake add_executable(YourTargetName main.cpp # 其他源文件,如mainwindow.cpp等 # 如果有界面文件,如mainwindow.ui # 如果有资源文件,如resources.qrc ) ``` 最后,将Qt5的Widgets模块链接到目标: ```cmake target_link_libraries(YourTargetName Qt5::Widgets) ``` 注意:如果还使用了其他Qt模块(如Core、Gui等),需要在`find_package`中添加,并在`target_link_libraries`中链接。例如,如果使用了Core和Gui,则: ```cmake find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED) target_link_libraries(YourTargetName Qt5::Core Qt5::Gui Qt5::Widgets) ``` 但是,由于QApplication在Widgets模块中,而Widgets模块依赖于Core和Gui,所以通常只需要链接Widgets模块即可。 另外,引用[1]中提到,如果环境变量或编译器设置不正确,也可能导致找不到Qt。因此,请确保: - 已正确安装Qt,并且安装路径已添加到系统环境变量(在Windows下,通常安装Qt时会自动添加;在Linux/macOS下,可能需要手动设置)。 - 在CLion中,检查Toolchains设置,确保选择了正确的Qt版本和编译器。 如果修改了CMakeLists.txt,需要重新加载CMake项目(在CLion中,通常会有提示,或者手动点击重新加载)。 如果问题仍然存在,可以尝试清除CMake缓存并重新生成(在CLion中,可以删除cmake-build-debug(或类似)目录,然后重新加载项目)。 另外,引用[4]提到,有时修改后需要重新运行qmake(但这里使用的是CMake,所以不需要qmake,而是需要重新运行CMake生成步骤)。 总结:关键步骤是在CMakeLists.txt中正确配置Qt模块,并确保链接到目标。 下面是一个完整的CMakeLists.txt示例(假设项目只有一个main.cpp): ```cmake cmake_minimum_required(VERSION 3.5) project(MyQtProject) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 自动处理moc、uic、rcc set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) # 查找Qt5 Widgets模块 find_package(Qt5 COMPONENTS Widgets REQUIRED) # 添加可执行文件 add_executable(MyQtApp main.cpp) # 链接Qt5 Widgets库 target_link_libraries(MyQtApp Qt5::Widgets) ``` 在main.cpp中,应该包含QApplication: ```cpp #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); // ... 你的代码 return a.exec(); } ``` 如果按照以上步骤配置,应该可以解决'QApplication' file not found的问题。 如果问题仍然存在,请检查: 1. Qt安装路径是否包含在CMake的查找路径中。 2. 是否安装了Qt的相应组件(例如,在安装Qt时是否选择了Widgets模块)。 另外,引用[5]中展示了一个使用QApplication的main函数示例,可以对照检查代码是否正确。 最后,如果使用的是非标准路径安装的Qt,可能需要通过设置`CMAKE_PREFIX_PATH`来指定Qt的安装路径。例如: ```bash cmake -DCMAKE_PREFIX_PATH=/path/to/qt -B build ``` 或者在CMakeLists.txt中设置: ```cmake set(CMAKE_PREFIX_PATH "/path/to/qt") ``` 希望这些步骤能够帮助解决问题。</think>### 解决CMake构建Qt项目时'QApplication'文件未找到的问题 在CMake构建Qt项目时出现`'QApplication' file not found`错误,通常是由于CMake配置不正确导致Qt库路径未正确引入。以下是系统性的解决方案,基于Qt官方文档和CMake最佳实践[^2][^3]。 #### 完整解决方案步骤 1. **确保CMakeLists.txt基础配置正确** 在CMakeLists开头添加Qt核心配置: ```cmake cmake_minimum_required(VERSION 3.5) # Qt5要求最低3.5,Qt6要求3.16+ project(YourProjectName) # 启用Qt元对象系统自动处理 set(CMAKE_AUTOMOC ON) # 自动处理moc set(CMAKE_AUTORCC ON) # 自动处理资源文件 set(CMAKE_AUTOUIC ON) # 自动处理UI文件 ``` 2. **查找并链接Qt Widgets模块** ```cmake # 查找Qt5核心模块(必须) find_package(Qt5 COMPONENTS Core REQUIRED) # 查找并链接Widgets模块(包含QApplication) find_package(Qt5 COMPONENTS Widgets REQUIRED) # [^2] # 添加可执行目标 add_executable(YourApp main.cpp other_files.cpp) # 链接Qt库 target_link_libraries(YourApp Qt5::Core Qt5::Widgets # 关键:必须链接Widgets模块 ) ``` 3. **设置Qt版本兼容性(可选但推荐)** 如果系统有多个Qt版本,明确指定版本范围: ```cmake # 优先使用Qt5,找不到时尝试Qt6 find_package(Qt5 5.15 COMPONENTS Widgets REQUIRED) ``` 4. **添加包含路径(特殊情况下需要)** 如果仍然报错,手动添加包含路径: ```cmake # 获取Qt包含路径 get_target_property(QtCore_includes Qt5::Core INTERFACE_INCLUDE_DIRECTORIES) # 添加到目标 target_include_directories(YourApp PRIVATE ${QtCore_includes}) ``` 5. **完整配置示例** ```cmake cmake_minimum_required(VERSION 3.16) project(MyQtApp) set(CMAKE_CXX_STANDARD 17) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) # 查找Qt组件 find_package(Qt5 5.15 COMPONENTS Core Widgets REQUIRED) # 添加源文件 file(GLOB SOURCES "src/*.cpp") file(GLOB HEADERS "include/*.h") file(GLOB FORMS "ui/*.ui") add_executable(MyApp ${SOURCES} ${HEADERS} ${FORMS}) # 链接库 target_link_libraries(MyApp Qt5::Core Qt5::Widgets ) ``` #### 验证步骤 1. 在CLion/Qt Creator中: - 删除`build`目录 - 选择 **Tools > CMake > Reset Cache and Reload Project** - 点击 **Reload CMake Project** 按钮[^1][^4] 2. 创建测试文件`main.cpp`: ```cpp #include <QApplication> // 验证头文件 #include <QLabel> int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label("Hello Qt!"); label.show(); return app.exec(); } ``` #### 常见错误原因 1. **缺少`find_package(Qt5 COMPONENTS Widgets)`**:90%的错误根源,QApplication在Widgets模块中[^2] 2. **未启用AUTOMOC**:导致moc预处理失败[^3] 3. **Qt版本不匹配**:项目使用Qt5代码但CMake配置了Qt6路径 4. **未链接Qt5::Widgets**:即使包含头文件,未链接库也会失败 #### 高级调试技巧 ```bash # 查看CMake找到的Qt路径 cmake --find-package -DNAME=Qt5 -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=COMPILE # 查看包含路径 cmake -E cmake --build . --target YourApp --verbose ``` > **关键点**:确保`find_package`指定了`Widgets`组件,并且通过`target_link_libraries`链接了`Qt5::Widgets`。这是解决QApplication问题的核心[^2][^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值