├── 3.Makefile-Tutorial ├── Makefile ├── GNU-make.pdf ├── 9.Errors.md ├── 8.SharedLibrary.md ├── 2.Variable.md ├── 1.Intro.md ├── 3.Operator&Symbols.md ├── 7.StaticLibrary.md ├── 5.ConditionalStatement.md ├── 4.Functions.md └── 6.Compile.md ├── update.sh ├── 1.C的编译 ├── src │ ├── add.c │ ├── minus.c │ └── main.c ├── imgs │ └── filenamesuffixes1.png └── CompileC.md ├── 0.Intro ├── GNU-GCC.pdf ├── imgs │ ├── ASCII.png │ ├── hello_1.png │ ├── hello_2.png │ ├── hello_3.png │ └── compilesystem.png ├── 0.Intro.md ├── 4.执行过程.md ├── 3.内存布局.md ├── 2.编译过程.md └── 1.GNU&GCC.md ├── 2.C++的编译 ├── imgs │ └── fundamentalcpp.png ├── src │ └── helloworld.cpp └── CompileC++.md ├── .gitignore ├── README.md └── Summary.md /3.Makefile-Tutorial/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | git add . 2 | git commit -m "update" 3 | git push -------------------------------------------------------------------------------- /1.C的编译/src/add.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | int add(int a, int b) 4 | { 5 | return a+b; 6 | } -------------------------------------------------------------------------------- /1.C的编译/src/minus.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | int minus(int a, int b) 4 | { 5 | return a-b; 6 | } -------------------------------------------------------------------------------- /0.Intro/GNU-GCC.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WohimLee/GNC-Tutorial/HEAD/0.Intro/GNU-GCC.pdf -------------------------------------------------------------------------------- /0.Intro/imgs/ASCII.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WohimLee/GNC-Tutorial/HEAD/0.Intro/imgs/ASCII.png -------------------------------------------------------------------------------- /0.Intro/imgs/hello_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WohimLee/GNC-Tutorial/HEAD/0.Intro/imgs/hello_1.png -------------------------------------------------------------------------------- /0.Intro/imgs/hello_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WohimLee/GNC-Tutorial/HEAD/0.Intro/imgs/hello_2.png -------------------------------------------------------------------------------- /0.Intro/imgs/hello_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WohimLee/GNC-Tutorial/HEAD/0.Intro/imgs/hello_3.png -------------------------------------------------------------------------------- /0.Intro/imgs/compilesystem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WohimLee/GNC-Tutorial/HEAD/0.Intro/imgs/compilesystem.png -------------------------------------------------------------------------------- /1.C的编译/imgs/filenamesuffixes1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WohimLee/GNC-Tutorial/HEAD/1.C的编译/imgs/filenamesuffixes1.png -------------------------------------------------------------------------------- /2.C++的编译/imgs/fundamentalcpp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WohimLee/GNC-Tutorial/HEAD/2.C++的编译/imgs/fundamentalcpp.png -------------------------------------------------------------------------------- /3.Makefile-Tutorial/GNU-make.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WohimLee/GNC-Tutorial/HEAD/3.Makefile-Tutorial/GNU-make.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # files 2 | *.o 3 | *.a 4 | *.so 5 | exec 6 | .DS_Store 7 | 8 | # folders 9 | objs 10 | workspace 11 | lib 12 | Test 13 | -------------------------------------------------------------------------------- /2.C++的编译/src/helloworld.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | std::cout << "hello, world\n"; 7 | return (0); 8 | } -------------------------------------------------------------------------------- /3.Makefile-Tutorial/9.Errors.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 常见 Error 4 | >*** missing separator. Stop. 5 | - 原因: Makefile 语法出错 6 | - 解决方法: 根据报错的行数,检查 tab 缩进,空格问题 7 | 8 | 9 | >*** commands commence before first target. Stop 10 | - 原因: if等语句里面用了 tab 缩进 11 | - 解决方法: 缩进的地方全部改为空格 12 | 13 | -------------------------------------------------------------------------------- /1.C的编译/src/main.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | int add(int a, int b); 5 | int minus(int a, int b); 6 | 7 | int main() 8 | { 9 | int a = 10; 10 | int b = 5; 11 | int c; 12 | c = add(a, b); 13 | printf("a+b = %d\n", c); 14 | c = minus(a, b); 15 | printf("a_b = %d\n", c); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /3.Makefile-Tutorial/8.SharedLibrary.md: -------------------------------------------------------------------------------- 1 |   2 | # 动态库(共享库) 3 | 4 | ## 1 编库 5 | >编译 .c 文件 6 | > 源文件[.c/cpp] -> Object文件[.o] 7 | ```makefile 8 | g++ -c [.c/cpp][.c/cpp]... -o [.o][.o]... -I[.h/hpp] -g -fpic 9 | ``` 10 | > Object文件[.o] -> 动态库文件[lib库名.so] 11 | ``` 12 | g++ -shared [.o][.o]... -o [lib库名.so] 13 | ``` 14 | > main文件[.c/cpp] -> Object文件[.o] 15 | ``` 16 | g++ -c [main.c/cpp] -o [.o] -I[.h/hpp] -g 17 | ``` 18 | 19 |   20 | ## 2 链接 21 | > 链接 main 的 Object 文件与动态库文件[lib库名.so] 22 | ``` 23 | g++ [main.o] -o [可执行文件] -l[库名] -L[库路径] -Wl,-rpath=[库路径] 24 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## GNC-Tutorial 2 | 3 | - Linux 下编译与 Makefile 的教程 4 | - Bilibili 配套视频教程:[GNU Makefile编译C/C++教程(Linux系统、VSCODE)](https://www.bilibili.com/video/BV1EM41177s1/?vd_source=ead820d10887c21595d014f264bcbb35) 5 | - git clone 完求个 star 啊~~~~ 6 | 7 | ## Contents 8 | ### 0 Intro 9 | - GCC 的介绍 10 | - 编译系统 11 | ### 1 C 12 | C 程序的编译过程 13 | ### 2 C++ 14 | C++ 程序的编译过程 15 | 16 | ### 3 Makefile 17 | - Intro: 18 | - Variable: Makefile 中变量的写法 19 | - Operator&Symbols: 操作符和 Makefile 常用的符号 20 | - Functions: 常用函数 21 | - Conditional Statement: 条件语句 22 | - Compile: 编译过程与编译选项 23 | - Static Library: 静态库的编译与链接 24 | - Shared Library: 共享库/动态库的编译与链接 25 | - Errors: 常见的一些错误 26 | 27 | -------------------------------------------------------------------------------- /3.Makefile-Tutorial/2.Variable.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |   4 | # Makefile 的变量 5 | 变量在声明时需要给予初值,而在使用时,需要给在变量名前加上 `$` 符号,并用小括号 `()` 把变量给包括起来。 6 | 7 |   8 | ## 1 变量的定义 9 | 10 | ```makefile 11 | cpp := src/main.cpp 12 | obj := objs/main.o 13 | ``` 14 | ## 2 变量的引用 15 | - 可以用 `()` 或 `{}` 16 | ```makefile 17 | cpp := src/main.cpp 18 | obj := objs/main.o 19 | 20 | $(obj) : ${cpp} 21 | @g++ -c $(cpp) -o $(obj) 22 | 23 | compile : $(obj) 24 | ``` 25 | 26 |   27 | ## 3 预定义变量 28 | - `$@`: 目标(target)的完整名称 29 | - `$<`: 第一个依赖文件(prerequisties)的名称 30 | - `$^`: 所有的依赖文件(prerequisties),以空格分开,不包含重复的依赖文件 31 | ```make 32 | cpp := src/main.cpp 33 | obj := objs/main.o 34 | 35 | $(obj) : ${cpp} 36 | @g++ -c $< -o $@ 37 | @echo $^ 38 | 39 | compile : $(obj) 40 | .PHONY : compile 41 | ``` 42 | 43 |   44 | ## 4 -------------------------------------------------------------------------------- /0.Intro/0.Intro.md: -------------------------------------------------------------------------------- 1 | ## Intro 2 | ### 1 Linux 的起源 3 | 4 | - 时间:1991 年 5 | - 作者:芬兰的大学生 Linus Torvalds 6 | - 最初动机:Linus 想要一个能在 个人电脑(Intel 80386 CPU) 上运行的类 Unix 系统,因为当时的 MINIX(一个教学用 Unix 克隆系统)功能受限,不够自由、不够强大 7 | - 目标:写一个新的 内核(kernel),并不是整个操作系统。 8 | 9 | ✅ 结论:Linux 不是为了玩游戏 诞生的,而是因为 Linus 想要一个更强大、自由的 Unix 式操作系统内核。 10 | 11 | ### 2 GNU 12 | 13 | 在 Linux 内核出现之前,**GNU 项目(1983 年起)** 已经开发了大量用户空间工具: 14 | 15 | - 编译器:GCC 16 | - 调试器:GDB 17 | - 库:glibc 18 | - 工具链:Binutils 19 | - Shell(bash)、核心工具(ls, cp, rm, make 等) 20 | 21 | 唯独缺少一个“自由的内核”,GNU 当时的 Hurd 内核开发进展缓慢。 22 | 23 | 于是,Linus 的 Linux 内核 就完美补上了这个空缺。 24 | 这就是为什么我们今天说的系统叫做 GNU/Linux: 25 | 26 | - GNU 提供工具和编译环境 27 | - Linux 提供内核 28 | 29 | ### 3 GCC 与 Linux 的关系 30 | 31 | - 没有 GCC,Linux 内核根本编不出来 32 | - Linus 当时用的就是 GNU GCC 编译器 来编写和构建 Linux 内核 33 | - 最初的 Linux 代码甚至明确要求用 GCC 编译,否则无法通过 34 | - 直到今天,Linux 内核依然主要依赖 GCC(虽然现在也支持 Clang/LLVM) 35 | 36 | 👉 可以说,GCC 是 Linux 的“出生工具”,两者紧密相连。 -------------------------------------------------------------------------------- /3.Makefile-Tutorial/1.Intro.md: -------------------------------------------------------------------------------- 1 |   2 | # Makefile 3 | 4 | - GNU Make 官方网站:https://www.gnu.org/software/make/ 5 | - GNU Make 官方文档下载地址:https://www.gnu.org/software/make/manual/ 6 | - Makefile Tutorial:https://makefiletutorial.com/ 7 | 8 |   9 | ## 1 基本格式 10 | ```makefile 11 | targets : prerequisties 12 | [tab键]command 13 | ``` 14 | - target:目标文件,可以是 OjectFile,也可以是执行文件,还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。 15 | - prerequisite:要生成那个 target 所需要的文件或是目标。 16 | - command:是 make 需要执行的命令, 17 | 18 | 19 |   20 | ## 2 Makefile 规则 21 | - make 会在当前目录下找到一个名字叫 `Makefile` 或 `makefile` 的文件 22 | - 如果找到,它会找文件中第一个目标文件(target),并把这个文件作为最终的目标文件 23 | - 如果 target 文件不存在,或是 target 文件依赖的 .o 文件(prerequities)的文件修改时间要比 target 这个文件新,就会执行后面所定义的命令 command 来生成 target 这个文件 24 | - 如果 target 依赖的 .o 文件(prerequisties)也存在,make 会在当前文件中找到 target 为 .o 文件的依赖性,如果找到,再根据那个规则生成 .o 文件 25 | 26 |   27 | ## 3 伪目标 28 | 29 | 30 | 为了避免 target 和 Makefile 同级目录下 `文件/文件夹` 重名的这种情况,我们可以使用一个特殊的标记 `.PHONY` 来显式地指明一个目标是 "伪目标",向 make 说明,不管是否有这个文件/文件夹,这个目标就是 "伪目标" 31 | 32 | ```makefile 33 | .PHONY : clean 34 | ``` 35 | 36 | 只要有这个声明,不管是否有 "clean" 文件/文件夹,要运行 "clean" 这个目标,只有"make clean" 这个命令 37 | 38 | >注意 39 | - 对于有 prerequisties 的 target -------------------------------------------------------------------------------- /0.Intro/4.执行过程.md: -------------------------------------------------------------------------------- 1 | ## 执行过程 2 | #### 1 hello, world 的执行过程 3 | ##### 第一步:命令输入 4 | 5 | - `shell` 在终端上等待用户输入命令 6 | - 当我们在键盘上输入 `./hello`(注意:这是已经编译好的可执行文件名)时,字符会先进入内核的输入缓冲区 7 | - `shell` 程序从缓冲区逐个读取字符,并在自己的内存空间中保存这条命令字符串 8 | 9 |
10 | 11 |
12 | 13 | ##### 第二步:程序加载 14 | 15 | - 当我们按下回车键时,`shell` 知道命令输入结束 16 | - `shell` 调用 `execve 系统调用`,请求内核加载并运行 `hello` 程序。 17 | - 内核读取磁盘上的 `hello` 可执行文件,将其中的代码段(机器指令)、数据段和只读数据段(其中包含 `"hello, world\n"` 字符串)加载到进程的虚拟内存空间 18 | 19 |
20 | 21 |
22 | 23 | ##### 第三步:程序执行 24 | 25 | - 一旦加载完成,`CPU` 的指令寄存器跳转到 `main` 函数的入口,开始执行机器指令。 26 | - 在执行过程中,`printf` 函数会调用底层的 `write` 系统调用。 27 | - 内核把 `"hello, world\n"` 这段字符串从用户态缓冲区复制到内核缓冲区,再通过设备驱动写入显示设备。 28 | - 最终,我们在屏幕上看到输出结果 29 | 30 |
31 | 32 |
33 | 34 | #### 2 程序在计算机内的存储与传递 35 | 36 | 通过这个过程,我们可以看到:程序的运行本质上是 信息在不同存储层次之间不断传递: 37 | 38 | - hello 的机器指令最初存放在 **磁盘** 上 39 | - 加载时,这些指令被复制(或映射)到 主存 40 | - 当处理器运行程序时,指令又从主存被取入 **CPU 寄存器 **执行 41 | 42 | 同样,数据 `"hello, world\n"`: 43 | 44 | - 最初位于可执行文件的 只读数据段(在磁盘上) 45 | - 程序加载时被映射到 内存 46 | - 执行时通过系统调用复制到 内核缓冲区 47 | - 最终写入 显示设备,呈现在屏幕上 48 | 49 | 这说明了一个重要规律:计算机系统的工作就是不断地在 `磁盘—内存—寄存器—外设` 之间搬运和处理数据。 -------------------------------------------------------------------------------- /3.Makefile-Tutorial/3.Operator&Symbols.md: -------------------------------------------------------------------------------- 1 | 2 |   3 | # Makefile 常用符号 4 | ## *1 = 5 | - 简单的赋值运算符 6 | - 用于将右边的值分配给左边的变量 7 | - 如果在后面的语句中重新定义了该变量,则将使用新的值 8 | 9 | >示例 10 | ```make 11 | HOST_ARCH = aarch64 12 | TARGET_ARCH = $(HOST_ARCH) 13 | 14 | # 更改了变量 a 15 | HOST_ARCH = amd64 16 | 17 | debug: 18 | @echo $(TARGET_ARCH) 19 | ``` 20 | 21 | ## 2 := 22 | - 立即赋值运算符 23 | - 用于在定义变量时立即求值 24 | - 该值在定义后不再更改 25 | - 即使在后面的语句中重新定义了该变量 26 | 27 | >示例 28 | ```make 29 | HOST_ARCH := aarch64 30 | TARGET_ARCH := $(HOST_ARCH) 31 | 32 | # 更改了变量 a 33 | HOST_ARCH := amd64 34 | 35 | debug: 36 | @echo $(TARGET_ARCH) 37 | ``` 38 | 39 | 40 |   41 | ## *3 ?= 42 | - 默认赋值运算符 43 | - 如果该变量已经定义,则不进行任何操作 44 | - 如果该变量尚未定义,则求值并分配 45 | ```make 46 | HOST_ARCH = aarch64 47 | HOST_ARCH ?= amd64 48 | 49 | debug: 50 | @echo $(HOST_ARCH) 51 | ``` 52 | 53 |   54 | ## 4 累加 += 55 | ```makefile 56 | CXXFLAGS := -m64 -fPIC -g -O0 -std=c++11 -w -fopenmp 57 | 58 | CXXFLAGS += $(include_paths) 59 | ``` 60 | 61 |   62 | ## 5 \ 63 | - 续行符 64 | >示例 65 | ```make 66 | LDLIBS := cudart opencv_core \ 67 | gomp nvinfer protobuf cudnn pthread \ 68 | cublas nvcaffe_parser nvinfer_plugin 69 | ``` 70 | 71 |   72 | ## 6 * 与 % 73 | - `*`: 通配符表示匹配任意字符串,可以用在目录名或文件名中 74 | - `%`: 通配符表示匹配任意字符串,并将匹配到的字符串作为变量使用 -------------------------------------------------------------------------------- /3.Makefile-Tutorial/7.StaticLibrary.md: -------------------------------------------------------------------------------- 1 | 2 |   3 | # Makefile 静态库编译 4 | 5 | ## 1 程序 6 | >add.hpp 7 | ```c++ 8 | #ifndef ADD_HPP 9 | #define ADD_HPP 10 | int add(int a, int b); 11 | 12 | #endif // ADD_HPP 13 | ``` 14 | >add.cpp 15 | ```c++ 16 | int add(int a, int b) 17 | { 18 | return a+b; 19 | } 20 | ``` 21 | >minus.hpp 22 | ```c++ 23 | #ifndef MINUS_HPP 24 | #define MINUS_HPP 25 | int minus(int a, int b); 26 | 27 | #endif // MINUS_HPP 28 | ``` 29 | >minus.cpp 30 | ```c++ 31 | int minus(int a, int b) 32 | { 33 | return a-b; 34 | } 35 | ``` 36 | >main.cpp 37 | ```c++ 38 | #include 39 | #include "add.hpp" 40 | #include "minus.hpp" 41 | 42 | int main() 43 | { 44 | int a=10; int b=5; 45 | int res = add(a, b); 46 | printf("a + b = %d\n", res); 47 | res = minus(a, b); 48 | printf("a - b = %d\n", res); 49 | 50 | return 0; 51 | } 52 | ``` 53 | 54 |   55 | ## 2 编译过程 56 | - 源文件[.c/cpp] -> Object文件[.o] 57 | ``` 58 | g++ -c [.c/cpp][.c/cpp]... -o [.o][.o]... -I[.h/hpp] -g 59 | ``` 60 | - Object文件[.o] -> 静态库文件[lib库名.a] 61 | ``` 62 | ar -r [lib库名.a] [.o][.o]... 63 | ``` 64 | - main 文件[.c/cpp] -> Object 文件[.o] 65 | ``` 66 | g++ -c [main.c/cpp] -o [.o] -I[.h/hpp] 67 | ``` 68 | - 链接 main 的 Object 文件与静态库文件 [lib库名.a] 69 | ``` 70 | g++ [main.o] -o [可执行文件] -l[库名] -L[库路径] 71 | ``` 72 | -------------------------------------------------------------------------------- /3.Makefile-Tutorial/5.ConditionalStatement.md: -------------------------------------------------------------------------------- 1 |   2 | # Conditional Rules 3 | >注意: 4 | - Condition 语句里面全部不能用 Tab 缩进, 你看到的 Makefile 如果好像有 "Tab", 那全部是空格 5 | - 使用 Tab 会报错:*** commands commence before first target 6 | 7 | 8 | ## 1 ifeq / else / endif 9 | ```makefile 10 | # build flags 11 | ifeq ($(TARGET_OS),darwin) 12 | LDFLAGS += -rpath $(CUDA_PATH)/lib 13 | CCFLAGS += -arch $(HOST_ARCH) 14 | else ifeq ($(HOST_ARCH)-$(TARGET_ARCH)-$(TARGET_OS),x86_64-armv7l-linux) 15 | LDFLAGS += --dynamic-linker=/lib/ld-linux-armhf.so.3 16 | CCFLAGS += -mfloat-abi=hard 17 | else ifeq ($(TARGET_OS),android) 18 | LDFLAGS += -pie 19 | CCFLAGS += -fpie -fpic -fexceptions 20 | endif 21 | ``` 22 | 23 | 24 |   25 | ## 2 ifneq / else / endif 26 | ```makefile 27 | HOST_ARCH := $(shell uname -m) 28 | TARGET_ARCH ?= $(HOST_ARCH) 29 | temp := $(filter $(TARGET_ARCH),x86_64 aarch64 sbsa ppc64le armv7l) 30 | 31 | ifneq (,$(filter $(TARGET_ARCH),x86_64 aarch64 sbsa ppc64le armv7l)) 32 | ifneq ($(TARGET_ARCH),$(HOST_ARCH)) 33 | ifneq (,$(filter $(TARGET_ARCH),x86_64 aarch64 sbsa ppc64le)) 34 | TARGET_SIZE := 64 35 | else ifneq (,$(filter $(TARGET_ARCH),armv7l)) 36 | TARGET_SIZE := 32 37 | endif 38 | else 39 | TARGET_SIZE := $(shell getconf LONG_BIT) 40 | endif 41 | else 42 | $(error ERROR - unsupported value $(TARGET_ARCH) for TARGET_ARCH!) 43 | endif 44 | ``` 45 | 46 |   47 | ## 3 ifdef / else / endif 48 | 49 | ```make 50 | ifdef TARGET_OVERRIDE # cuda toolkit targets override 51 | NVCCFLAGS += -target-dir $(TARGET_OVERRIDE) 52 | endif 53 | ``` -------------------------------------------------------------------------------- /Summary.md: -------------------------------------------------------------------------------- 1 |   2 | # 总结 3 | 4 | 5 |   6 | # 1 基本介绍 7 | 8 | ## 1.1 Linux 9 | - 文件目录 10 | - 基本操作命令 11 | - vim 基本操作命令 12 | 13 |   14 | # 2 编译过程 15 | 16 | ## 2.1 C 语言编译 17 | - 预编译 18 | 19 | 把 .c源文件编译成 .i 预处理文件 20 | ``` 21 | gcc -E [源文件.c] -o [自定义名.i] 22 | ``` 23 | - 编译成汇编语言 24 | 25 | 把 .i 文件编译成 .s 汇编语言文件 26 | ``` 27 | gcc -S [源文件.c] 28 | ``` 29 | - 注意:隐藏了预编译、删除预处理i文件的过程 30 | 31 | - 编译成二进制 32 | 把 .s 编译成二进制.o 文件 33 | ``` 34 | gcc -c [源文件.c] -o [自定义文件名.o] [编译选项] 35 | ``` 36 | - 注意:隐藏了。。。 37 | 38 | - 链接成可执行文件 39 | 把 .o 文件,链接成可执行的二进制文件 40 | ``` 41 | gcc [.o] -o [自定义文件名] [链接选项] 42 | ``` 43 | 44 | ## 2.2 C++ 语言编译 45 | - 预编译 46 | 47 | 把 .c源文件编译成 .ii 预处理文件 48 | ``` 49 | gcc -E [源文件.c] -o [自定义名.ii] 50 | ``` 51 | - 编译成汇编语言 52 | 53 | 把 .i 文件编译成 .s 汇编语言文件 54 | ``` 55 | gcc -S [源文件.c] 56 | ``` 57 | - 注意:隐藏了预编译、删除预处理i文件的过程 58 | 59 | - 编译成二进制 60 | 把 .s 编译成二进制.o 文件 61 | ``` 62 | gcc -c [源文件.c] -o [自定义文件名.o] [编译选项] 63 | ``` 64 | - 注意:隐藏了。。。 65 | 66 | - 链接成可执行文件 67 | 把 .o 文件,链接成可执行的二进制文件 68 | ``` 69 | gcc [.o] -o [自定义文件名] [链接选项] 70 | ``` 71 |   72 | # 3 GCC 73 | 74 | 75 |   76 | # 4 Makefile 77 | 78 | ## 4.1 基础语法 79 | ``` 80 | target : prequisties 81 | @command 82 | ``` 83 | 84 | ## 4.2 变量使用 85 | 先赋初值,调用语法如下: 86 | ``` 87 | $(变量名) 88 | ``` 89 | 90 | ## 4.3 函数 91 | 92 | ### shell 93 | - 使用Linux命令 94 | 95 | ### patsubst 96 | - 按照一定的模式替换字符串 97 | 98 | ### subst 99 | - 直接替换字符串 100 | 101 | ### dir 102 | - 取父级目录 103 | 104 | ## 4.4 其它 105 | (1)增加编译选项的方法 106 | ``` 107 | include_paths := /datav/Lean/opencv4/include/opencv4 \ 108 | /datav/Lean/OpenBLAS/include 109 | 110 | I_option := $(include_paths:%=-I%) 111 | ``` 112 | (2)伪目标.PHONY 113 | 告诉 Make 在.PHONY 后面的都是伪目标(命令),不用生成文件,每次都会执行下面的 command(如果有 command 的话) 114 | 115 | -------------------------------------------------------------------------------- /0.Intro/3.内存布局.md: -------------------------------------------------------------------------------- 1 | 2 | ## 虚拟内存布局 3 | 4 | 当可执行程序被操作系统加载运行时,会被分配到一个 虚拟地址空间。在 `32 位`系统中,虚拟空间通常最大为 `4GB`;在 `64 位` 系统中则远大于此。虚拟空间通常被划分为以下区域: 5 | 6 | >Hello World 7 | ```cpp 8 | #include 9 | 10 | int g = 10; // 在 .data 段 11 | static int bssVal; // 在 .bss 段,值默认为 0 12 | 13 | int main() { 14 | int local = 5; // 在 栈 上 15 | char *msg = "Hello!"; // "Hello!" 在 .rodata 16 | printf("%s\n", msg); // 调用库函数,最终系统调用 write 17 | return 0; 18 | } 19 | ``` 20 | 21 | #### 1 主要分区 22 | - **代码段(Text Segment / Code Segment)** 23 | 存放程序的机器指令。一般为只读,避免运行时修改代码 24 | 例如:main 函数和 printf 函数的指令都在这里 25 | 26 | - **已初始化数据段(.data)** 27 | 存放已赋初值的全局变量和静态变量。 28 | ```cpp 29 | int g = 10; // 在 .data 段 30 | ``` 31 | 32 | - **未初始化数据段(.bss)** 33 | 存放未赋值的全局变量和静态变量,运行时由系统初始化为 0。 34 | ```cpp 35 | static int counter; // 在 .bss 段,值默认为 0 36 | ``` 37 | 38 | - **堆(Heap)** 39 | 存放动态分配的内存(malloc/new)。地址由低向高增长,需要程序员手动释放 40 | ```cpp 41 | int *p = malloc(sizeof(int) * 100); // 100 个整数在堆上 42 | ``` 43 | 44 | - **栈(Stack)** 45 | 存放局部变量、函数参数和返回地址。地址由高向低增长,由编译器自动管理。 46 | ```cpp 47 | void foo() { 48 | int a = 5; // 在栈上 49 | } 50 | ``` 51 | 52 | 栈同时记录函数调用关系(调用栈),保证函数执行完能正确返回。 53 | 54 | - **只读数据段(.rodata)** 55 | 存放常量字符串和常量数据。 56 | ```cpp 57 | char *msg = "Hello!"; // "Hello!" 在 .rodata 58 | ``` 59 | 60 | #### 2 典型虚拟内存布局(简化) 61 | ``` 62 | 高地址 63 | │ 64 | │ +--------------------+ 65 | │ | 栈 (Stack) | ← 局部变量、调用记录 66 | │ | 向下增长 ↓ | 67 | │ +--------------------+ 68 | │ | 空闲 | 69 | │ +--------------------+ 70 | │ | 堆 (Heap) | ← 动态分配内存 71 | │ | 向上增长 ↑ | 72 | │ +--------------------+ 73 | │ | 未初始化数据 (.bss) | 74 | │ +--------------------+ 75 | │ | 已初始化数据 (.data)| 76 | │ +--------------------+ 77 | │ | 只读数据 (.rodata) | 78 | │ +--------------------+ 79 | │ | 代码段 (Text) | ← 程序机器指令 80 | │ +--------------------+ 81 | │ 82 | 低地址 83 | ``` 84 | 85 | #### 3 Hello World 程序内存分布示例 86 | - `代码段`:main、printf 的机器指令 87 | - `.data 段`:全局变量 g 88 | - `.bss 段`:静态变量 bssVal 89 | - `堆`:若使用 malloc 分配的动态内存 90 | - `栈`:局部变量 local,函数调用信息 91 | - `.rodata`:字符串常量 "Hello!" 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /0.Intro/2.编译过程.md: -------------------------------------------------------------------------------- 1 | 2 | ## 编译原理 3 | ### 1 hello, world 在计算机的表示 4 | 一个 `hello` 程序的生命周期始于源程序(源文件),例如程序员通过编辑器创建并保存的文本文件 `hello.c`。源程序本质上是由一系列字符组成的文本。在存储到磁盘时,这些字符会按照某种编码方式(通常是 `ASCII`)转换为二进制的 `0` 和 `1 `序列。 5 | 6 | 在 `ASCII` 编码中,每个字符都对应一个唯一的整数值,并以 单字节形式存储。于是,`hello.c` 文件最终就是一串按字节组织的比特序列。 7 | 8 | 这种表示方式揭示了计算机系统中的一个核心思想:无论是磁盘文件、内存中的程序,还是用户数据与网络传输的数据,系统中的所有信息最终都是由比特序列表示的。 9 |
10 | 11 |
12 | 13 | 14 | 15 | ### 2 编译过程 16 | hello 程序的生命周期始于一个由程序员编写的 C 源代码文件(如 `hello.c`)。为了让计算机能够运行该程序,源代码必须经过一系列的翻译步骤,被转换成 **机器能够直接执行的指令** 17 | 18 | 具体来说,每条 `C` 语句会逐步转化为机器语言指令。这些指令及相关信息会按照一种标准的 **可执行目标文件格式**(如 `ELF` 在 Linux 上、`PE` 在 Windows 上)打包,并最终以二进制文件的形式存放在磁盘中 19 | 20 | 在 Linux 系统中,常用的 **GCC 编译器** 会完成这个翻译过程,它将 hello.c 转换为一个可执行文件 hello。这个过程分为四个阶段: 21 | 22 | - **预处理(Preprocessing)**: 处理 #include、#define 等指令,生成纯粹的 C 源代码 23 | - **编译(Compilation)**: 将 C 源代码翻译成汇编代码 24 | - **汇编(Assembly)**: 把汇编代码转换为机器指令,形成`目标文件(object file)` 25 | - **链接(Linking)**: 将 `目标文件` 与 `所需的库文件`(如标准库函数)整合,生成最终的 `可执行目标文件` 26 | 27 | 执行这四个阶段的工具程序(预处理器、编译器、汇编器和链接器)共同组成了 **编译系统(compilation system)** 28 | 29 | 30 | 31 |
32 | 33 | 34 | 35 | #### 2.1 预处理阶段 36 | 37 | **预处理器(cpp)** 会根据以 # 开头的指令修改原始 C 源程序。例如,在 hello.c 的第 1 行: 38 | ```cpp 39 | #include 40 | ``` 41 | 42 | 这条命令告诉预处理器将系统头文件 stdio.h 的内容插入到程序文本中。处理完成后,得到的结果仍然是一个 C 程序,通常扩展名为 .i。 43 | 44 | #### 2.2 编译阶段 45 | 46 | **编译器(cc1)** 将 `hello.i` 翻译成汇编代码文件 hello.s。该文件以文本形式描述了低级的汇编语言指令,例如: 47 | ```asm 48 | main: 49 | subq $8, %rsp 50 | movl $.LC0, %edi 51 | call puts 52 | movl $0, %eax 53 | addq $8, %rsp 54 | ret 55 | ``` 56 | 57 | 这里的每条汇编语句都对应一条或几条机器指令。汇编语言提供了一种对机器指令的可读表示,方便不同高级语言的编译器输出统一的目标。 58 | 59 | #### 2.3 汇编阶段 60 | 61 | **汇编器(as)** 将 `hello.s` 翻译成机器语言指令,并将这些指令及相关符号表打包成一种称为 可重定位目标文件(relocatable object file) 的格式,保存为二进制文件 `hello.o` 62 | 63 | - `hello.o` 文件包含 `main` 函数的机器指令编码,以及额外的元信息(如 ELF 文件头、符号表等) 64 | - 如果用文本编辑器打开 `hello.o`,会看到大量“乱码”,因为它是二进制格式而非文本 65 | 66 | #### 2.4 链接阶段 67 | 68 | 我们的 hello 程序调用了标准库函数 printf。这个函数并不在 hello.o 文件里,而是在 C 标准库(libc)中,通常以预编译好的目标文件集合存在(`静态库 .a` 或 `共享库 .so`) 69 | 70 | **链接器(ld)** 负责将 hello.o 与所需的库文件(如包含 printf 的 libc)进行合并,解析外部符号引用,最终生成 可执行目标文件 hello。 71 | 72 | 这个 hello 文件就是可以被操作系统加载到内存并运行的 可执行文件。 73 | -------------------------------------------------------------------------------- /0.Intro/1.GNU&GCC.md: -------------------------------------------------------------------------------- 1 | ## GNU 与 GCC 2 | 3 | 4 | ### 1 GNU 是什么? 5 | 6 | `GNU = "GNU's Not Unix"`(递归缩写) 7 | 8 | - **起源**:1983 年由 Richard Stallman 发起的自由软件计划,目标是创建一个 **完全自由的类 Unix 操作系统** 9 | - **核心思想**:用户应当拥有使用、研究、修改和分发软件的自由 10 | - **GNU 提供的基础工具**: 11 | - **GNU C 库(glibc)** → 提供系统调用接口和标准 C 函数库 12 | - **GNU 编译器套件(GCC)** 13 | - **GNU 调试器(GDB)** 14 | - **GNU Binutils**(汇编器、链接器、目标文件操作工具) 15 | 16 | - GNU 的内核 **Hurd** 并未成功取代 Linux 内核,但与 **Linux 内核**结合,形成了我们熟知的 **GNU/Linux 系统**(通常简称为 Linux) 17 | 18 | 19 | 20 | ### 2 GCC 是什么? 21 | 22 | - **官网**:[GCC Online Docs](https://gcc.gnu.org/onlinedocs/) 23 | - **定义**:`GCC = GNU Compiler Collection`(GNU 编译器套件) 24 | - **历史**:最初只是 `GNU C Compiler`,后来扩展支持了多种语言,所以改名为 **Collection** 25 | - **支持的语言**: 26 | - C 27 | - C++ 28 | - Objective-C 29 | - Fortran 30 | - Ada 31 | - Go 32 | - Rust(实验性支持) 33 | 34 | >作用 35 | - 把源代码(C/C++ 等)编译为 **汇编 → 机器码 → 可执行文件** 36 | - 支持跨平台、跨架构(ARM、x86、RISC-V 等) 37 | - 提供多级优化(`-O1` / `-O2` / `-O3` / `-Ofast` 等) 38 | 39 | >意义 40 | - GCC 是自由软件运动的基石 41 | - 几乎所有开源软件在某种层次上依赖 GCC 42 | - 许多语言(如 Python 解释器本身)也是由 C 编写并通过 GCC 编译的 43 | - 没有 GCC,就不会有如今的 Linux 生态和自由软件运动 44 | 45 | 46 | 47 | ### 3 GCC 的组成部分 48 | 49 | GCC 既包括核心编译器,也包括配套工具和库: 50 | 51 | ##### 编译器与前端 52 | - **gcc**:核心驱动程序,控制编译、汇编和链接过程 53 | - **g++**:GCC 的 C++ 前端,自动启用 C++ 语法并链接标准 C++ 库 54 | - **c++**:与 `g++` 等价的命令别名 55 | 56 | ##### 配置与构建工具 57 | - **configure**:GCC 源代码树中的配置脚本,用于生成适配目标平台的 Makefile 58 | - **make**:构建工具,读取 `Makefile`,按依赖关系自动编译与链接 59 | 60 | ##### 库 61 | - **libgcc**:编译器运行时支持库,包含基本例程(如整数除法、浮点运算),通常平台相关 62 | - **libstdc++**:C++ 标准运行时库,包含 STL 容器、算法、IOStream 等 63 | 64 | 65 | 66 | ### 4 GCC 相关的常见工具 67 | 68 | 这些工具大多来自 GNU Binutils 和 GNU 工具链: 69 | 70 | - **ar**:静态库管理工具,用于创建、修改、提取 `.a` 格式的目标库文件 71 | - **as**:GNU 汇编器,支持多平台 72 | - **ld**:GNU 链接器,把目标文件 `.o` 和库组合为可执行文件或共享库 73 | - **gdb**:GNU 调试器,用于程序运行时的调试和状态检查 74 | - **gprof**:性能分析工具,统计函数调用和执行时间,用于性能优化 75 | - **libtool**:简化共享库的使用和构建的脚本工具 76 | - **autoconf**:自动生成配置脚本,使软件能在不同类 Unix 平台编译 77 | - **GNATS**:GNU Bug Tracking System,用于跟踪 GCC 和其他 GNU 软件的问题 78 | 79 | 80 | 81 | ### 5 GCC 默认头文件搜索路径 82 | 83 | GCC 编译 C/C++ 程序时,会在一系列目录中搜索头文件, 可以通过命令查看: 84 | 85 | ```bash 86 | echo | gcc -v -x c -E - 87 | ``` 88 | 89 | 常见的默认搜索路径(不同版本、系统可能有所差异): 90 | 91 | - `/usr/lib/gcc/x86_64-linux-gnu/7/include` 92 | - `/usr/local/include` 93 | - `/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed` 94 | - `/usr/include/x86_64-linux-gnu` 95 | - `/usr/include` 96 | 97 | -------------------------------------------------------------------------------- /1.C的编译/CompileC.md: -------------------------------------------------------------------------------- 1 | 2 | ## 1 Fundamental Compiling 3 | >编译 C 语言相关的后缀 4 |
5 | 6 |
7 | 8 | ## 2 Compiling C 9 | ### 2.1 Preprocessing 10 | 11 | ```shell 12 | # 不会生成 .i 文件 13 | gcc -E main.c 14 | gcc -E main.c -o helloworld.i 15 | ``` 16 | - -E 选项告诉编译器只进行预处理操作 17 | - -o 选项把预处理的结果输出到指定文件 18 | 19 | 20 | ### 2.2 Generating Assembly Language 21 | ```shell 22 | gcc -S main.c 23 | gcc -S main.c -o xxx.s 24 | ``` 25 | - -S 选项告诉编译器,进行预处理和编译成汇编语言操作 26 | 27 | 每个平台对应的汇编语言的形式是不同的,例如有很多型号的开发板,有很多型号的 CPU 28 | 29 | 30 | ### 2.3 Source File to Object File 31 | ```shell 32 | gcc -c main.c 33 | gcc -c main.c -o xxx.o 34 | # 编译多个 .c 文件 35 | gcc -c main.c add.c minus.c 36 | ``` 37 | 38 | 39 | ### 2.4 Single Source to Executable 40 | - 注意:后面三个命令执行后并没有按编译过程出现 .i .s 或 .o 文件,并不意味着没有经历这些过程 41 | 42 | ```shell 43 | gcc main.c 44 | gcc main.c -o xxx 45 | ``` 46 | 47 | 执行程序 48 | ``` 49 | ./可执行文件 50 | ``` 51 | 52 | ## 2.5 Multiple Sources to Executable 53 | ``` 54 | gcc main.c add.c minus.c -o exec 55 | ./exec 56 | ``` 57 | 58 | 59 | 60 | ## 3 Creating a Static Library 61 | 62 | ##### 编译成 .o 的文件 63 | ```shell 64 | gcc -c [.c] -o [自定义文件名] 65 | gcc -c [.c] [.c] ... 66 | ``` 67 | ##### 编静态库 68 | ```shell 69 | ar -r [lib自定义库名.a] [.o] [.o] ... 70 | ``` 71 | ##### 链接成可执行文件 72 | ```shell 73 | gcc [.c] [.a] -o [自定义输出文件名] 74 | gcc [.c] -o [自定义输出文件名] -l[库名] -L[库所在路径] 75 | ``` 76 | 77 | 78 | 79 | ## 4 Creating a Shared Library 80 | 81 | >编译二进制.o文件 82 | ```shell 83 | gcc -c -fpic [.c/.cpp][.c/.cpp]... 84 | ``` 85 | >编库 86 | ```shell 87 | gcc -shared [.o][.o]... -o [lib自定义库名.so] 88 | ``` 89 | - 链接库到可执行文件 90 | ```shell 91 | gcc [.c/.cpp] -o [自定义可执行文件名] -l[库名] -L[库路径] -Wl,-rpath=[库路径] 92 | ``` 93 | 94 | 95 | ## 总结 96 | ### 1 编译过程 97 | 源文件.c文件 -> 预编译成.i文件 -> 编译成汇编语言.s -> 汇编成.o文件 -> 链接成可执行文件(名字自定义,后缀没关系) 98 | 99 | ### 2 编译过程命令 100 | - 预处理: 101 | ``` 102 | gcc -E [.c源文件] -o [自定义输出文件名.i] 103 | ``` 104 | - 编译成汇编语言(隐藏了预处理操作) : 105 | ``` 106 | gcc -S [.c源文件] 107 | ``` 108 | - 会变成.o的object文件(二进制文件,可用于链接) : 109 | ``` 110 | gcc -c [.c源文件] [.c源文件] [...] (可选选项:-o [自定文件名]) 111 | ``` 112 | ### 3 库 113 | >静态库 114 | - 编库(先转成.o文件,再编成lib[自定库名].a) 115 | ``` 116 | gcc -c [.c源文件] [.c源文件] [...] (可选选项:-o [自定文件名]) 117 | ``` 118 | ``` 119 | ar -r lib[自定库名].a [.o文件] [.o文件] [...] 120 | ``` 121 | - 链接 122 | ``` 123 | gcc [main文件] -o [自定义输出可执行文件名] -l[库名] -L[库的路径] 124 | ``` 125 | >动态库 126 | - 编库 127 | - 第一种做法, 先转成.o文件,再编成.so文件 128 | ```shell 129 | gcc -c -fpic [.c源文件] [.c源文件] [...] 130 | ``` 131 | ```shell 132 | gcc -shared [.o文件] [.o文件] [...] -o lib[库名].so 133 | ``` 134 | - 第二种做法,直接转成.so 135 | ```shell 136 | gcc -fpic -shared [.c源文件] [.c源文件] [...] -o lib[库名].so 137 | ``` 138 | - 链接 139 | 140 | ```shell 141 | gcc [main文件] -o [自定义输出可执行文件名] -l[库名] -L[库所在路径] -Wl,-rpath=[库所在路径] 142 | ``` 143 | -------------------------------------------------------------------------------- /2.C++的编译/CompileC++.md: -------------------------------------------------------------------------------- 1 |   2 | # 1 Fundamental Compiling 3 | >编译 C++ 语言相关的后缀 4 | 5 |
6 | 7 |
8 | 9 |   10 | # 2 Compiling C++ 11 | 12 | ## 2.1 Preprocessing 13 | 14 | ```shell 15 | g++ -E helloworld.c 16 | g++ -E helloworld.c -o helloworld.i 17 | ``` 18 | - -E 选项告诉编译器只进行预处理操作 19 | - -o 选项把预处理的结果输出到指定文件 20 | 21 | 22 | 23 |   24 | ## 2.2 Generating Assembly Language 25 | ```shell 26 | g++ -S helloworld.c 27 | g++ -S helloworld.c -o helloworld.s 28 | ``` 29 | - -S 选项告诉编译器,进行预处理和编译成汇编语言操作 30 | 31 | 每个平台对应的汇编语言的形式是不同的,例如有很多型号的开发板,有很多型号的 CPU 32 | 33 | 34 |   35 | ## 2.3 Source File to Object File 36 | ```shell 37 | g++ -c helloworld.c 38 | g++ -c helloworld.c -o harumph.o 39 | # 编译多个 .c 文件 40 | g++ -c helloworld.c helloworld1.c helloworld2.c 41 | ``` 42 | 43 | 44 |   45 | ## 2.4 Single Source to Executable 46 | - 注意:后面三个命令执行后并没有按编译过程出现 .i .s 或 .o 文件,并不意味着没有经历这些过程 47 | 48 | 49 | ```shell 50 | g++ helloworld.c 51 | g++ helloworld.c -o howdy 52 | ``` 53 | 54 | 执行程序 55 | ``` 56 | ./可执行文件 57 | ``` 58 | 59 |   60 | ## 2.5 Multiple Source to Executable 61 | 62 | ``` 63 | $ g++ hellomain.c sayhello.c -o hello 64 | ``` 65 | 66 | 67 |   68 | # 3 Creating a Static Library 69 | 70 | - 编译成 .o 的文件 71 | ```shell 72 | g++ -c [.c] -o [自定义文件名] 73 | g++ -c [.c] [.c] ... 74 | ``` 75 | - 编静态库 76 | ```shell 77 | ar -r [lib自定义库名.a] [.o] [.o] ... 78 | ``` 79 | - 链接成可执行文件 80 | ```shell 81 | g++ [.c] [.a] -o [自定义输出文件名] 82 | g++ [.c] -o [自定义输出文件名] -l[库名] -L[库所在路径] 83 | ``` 84 | 85 | 86 |   87 | # 4 Creating a Shared Library 88 | 89 | - 编译二进制.o文件 90 | ```shell 91 | g++ -c -fpic [.c/.cpp][.c/.cpp]... 92 | ``` 93 | - 编库 94 | ```shell 95 | g++ -shared [.o][.o]... -o [lib自定义库名.so] 96 | ``` 97 | - 连接动态库到可执行文件 98 | ```shell 99 | g++ [.c/.cpp] -o [自定义可执行文件名] -l[库名] -L[库路径] -Wl,-rpath=[库路径] 100 | ``` 101 | 102 |   103 | # 总结 104 | ## 1 编译过程 105 | 源文件.c文件 -> 预编译成.i文件 -> 编译成汇编语言.s -> 汇编成.o文件 -> 链接成可执行文件(名字自定义,后缀没关系) 106 | 107 | ## 2 编译过程命令 108 | - 预处理: 109 | ``` 110 | g++ -E [.c源文件] -o [自定义输出文件名.i] 111 | ``` 112 | - 编译成汇编语言(隐藏了预处理操作) : 113 | ``` 114 | g++ -S [.c源文件] 115 | ``` 116 | - 会变成.o的object文件(二进制文件,可用于链接) : 117 | ``` 118 | g++ -c [.c源文件] [.c源文件] [...] (可选选项:-o [自定文件名]) 119 | ``` 120 | ## 3 库 121 | >静态库 122 | - 编库(先转成.o文件,再编成lib[自定库名].a) 123 | ``` 124 | g++ -c [.c源文件] [.c源文件] [...] (可选选项:-o [自定文件名]) 125 | ``` 126 | ``` 127 | ar -r lib[自定库名].a [.o文件] [.o文件] [...] 128 | ``` 129 | - 链接 130 | ``` 131 | g++ [main文件] -o [自定义输出可执行文件名] -l[库名] -L[库的路径] 132 | ``` 133 | >动态库 134 | - 编库 135 | - 第一种做法, 先转成.o文件,再编成.so文件 136 | ```shell 137 | g++ -c -fpic [.c源文件] [.c源文件] [...] 138 | ``` 139 | ```shell 140 | g++ -shared [.o文件] [.o文件] [...] -o lib[库名].so 141 | ``` 142 | - 第二种做法,直接转成.so 143 | ```shell 144 | g++ -fpic -shared [.c源文件] [.c源文件] [...] -o lib[库名].so 145 | ``` 146 | - 链接 147 | 148 | ```shell 149 | g++ [main文件] -o [自定义输出可执行文件名] -l[库名] -L[库所在路径] -Wl,-rpath=[库所在路径] 150 | ``` 151 | -------------------------------------------------------------------------------- /3.Makefile-Tutorial/4.Functions.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |   4 | # Makefile 的常用函数 5 | 函数调用,很像变量的使用,也是以 “$” 来标识的,其语法如下: 6 | ```makefile 7 | $(fn, arguments) or ${fn, arguments} 8 | ``` 9 | - fn: 函数名 10 | - arguments: 函数参数,参数间以逗号 `,` 分隔,而函数名和参数之间以“空格”分隔 11 | 12 |   13 | ## 1 shell 14 | ```makefile 15 | $(shell ) 16 | ``` 17 | - 名称:shell 命令函数 —— shell 18 | - 功能:调用 shell 命令 command 19 | - 返回:函数返回 shell 命令 command 的执行结果 20 | 21 | 22 | >示例 23 | ```makefile 24 | # shell 指令,src 文件夹下找到 .cpp 文件 25 | cpp_srcs := $(shell find src -name "*.cpp") 26 | # shell 指令, 获取计算机架构 27 | HOST_ARCH := $(shell uname -m) 28 | ``` 29 | 30 |   31 | ## 2 subst 32 | ```makefile 33 | $(subst ,,) 34 | ``` 35 | - 名称:字符串替换函数——subst 36 | - 功能:把字串 \ 中的 \ 字符串替换成 \ 37 | - 返回:函数返回被替换过后的字符串 38 | >示例: 39 | ```makefile 40 | 41 | cpp_srcs := $(shell find src -name "*.cpp") 42 | cpp_objs := $(subst src/,objs/,$(cpp_objs)) 43 | 44 | ``` 45 | 46 |   47 | ## 3 patsubst 48 | ```makefile 49 | $(patsubst ,,) 50 | ``` 51 | - 名称:模式字符串替换函数 —— patsubst 52 | - 功能:通配符 `%`,表示任意长度的字串,从 text 中取出 patttern, 替换成 replacement 53 | - 返回:函数返回被替换过后的字符串 54 | >示例 55 | ```makefile 56 | cpp_srcs := $(shell find src -name "*.cpp") #shell指令,src文件夹下找到.cpp文件 57 | cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs)) #cpp_srcs变量下cpp文件替换成 .o文件 58 | ``` 59 | 60 | 61 | 62 |   63 | ## 4 foreach 64 | ```makefile 65 | $(foreach ,,) 66 | ``` 67 | - 名称:循环函数——foreach。 68 | - 功能:把字串\中的元素逐一取出来,执行\包含的表达式 69 | - 返回:\所返回的每个字符串所组成的整个字符串(以空格分隔) 70 | 71 | >示例: 72 | ```makefile 73 | library_paths := /datav/shared/100_du/03.08/lean/protobuf-3.11.4/lib \ 74 | /usr/local/cuda-10.1/lib64 75 | 76 | library_paths := $(foreach item,$(library_paths),-L$(item)) 77 | ``` 78 | >同等效果 79 | ```makefile 80 | I_flag := $(include_paths:%=-I%) 81 | ``` 82 | 83 |   84 | ## 5 dir 85 | ```makefile 86 | $(dir ) 87 | ``` 88 | - 名称:取目录函数——dir。 89 | - 功能:从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前 90 | 的部分。如果没有反斜杠,那么返回“./”。 91 | - 返回:返回文件名序列的目录部分。 92 | - 示例: 93 | ```makefile 94 | $(dir src/foo.c hacks) # 返回值是“src/ ./”。 95 | ``` 96 | 97 |   98 | ## 6 notdir 99 | ```makefile 100 | $(notdir ) 101 | ``` 102 | >示例 103 | ```makefile 104 | libs := $(notdir $(shell find /usr/lib -name lib*)) 105 | ``` 106 | 107 | 108 |   109 | ## 7 filter 110 | ```makefile 111 | $(filter ) 112 | ``` 113 | 114 | ```makefile 115 | libs := $(notdir $(shell find /usr/lib -name lib*)) 116 | a_libs := $(filter %.a,$(libs)) 117 | so_libs := $(filter %.so,$(libs)) 118 | ``` 119 | 120 |   121 | ## 8 basename 122 | ```makefile 123 | $(basename ) 124 | ``` 125 | 126 | ```makefile 127 | libs := $(notdir $(shell find /usr/lib -name lib*)) 128 | a_libs := $(subst lib,,$(basename $(filter %.a,$(libs)))) 129 | so_libs := $(subst lib,,$(basename $(filter %.so,$(libs)))) 130 | ``` 131 | 132 |   133 | ## 9 filter-out 134 | - 剔除不想要的字符串 135 | ```makefile 136 | objs := objs/add.o objs/minus.o objs/main.o 137 | cpp_objs := $(filter-out objs/main.o, $(objs)) 138 | ``` 139 | 140 |   141 | ## 10 wildcard 142 | - The wildcard function expands to a space-separated list of filenames that match the given patterns 143 | 144 | ```makefile 145 | cpp_srcs := $(wildcard src/*.cc src/*.cpp src/*.c) 146 | ``` 147 | 148 | 149 |   150 | ## Examples 151 | 152 | >boost.mk 153 | ```makefile 154 | ROOT := /usr 155 | 156 | sys_INCLUDE := $(ROOT)/include 157 | sys_LIB_DIR := /usr/lib/x86_64-linux-gnu 158 | full_paths := $(shell find $(sys_LIB_DIR) -name "libboost_*") 159 | sys_LIBS = $(filter %.a %.so, $(full_paths)) 160 | # sys_LIBS := $(wildcard $(sys_LIBS)*.a $(sys_LIBS)*.so) 161 | sys_LIBS := $(basename $(notdir $(sys_LIBS))) 162 | sys_LIBS := $(patsubst lib%,%,$(sys_LIBS)) 163 | ``` -------------------------------------------------------------------------------- /3.Makefile-Tutorial/6.Compile.md: -------------------------------------------------------------------------------- 1 |   2 | # Compile 3 | 4 | # 1 编译过程 5 | ## 1.1 预处理 6 | 7 | >示例 8 | ```makefile 9 | cpp_srcs := $(shell find src -name *.cpp) 10 | pp_files := $(patsubst src/%.cpp,src/%.i,$(cpp_srcs)) 11 | 12 | src/%.i : src/%.cpp 13 | @g++ -E $^ -o $@ 14 | 15 | preprocess : $(pp_files) 16 | 17 | clean : 18 | @rm -f src/*.i 19 | 20 | debug : 21 | @echo $(pp_files) 22 | 23 | .PHONY : debug preprocess clean 24 | ``` 25 | 26 |   27 | ## 1.2 编译成汇编语言 28 | >示例 29 | ```makefile 30 | cpp_srcs := $(shell find src -name *.cpp) 31 | as_files := $(patsubst src/%.cpp,src/%.s,$(cpp_srcs)) 32 | 33 | src/%.s : src/%.cpp 34 | @g++ -S $^ -o $@ 35 | 36 | assemble : $(as_files) 37 | 38 | clean : 39 | @rm -f src/*.s 40 | 41 | debug : 42 | @echo $(as_files) 43 | 44 | .PHONY : debug assemble clean 45 | ``` 46 | 47 |   48 | ## 1.3 编译成目标文件 49 | >示例 50 | ```makefile 51 | cpp_srcs := $(shell find src -name *.cpp) 52 | cpp_objs := $(patsubst src/%.cpp,objs/%.o,$(cpp_srcs)) 53 | 54 | objs/%.o : src/%.cpp 55 | @mkdir -p $(dir $@) 56 | @g++ -c $^ -o $@ 57 | 58 | objects : $(cpp_objs) 59 | 60 | clean : 61 | @rm -rf objs src/*.o 62 | 63 | debug : 64 | @echo $(as_files) 65 | 66 | .PHONY : debug objects clean 67 | ``` 68 | 69 |   70 | ## 1.4 链接可执行文件 71 | ```makefile 72 | cpp_srcs := $(shell find src -name *.cpp) 73 | cpp_objs := $(patsubst src/%.cpp,objs/%.o,$(cpp_srcs)) 74 | 75 | 76 | objs/%.o : src/%.cpp 77 | @mkdir -p $(dir $@) 78 | @g++ -c $^ -o $@ 79 | 80 | workspace/exec : $(cpp_objs) 81 | @mkdir workspace/exec 82 | @g++ $^ -o $@ 83 | 84 | run : workspace 85 | @./$< 86 | 87 | clean : 88 | @rm -rf objs workspace/exec 89 | 90 | debug : 91 | @echo $(as_files) 92 | 93 | .PHONY : debug run clean 94 | ``` 95 | 96 | 97 |   98 | # 2 编译选项 99 | >编译选项 100 | - `-m64`: 指定编译为 64 位应用程序 101 | - `-std=`: 指定编译标准,例如:-std=c++11、-std=c++14 102 | - `-g`: 包含调试信息 103 | - `-w`: 不显示警告 104 | - `-O`: 优化等级,通常使用:-O3 105 | - `-I`: 加在头文件路径前 106 | - `fPIC`: (Position-Independent Code), 产生的没有绝对地址,全部使用相对地址,代码可以被加载到内存的任意位置,且可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的 107 | 108 | 109 | >链接选项 110 | - `-l`: 加在库名前面 111 | - `-L`: 加在库路径前面 112 | - `-Wl,<选项>`: 将逗号分隔的 <选项> 传递给链接器 113 | - `-rpath=`: "运行" 的时候,去找的目录。运行的时候,要找 .so 文件,会从这个选项里指定的地方去找 114 | 115 | 116 | 117 |   118 | # 3 Implicit Rules 119 | - CC: Program for compiling C programs; default cc 120 | - CXX: Program for compiling C++ programs; default g++ 121 | - CFLAGS: Extra flags to give to the C compiler 122 | - CXXFLAGS: Extra flags to give to the C++ compiler 123 | - CPPFLAGS: Extra flags to give to the C preprocessor 124 | - LDFLAGS: Extra flags to give to compilers when they are supposed to invoke the linker 125 | 126 | 127 | 128 | 129 |   130 | # 4 编译带头文件的程序 131 | >add.hpp 132 | ```c++ 133 | #ifndef ADD_HPP 134 | #define ADD_HPP 135 | int add(int a, int b); 136 | 137 | #endif // ADD_HPP 138 | ``` 139 | >add.cpp 140 | ```c++ 141 | int add(int a, int b) 142 | { 143 | return a+b; 144 | } 145 | ``` 146 | >minus.hpp 147 | ```c++ 148 | #ifndef MINUS_HPP 149 | #define MINUS_HPP 150 | int minus(int a, int b); 151 | 152 | #endif // MINUS_HPP 153 | ``` 154 | >minus.cpp 155 | ```c++ 156 | int minus(int a, int b) 157 | { 158 | return a-b; 159 | } 160 | ``` 161 | >main.cpp 162 | ```c++ 163 | #include 164 | #include "add.hpp" 165 | #include "minus.hpp" 166 | 167 | int main() 168 | { 169 | int a=10; int b=5; 170 | int res = add(a, b); 171 | printf("a + b = %d\n", res); 172 | res = minus(a, b); 173 | printf("a - b = %d\n", res); 174 | 175 | return 0; 176 | } 177 | ``` 178 | 179 | >Makefile 180 | ```makefile 181 | cpp_srcs := $(shell find src -name *.cpp) 182 | cpp_objs := $(patsubst src/%.cpp,objs/%.o,$(cpp_srcs)) 183 | 184 | # 你的头文件所在文件夹路径(建议绝对路径) 185 | include_paths := 186 | I_flag := $(include_paths:%=-I%) 187 | 188 | 189 | objs/%.o : src/%.cpp 190 | @mkdir -p $(dir $@) 191 | @g++ -c $^ -o $@ $(I_flag) 192 | 193 | workspace/exec : $(cpp_objs) 194 | @mkdir -p $(dir $@) 195 | @g++ $^ -o $@ 196 | 197 | run : workspace/exec 198 | @./$< 199 | 200 | debug : 201 | @echo $(I_flag) 202 | 203 | clean : 204 | @rm -rf objs 205 | 206 | .PHONY : debug run 207 | ``` 208 | --------------------------------------------------------------------------------