序号 | 内容 | 链接 |
---|---|---|
1 | 多进程 | 点我访问 |
2 | 进程间通信 | 点我访问 |
3 | 多线程 | 点我访问 |
4 | 网络编程 | 点我访问 |
5 | shell | 点我访问 |
6 | Makefile | 点我访问 |
7 | 串口通信 | 点我访问 |
8 | I2C通信 | 点我访问 |
1 Makefile
1.1 什么是Makefile
Makefile叫做工程管理器;
它的作用是用来管理多数的文件的编译关系的,例如相互依赖关系…
总之一句话,它是用来帮我们自动编译代码的,然后呢它会自动去按照我们的指示搜索库和头文件然后根据我们自定义的规则编译出执行文件或者其他东东;
有同学说,编译代码不久一个gcc hello.c -o hello 就完了吗,为什么还要Makefile呢?
(1) 因为假如代码用到的库非常多,达到上百个,假如这样你还一个一个敲就非常的麻烦;
(2) 我突然加了几个新功能,多加了几个.c和.h,假如手敲又要重新加进去;
(3) 假如客户忽然又不要这个功能了,又删了几个.c和.h,这样手敲就很麻烦了;
所以Makefile最大的特点是可以自动化处理;
Makefile在Linux下是用途是非常广的,不管是uboot源码,或者Linux源码,或者其他的第三方工具,都是使用Makefile进行编译到;
Makefile的文件名必须是Makefile,不能是aaa,bbb之类的东西;
而且Makefile的M一定要大写,不要小写,因为有些时候会出问题;
Makefile一般和源码放在同一目录;
外部知识:
argc是外部参数的意思,最小是1;
argv是外部传进来的参数,类型是char *;
注意: 不要在windows下写Makefile,因为windows的tab键的机制和linux不一样;
1.2 语法介绍
Makefile是由一个一个规则组成的,所谓的规则的格式如下:
目标:依赖
<Tab>命令1 <Tab>命令2
注意: tab是命令前必须要加的东西,否则会出问题;
目标: 规则的目标,就是你想要用这个规则编译出什么东西;
依赖: 编译出这个目标依赖哪些东西;
命令n: 执行的命令,自定义;
例子:
明确目标:我们的目标是编译出执行文件hello
目标的依赖: hello.c
命令: 我们怎么样用hello.c编译出hello?
gcc hello.c -o hello
如何执行Makefile?
make
#你在输入make的时候,系统会自动在你当前的目录搜寻Makefile,然后根据它里边的规则进行编译东西;
#必须在有Makefile的目录下make,不能在任意目录make
Makefile它有一个时间检测机制,假如hello的依赖文件hello.c自从上次编译之后没有进行过更改,那么假如你重新make一次,它就不会重新编译,这样的好处是提高编译的效率;
假如我就是想让他从新编译,怎么办?
(1) 删除最终的目标;
(2) 改依赖文件;
在Makefile中,标准情况下都会有一个clean选项,作用是清除编译产物;
clean:
rm -rf hello
调用clean选项的方法:
make clean
hello:hello.c
gcc hello.c -o hello
xixixi:
echo xixixi is call
hahaha:
echo hahaha is call
clean:
rm -rf hello
调用xixixi的方法:
make xixixi
调用hahaha的方法:
make hahaha
指针数组和数组指针有什么区别;
牛奶 -> 首先它是奶,它是牛的奶;
奶牛 -> 首先它是牛,是产奶的牛;
指针数组 -> 数组 -> 装的是指针;
数组指针 -> 指针 -> 指向的是一个数组;
小总结:
(1) 一个Makefile只有一个最终规则,哪个规则写在Makefile的最前面,哪个就是最终规则
(2) Makefile在寻找依赖的时候,它会自动去搜寻本文件里的所有规则看是否有对应的规则是处理自己的依赖的;
2 高级Makefile
之前说的只是让大家了解一下Makefile的基本语法,在实际写Makefile的时候我们不会这样写的,当源码比较多的时候,我们就会用变量进行编写,达到事半功倍的效果;
变量有如下类型:
(1) 用户自定义变量;
(2) 自动变量;
(3) 预定义变量;
2.1 用户自定义变量
这个是用户自己定一个变量,值可以是任意值,名字也任意;
注意,变量的值的类型是字符串,当它被改变的时候,现实对应的文件不会;
格式:
定义变量: SRC=main.c
引用变量: $(SRC)
叠加变量: SRC+=libballoon.a
举例:
SRC=main.c libballoon.a
ELF=main
$(ELF):$(SRC)
gcc $(SRC) -o main
libballoon.a:balloon.o
ar -r libballoon.a balloon.o
balloon.o:balloon.c
gcc -c balloon.c -o balloon.o
clean:
rm -rf balloon.o libballoon.a main
2.2 自动变量
自动变量的值会根据它所在的规则的目标和依赖进行自动变化;
常用的自动变量:
变量 | 说明 |
---|---|
$@ | 当前规则的目标 |
$< | 当前规则的第一个依赖文件 |
$^ | 当前规则的所有依赖文件,以逗号分隔 |
$? | 规则中日期新于目标文件的所有相关文件列表,逗号分隔 |
$(@D) | 目标文件的目录名部分,若 @ 为 ” s u m / a d d . c , 则 @为”sum/add.c,则 @为”sum/add.c,则(@D)为sum |
$(@F) | 目标文件的文件名部分,若 @ 为 ” s u m / a d d . c , 则 @为”sum/add.c,则 @为”sum/add.c,则(@F)为add.c |
2.3 预定义变量
预定义变量是系统事先定义好的变量;
它们有些有一个默认值,当然,你也可以修改它;
变量 | 描述 |
---|---|
AS | 汇编程序,默认为 as |
CC | c 编译器默认为gcc |
CPP | c 预编译器,默认为$(CC) -E |
CXX | c++编译器,默认为 g++ |
RM | 删除,默认为 rm -f |
ARFLAGS | 库选项,无默认 |
ASFLAGS | 汇编选项,无默认 |
CFLAGS | c 编译器选项,无默认 |
CPPFLAGS | c 预编译器选项,无默认 |
CXXFLAGS | c++编译器选项,无默认 |
3 Makefile规则
之前我们编译代码的方法是直接把.c文件编译成执行文件:
但是在大型的项目代码中,也就是代码非常多的时候,例如Linux内核源码,或者uboot的源码,它并不是这样子直接简单编译的,它们的方法是:
当你源文件非常少的时候,没必要像上述一样编译;
3.1 隐式规则
隐式规则的意思是Makefile默认有这个规则;
例如:
.o文件会自动去找相对应名字的.c或者.cc文件,然后自动把它们编译成.o文件,就算我们没写,它也会自动去做;
3.2 模式规则
有些时候,某些文件的后缀都是一样的,但是不一样,我们相用一些特殊的符号来代表这一类后缀的文件,我们就可以使用模式规则,也就是我们的%号,%的在Makefile的意思是通配符的意思,它可以代表一个或者多个字符;
例如:
%.c -> 所有的.c文件,只要你是.c结尾的,我不管你前面是多少个字符,都会被他匹配到
%.cpp -> 所有的.cpp结尾的文件
在这里,我们假装没学过隐式规则;
SRC=func.o main.o
ELF=main
$(ELF):$(SRC)
$(CC) $^ -o $@
#这里的意思是所有的.o都依赖所有的.c,会自动编译成相应的.o
#当然,这句话和隐式规则是一样的,但是有些时候我们想指定是哪个.o,隐式规则就不适用的,日后你们会懂的
%.o:%.c
$(CC) -c -o $@ $^
4 Makefile函数
在Makefile中,它给我们提供了一些方便的函数,可以协助我们进行设计Makefile;
4.1 wildcard函数
功能:
自动搜索某种后缀结尾的文件,然后返回到用户的自定义变量中;
语法:
wildcard *.后缀
例子:
假设当前目录有: 1.c 2.cpp 3.S 4.c 5.bmp 6.png 7.jpg
执行了: SRC=$(wildcard *.c)
SRC的值 -> 1.c 4.c
执行了: SRC=$(wildcard *.bmp)
SRC的值 -> 5.bmp
4.2 patsubst函数
它的作用是指定字符串替换;
注意,它的作用仅仅是文本替换,并不会改变现实文件的东西;
例如main.c被改成了main.o,实际中的main.c是没有被改动的,也没有main.o生成,因为patsubst只是简单的字符串替换;
$(patsubst 要替换的字符串, 替换的字符串, 源字符串)
例如: SRC=main.c func.c
OBJ=$(patsubst %.c, %.o, $(SRC))
把SRC字符串里的所有.c替换成.o,结果是:
OBJ=main.o func.o