3、CMake基础回顾:CMakeLists.txt结构、add_library与add_executable、install命令初识
好,咱们正式开始第三讲。说实话,很多同学一上来就急着写CPack配置,结果连CMakeLists.txt的基本结构都没搞明白。这就像盖楼不打地基,迟早要塌。今天我就带你把这块地基夯实了。
3.1 CMakeLists.txt 的基本骨架
每个CMake项目,说白了就是一个或多个CMakeLists.txt文件。我见过最离谱的项目,一个CMakeLists.txt写了三千行,那维护起来简直是噩梦。咱们先从最标准的骨架说起。
cmake_minimum_required(VERSION 3.16)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# 这里放你的库和可执行文件定义
# 这里放你的安装规则
嗯,就这么简单。但这里有个坑——cmake_minimum_required的版本号。我曾经接手过一个项目,它写的是2.8,结果很多新特性用不了,改起来那叫一个痛苦。我个人建议,至少设到3.16,这个版本对CPack的支持比较成熟。
project()命令里,我习惯把版本号和语言都写清楚。你想想看,如果不写LANGUAGES,CMake默认会检测C和C++,有时候会多出一些不必要的检查。
3.2 add_library 与 add_executable
这两个命令,是CMake里最常用的。咱们先看add_library。
3.2.1 静态库与动态库
# 静态库
add_library(mylib STATIC src/lib.cpp include/lib.h)
# 动态库
add_library(mylib SHARED src/lib.cpp include/lib.h)
# 对象库(不打包,只编译)
add_library(mylib OBJECT src/lib.cpp)
这里我要多说一句。很多新手喜欢把所有源文件都列出来,其实没必要。你只需要列出直接属于这个目标的源文件就行。头文件可以列,也可以不列——但列出来有个好处,在IDE里能看到依赖关系。
我在项目中遇到过一个问题:动态库在Windows上需要导出符号,否则外部调用不了。那时候我还没加dllexport,折腾了半天才发现。所以如果你做跨平台项目,记得处理符号导出。
3.2.2 可执行文件
add_executable(myapp src/main.cpp src/utils.cpp)
target_link_libraries(myapp PRIVATE mylib)
注意target_link_libraries的PRIVATE关键字。它表示mylib只是myapp内部使用的,不会传递给链接myapp的其他目标。说白了,就是控制依赖的可见性。
PUBLIC或PRIVATE关键字,哪怕只有一个依赖。这样以后加依赖时,不会忘记写。
3.3 install 命令初识
好,终于到install了。这个命令是CPack的基础,你想想看,没有安装规则,CPack怎么知道要打包什么?
3.3.1 安装目标
install(TARGETS mylib myapp
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
这里要注意:RUNTIME对应可执行文件和DLL,LIBRARY对应动态库(.so/.dylib),ARCHIVE对应静态库(.a/.lib)。Windows上DLL要放到RUNTIME目录,而不是LIBRARY。我曾经因为这个搞错过,打包出来的程序运行时报找不到DLL。
3.3.2 安装头文件
install(DIRECTORY include/ DESTINATION include)
这个命令会把include目录下的所有文件安装到目标系统的include目录。注意后面的斜杠——include/表示只复制目录内容,不复制include目录本身。如果不加斜杠,就会多一层目录。
3.3.3 安装配置文件
install(FILES config.json DESTINATION etc/myapp)
配置文件、资源文件、文档,都可以用这种方式安装。我习惯把配置文件单独放一个目录,方便用户查找。
3.4 一个完整的例子
咱们把上面的知识点串起来,写一个完整的CMakeLists.txt。
cmake_minimum_required(VERSION 3.16)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# 定义库
add_library(mylib SHARED src/lib.cpp include/lib.h)
target_include_directories(mylib PUBLIC include)
# 定义可执行文件
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
# 安装规则
install(TARGETS mylib myapp
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
install(DIRECTORY include/ DESTINATION include)
install(FILES README.md DESTINATION share/doc/myapp)
这个例子虽然简单,但已经覆盖了大部分常见场景。你把这个跑通了,后面学CPack就轻松多了。
3.5 避坑指南
- 路径问题: 在
install(DIRECTORY ...)里,源路径末尾的斜杠很关键。加与不加,结果完全不同。 - 权限问题: 安装到系统目录(如
/usr/local)需要sudo。我建议开发阶段先安装到自定义前缀,比如cmake --install . --prefix ./install。 - 跨平台路径: Windows上
DESTINATION用反斜杠?不,CMake内部会处理,你统一用正斜杠就行。
3.6 小结
今天咱们把CMakeLists.txt的基本结构、add_library和add_executable的用法、以及install命令的初步使用都过了一遍。说白了,这些就是CPack的基石。下一讲,咱们就要正式进入CPack的世界了。
嗯,如果你现在手头有项目,不妨试着写一个简单的CMakeLists.txt,把库和可执行文件都装上。实践出真知,光看是学不会的。