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)

Windows下使用VSCode的MSCV编译动态库

如果是Windows下的VisualStudioCode的MSCV编译动态库,
最终会同时生成.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++编译生成可执行文件流程

  1. 预处理: 头文件展开 宏替换 去除注释

    # 预处理main.cpp
    g++ -E main.cpp -o main.i
    # 预处理add.cpp
    g++ -E add.cpp -o add.i
  2. 编译: 编译器对文件进行汇编

    # 编译main.i
    g++ -S main.i -o main.s
    # 编译add.i
    g++ -S add.i -o add.s
  3. 汇编: 使用汇编器对文件进行汇编, 产生机器码

    # 汇编main.i
    g++ -c main.s -o main.o
    # 汇编add.i
    g++ -c add.s -o add.o
  4. 链接: 调用链接器对程序需要库进行链接

    # 链接main.i
    g++ main.o add.o -o lesson3_1
  5. 一步到位

    # 直接将多个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)

课程4_1

最后修改:2025 年 02 月 26 日
赛博讨口子