【CMake】`add_executable()` 命令详解

add_executable() 是 CMake 中用于创建可执行文件目标的核心命令,它将源代码文件编译链接成可执行程序。

基本语法

1. 普通可执行文件

cmake 复制代码
add_executable(<target> [WIN32] [MACOSX_BUNDLE]
               [EXCLUDE_FROM_ALL]
               source1 [source2 ...])

2. 导入的可执行文件

cmake 复制代码
add_executable(<target> IMPORTED [GLOBAL])

3. 别名可执行文件

cmake 复制代码
add_executable(<target> ALIAS <aliased-target>)

参数详解

必需参数

<target>
  • 作用:定义可执行文件的目标名称
  • 命名规则
    • 在项目中必须唯一
    • 建议使用小写字母、数字和下划线
    • 避免使用 CMake 关键字
  • 生成的文件名
    • Unix/Linux/macOS: <target>
    • Windows: <target>.exe
    • 可通过 set_target_properties() 修改

可选关键字参数

WIN32
  • 作用:创建 Windows GUI 应用程序(非控制台程序)
  • 效果
    • Windows:链接时添加 /SUBSYSTEM:WINDOWS
    • 其他平台:忽略此标志
  • 使用场景:创建不带控制台窗口的 GUI 应用程序
cmake 复制代码
# Windows GUI 应用程序
add_executable(myapp WIN32 main.cpp gui.cpp)

# 等价的手动设置(不推荐)
if(WIN32)
    set_target_properties(myapp PROPERTIES
        WIN32_EXECUTABLE TRUE
    )
endif()
MACOSX_BUNDLE
  • 作用:创建 macOS 应用程序包(.app)
  • 效果
    • 生成 .app 应用程序包
    • 设置正确的 macOS 属性
    • 需要配合 set_target_properties() 设置 bundle 属性
cmake 复制代码
# macOS 应用程序包
add_executable(myapp MACOSX_BUNDLE main.cpp)

# 设置 bundle 属性
set_target_properties(myapp PROPERTIES
    MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
    MACOSX_BUNDLE_BUNDLE_NAME "My Application"
    MACOSX_BUNDLE_BUNDLE_VERSION "1.0.0"
    MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0"
    MACOSX_BUNDLE_GUI_IDENTIFIER "com.company.myapp"
)
EXCLUDE_FROM_ALL
  • 作用:从默认构建目标中排除
  • 效果
    • 运行 makecmake --build . 时不自动构建
    • 必须显式指定目标名构建:make myapp
  • 使用场景
    • 示例程序
    • 测试工具
    • 可选组件
cmake 复制代码
# 示例程序,不包含在默认构建中
add_executable(example EXCLUDE_FROM_ALL example.cpp)
add_executable(demo EXCLUDE_FROM_ALL demo.cpp)

# 构建所有示例
add_custom_target(examples
    DEPENDS example demo
    COMMENT "构建所有示例程序"
)

源代码文件

  • 可以列出多个源文件
  • 支持相对路径和绝对路径
  • 可以使用生成器表达式
  • 可以使用变量和文件列表

详细用法示例

基础示例

cmake 复制代码
# 最简单的单文件程序
add_executable(hello_world main.cpp)

# 多文件程序
add_executable(calculator
    main.cpp
    calculator.cpp
    calculator.h
    ui.cpp
    ui.h
)

# 使用变量组织源文件
set(APP_SOURCES
    src/main.cpp
    src/core.cpp
    src/utils.cpp
)

set(APP_HEADERS
    include/core.h
    include/utils.h
)

add_executable(myapp ${APP_SOURCES} ${APP_HEADERS})

使用文件通配符

cmake 复制代码
# 方法1:使用 file(GLOB) - 注意:新增文件不会自动重新配置
file(GLOB SOURCES "src/*.cpp")
file(GLOB HEADERS "include/*.h")
add_executable(myapp ${SOURCES} ${HEADERS})

# 方法2:更安全的通配方式
file(GLOB SOURCES_CONFIGURE "src/*.cpp" "src/*.c")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/sources.cmake.in
               ${CMAKE_CURRENT_BINARY_DIR}/sources.cmake)
include(${CMAKE_CURRENT_BINARY_DIR}/sources.cmake)

# 方法3:显式列出所有文件(最推荐)
set(MANUAL_SOURCES
    src/main.cpp
    src/core/core.cpp
    src/core/processor.cpp
    src/utils/fileutil.cpp
    src/utils/stringutil.cpp
)
add_executable(myapp ${MANUAL_SOURCES})

