# cmake tutorials note
dabing cmake (opens new window), doc-1 (opens new window), doc-2 (opens new window)
# 快速上手
gcc 步骤:
- 预处理
- 编译(编译器)
- 汇编(汇编器)
- 链接(链接器)
如果有很多文件(比如超过 10 个)需要在一起编译,那么使用 gcc 命令似乎很棘手,通常我们会使用 makefile 或 cmake 工具辅助编译。
步骤:
- create file
CmakeLists.txt
- execute cmd
cmake <path>
to generateMakefile
(2nd path can be relative path ofCMakeLists.txt
) - use cmd
make
to obtain executable file
# cmake 安装与基本使用
sudo apt install cmake
cmake --version
# centos
# https://stackoverflow.com/questions/55345373/how-to-install-gcc-g-8-on-centos#comment136335547_66016318
sudo dnf groupinstall "Development Tools"
create file CMakeLists.txt
and fill with:
# 定义最小的 cmake 版本,可选
cmake_minimum_required(VERSION 3.15)
# 定义项目名
project(helloworld)
# 指定最终可执行文件以及源文件
# 参数1:可执行文件名子
# 参数2:原文件列表,如果有多个可使用 space 或 ; 间隔
add_executable(app main.cpp add.cpp sub.cpp mul.cpp div.cpp)
# 注意:指定源文件时无需指定 .h 文件
利用 cmake 进行编译
$ cmake .
.
├── add.cpp
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
├── CMakeLists.txt
├── div.cpp
├── head.h
├── main.cpp
├── Makefile
├── mul.cpp
└── sub.cpp
推荐另一种方式:将所有 cmake 和 make 中间产物统一放在一个目录下
尝试这种方式之间需要删掉上边一步产生的所有中间产物
rm -rf CMakeFiles cmake_install.cmake CMakeCache.txt Makefile
- 创建
build
目录并cd
进入它 - 执行命令
cmake ..
- 然后所有的编译中间产物都出现在了 build 目录中
.
├── add.cpp
├── build
├── CMakeLists.txt
├── div.cpp
├── head.h
├── main.cpp
├── mul.cpp
└── sub.cpp
- 然后执行
make
命令,然后可以得到app
可执行文件
# cmake - 添加变量
cmake_minimum_required(VERSION 3.15)
project(helloworld)
# 1.指定标准版本(auto keyword is supported in std11+)
set(CMAKE_CXX_STANDARD 11)
# 2. 定义变量
set(SRC_LIST main.cpp add.cpp sub.cpp mul.cpp div.cpp)
# same as
# set(SRC_LIST main.cpp;add.cpp;sub.cpp;mul.cpp;div.cpp)
add_executable(app ${SRC_LIST})
第三种方式:通过为 cmake 命令添加参数添加
cmake <path> -DCMAKE_CXX_STANDARD=11
另一个宏 EXECUTABLE_OUTPUT_PATH
用来指定生成的可执行文件的路径。如果路径不存在会自动被创建
...
SET(HOME /home/eugene/tools)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
cmake_minimum_required(VERSION 3.15)
project(helloworld)
# 定义标准库版本
set(CMAKE_CXX_STANDARD 11)
set(SRC_LIST main.cpp add.cpp sub.cpp mul.cpp div.cpp)
# set(SRC_LIST main.cpp;add.cpp;sub.cpp;mul.cpp;div.cpp)
add_executable(app ${SRC_LIST})
set(HOME /home/eugene)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/aa/bb/app)
➜ build cmake .. && make
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/eugene/Documents/cmake_workspace/01proj_helloworld/build
[ 16%] Building CXX object CMakeFiles/app.dir/main.cpp.o
[ 33%] Building CXX object CMakeFiles/app.dir/add.cpp.o
[ 50%] Building CXX object CMakeFiles/app.dir/sub.cpp.o
[ 66%] Building CXX object CMakeFiles/app.dir/mul.cpp.o
[ 83%] Building CXX object CMakeFiles/app.dir/div.cpp.o
[100%] Linking CXX executable /home/eugene/aa/bb/app/app
[100%] Built target app
➜ build cmake .. && make
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/eugene/Documents/cmake_workspace/01proj_helloworld/build
[ 16%] Building CXX object CMakeFiles/app.dir/main.cpp.o
[ 33%] Building CXX object CMakeFiles/app.dir/add.cpp.o
[ 50%] Building CXX object CMakeFiles/app.dir/sub.cpp.o
[ 66%] Building CXX object CMakeFiles/app.dir/mul.cpp.o
[ 83%] Building CXX object CMakeFiles/app.dir/div.cpp.o
[100%] Linking CXX executable /home/eugene/aa/bb/app/app
[100%] Built target app
➜ build /home/eugene/aa/bb/app/app
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667
➜ build
➜ build
动手尝试:change CMAKE_CXX_STANDARD value from 11 to 98, and see what'll happen.
# cmake - 搜索文件
上一节中我们使用 set 命令指定了所有的源文件,但当文件很多时这种方法太繁琐,我们可以使用搜索命令自动扫描某个目录下的文件
方式一:aux_source_directory
命令
cmake_minimum_required(VERSION 3.15)
project(helloworld)
set(CMAKE_CXX_STANDARD 11)
# set(SRC_LIST main.cpp add.cpp sub.cpp mul.cpp div.cpp)
# 搜索指定目录下的所有文件并将其赋值给一个变量
aux_source_directory(${PROJECT_SOURCE_DIR} SRC_LIST)
# 使用变量
add_executable(app ${SRC_LIST})
PROJECT_SOURCE_DIR
这个变量所指的目录是我们使用 cmake 命令时传递的第一个参数所指的目录
方式二:file
命令,功能比较强大
cmake_minimum_required(VERSION 3.15)
project(helloworld)
set(CMAKE_CXX_STANDARD 11)
# set(SRC_LIST main.cpp add.cpp sub.cpp mul.cpp div.cpp)
# 根据 ${PROJECT_SOURCE_DIR}/*.cpp 规则搜索所有文件(无递归)并将他们保存在变量 SRC_LIST 中
file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/*.cpp)
add_executable(app ${SRC_LIST})
file — CMake 3.29.0-rc1 Documentation (opens new window)
# 指定头文件目录
创建两个目录:include
用来存放所有头文件;src
用来存放所有源文件
结构为
➜ 01proj_helloworld tree -L 2
.
├── build
│ ├── app
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ └── Makefile
├── CMakeLists.txt
├── include
│ └── head.h
└── src
├── add.cpp
├── div.cpp
├── main.cpp
├── mul.cpp
└── sub.cpp
编写 CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(helloworld)
set(CMAKE_CXX_STANDARD 11)
# file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/*.cpp)
file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
add_executable(app ${SRC_LIST})
在 build 目录中进行编译,发现出错了
➜ build cmake .. && make
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/eugene/Documents/cmake_workspace/01proj_helloworld/build
[ 16%] Building CXX object CMakeFiles/app.dir/src/add.cpp.o
/home/eugene/Documents/cmake_workspace/01proj_helloworld/src/add.cpp:2:10: fatal error: head.h: No such file or directory
2 | #include "head.h"
| ^~~~~~~~
compilation terminated.
make[2]: *** [CMakeFiles/app.dir/build.make:76: CMakeFiles/app.dir/src/add.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/app.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
➜ build
原因:add.cpp
找不到头文件
解决方法
- 修改
add.cpp
的#include "head.h"
为#include "../include/head.h"
: 如果文件太多,那么修改起来很麻烦 - (推荐) 指定 header 目录,使用命令:
include_directories(${PROJECT_SOURCE_DIR}/include)
最终的 CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(helloworld)
set(CMAKE_CXX_STANDARD 11)
file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
# 指定头文件所在目录
include_directories(${PROJECT_SOURCE_DIR}/include)
add_executable(app ${SRC_LIST})
# 基于 cmake 构建库和使用库
项目:02project_lib
创建项目有哪些目的?
- 构建可执行文件
- 组合源文件到一个二进制文件,也称为库,有动态库和静态库之分
# 创建动态库
cmake_minimum_required(VERSION 3.15)
project(mylib)
set(CMAKE_CXX_STANDARD 11)
file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
include_directories(${PROJECT_SOURCE_DIR}/include)
# add_executable(app ${SRC_LIST})
# 使用 add_library 命令
add_library(mycalc SHARED ${SRC_LIST})
$ tree -L 2
.
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ ├── libmycalc.so # 动态库
│ └── Makefile
├── CMakeLists.txt
├── include
│ └── head.h
└── src
├── add.cpp
├── div.cpp
├── mul.cpp
└── sub.cpp
dynamic library have executable permission while static library do not have!
# 创建静态库
利用上一节代码,只需改动一个关键字
CMakeLists.txt
:
#add_library(mycalc SHARED ${SRC_LIST})
add_library(mycalc STATIC ${SRC_LIST})
.
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ ├── libmycalc.a # 静态库
│ └── Makefile
├── CMakeLists.txt
├── include
│ └── head.h
└── src
├── add.cpp
├── div.cpp
├── mul.cpp
└── sub.cpp
可以使用宏 LIBRARY_OUTPUT_PATH
指定动态库或静态库的输出目录
cmake_minimum_required(VERSION 3.15)
project(mylib)
set(CMAKE_CXX_STANDARD 11)
file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
include_directories(${PROJECT_SOURCE_DIR}/include)
# set library output path
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_library(mycalc STATIC ${SRC_LIST})
# 链接静态库
准备目录(项目: 03proj_use_static_lib
)
.
├── build
├── CMakeLists.txt
├── include
│ └── head.h
├── lib
│ └── libmycalc.a
└── src
└── main.cpp
链接静态库
project(helloworld)
set(CMAKE_CXX_STANDARD 11)
file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
include_directories(${PROJECT_SOURCE_DIR}/include)
# way 1
# link_libraries(${PROJECT_SOURCE_DIR}/lib/libmycalc.a) # specify a staic lib path
# way 2
# link_libraries(libmycalc.a)
# link_directories(${PROJECT_SOURCE_DIR}/lib) # specify lib directories
# way 3
# link_libraries(mycalc)
# link_directories(${PROJECT_SOURCE_DIR}/lib) # specify lib directories
# way 4
file(GLOB LIB_LIST ${PROJECT_SOURCE_DIR}/lib/*.a)
link_libraries(${LIB_LIST}) # support multiple staic lib paths
add_executable(app ${SRC_LIST})
注意
link_libraries()
andlink_directories()
命令支持多个参数link_libraries()
只能链接静态库link_directories()
指定要链接库所在的目录
# 链接动态库
除了在项目中引入静态库,很多时候也会使用一些标准的或者第三方提供的一些动态库,关于动态库的制作、使用以及在内存中的加载方式和静态库都是不同的。
在 cmake 中链接动态库需要用到 target_link_libraries (opens new window) 命令
target_link_libraries(<target>
<PRIVATE|PUBLIC|INTERFACE> <item>...
[<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
target
表示要链接到哪里,即目标。可以是可执行文件或者一个库- 后续的参数格式为
修饰符 动态库
,不同的修饰符表示不同的权限- 如果链接的动态库之间没有依赖关系,则无需做任何设置,一般无需指定,使用默认的 PUBLIC 即可
- 动态库的链接具有
传递性
,例如动态库 A 链接了动态库 B 和动态库 C(可表示为target_link_libraries(a, b, c)
),动态库 D 链接了动态库 A,那么此时动态库 D 相当于也链接了动态库 B、C,并可以使用 B 和 C 中定义的方法 - 权限
PUBLIC
:用 public 修饰的库会被链接到 target 中,并且其中的符号也会被导出提供给第三方使用 - 权限
PRIVATE
:用 private 修饰的库仅被链接到 target 中,并且终结掉,第三方无法感知调用了什么库 - 权限
INTERFACE
:用 interface 修饰的库不会被连接到前面的 taget 中,只会导出符号
target_link_libraries
即可用来链接静态库,也可用来链接动态库
例子:链接之前创建的动态库 mycalc
cmake_minimum_required(VERSION 3.15)
project(helloworld)
set(CMAKE_CXX_STANDARD 11)
file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
include_directories(${PROJECT_SOURCE_DIR}/include)
# 链接动态库所在目录(需要放在add_executable命令前)
link_directories(${PROJECT_SOURCE_DIR}/lib)
add_executable(app ${SRC_LIST})
# 链接动态库到可执行文件
target_link_libraries(app mycalc)
注意:target_link_libraries()
语句必须放在最后一行
# 补充:库的加载工作
静态库如何被加载
在程序编译的最后一个阶段也就是链接阶段,提供的静态库会被打包到可执行程序中。当可执行程序被执行,静态库中的代码也会一并被加载到内存中,因此不会出现静态库找不到无法被加载的问题。
优点:
- 静态库被打包到应用程序中加载速度快
- 发布程序无需提供静态库,移植方便
缺点:
- 相同的库文件数据可能在内存中被加载多份, 消耗系统资源,浪费内存
- 库文件更新需要重新编译项目文件, 生成新的可执行程序, 浪费时间。
动态库如何被加载
在程序编译的最后一个阶段也就是链接阶段:
- 在 gcc 命令中虽然指定了库路径(使用参数 -L),但是这个路径并没有记录到可执行程序中,只是检查了这个路径下的库文件是否存在。
- 同样对应的动态库文件也没有被打包到可执行程序中,只是在可执行程序中记录了库的名字。
可执行程序被执行起来之后:
- 程序执行的时候会先检测需要的动态库是否可以被加载,加载不到就会提示上边的错误信息
- 当动态库中的函数在程序中被调用了, 这个时候动态库才加载到内存,如果不被调用就不加载
- 动态库的检测和内存加载操作都是由动态连接器来完成的
优点:
- 可实现不同进程间的资源共享
- 动态库升级简单, 只需要替换库文件, 无需重新编译应用程序
- 程序猿可以控制何时加载动态库, 不调用库函数动态库不会被加载
缺点:
加载速度比静态库慢, 以现在计算机的性能可以忽略
发布程序需要提供依赖的动态库
# 日志
可以利用 message (opens new window) 命令记录日志
message([STATUS|WARNING|AUTHOR_WARNING|SEND_ERROR|FATAL_ERROR] "message here...")
- 不添加模式:表示重要信息
STATUS
:表示非重要信息WARNING
:警告,会继续执行AUTHOR_WARNING
:警告(dev),会继续执行SEND_ERROR
:错误,继续执行,跳过生成FATAL_ERROR
:致命错误,终止运行和生成
例子
file(GLOB SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
# -- source files:/home/.../04proj_use_dynamic_lib/src/main.cpp
message(STATUS "source files:" ${SRC_LIST})
# 变量和列表
# 字符串拼接
使用 set 命令进行字符串拼接
set(STR1 "NeuralNetwork")
set(STR2 "Hareware")
set(STR3 "I love " ${STR1} " and " ${STR2})
message(STATUS ${STR3})
# -- I love NeuralNetwork and Hareware
set(STR4 "I love ${STR1} and ${STR2}")
message(STATUS ${STR4})
# -- I love NeuralNetwork and Hareware
# List 操作
基本操作:
list([APPEND|LENGTH|GET|REMOVE_ITEM] 操作的变量 参数 [结果变量])
# 创建 list
set(A_LIST apple banana grape)
message(${A_LIST}) # applebananagrape
# 向 list 中追加元素
list(APPEND A_LIST "cake")
message(${A_LIST}) # applebananagrapecake
# 获取长度
list(LENGTH A_LIST LIST_LEN)
message("len: ${LIST_LEN}") # 4
# 获取 A_LIST[0]
list(GET A_LIST 0 LIST_STR)
message("A_LIST[0]: ${LIST_STR}") # apple
# A_LIST[3]
list(GET A_LIST 3 LIST_STR)
message("A_LIST[3]: ${LIST_STR}") # cake
# 移除元素
list(REMOVE_ITEM A_LIST grape)
# A_LIST[2]
list(GET A_LIST 2 LIST_STR)
message("A_LIST[2]: ${LIST_STR}") # cake
其他操作:在指定位置插入若干元素、将元素插入 i=0 的位置、删除第一个元素、移除最后一个元素、移除指定索引的元素、移除指定元素、移除重复元素、翻转元素、元素排序等等
# 宏定义
代码所在项目:05_micro_definition
在代码中,我们会根据宏定义判断是否执行某一段逻辑,比如
#include<stdio.h>
void process() {
printf("process...\n");
}
int main() {
//有宏定义时才会执行代码
#ifdef DEBUG_MODE
printf("debugging...\n");
#endif
for (int i=0; i<10; i++) {
process();
}
return 0;
}
为了让测试更灵活,我们可以不在代码中定义宏,而是在 gcc/g++ 命令中指定,比如
$ gcc test.c -DDEBUG_MODE -o app
在 CMake 中我们不直接配置 gcc/g++,而是在 CMakeLists.txt
中进行宏定义
add_definitions(-DDEBUG_MODE)
测试:
cmake_minimum_required(VERSION 3.15)
project(helloworld)
# 进行宏定义
add_definitions(-DDEBUG_MODE)
add_executable(app main.cpp)
# CMake 嵌套
当项目功能比较复杂时,需要进行“模块化”拆分简化,每个模块中包含一个 CMakeLists.txt
比如我们有这样一个“复杂”的项目
.
|-- calc <--- 计算工具模块
| |-- add.cpp
| |-- CMakeLists.txt
| |-- div.cpp
| |-- mul.cpp
| `-- sub.cpp
|-- CMakeLists.txt
|-- include <---- 声明模块
| |-- calc.h
| `-- sort.h
|-- sort <---- 排序模块
| |-- CMakeLists.txt
| |-- insert_sort.cpp
| `-- select_sort.cpp
|-- test_calc <---- 测试模块,测试计算工具
| |-- CMakeLists.txt
| `-- test_calc.cpp
`-- test_sort <---- 测试模块,测试排序工具
|-- CMakeLists.txt
`-- test_sort.cpp
变量共享:子节点使用父节点中定义的变量
指定父子关系:使用 add_subdirectory
命令
# 编写根 CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(test)
# 定义变量
# 1.定义静态库生成路径
SET(SLIB_PATH ${PROJECT_SOURCE_DIR}/lib)
# 2.定义可执行程序的存储路径
SET(EXE_PATH ${PROJECT_SOURCE_DIR}/bin)
# 3.定义头文件路径
SET(HEAD_PATH ${PROJECT_SOURCE_DIR}/include)
# 4.定义库文件名
SET(LIB_CALC calc)
SET(LIB_SORT sort)
# 5.定义可执行文件名
SET(APP_TEST_CALC app_test_calc)
SET(APP_TEST_SORT app_test_sort)
# 添加子目录
add_subdirectory(calc)
add_subdirectory(sort)
add_subdirectory(test_calc)
add_subdirectory(test_sort)
在此定义的变量可以在子模块中使用
# calc 和 sort 两个模块的 CMakeLists.txt
calc/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(calc)
# 搜索所有源文件
file(GLOB SRC_LIST ./*.cpp)
# 指定头文件
include_directories(${HEAD_PATH})
# 指定库输出目录
set(LIBRARY_OUTPUT_PATH ${SLIB_PATH})
# 编译静态库。指定库名
add_library(${LIB_CALC} STATIC ${SRC_LIST})
sort/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(sort)
# 搜索所有源文件
file(GLOB SRC_LIST ./*.cpp)
# 指定头文件
include_directories(${HEAD_PATH})
# 指定库输出目录
set(LIBRARY_OUTPUT_PATH ${SLIB_PATH})
# 编译静态库。指定库名
add_library(${LIB_SORT} STATIC ${SRC_LIST})
# 两个测试目录里的 CMakeLists.txt
test_calc/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(test_calc)
# 搜索源文件
file(GLOB SRC_LIST ./*.cpp)
# 包含声明文件目录
include_directories(${HEAD_PATH})
# 链接静态库
link_libraries(${LIB_CALC})
# 指定库目录
link_directories(${SLIB_PATH})
# 指定生成程序存储位置
SET(EXECUTABLE_OUTPUT_PATH ${EXE_PATH})
# 生成程序
add_executable(${APP_TEST_CALC} ${SRC_LIST})
test_sort/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(test_sort)
# 搜索源文件
file(GLOB SRC_LIST ./*.cpp)
# 包含声明文件目录
include_directories(${HEAD_PATH})
# 链接静态库
link_libraries(${LIB_SORT})
# 指定库目录
link_directories(${SLIB_PATH})
# 指定生成程序存储位置
SET(EXECUTABLE_OUTPUT_PATH ${EXE_PATH})
# 生成程序
add_executable(${APP_TEST_SORT} ${SRC_LIST})
# 执行构建
在根目录中创建 build 目录,然后在其中执行 cmake ..
,然后执行 make
命令
[engure@ali build]$ cmake .. && make
-- The C compiler identification is GNU 8.5.0
-- The CXX compiler identification is GNU 8.5.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/engure/cmake_dabing/cmake-tutorials-dabing/06_modulization/build
[ 8%] Building CXX object calc/CMakeFiles/calc.dir/add.cpp.o
[ 16%] Building CXX object calc/CMakeFiles/calc.dir/div.cpp.o
[ 25%] Building CXX object calc/CMakeFiles/calc.dir/mul.cpp.o
[ 33%] Building CXX object calc/CMakeFiles/calc.dir/sub.cpp.o
[ 41%] Linking CXX static library ../../lib/libcalc.a
[ 41%] Built target calc
[ 50%] Building CXX object sort/CMakeFiles/sort.dir/insert_sort.cpp.o
[ 58%] Building CXX object sort/CMakeFiles/sort.dir/select_sort.cpp.o
[ 66%] Linking CXX static library ../../lib/libsort.a
[ 66%] Built target sort
[ 75%] Building CXX object test_calc/CMakeFiles/app_test_calc.dir/test_calc.cpp.o
[ 83%] Linking CXX executable ../../bin/app_test_calc
[ 83%] Built target app_test_calc
[ 91%] Building CXX object test_sort/CMakeFiles/app_test_sort.dir/test_sort.cpp.o
[100%] Linking CXX executable ../../bin/app_test_sort
[100%] Built target app_test_sort
# 在 sort 库中链接 calc 库
在 sort 静态库中链接 calc 静态库,只需要保留 test_sort
一个测试模块即可
项目:07_modulization_lib_link
1、指定 sort 库链接 calc 库,sort/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(sort)
file(GLOB SRC_LIST ./*.cpp)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${SLIB_PATH})
# 链接 calc 库
link_directories(${SLIB_PATH})
link_libraries(${LIB_CALC})
add_library(${LIB_SORT} STATIC ${SRC_LIST})
2、在 sort 库中使用 calc 库中的的函数,sort/insert_sort.cpp
#include<stdio.h>
#include "sort.h"
//引入声明
#include "calc.h"
void insert_sort(int *arr, int size) {
// 调用 add 方法
int c = add(100, 200);
printf("---\nc=%d\n---\n", c);
int i, j, key;
for (i = 1; i < size; i++) {
key = arr[i];
j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
3、一点说明 test_sort/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(test_sort)
file(GLOB SRC_LIST ./*.cpp)
include_directories(${HEAD_PATH})
# 链接 sort 库以使用其中的排序函数
# 因为 sort 库链接了 calc 库(可理解为 calc 打包到了 sort 库中)
# 所以相当于该测试模块也链接了 calc 库,可直接使用 calc 库
link_libraries(${LIB_SORT})
link_directories(${SLIB_PATH})
SET(EXECUTABLE_OUTPUT_PATH ${EXE_PATH})
add_executable(${APP_TEST_SORT} ${SRC_LIST})
4、在 test_sort
测试项目中使用 calc 库,test_sort/test_sort.cpp
#include<stdio.h>
#include "sort.h"
#include "calc.h"
int main() {
int arr1[] = {5, 2, 8, 12, 1};
int arr2[] = {5, 2, 8, 12, 1};
int size = 5;
insert_sort(arr1, size);
select_sort(arr2, size);
for (int i = 0; i < size; i++) {
printf("%d ", arr1[i]);
}
printf("\n");
for (int i = 0; i < size; i++) {
printf("%d ", arr2[i]);
}
printf("\n");
// 使用 calc 库中的函数
printf("10 + 20 = %d\n", add(10, 20));
return 0;
}
5、在 build 目录中构建,测试
# 在静态库中链接动态库
拷贝上一节最终的项目代码,命名为 08_modulization_lib_link_2
1、指定 calc 项目最终生成动态库 calc/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(calc)
file(GLOB SRC_LIST ./*.cpp)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${SLIB_PATH})
# 将 STATIC 修改为 SHARED
add_library(${LIB_CALC} SHARED ${SRC_LIST})
2、在 sort 中链接 calc 动态库 sort/CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(sort)
file(GLOB SRC_LIST ./*.cpp)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${SLIB_PATH})
link_directories(${SLIB_PATH})
# 生成静态库 sort
add_library(${LIB_SORT} STATIC ${SRC_LIST})
# 链接动态库 calc 到 sort 静态库
target_link_libraries(${LIB_SORT} ${LIB_CALC})
3、构建项目
此时 calc 库并未被打包到 sort 库中,而是保存了符号
# 项目
内容:使用 cmake 重新构建一个 ReactorHttp-Cpp(大丙老师另一个教程) (opens new window) 项目
步骤:
- 按照不同功能将文件分组,编写根 cmake 配置文件和子配置文件
- 子模块有 common、http、reactor、tcp、thread,在构建时都作为静态库单独构建
- …
此章很重要,值得多看 p19 (opens new window)
checkpoint: cmake-tutorials-dabing: cmake 课程大丙老师 - Gitee.com (opens new window)