add_executable必须先调用且target_name全局唯一,源文件仅限.cpp/.c等编译单元,路径需相对cmakelists.txt,win32/macosx_bundle影响入口点和输出结构,动态加源文件应使用target_sources而非变量展开。

add_executable 不是“随便列几个文件就能跑通”的命令,它本质是定义一个构建目标的起点,后续所有编译、链接、路径配置都依赖这个目标是否被正确定义。用错位置、漏写源文件、混用变量展开方式,都会导致构建失败或行为异常。
add_executable 必须先调用,且 target_name 全局唯一
你不能在 target_sources 或 target_link_libraries 之后才调用 add_executable —— 目标必须先存在。CMake 会检查整个项目中所有 add_executable 的第一个参数(即 <target_name></target_name>),重复则报错:CMake Error: add_executable called with incorrect number of arguments 或更隐晦的 duplicate target。
-
add_executable(my_app main.cpp)和add_executable(my_app utils.cpp)是非法的,第二个会覆盖或冲突 - 即使不同子目录下,
my_app也不能重复出现;可用命名空间前缀,如cli_my_app、gui_my_app -
target_name和最终生成的二进制文件名默认一致,但可通过set_target_properties(my_app PROPERTIES OUTPUT_NAME "runme")修改
源文件列表里别乱塞头文件,也别漏掉 .cpp/.c
虽然 CMake 允许你在 add_executable 后面写 include/utils.h,但这只是让 IDE(如 CLion、VS)能识别依赖关系,头文件本身不参与编译。真正决定编译单元的是 .cpp、.c、.cc 等后缀文件。
- 漏掉某个
.cpp文件 → 链接时报undefined reference to `xxx` - 只写了
.h没写对应.cpp→ 编译可能通过,但运行时崩溃或逻辑缺失 - 用变量展开时,确保变量已定义且非空:
add_executable(my_app ${SRC_FILES})中若SRC_FILES为空,CMake 会静默忽略,不报错但生成空目标 - 路径必须相对
CMakeLists.txt所在目录,比如src/main.cpp不能写成../src/main.cpp(除非你显式project()前设置了CMAKE_SOURCE_DIR)
WIN32 和 MACOSX_BUNDLE 不是可有可无的修饰符
这两个参数改变的是入口点和输出结构,不是加了就“适配平台”,而是直接影响二进制行为。Windows 下加了 WIN32 但没提供 WinMain,或者 macOS 下加了 MACOSX_BUNDLE 但没组织好 Contents/ 目录结构,都会导致程序无法启动。
-
add_executable(my_gui WIN32 main.cpp)→ 链接器期望WinMain而非main;若代码仍是int main(...),Windows 上可能黑窗一闪而过或直接退出 -
add_executable(my_app MACOSX_BUNDLE app_main.mm)→ 输出变成my_app.app目录,内部需含Contents/MacOS/my_app可执行体和Info.plist,否则双击无响应 -
EXCLUDE_FROM_ALL常用于测试程序或工具链辅助程序,避免污染主构建:比如add_executable(gen_config EXCLUDE_FROM_ALL gen.cpp),之后要用cmake --build . --target gen_config显式构建
动态加源文件得用 target_sources,别硬塞进 add_executable
一个 add_executable 调用就该完成目标注册,后续源文件增补统一走 target_sources。硬把条件逻辑塞进 add_executable 参数列表,容易因变量未定义或空列表引发语法错误。
- 错误写法:
add_executable(my_app main.cpp ${PLATFORM_SRC})—— 若PLATFORM_SRC未定义,CMake 展开为空,变成add_executable(my_app main.cpp ),合法但遗漏文件 - 正确写法:
add_executable(my_app main.cpp)+if(WIN32) target_sources(my_app PRIVATE win_impl.cpp) endif() -
target_sources的PRIVATE表示仅本目标可见;PUBLIC会把该源文件的 include 路径传递给依赖它的其他目标,慎用 - 多次调用
target_sources安全,但注意顺序:CMake 不保证源文件编译顺序,有强依赖时应拆成多个目标或用add_custom_command
最容易被忽略的是:CMake 对 add_executable 的解析发生在 configure 阶段,而不是 build 阶段。这意味着所有源文件路径、变量值、条件判断都必须在此时可确定。任何依赖构建时生成文件(比如自动生成的 version.cpp)的操作,都不能直接放在 add_executable 参数里 —— 得先用 add_custom_command 生成,再用 target_sources 加入。