条件编译

cmake 复制代码
# 根据平台选择不同的源文件
set(COMMON_SOURCES
    main.cpp
    common.cpp
)

if(WIN32)
    set(PLATFORM_SOURCES win32/platform.cpp)
    set(PLATFORM_DEFINES WIN32_LEAN_AND_MEAN)
elseif(APPLE)
    set(PLATFORM_SOURCES macos/platform.mm)  # 注意 .mm 是 Objective-C++
    set(PLATFORM_DEFINES APPLE_SILICON)
elseif(UNIX)
    set(PLATFORM_SOURCES linux/platform.cpp)
    set(PLATFORM_DEFINES LINUX)
endif()

add_executable(myapp 
    ${COMMON_SOURCES}
    ${PLATFORM_SOURCES}
)

target_compile_definitions(myapp PRIVATE ${PLATFORM_DEFINES})

使用生成器表达式

cmake 复制代码
# 根据配置选择不同的源文件
add_executable(myapp
    main.cpp
    $<$<CONFIG:Debug>:debug_utils.cpp>
    $<$<CONFIG:Release>:release_optimizations.cpp>
    $<$<BOOL:${ENABLE_PROFILING}>:profiling.cpp>
)

# 根据编译器选择实现
add_executable(myapp
    main.cpp
    $<$<CXX_COMPILER_ID:GNU>:gnu_specific.cpp>
    $<$<CXX_COMPILER_ID:MSVC>:msvc_specific.cpp>
    $<$<CXX_COMPILER_ID:Clang,AppleClang>:clang_specific.cpp>
)

高级用法

1. 导入的可执行文件

用于引用外部构建的可执行文件。

cmake 复制代码
# 导入外部构建的可执行文件
add_executable(external_tool IMPORTED)

# 设置导入的可执行文件路径
set_target_properties(external_tool PROPERTIES
    IMPORTED_LOCATION "/usr/bin/ffmpeg"
    IMPORTED_LOCATION_DEBUG "/usr/bin/ffmpeg-debug"
    IMPORTED_LOCATION_RELEASE "/usr/bin/ffmpeg"
)

# 在自定义命令中使用
add_custom_command(
    OUTPUT processed.mp4
    COMMAND external_tool -i input.mp4 processed.mp4
    DEPENDS input.mp4
)

# GLOBAL 版本(在整个构建树中可见)
add_executable(global_tool IMPORTED GLOBAL)
set_target_properties(global_tool PROPERTIES
    IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/tools/generator"
)

2. 别名目标

为目标创建别名,常用于提供兼容性或简化名称。

cmake 复制代码
# 创建主要目标
add_executable(my_application_long_name
    main.cpp
    app.cpp
)

# 创建别名(同目录中)
add_executable(app ALIAS my_application_long_name)

# 在 add_subdirectory 中使用
# 父目录 CMakeLists.txt
add_subdirectory(src)

# src/CMakeLists.txt
add_executable(internal_app main.cpp)

# 导出到父作用域
add_executable(app ALIAS internal_app)
set_target_properties(internal_app PROPERTIES EXPORT_NAME app)

3. 多配置生成器

针对不同的构建类型。

cmake 复制代码
# Visual Studio 等多配置生成器
add_executable(myapp main.cpp)

# 为不同配置设置不同属性
set_target_properties(myapp PROPERTIES
    DEBUG_POSTFIX "_d"
    RELEASE_POSTFIX ""
    RELWITHDEBINFO_POSTFIX "_rd"
    MINSIZEREL_POSTFIX "_ms"
)

# 设置不同配置的输出目录
set_target_properties(myapp PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/Debug"
    RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release"
    RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/RelWithDebInfo"
    RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_BINARY_DIR}/MinSizeRel"
)

4. 资源文件

处理各种类型的资源文件。

cmake 复制代码
# Windows 资源文件 (.rc)
if(WIN32)
    add_executable(myapp
        main.cpp
        resources.rc  # 包含图标、版本信息等
        myapp.manifest
    )
endif()

# Qt 资源文件
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
qt5_add_resources(QT_RESOURCES resources.qrc)

add_executable(myapp
    main.cpp
    mainwindow.cpp
    ${QT_RESOURCES}
)

# 处理数据文件
# 方法1:复制到输出目录
configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/data/config.json
    ${CMAKE_CURRENT_BINARY_DIR}/config.json
    COPYONLY
)

# 方法2:作为源文件包含(嵌入资源)
add_executable(myapp
    main.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/data/default_config.json
)

