CMake源码编译安装
前往官网下载源码(Source distributions)
# 安装部分依赖
sudo apt install libssh-dev build-essential -y
# 目前的长期支持版
wget https://github.com/Kitware/CMake/releases/download/v3.31.5/cmake-3.31.5.tar.gz
# 解压压缩包
tar -zxvf cmake-3.31.5.tar.gz
# 进入文件夹
cd ./cmake-3.31.5/
# 检查配置完整性, 并生成配置文件
./bootstrap
# 根据配置文件编译
make
# 安装编译后文件
make install
# 检验安装是否成功
cmake --version
## 安装完成后可以把之前的cmake文件夹删除CMake基本使用
CMake首先会去寻找文件夹下名为CMakeLists.txt的文件, 作为执行命令的入口
# 运行该项目最小CMake版本要求, 不写也行, 会报警告
cmake_minimum_required(VERSION 3.31)
# 项目名称
project(cmake_project_test)
# 添加子文件夹进入编译
add_subdirectory(./subdir_name)课程1_1
创建3个文件
// ./lesson1_1/main.cpp
// 该文件夹为c函数入口, 其中调用了一个名为add.h的头文件
#include "add.h"
int main() {
int c = add(1, 2);
return 0;
}// ./lesson1_1/add.h
// 该文件为add.cpp的头文件, 声明了名为add的函数
#pragma once
int add(int a, int b);// ./lesson1_1/add.cpp
// 该文件中定义了add函数的具体实现
int add(int a, int b) {
return a + b;
}上面3个文件定义了一个1+2的程序, 接下来编写./lesson1_1/CMakeLists.txt
# ./lesson1_1/CMakeLists.txt
# 添加编译链接的程序文件
# 不添加add.cpp的话会报LNK2019链接错误
add_executable(lesson1_1 ./main.cpp ./add.cpp)使用cmake编译运行
# 编译当前文件夹, 自动寻找CMakeLists.txt
cmake .
# 链接生成可执行文件
make课程1_2
创建1个文件
// ./lesson1_2/main.cpp
// 该文件夹为c函数入口, 其中调用了一个名为add.h的头文件
#include "../lesson1_1/add.h"
int main() {
int c = add(1, 2);
return 0;
}编写./lesson1_2/CMakeLists.txt文件, 指明c和cpp文件位置, 减少include头文件路径依赖
# ./lesson1_2/CMakeLists.txt
# 但不建议使用该句, 会造成过度依赖
include_directories(../lesson1_1)
add_executable(lesson1_2 ./main.cpp ../lesson1_1/add.cpp)课程2_1
静态库: 将依赖库函数压入可执行文件中
动态库: 只有在程序运行到依赖库时才寻找库函数
创建2个文件
// ./lesson2_1/add/add.h
// 该文件为add.cpp的头文件, 声明了名为add的函数
#pragma once
int add(int a, int b);// ./lesson2_1/add/add.cpp
// 该文件中定义了add函数的具体实现
int add(int a, int b) {
return a + b;
}再编写./lesson2_1/add/CMakeLists.txt, 创建静态库
# ./lesson2_1/add/CMakeLists.txt
# 先创建静态库名字, 然后再添加cpp文件进去
add_library(add_static add.cpp)然后进行编译链接生成静态库文件, 在linux下会生成.a文件, 在windows下会生成.lib文件
将生成的静态库文件移动到./lesson2_1/lib/libadd_static.a
为方便将add.h同样复制到./lesson2_1/lib/下
创建1个文件
// ./lesson2_1/main.cpp
#include "add.h"
int main() {
int c = add(1, 2);
return 0;
}在./lesson2_1/CMakeLists.txt中添加头文件路径
# ./lesson2_1/CMakeLists.txt
# 添加子文件夹解析
add_subdirectory(./add)
# 添加头文件依赖路径
include_directories(./lib)
# 三种调用静态库方法
## 需要写绝对路径, 并且有依赖问题? 无法为每个生成文件指定不同静态库
link_libraries($PWD/lib/libadd_static.a)
## 也要绝对路径, 需要指定静态库所在文件夹, 有点臃肿
link_directories($PWD/lib/)
## 单独给生成的可执行文件链接静态库
target_link_libraries(lesson2_1 libadd_static.a)
## 同样绝对路径, 直接给生成可执行文件链接静态库文件
target_link_libraries(lesson2_1 $PWD/lib/libadd_static.a)
# 生成可执行文件
add_executable(lesson2_1 ./main.cpp)课程2_2
创建2个文件
// ./lesson2_2/add/add.h
// 该文件为add.cpp的头文件, 声明了名为add的函数
#pragma once
int add(int a, int b);// ./lesson2_2/add/add.cpp
// 该文件中定义了add函数的具体实现
int add(int a, int b) {
return a + b;
}然后编写./lesson2_2/add/CMakeLists.txt, 生成动态库
# ./lesson2_1/add/CMakeLists.txt
# 生成动态库多加个SHARED就可以了
add_library(add_shared SHARED add.cpp)编译链接后生成libadd_shared.so动态库文件, linux下动态库后缀为.so, windows下动态库后缀为.dll
将生成的动态库文件移动到./lesson2_2/bin/libadd_shared.so
为方便将add.h同样复制到./lesson2_2/bin/下
创建1个文件
// ./lesson2_2/main.cpp
#include "add.h"
int main() {
int c = add(1, 2);
return 0;
}在./lesson2_2/CMakeLists.txt中添加头文件路径
# ./lesson2_2/CMakeLists.txt
# 添加子文件夹解析
add_subdirectory(./add)
# 添加头文件依赖路径
include_directories(./lib)
# 绝对路径, 直接给生成可执行文件链接静态库文件
target_link_libraries(lesson2_2 ./lib/libadd_shared.so)
# 生成可执行文件
add_executable(lesson2_1 ./main.cpp libadd_shared.so)最终会同时生成
.lib和.dll文件, 并且在
./lesson2_2/CMakeLists.txt里面链接的还是.lib静态库, 直到编译及运行时才会去真正调用
.dll动态库, 且.dll文件要么与可执行文件的同级, 要么在环境变量中加入动态库文件路径 同时代码需要做一些修改
创建1个文件
// ./lesson2_2/add/export.h
# pragma once
// 如果前面定义了EXPORT
#ifdef EXPORT
// 则定义CMAKE_STUDY_API等价于__declspec(dllexport)
#define CMAKE_STUDY_API __declspec(dllexport)
// 否则定义CMAKE_STUDY_API等价于__declspec(dllimport)
#define CMAKE_STUDY_API __declspec(dllimport)修改./lesson2_1/add/CMakeLists.txt
# ./lesson2_1/add/CMakeLists.txt
add_library(add_shared SHARED add.cpp)
# 在编译add.cpp时, 向add_shared可执行文件中添加一个PRIVATE类型的变量EXPORT, 使所有CMAKE_STUDY_API等价于__declspec(dllexport)
# 导出函数
target_compile_definitions(add_shared PRIVATE EXPORT)修改2个文件
// ./lesson2_2/add/add.h
// ./lesson2_2/bin/add.h
#pragma once
CMAKE_STUDY_API int add(int a, int b);// ./lesson2_2/add/add.cpp
CMAKE_STUDY_API int add(int a, int b) {
return a + b;
}将./lesson2_2/add/export.h同样复制到./lesson2_2/bin/下, 这样在编译动态库时调用的__declspec(dllexport), 在实际运行时调用的__declspec(dllimport)
课程3_1
与课程1_1代码相同
Linux下g++编译生成可执行文件流程
预处理: 头文件展开 宏替换 去除注释
# 预处理main.cpp g++ -E main.cpp -o main.i # 预处理add.cpp g++ -E add.cpp -o add.i编译: 编译器对文件进行汇编
# 编译main.i g++ -S main.i -o main.s # 编译add.i g++ -S add.i -o add.s汇编: 使用汇编器对文件进行汇编, 产生机器码
# 汇编main.i g++ -c main.s -o main.o # 汇编add.i g++ -c add.s -o add.o链接: 调用链接器对程序需要库进行链接
# 链接main.i g++ main.o add.o -o lesson3_1一步到位
# 直接将多个cpp文件生成可执行文件 g++ main.cpp add.cpp -o lesson3_1
可以使用Makefile代替手写代码
# 获得 lesson3_1 文件需要 main.cpp add.cpp 两个依赖文件
lesson3_1:main.cpp add.cpp
# 当有上面两个依赖文件后执行后续命令, 生成 lesson3_1 文件
g++ main.cpp add.cpp -o lesson3_1一步一步实现
# 链接: 获得 lesson3_1 文件需要 main.o add.o 两个依赖文件
lesson3_1:main.o add.o
# 当有上面两个依赖文件后执行后续命令, 生成 lesson3_1 文件
g++ main.o add.o -o lesson3_1
# 汇编: 获得 main.o add.o 文件需要 main.s add.s 两个依赖文件
main.o add.o:main.s add.s
# 当有上面两个依赖文件后执行后续命令, 生成 main.o add.o 文件
g++ -c main.s -o main.o
g++ -c add.s -o add.o
# 编译: 获得 main.s add.s 文件需要 main.i add.i 两个依赖文件
main.s add.s:main.i add.i
# 当有上面两个依赖文件后执行后续命令, 生成 main.s add.s 文件
g++ -S main.i -o main.s
g++ -S add.i -o add.s
# 预处理: 获得 main.i add.i 文件需要 main.cpp add.cpp 两个依赖文件
main.i add.i:main.cpp add.cpp
# 当有上面两个依赖文件后执行后续命令, 生成 main.i add.i 文件
g++ -E main.cpp -o main.i
g++ -E add.cpp -o add.i但在windows中, 若使用VisualStudio开发, 不会生成Makefile, 而是.sln文件
CMake则将二者统一起来, windows下和linux下均可使用
# 在当前文件夹下进行编译
cmake CMakeLists_dir
# 生成可执行文件
cmake --build .
# 加上这个可选项可显示详细信息
cmake .. -DCMAKE_VERBOSE_MAKEFILE=ON课程3_2
与课程1_2代码相同
进入./build/文件夹下运行cmake CMakeLists_dir和cmake --build ., 观察输出
...
[ 33%] Building CXX object lesson3_2/CMakeFiles/lesson1_2.dir/main.cpp.o
cd /home/why/Project/CmakeLearn/build/lesson3_2 && /usr/bin/c++ -I/home/why/Project/CmakeLearn/lesson3_2/../lesson1_1 -MD -MT lesson3_2/CMakeFiles/lesson1_2.dir/main.cpp.o -MF CMakeFiles/lesson1_2.dir/main.cpp.o.d -o CMakeFiles/lesson1_2.dir/main.cpp.o -c /home/why/Project/CmakeLearn/lesson3_2/main.cpp
此处-I参数是为了指定头文件包含路径(后面无空格)
此处-c参数表示只编译和汇编
此处-MD -MT -MF均可忽略
由此可得, 该处指定了头文件目录, 并将cpp文件编译和汇编为o文件
[ 66%] Building CXX object lesson3_2/CMakeFiles/lesson1_2.dir/__/lesson1_1/add.cpp.o
cd /home/why/Project/CmakeLearn/build/lesson3_2 && /usr/bin/c++ -I/home/why/Project/CmakeLearn/lesson3_2/../lesson1_1 -MD -MT lesson3_2/CMakeFiles/lesson1_2.dir/__/lesson1_1/add.cpp.o -MF CMakeFiles/lesson1_2.dir/__/lesson1_1/add.cpp.o.d -o CMakeFiles/lesson1_2.dir/__/lesson1_1/add.cpp.o -c /home/why/Project/CmakeLearn/lesson1_1/add.cpp
由上一条可得, 此处同样指定头文件所在目录, 并编译和汇编cpp文件为o文件
[100%] Linking CXX executable lesson1_2
...
/usr/bin/c++ CMakeFiles/lesson1_2.dir/main.cpp.o CMakeFiles/lesson1_2.dir/__/lesson1_1/add.cpp.o -o lesson1_2
由此可得, cmake调用c++, 将两个.o文件链接, 生成可执行文件
...课程3_3
与课程2_1代码相同
首先修改./lesson3_3/CMakeLists.txt内的内容, 仅生成静态库
# ./lesson3_3/CMakeLists.txt
add_subdirectory(./add)进入./build/文件夹下运行cmake CMakeLists_dir和cmake --build ., 观察输出
...
[ 50%] Building CXX object lesson3_3/add/CMakeFiles/add_static.dir/add.cpp.o
cd /home/why/Project/CmakeLearn/build/lesson3_3/add && /usr/bin/c++ -MD -MT lesson3_3/add/CMakeFiles/add_static.dir/add.cpp.o -MF CMakeFiles/add_static.dir/add.cpp.o.d -o CMakeFiles/add_static.dir/add.cpp.o -c /home/why/Project/CmakeLearn/lesson3_3/add/add.cpp
由此可得, 此处编译和汇编生成了.o文件
[100%] Linking CXX static library libadd_static.a
...
/usr/bin/ar qc libadd_static.a CMakeFiles/add_static.dir/add.cpp.o
/usr/bin/ranlib libadd_static.a
由此可得, 此处调用了ar命令, 生成了.a静态库文件
...再修改./lesson3_3/CMakeLists.txt内的内容, 观察静态库如何压进可执行文件的
# ./lesson3_3/CMakeLists.txt
include_directories(./lib)
link_libraries(/home/why/Project/CmakeLearn/lesson3_3/lib/libadd_static.a)
# 或者
target_link_libraries(lesson3_3 /home/why/Project/CmakeLearn/lesson3_3/lib/libadd_static.a)
add_executable(lesson3_3 ./main.cpp)进入./build/文件夹下运行cmake CMakeLists_dir和cmake --build ., 观察输出
...
[ 50%] Building CXX object lesson3_3/CMakeFiles/lesson3_3.dir/main.cpp.o
cd /home/why/Project/CmakeLearn/build/lesson3_3 && /usr/bin/c++ -I/home/why/Project/CmakeLearn/lesson3_3/./lib -MD -MT lesson3_3/CMakeFiles/lesson3_3.dir/main.cpp.o -MF CMakeFiles/lesson3_3.dir/main.cpp.o.d -o CMakeFiles/lesson3_3.dir/main.cpp.o -c /home/why/Project/CmakeLearn/lesson3_3/main.cpp
由此可得, 此处编译和汇编生成了.o文件
[100%] Linking CXX executable lesson3_3
...
/usr/bin/c++ CMakeFiles/lesson3_3.dir/main.cpp.o -o test3_3 /home/why/Project/CmakeLearn/lesson3_3/lib/libadd_static.a
生成可执行文件, 并压入静态库
...再修改./lesson3_3/CMakeLists.txt内的内容, 观察另外一种压入静态库方法
# ./lesson3_3/CMakeLists.txt
include_directories(./lib)
link_directories(/home/why/Project/CmakeLearn/lesson3_3/lib/)
target_link_libraries(lesson3_3 libadd_static.a)
add_executable(lesson3_3 ./main.cpp)进入./build/文件夹下运行cmake CMakeLists_dir和cmake --build ., 观察输出
...
[ 50%] Building CXX object lesson3_3/CMakeFiles/lesson3_3.dir/main.cpp.o
cd /home/why/Project/CmakeLearn/build/lesson3_3 && /usr/bin/c++ -I/home/why/Project/CmakeLearn/lesson3_3/./lib -MD -MT lesson3_3/CMakeFiles/lesson3_3.dir/main.cpp.o -MF CMakeFiles/lesson3_3.dir/main.cpp.o.d -o CMakeFiles/lesson3_3.dir/main.cpp.o -c /home/why/Project/CmakeLearn/lesson3_3/main.cpp
由此可得, 此处编译和汇编生成了.o文件
[100%] Linking CXX executable lesson3_3
...
/usr/bin/c++ CMakeFiles/test3_3.dir/main.cpp.o -o test3_3 -L/home/why/Project/CmakeLearn/test3_3/lib -Wl,-rpath,/home/why/Project/CmakeLearn/test3_3/lib -ltest2_1_add_static
-L指定链接库的包含路径
-l指定静态库???
-Wl...为动态库时候用, 可能可以删除?
...课程3_4
与课程2_2代码相同
首先修改./lesson3_4/CMakeLists.txt内的内容, 仅生成动态库
# ./lesson3_4/CMakeLists.txt
add_subdirectory(./add)进入./build/文件夹下运行cmake CMakeLists_dir和cmake --build ., 观察输出
...
[ 50%] Building CXX object lesson3_4/add/CMakeFiles/add_shared.dir/add.cpp.o
cd /home/why/Project/CmakeLearn/build/lesson3_4/add && /usr/bin/c++ -Dadd_shared_EXPORTS -fPIC -MD -MT lesson3_4/add/CMakeFiles/add_shared.dir/add.cpp.o -MF CMakeFiles/add_shared.dir/add.cpp.o.d -o CMakeFiles/add_shared.dir/add.cpp.o -c /home/why/Project/CmakeLearn/lesson3_4/add/add.cpp
-D加_EXPORTS 似乎是在编译汇编动态库
[100%] Linking CXX shared library libadd_shared.so
...
/usr/bin/c++ -fPIC -shared -Wl,-soname,libadd_shared.so -o libadd_shared.so CMakeFiles/d_shared.dir/add.cpp.o
-Wl是指定动态库相关东西
...修改./lesson3_4/CMakeLists.txt内的内容, 调用动态库
# ./lesson3_4/CMakeLists.txt
include_directories(./bin)
add_executable(lesson2_2 ./main.cpp)
target_link_libraries(lesson2_2 /home/why/Project/CmakeLearn/lesson2_2/bin/libadd_shared.so)...
[ 50%] Building CXX object lesson3_4/CMakeFiles/lesson2_2.dir/main.cpp.o
cd /home/why/Project/CmakeLearn/build/lesson3_4 && /usr/bin/c++ -I/home/why/Project/CmakeLearn/lesson3_4/./bin -MD -MT lesson3_4/CMakeFiles/lesson2_2.dir/main.cpp.o -MF CMakeFiles/lesson2_2.dir/main.cpp.o.d -o CMakeFiles/lesson2_2.dir/main.cpp.o -c /home/why/Project/CmakeLearn/lesson3_4/main.cpp
编译汇编生成.o文件
[100%] Linking CXX executable lesson2_2
...
/usr/bin/c++ CMakeFiles/lesson2_2.dir/main.cpp.o -o lesson2_2 -Wl,-rpath,/home/why/Project/CmakeLearn/lesson2_2/bin /home/why/Project/CmakeLearn/lesson2_2/bin/libadd_shared.so
生成可执行文件, 调用动态库
...# 查看生成的可执行文件的动态库依赖
ldd ./lesson3_2/lesson2_2
linux-vdso.so.1 (0x00007ffd8ed9f000)
libadd_shared.so => /home/why/Project/CmakeLearn/lesson2_2/bin/libadd_shared.so (0x00007f3f75f14000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3f75d16000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f3f75a00000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3f75f20000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f3f75c37000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f3f759e0000)