工具链
本文介绍了Git,GDB,Makefile,CMake四种C/C++编程中常用的工具链,还涉及一点GCC制作库文件的内容
工具链
Git
Git本地操作
版本控制系统分为2种
- 集中式版本控制系统 如SVN
- 分布式版本控制系统 如Git
相比SVN,Git可靠性高但操作复杂
1 | git init [dir]//在dir下创建新的本地仓库,缺省则在当前目录创建 |
git的数据管理分为三部分
- 工作区 .git所在目录,正常编辑代码的区域
- 暂存区 .git/index所在目录,即将提交到git的文件区域
- 本地仓库 .git/objectsgit所在目录 init生成的文件区域,是git存储代码和版本信息的主要位置
每次修改文件都需要提交很麻烦,因此产生了暂存区,当修改所有文件完毕后再统一提交至本地仓库
1 |
|
:::alert-danger
需要将文件提交到暂存区内才能使用 git commit命令
:::
1 |
|
使用 –hard 参数需要谨慎考虑,git reset默认执行 –soft命令
1 | /* 从工作区和暂存区中删除文件 */ |
diff后红色内容表示删除内容,绿色表示增加内容
Git远程操作
可以配置.gitignore来不想被版本控制的文件,只需将文件或文件夹添加到.gitignore即可,语法支持如上
1 |
|
::: alert-info
merge时需要切换到主分支
merge后分支依然存在,除非手动删除分支
:::
GitFlow
适用于团队水平适中的情况
五种分支: main hotfix release develop feature
main 包含项目最新的稳定代码
hotfix 用于解决线上问题,均从main分离出来
dev 用于开发测试
release 均来源于develop,测试稳定后发布到main,发现bug发布到dev
主要分支包括dev和main
GitHub Flow
适用于团队水平较高的情况
整个项目只有两个分支,开发者从Master中branch一个分支用于开发,需要进行merge时发起一个Pull Request请求,待团队审核后再merge到Master
GitHub
配置Github的SSH密钥时,最好把密钥统一放在.ssh文件夹下(win下是C:\Users\李政轩.ssh),并且写好config文件来管理多份密钥
Repo
repo是什么?
- repo是Google开发的用于管理Android版本库的一个工具,repo是使用Python对git进行了一定的封装,并不是用于取代git,它简化了对多个Git版本库的管理。用repo管理的版本库都需要使用git命令来进行操作。因此,使用repo工具之前,请先确保已经安装git
为什么要用repo?
- 项目模块化/组件化之后各模块也作为独立的 Git 仓库从主项目里剥离了出去,各模块各自管理自己的版本。Android源码引用了很多开源项目,每一个子项目都是一个Git仓库,每个Git仓库都有很多分支版本,为了方便统一管理各个子项目的Git仓库,需要一个上层工具批量进行处理,因此repo诞生。
repo用于Android项目管理
GCC
gcc(g++)
options
-o
-fexec-charset=GBK 指定运行时编码
-finput-charset=UTF-8 指定源文件编码
-Wall 输出警告信息
-O(0-3) 指定代码优化等级(0为不优化)
预处理阶段 (-E,得到.i文件)(巧记:ESC,iso)
- 处理#include预编译指令,将被包含的文件直接插入到预编译指令的位置
- 处理所有的条件预编译指令,比如#if,#else,#endif,#ifdef等
- 预处理器将所有的#define删除,并且展开所有的宏定义
- 删除所有的注释
- 添加行号和文件标识,以便编译错误时提供错误或警告的行号
- 保留所有#pragma编译器指令
1
gcc -E -o hello.i hello.c
编译阶段 (-S,调用cc1程序得到.s文件)
- 将预处理后的file.i文件进行语法词法分析,翻译成文本文件file.s,里面储存了各种汇编指令
1
gcc -S -o hello.s hello.i
- 将预处理后的file.i文件进行语法词法分析,翻译成文本文件file.s,里面储存了各种汇编指令
汇编阶段 (-c,调用ar程序得到.o文件)
- 根据编译文件中的汇编码得到二进制机器码文件,生成各个段,生成符号表
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
39gcc -c -o hello.o hello.s
```
* 链接阶段 (gcc file.o,调用collect2程序)
* 将链接文件链接到file.o文件中
* 静态链接:内存开销大,速度快。将所有需要的函数的二进制代码拷贝到可执行文件中去
* 动态链接:内存开销小,速度慢。不需要将所有需要的函数的二进制代码拷贝到可执行文件中去,而是记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统,操作系统将这些动态库加载到内存中,然后当程序运行到指定代码时,去共享执行内存中找到已经加载动态库可执行代码,最终达到运行时链接的目的
```shell
gcc -M hello.c 打印hello.c的依赖
gcc -M -MF hello.d hello.c 输出hello.c的依赖到hello.d文件内
gcc -c -o hello.o hello.c -MD -MF hello.d 编译hello.c并输出依赖到hello.d文件
```
linux-gcc默认最终的输出文件为elf类型,经过编译的.c文件是 .o文件(object,目标文件),链接后的为.elf文件,但同时也可以指定输出.out文件,这是一种老的可执行文件格式
# GDB
```shell
gcc -g //编译时记录调试信息,否则无法调试
gdb 可执行文件 //调试可执行文件
进入gdb后
回车默认重复上一条命令
r(run) 运行程序
quit 退出调试
list 查看源代码及行号
b(break) 行号/函数名 在行号或函数名打断点
info b 查看打断点的位置
delet b 删除断点
n(next) 逐步调试,但是不进入调用的函数内
s(step) 逐步调试,进入调用的函数内
p(print) 表达式 打印变量或地址
watch 表达式 监视表达式的值,一旦变化,调试将会停在对应的行
shell 终端命令 调用终端命令
- 根据编译文件中的汇编码得到二进制机器码文件,生成各个段,生成符号表
Makefile
- 如何判断哪个文件被更改了?
- 比较源文件与输出文件的时间,如果源文件更新的话,那就意味着源文件已经被修改了
1 | #makefile核心:规则 |
通配符:
*: 任意字符,但对于目标文件,优先使用%代替*
$:取值
@:目标值,@后加shell命令可以不显示命令但输出结果
$@:目标文件
$<:第一个依赖文件
$^:所有依赖文件
$(shell 命令)可以执行shell命令
假想目标文件:.PHONY 用于当目标文件与make命令重名时使用,用法:在makefile文件末尾加上.PHONY: 命令
A = xxx 变量赋值
$(A) 变量取值
= 延时变量,makefile分析完成整个文档后才会对变量赋值,没有写程序时的类似顺序执行的逻辑
:= 即时变量,类似写程序时的顺序执行的关系,需要在当前行之前声明定义变量
?= 在此行之前就已经定义变量时该语句无效,类似#ifndef
+= 附加,并不是加,是延时变量还是即时变量取决于前文
$(foreach var,list,text) 在list中的每一个var,都换为text
$(filter pattern,text) 在text中取出符合pattern的值
$(filter-out pattern,text) 在text中取出不符合pattern的值
$(wildcard pattern) 在已存在的文件中取出符合pattern的值
$(patsubst pattern,replacement,$(var)) 在var中取出符合pattern的值,并替换为replacement
CFLAGS 这个变量可以后加gcc的编译选项