# 在代码中通过文件系统访问

目标属性设置

常用属性设置

cmake 复制代码
add_executable(myapp main.cpp)

# 设置输出名称
set_target_properties(myapp PROPERTIES
    OUTPUT_NAME "MyApplication"  # 生成 MyApplication 或 MyApplication.exe
    PREFIX ""                    # 移除 lib 前缀
    SUFFIX ""                    # 自定义后缀
)

# 设置版本信息
set_target_properties(myapp PROPERTIES
    VERSION 1.2.3
    SOVERSION 1
)

# 设置编译特性
set_target_properties(myapp PROPERTIES
    CXX_STANDARD 17
    CXX_STANDARD_REQUIRED ON
    CXX_EXTENSIONS OFF
)

# 平台特定属性
if(APPLE)
    set_target_properties(myapp PROPERTIES
        MACOSX_BUNDLE TRUE
        MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist
    )
endif()

链接属性

cmake 复制代码
add_executable(myapp main.cpp)

# 设置链接库
target_link_libraries(myapp
    PRIVATE
        Threads::Threads
        ${CMAKE_DL_LIBS}
)

# 设置链接选项
set_target_properties(myapp PROPERTIES
    LINK_FLAGS "-Wl,--as-needed"
    LINK_FLAGS_DEBUG "-g"
    LINK_FLAGS_RELEASE "-O3 -s"
)

# Windows 特定链接选项
if(WIN32)
    set_target_properties(myapp PROPERTIES
        LINK_FLAGS "/DEBUG:FULL"
        WIN32_EXECUTABLE TRUE
    )
endif()

完整示例项目

示例 1:跨平台 GUI 应用程序

cmake 复制代码
cmake_minimum_required(VERSION 3.15)
project(ImageEditor VERSION 1.0.0 LANGUAGES CXX)

# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 查找依赖
find_package(OpenCV REQUIRED)
find_package(Qt5 COMPONENTS Core Widgets Gui REQUIRED)

# 源文件分组
set(APP_SOURCES
    src/main.cpp
    src/mainwindow.cpp
    src/imageprocessor.cpp
    src/tools.cpp
)

set(APP_HEADERS
    include/mainwindow.h
    include/imageprocessor.h
    include/tools.h
)

set(APP_RESOURCES
    resources/icons.qrc
    resources/images.qrc
)

set(APP_UI
    ui/mainwindow.ui
    ui/toolbar.ui
)

# 处理 Qt 资源
qt5_wrap_ui(UI_HEADERS ${APP_UI})
qt5_add_resources(QT_RESOURCES ${APP_RESOURCES})

# 创建应用程序
if(WIN32)
    add_executable(imageeditor WIN32 ${APP_SOURCES} ${APP_HEADERS} ${UI_HEADERS} ${QT_RESOURCES})
elseif(APPLE)
    add_executable(imageeditor MACOSX_BUNDLE ${APP_SOURCES} ${APP_HEADERS} ${UI_HEADERS} ${QT_RESOURCES})
else()
    add_executable(imageeditor ${APP_SOURCES} ${APP_HEADERS} ${UI_HEADERS} ${QT_RESOURCES})
endif()

# 设置目标属性
set_target_properties(imageeditor PROPERTIES
    OUTPUT_NAME "ImageEditor"
    DEBUG_POSTFIX "_d"
    CXX_VISIBILITY_PRESET hidden
    VISIBILITY_INLINES_HIDDEN ON
)

# 包含目录
target_include_directories(imageeditor
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/include
        ${CMAKE_CURRENT_BINARY_DIR}/ui  # 生成的 UI 头文件
)

# 链接库
target_link_libraries(imageeditor
    PRIVATE
        Qt5::Core
        Qt5::Widgets
        Qt5::Gui
        OpenCV::opencv
)

# 安装目标
install(TARGETS imageeditor
    RUNTIME DESTINATION bin
    BUNDLE DESTINATION .
)

if(APPLE)
    # macOS 特定安装
    install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/resources/AppIcon.icns"
        DESTINATION "${CMAKE_INSTALL_PREFIX}/ImageEditor.app/Contents/Resources"
    )
endif()

示例 2:命令行工具

cmake 复制代码
cmake_minimum_required(VERSION 3.10)
project(FileConverter VERSION 1.2.0 LANGUAGES CXX)

# 工具选项
option(ENABLE_GUI "启用 GUI 版本" OFF)
option(BUILD_TESTS "构建测试" ON)

