跳到主要内容

CMake 可执行目标

本章将介绍如何使用 CMake 创建和管理可执行目标。

创建可执行目标​

基本语法​

add_executable(target_name [source1] [source2 ...])

简单示例​

cmake_minimum_required(VERSION 3.15)
project(MyApp)

add_executable(myapp main.cpp)

多文件项目​

# 方式一:直接列出
add_executable(myapp
src/main.cpp
src/app.cpp
src/utils.cpp
include/app.h
)

# 方式二:使用变量
set(SOURCES
src/main.cpp
src/app.cpp
src/utils.cpp
)
add_executable(myapp ${SOURCES})

使用 GLOB 收集文件​

# 收集单个目录
file(GLOB SOURCES "src/*.cpp")

# 递归收集
file(GLOB_RECURSE SOURCES
"src/*.cpp"
"include/*.h"
)

add_executable(myapp ${SOURCES})
注意

使用 GLOB 时,新增文件后需要重新运行 cmake 命令,否则不会自动检测新文件。

可执行文件属性​

设置输出名称​

add_executable(myapp main.cpp)

# 设置输出文件名
set_target_properties(myapp PROPERTIES
OUTPUT_NAME "my_application"
)

设置输出目录​

# 设置可执行文件输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

add_executable(myapp main.cpp)

设置 C++ 标准​

add_executable(myapp main.cpp)

# 方式一:使用目标属性
set_target_properties(myapp PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)

# 方式二:使用编译特性
target_compile_features(myapp PUBLIC cxx_std_17)

设置运行时路径​

# 设置可执行文件的 RPATH
set_target_properties(myapp PROPERTIES
INSTALL_RPATH "$ORIGIN/../lib"
BUILD_WITH_INSTALL_RPATH FALSE
)

编译选项​

头文件路径​

target_include_directories(myapp
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)

预处理器定义​

target_compile_definitions(myapp
PRIVATE
DEBUG_MODE
VERSION="1.0.0"
PUBLIC
API_EXPORT
)

编译选项​

target_compile_options(myapp
PRIVATE
-Wall
-Wextra
-Wpedantic
$<$<CONFIG:Debug>:-g -O0>
$<$<CONFIG:Release>:-O3 -DNDEBUG>
)

链接库​

链接项目内库​

# 创建库
add_library(mylib src/lib.cpp)
target_include_directories(mylib PUBLIC include)

# 创建可执行文件并链接
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)

链接系统库​

# 链接线程库
find_package(Threads REQUIRED)
target_link_libraries(myapp PRIVATE Threads::Threads)

# 链接数学库(Linux)
target_link_libraries(myapp PRIVATE m)

链接外部库​

# 使用 find_package
find_package(OpenCV REQUIRED)
target_link_libraries(myapp PRIVATE ${OpenCV_LIBS})

# 或使用现代目标
find_package(fmt REQUIRED)
target_link_libraries(myapp PRIVATE fmt::fmt)

运行可执行文件​

基本运行方式​

构建完成后,可执行文件将生成在构建目录中。可以通过以下方式运行:

# 在构建目录中直接运行
cd build
./myapp

# 或使用 CMake 的运行命令
cmake --build build --target run

使用自定义运行目标​

可以在 CMakeLists.txt 中定义自定义目标来运行可执行文件:

add_custom_target(run
COMMAND myapp
DEPENDS myapp
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

使用 CTest 运行​

可以将可执行文件作为测试运行:

enable_testing()

add_test(NAME myapp_test
COMMAND myapp --test
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

运行测试:

cd build
ctest

调试运行​

对于调试,可以使用 IDE 集成或命令行调试器:

# 使用 GDB 调试
gdb ./myapp

# 使用 LLDB 调试
lldb ./myapp

# 使用 Visual Studio 调试器(Windows)
devenv myapp.exe

运行时环境变量​

可以设置运行时环境变量:

# 设置环境变量
set_target_properties(myapp PROPERTIES
ENVIRONMENT "MY_VAR=value"
)

# 或在运行时使用
add_custom_target(run_with_env
COMMAND ${CMAKE_COMMAND} -E env MY_VAR=value ./myapp
DEPENDS myapp
)

跨平台注意事项​

平台特定源文件​

set(SOURCES
src/main.cpp
src/common.cpp
)

if(WIN32)
list(APPEND SOURCES src/windows_specific.cpp)
elseif(UNIX)
list(APPEND SOURCES src/linux_specific.cpp)
endif()

add_executable(myapp ${SOURCES})

平台特定编译选项​

if(MSVC)
target_compile_options(myapp PRIVATE /W4 /utf-8)
else()
target_compile_options(myapp PRIVATE -Wall -Wextra)
endif()

跨平台输出名称​

# 根据平台设置不同的输出名称
if(WIN32)
set_target_properties(myapp PROPERTIES
OUTPUT_NAME "myapp_win"
)
elseif(APPLE)
set_target_properties(myapp PROPERTIES
OUTPUT_NAME "myapp_mac"
)
else()
set_target_properties(myapp PROPERTIES
OUTPUT_NAME "myapp_linux"
)
endif()

跨平台运行时依赖​

# Windows DLL 路径
if(WIN32)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
else()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
endif()

跨平台安装路径​

include(GNUInstallDirs)

install(TARGETS myapp
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

安装配置​

基本安装​

add_executable(myapp src/main.cpp)

# 安装可执行文件
install(TARGETS myapp
RUNTIME DESTINATION bin
)

完整安装配置​

install(TARGETS myapp
RUNTIME DESTINATION bin
BUNDLE DESTINATION . # macOS 应用包
)

# 安装配置文件
install(FILES config/settings.json
DESTINATION etc/myapp
)

# 安装文档
install(DIRECTORY docs/
DESTINATION share/doc/myapp
)

安装时设置 RPATH​

# 设置安装后的 RPATH
set_target_properties(myapp PROPERTIES
INSTALL_RPATH "$ORIGIN/../lib"
)

# 安装时自动设置 RPATH
include(GNUInstallDirs)
install(TARGETS myapp
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

实战示例​

完整项目示例​

cmake_minimum_required(VERSION 3.15)
project(MyApp VERSION 1.0.0 LANGUAGES CXX)

# 设置默认构建类型
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()

# 设置输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# 收集源文件
file(GLOB_RECURSE SOURCES "src/*.cpp")
file(GLOB_RECURSE HEADERS "include/*.h")

# 创建可执行目标
add_executable(myapp ${SOURCES} ${HEADERS})

# 设置头文件目录
target_include_directories(myapp
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)

# 设置 C++ 标准
target_compile_features(myapp PUBLIC cxx_std_17)

# 编译选项
target_compile_options(myapp
PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/W4 /utf-8>
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wpedantic>
$<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra -Wpedantic>
)

# 预处理器定义
target_compile_definitions(myapp
PRIVATE
PROJECT_VERSION="${PROJECT_VERSION}"
PUBLIC
$<$<CONFIG:Debug>:DEBUG_MODE>
)

# 查找并链接依赖
find_package(Threads REQUIRED)
target_link_libraries(myapp PRIVATE Threads::Threads)

# 安装配置
install(TARGETS myapp RUNTIME DESTINATION bin)

# 添加运行目标
add_custom_target(run
COMMAND myapp
DEPENDS myapp
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

# 添加测试
enable_testing()
add_test(NAME myapp_test COMMAND myapp --test)

小结​

本章我们学习了:

  1. 创建可执行目标:add_executable
  2. 可执行文件属性:输出名称、目录、C++ 标准、RPATH
  3. 编译选项:头文件路径、预处理器定义、编译选项
  4. 链接库:项目内库、系统库、外部库
  5. 运行可执行文件:基本运行、自定义目标、CTest、调试
  6. 跨平台注意事项:平台特定配置、输出名称、安装路径
  7. 安装配置:安装目标文件、设置 RPATH

练习​

  1. 创建一个多文件的可执行项目
  2. 配置 Debug 和 Release 不同的编译选项
  3. 实现跨平台的可执行文件构建
  4. 添加自定义运行目标和测试
  5. 编写完整的安装配置,包括跨平台 RPATH 设置