惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
T
The Blog of Author Tim Ferriss
S
SegmentFault 最新的问题
D
DataBreaches.Net
博客园_首页
罗磊的独立博客
B
Blog
T
Threat Research - Cisco Blogs
C
Cisco Blogs
GbyAI
GbyAI
Engineering at Meta
Engineering at Meta
WordPress大学
WordPress大学
G
GRAHAM CLULEY
H
Help Net Security
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
爱范儿
爱范儿
SecWiki News
SecWiki News
T
Threatpost
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Schneier on Security
Schneier on Security
T
The Exploit Database - CXSecurity.com
Google Online Security Blog
Google Online Security Blog
T
Tor Project blog
小众软件
小众软件
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Y
Y Combinator Blog
H
Hacker News: Front Page
V
V2EX
Security Latest
Security Latest
Cloudbric
Cloudbric
Simon Willison's Weblog
Simon Willison's Weblog
Attack and Defense Labs
Attack and Defense Labs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
P
Proofpoint News Feed
博客园 - 三生石上(FineUI控件)
NISL@THU
NISL@THU
S
Secure Thoughts
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
V2EX - 技术
V2EX - 技术
Vercel News
Vercel News
P
Palo Alto Networks Blog
IT之家
IT之家
MyScale Blog
MyScale Blog
有赞技术团队
有赞技术团队
Application and Cybersecurity Blog
Application and Cybersecurity Blog
D
Docker
Google DeepMind News
Google DeepMind News
Webroot Blog
Webroot Blog

Jun's Blog

2023 年度总结 OS 学习记 之 XV6 如何编译 GraalVM LLVM 中端优化之 InstCombine C++ 中 inline 关键字的语义 链接与库 C++ 移动语义基础 如何优化矩阵相乘 2022年度总结 CUDA初学笔记 汇编语言之保护模式 汇编语言之实模式 浅析 libc++ 中的 string 实现 std::expected 基本使用 C++模板基础 CSAPP第九章笔记之虚拟内存 CSAPP第八章笔记之异常控制流 2021年度总结 编译安装GCC12 如何给LLVM贡献代码 CSAPP第三章笔记Part 2 CSAPP第三章笔记Part 1 GDB基本使用笔记 读Effetive Modern C++ 之类型推导 给计算机新生的一封信 由Redis学习数据结构--字典 C++中lambda表达式基础 由Redis学习数据结构--链表 对Python及爬虫行业的思考 Vim实用技巧 浅谈C++中的类 简要剖析const关键字 正则表达式基础总结 使用Hugo和Firebase部署个人博客 浅谈HTTPS证书 Linux常用命令总结 Git学习笔记 Linux下使用v2ray 娱乐至死读书笔记 自控力读书笔记 少有人走的路力读书笔记 Postfix & Dovecot 自建邮箱服务 Linux用户管理 Linux开机流程 Linux文件与目录 Linux硬盘管理 Linux服务浅谈 About Me
CMake学习笔记
Jun · 2021-06-19 · via Jun's Blog

· Jun

CMake简介

cmake是一个跨平台的构建系统。众所周知,像C++这样的语言构建过程非常痛苦,各个不同的平台使用的工具也各不相同。在Linux系统上我们一般使用Makefile,在Windows和Mac OS X 上也有各家IDE的配置文件。cmake则可以实现编写一次,在各个平台上生成所需的编译配置文件,最后我们可以再用生成的文件构建即可。可以把cmake理解为一个中间层,对于复杂多样的构建过程提供了统一的接口。

下面是我学习cmake的笔记和思考。以下均在Linux下操作,适用于所有Unix-like的操作系统。

资料

基本操作

cmake的配置文件名一般为CMakeLists.txt,至少存在于项目的根目录,对于复杂的项目,子项目中也会有。

因为cmake会生成一系列配置文件,为了不污染项目,我们一般采用外部构建的办法,具体操作如下:

1
2
3
4
5
6
project
├── CMakeLists.txt
├── include
│   └── source.h
└── src
    └── source.cc

假如有一个结构如上的项目,我们一般在项目根目录下再手动创建一个目录为build,进入此目录后cmake ..,将配置文件写入build目录。

1
2
3
4
mkdir build
cd build
cmake ..
make

通用选项

限定cmake最低版本

1
cmake_minimum_required(VERSION 3.5)

设置项目的名称

设置编译器