# 公共源文件
set(COMMON_SOURCES
    src/common/utils.cpp
    src/common/fileops.cpp
    src/common/format.cpp
)

set(COMMON_HEADERS
    include/common/utils.h
    include/common/fileops.h
    include/common/format.h
)

# 命令行版本
add_executable(fconverter_cli
    src/cli/main.cpp
    src/cli/cli_parser.cpp
    ${COMMON_SOURCES}
)

target_include_directories(fconverter_cli
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/include
)

target_link_libraries(fconverter_cli
    PRIVATE
        Threads::Threads
)

# 设置命令行工具属性
set_target_properties(fconverter_cli PROPERTIES
    OUTPUT_NAME "fconvert"
    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tools
)

# 可选 GUI 版本
if(ENABLE_GUI)
    find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
    
    add_executable(fconverter_gui
        src/gui/main.cpp
        src/gui/mainwindow.cpp
        ${COMMON_SOURCES}
    )
    
    target_link_libraries(fconverter_gui
        PRIVATE
            Qt5::Core
            Qt5::Widgets
            Threads::Threads
    )
    
    set_target_properties(fconverter_gui PROPERTIES
        OUTPUT_NAME "FileConverter"
        WIN32_EXECUTABLE TRUE
    )
endif()

# 测试工具(不包含在默认构建中)
if(BUILD_TESTS)
    add_executable(test_runner EXCLUDE_FROM_ALL
        tests/main.cpp
        tests/test_utils.cpp
        tests/test_fileops.cpp
        ${COMMON_SOURCES}
    )
    
    target_link_libraries(test_runner
        PRIVATE
            GTest::gtest_main
    )
    
    # 添加测试
    add_test(NAME UnitTests COMMAND test_runner)
endif()

示例 3:多目标项目

cmake 复制代码
cmake_minimum_required(VERSION 3.14)
project(NetworkTools VERSION 2.1.0 LANGUAGES C)

# 公共设置
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

# 公共源代码
set(COMMON_SOURCES
    src/common/net_utils.c
    src/common/logger.c
)

set(COMMON_HEADERS
    include/common/net_utils.h
    include/common/logger.h
)

# 工具1:端口扫描器
add_executable(port_scanner
    tools/scanner/main.c
    tools/scanner/portscan.c
    ${COMMON_SOURCES}
)

target_include_directories(port_scanner
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/include
)

target_compile_definitions(port_scanner
    PRIVATE
        TOOL_NAME="Port Scanner"
)

# 工具2:网络监视器
add_executable(net_monitor
    tools/monitor/main.c
    tools/monitor/traffic.c
    ${COMMON_SOURCES}
)

target_include_directories(net_monitor
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/include
)

target_compile_definitions(net_monitor
    PRIVATE
        TOOL_NAME="Network Monitor"
)

# 工具3:数据包分析器(可选)
option(BUILD_PACKET_ANALYZER "构建数据包分析器" OFF)
if(BUILD_PACKET_ANALYZER)
    find_package(PCAP REQUIRED)
    
    add_executable(packet_analyzer
        tools/analyzer/main.c
        tools/analyzer/packet.c
        ${COMMON_SOURCES}
    )
    
    target_include_directories(packet_analyzer
        PRIVATE
            ${CMAKE_CURRENT_SOURCE_DIR}/include
            ${PCAP_INCLUDE_DIRS}
    )
    
    target_link_libraries(packet_analyzer
        PRIVATE
            ${PCAP_LIBRARIES}
    )
endif()

# 创建聚合目标
add_custom_target(all_tools
    DEPENDS port_scanner net_monitor
    COMMENT "构建所有网络工具"
)

if(BUILD_PACKET_ANALYZER)
    add_dependencies(all_tools packet_analyzer)
endif()

最佳实践

1. 源文件管理

cmake 复制代码
# 不推荐:使用 GLOB(新增文件不会触发重新配置)
file(GLOB ALL_SOURCES "src/*.cpp")
add_executable(app ${ALL_SOURCES})

# 推荐:显式列出源文件
set(SOURCES
    src/main.cpp
    src/core.cpp
    src/utils.cpp
)

# 或使用辅助脚本生成源文件列表
configure_file(sources.cmake.in sources.cmake @ONLY)
include(sources.cmake)

2. 头文件处理

cmake 复制代码
# 将头文件添加到源文件列表(支持 IDE)
add_executable(myapp
    src/main.cpp
    include/myapp.h
    src/internal.h
)

