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()修改
- Unix/Linux/macOS:
可选关键字参数
WIN32
- 作用:创建 Windows GUI 应用程序(非控制台程序)
- 效果 :
- Windows:链接时添加
/SUBSYSTEM:WINDOWS - 其他平台:忽略此标志
- 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
- 作用:从默认构建目标中排除
- 效果 :
- 运行
make或cmake --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 构建可执行程序的基础,合理使用可以创建高效、可维护的跨平台应用程序。