1
2
set(CMAKE_C_COMPILER "/usr/bin/clang") # C++ 编译器
set(CMAKE_CXX_COMPILER "/usr/bin/clang++") # C++ 编译器

也可以在调用 cmake 命令时使用:

1
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ../

设置C++标准

1
set(CMAKE_CXX_STANDARD 17)

添加源文件

SET()可以用来创建一个变量,第一个参数为参数名,剩下的参数则为值。我们一般用它创建源文件的别名,这样就可以用${变量名}指代所有源文件了。

1
2
3
4
5
SET(SOURCES
  src/source1.cc
  src/source2.cc
  ...
)

添加可执行文件

1
add_executable(executable_name sources_files)

我们用add_executable()添加一个可执行文件,第一个参数是可执行文件的名字,剩下的参数可以是指定的文件名,也可以是之前创建的SOURCES变量

1
2
3
4
5
6
7
#直接指定文件名
add_executable(demo
  src/source1.cc
  src/source2.cc
)
#用变量名
add_executable(demo ${SOURCES})

添加库

我们用add_library()添加一个库,第一个参数为添加的库名,第二个参数表明为是static(静态库),或者是shared(动态库),剩下的参数则是源文件,当然与上面一样,可以是指定的文件名,或者是用SET()创建的变量

1
2
add_library(lib STATIC src/lib.cpp)
add_library(lib SHARED src/lib.cpp)

设置头文件路径

我们用target_include_directories()指定头文件的路径,第一个参数可以为此前添加的可执行文件名,或者为此前添加的库名。注意我们应该在添加完target之后再使用此方法,否则会提示错误。因为我们这里include的第一个参数是指定需要添加头文件的target。

1
2
3
4
5
target_include_directories(
  target_name
  PUBLIC
  ${PROJECT_SOURCE_DIR}/include
)

链接库

1
2
3
4
5
target_link_libraries(
  target_name
  PRIVATE
  lib_name
)

安装

cmake提供了命令行参数,使我们可以指定安装的前缀

1
cmake .. -DCMAKE_INSTALL_PREFIX=/install/location

我们也可以在配置中指定

1
2
3
4
5
install(TARGETS
  target_name
  DESTINATION
  bin
)
1
2
3
4
5
6
install (TARGETS 
  target_name
  LIBRARY 
  DESTINATION 
  lib
)

含有第三方库或子项目

有多个子项目的大型项目,我们可以把各个子项目组合起来,每个子项目都有自己的CMakeLists.txt,在根目录的CMakeLists.txt使用add_subdirectory()

1
add_subdirectory(sub_project_name)

其中sub_project_name为子项目的名称。

单元测试

cmake已经包含一个一个工具CTest,我们可以通过它make test从而进行单元测试。至于单元测试的框架,我们可以使用googletest

具体过程如下:

  • 先将gooletest作为一个子项目用add_subdirectory()添加进来
  • enable_testing() 开启CTest,使用单元测试
  • target_link_libraries(target GTest::GTest GTest::Main) 将googletest链接进target
  • add_test(test_all target)添加测试

例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
├── build
├── CMakeLists.txt
├── include
│   ├── a.h
│   └── b.h
├── src
│   ├── a.cc
│   ├── b.cc
│   └── unittest.cc
└── third-party
    └── google-test
        ├── CMakeLists.txt
        └── CMakeLists.txt.in

对于像上面这样的一个小项目,我们编译了一个静态库(lib),并使用googletest作为单元测试框架。它的MakeLists.txt如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 指定最低版本
cmake_minimum_required(VERSION 3.5)
# 指定编译器
set(CMAKE_CXX_COMPILER "/usr/bin/clang++")
# 指定C++标准
set(CMAKE_CXX_STANDARD 17)
# 指定项目名
project(test)
# 导入子项目googletest
add_subdirectory(third-party/google-test)
# 添加静态库
add_library(lib
  STATIC 
  src/a.cc
  src/b.cc
)
# 添加头文件目录
target_include_directories(
  lib
  PUBLIC
  ${PROJECT_SOURCE_DIR}/include
)
# 开启单元测试
enable_testing()
# 添加可执行文件
add_executable(
  main
  src/unittest.cc
)
# 链接静态库与第三方库
target_link_libraries(
  main
  PRIVATE
  test
  GTest::GTest 
  GTest::Main
)
# 启动测试
add_test(test_all main)