# 或者,如果不希望在 IDE 中看到头文件
add_executable(myapp src/main.cpp)
target_include_directories(myapp PRIVATE include)

3. 目录组织

cmake 复制代码
# 清晰的目录结构
add_executable(myapp
    src/main.cpp
    src/core/
        core.cpp
        processor.cpp
    src/utils/
        fileutil.cpp
        stringutil.cpp
    src/gui/
        mainwindow.cpp
        dialog.cpp
)

# 设置不同的包含目录
target_include_directories(myapp
    PRIVATE
        src/core
        src/utils
        src/gui
        ${CMAKE_CURRENT_BINARY_DIR}  # 生成的头文件
)

4. 平台兼容性

cmake 复制代码
add_executable(myapp main.cpp)

# 平台特定设置
if(WIN32)
    target_sources(myapp PRIVATE
        src/platform/win32.cpp
        $<$<CONFIG:DEBUG>:src/platform/win32_debug.cpp>
    )
    target_compile_definitions(myapp PRIVATE _WIN32_WINNT=0x0A00)
    
elseif(APPLE)
    target_sources(myapp PRIVATE src/platform/macos.mm)
    set_target_properties(myapp PROPERTIES
        MACOSX_BUNDLE TRUE
        MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    )
    
elseif(UNIX)
    target_sources(myapp PRIVATE src/platform/linux.cpp)
    target_link_libraries(myapp PRIVATE pthread dl)
endif()

常见问题

问题 1:源文件未找到

cmake 复制代码
# 错误:相对路径问题
add_executable(app ../other_dir/main.cpp)  # 可能工作,但不推荐

# 正确:使用绝对路径或相对于 CMakeLists.txt 的路径
add_executable(app
    ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/src/core.cpp
)

问题 2:重复目标名

cmake 复制代码
# 错误:目标名重复
add_executable(myapp main.cpp)
add_executable(myapp other.cpp)  # 错误:目标名已存在

# 正确:使用不同的目标名
add_executable(app1 main.cpp)
add_executable(app2 other.cpp)

问题 3:生成器表达式错误

cmake 复制代码
# 错误:生成器表达式使用不当
add_executable(app $<IF:1,main.cpp,other.cpp>)  # 错误:IF 不能这样用

# 正确:使用合适的生成器表达式
add_executable(app
    main.cpp
    $<$<CONFIG:Debug>:debug_helpers.cpp>
)

调试技巧

检查目标属性

cmake 复制代码
add_executable(myapp main.cpp)

# 查看目标的所有属性
get_target_property(SOURCES myapp SOURCES)
message(STATUS "源文件: ${SOURCES}")

# 查看链接库
get_target_property(LIBS myapp LINK_LIBRARIES)
message(STATUS "链接库: ${LIBS}")

# 查看包含目录
get_target_property(INCLUDES myapp INCLUDE_DIRECTORIES)
message(STATUS "包含目录: ${INCLUDES}")

验证目标

cmake 复制代码
function(validate_target target_name)
    if(NOT TARGET ${target_name})
        message(FATAL_ERROR "目标 ${target_name} 不存在")
    endif()
    
    get_target_property(target_type ${target_name} TYPE)
    if(NOT target_type STREQUAL "EXECUTABLE")
        message(WARNING "${target_name} 不是可执行文件目标")
    endif()
endfunction()

add_executable(myapp main.cpp)
validate_target(myapp)

add_executable() 是 CMake 构建可执行程序的基础,合理使用可以创建高效、可维护的跨平台应用程序。

相关推荐
Source.Liu1 天前
【CMake】概述
cmake
charlee442 天前
CMake构建学习笔记32-CMake版本切换
cmake·版本管理·构建·update·alternatives
阳洞洞3 天前
cmake中如何从include_directories中移除某个特定的头文件
c++·cmake
Mr_WangAndy3 天前
cmake_CMake内置属性解决头文件包含/CMake定义C/C++标准/include_directories()/宏定义
cmake·宏定义·c++标准·头文件包含
番茄灭世神4 天前
使用VScode开发ARM核芯片通用配置
arm开发·vscode·mcu·cmake·clangd·llvm·ninja
charlee445 天前
CMake构建学习笔记31-构建前执行可执行程序
sqlite·cmake·构建·构建前脚本
Mr_WangAndy6 天前
cmake_file(GLOB)详解
cmake·cmake file·cmake文件操作
Tipriest_6 天前
CMake 常用预设命令说明
cmake
加成BUFF6 天前
Qt开发核心工具:CMake与qmake全面解析
开发语言·qt·cmake·qmake