预处理:gcc -E hello.c -o hello.i
1.展开宏定义(#define)
2.处理所有条件预编译指令(#if、#ifdef、#else、#else if、#endif)
3.处理#include预编译指令,将被包含的文件插入到该项预编译指令当中(递归执行)
4.删除所有的注释
5.保留所有#progrma编译器指令,因为编译器需要使用它们
编译:编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。
gcc -S hello.i -o hello.s
汇编:汇编器是将汇编代码转变成机器可以执行的代码
gcc -c hello.s -o hello.o
链接:生成可执行文件(地址和空间分配、符号决议、重定位)
ld -static /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.9/crtbeginT.o -L/usr/lib/gcc/x86_64-linux-gnu/4.9 -L/usr/lib -L/lib hello.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/x86_64-linux-gnu/4.9/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o
Makefile书写规则
targets : prerequisites
command \
command
...
一、文件搜索
-
VPATH = src:…/headers (当前目录、src、…/headers)
-
vpath %.h …/headers make在“…/headers”目录下搜索所有以 .h 结尾的文件。(如果某文件在当前目录没有找到的话)
二、伪目标
.PHONY : clean
clean :
rm *.o temp
三、静态模式
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
等价于下面
objects = foo.o bar.o
all : $(objects)
objects : %.o : %.c
$(CC) -c $(CFLAGS) %< -o %@
四、显示命令
-
@echo
-
-n 或 --just-print ,那么其只是显示命令,但不会执行命令
-
-s 或 --silent 或 --quiet 则是全面禁止命令的显示
五、命令执行
当前目录,而不是 /home/hchen目录
exec:
cd /home/hchen
pwd
/home/hchen目录
exec:
cd /home/hchen; pwd
- 用;分割
- -(在Tab键之后),标记为不管命令出不出错都认为是成功的
- -i 或是 --ignore-errors 参数,那么,Makefile中所有命令都会忽略错误
- -k 或是 --keep-going ,这个参数的意思是,如果某规则中的命令出错了,那么就终止该规则的执行,但继续执行其它规则
六、变量
定义变量:
- = 右侧中的变量不一定非要是已定义好的值,其也可以使用后面定义的值
- := 前面的变量不能使用后面的变量,只能使用前面已定义好了的变量
- FOO ?= bar,如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做
变量值的替换:
我们可以替换变量中的共有的部分,其格式是 $(var:a=b) 或是 ${var:a=b} ,其意思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。
foo := a.o b.o c.o
bar := $(foo:.o=.c)
这个示例中,我们先定义了一个 $(foo) 变量,而第二行的意思是把 $(foo) 中所有以 .o 字串“结尾”全部替换成 .c ,所以我们的 $(bar) 的值就是“a.c b.c c.c”。与下面静态模式的替换效果一样。
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
七、条件判断
ifeq、ifneq、ifdef、ifndef
八、函数
$( )
就是函数名, 为函数的参数,参数间以逗号 , 分隔,而函数名和参数之间以“空格”分隔。函数调用以 $ 开头,以圆括号或花括号把函数名和参数括起。
字符串处理函数:
字符串替换函数:
(
s
u
b
s
t
<
f
r
o
m
>
,
<
t
o
>
,
<
t
e
x
t
>
)
模
式
字
符
串
替
换
函
数
:
(subst <from>,<to>,<text>) 模式字符串替换函数:
(subst<from>,<to>,<text>)模式字符串替换函数:(patsubst ,,
(
p
a
t
s
u
b
s
t
去
空
格
函
数
:
(patsubst %.c,%.o,x.c.c bar.c) x.c.o bar.o 去空格函数:
(patsubst去空格函数: (strip )
去掉字符串开头和结尾的空格
查找字符串函数:
(
f
i
n
d
s
t
r
i
n
g
<
f
i
n
d
>
,
<
i
n
>
)
在
字
符
串
<
i
n
>
中
查
找
<
f
i
n
d
>
字
符
串
。
如
果
找
到
返
回
<
f
i
n
d
>
,
未
找
到
返
回
空
字
符
串
过
滤
函
数
:
(findstring <find>,<in>) 在字符串<in>中查找<find>字符串。如果找到返回<find>,未找到返回空字符串 过滤函数:
(findstring<find>,<in>) 在字符串<in>中查找<find>字符串。如果找到返回<find>,未找到返回空字符串过滤函数: (filter <pattern…>,
以模式过滤
反过滤函数:
(
f
i
l
t
e
r
−
o
u
t
<
p
a
t
t
e
r
n
.
.
.
>
,
<
t
e
x
t
>
)
去
除
符
合
<
p
a
t
t
e
r
n
>
模
式
的
单
词
,
可
以
有
多
个
<
p
a
t
t
e
r
n
>
模
式
排
序
函
数
:
(filter-out <pattern...>,<text>) 去除符合<pattern>模式的单词,可以有多个<pattern>模式 排序函数:
(filter−out<pattern...>,<text>) 去除符合<pattern>模式的单词,可以有多个<pattern>模式排序函数: (sort ) 升序
取单词函数:
(
w
o
r
d
<
n
>
,
<
t
e
x
t
>
)
取
<
t
e
x
t
>
第
n
个
单
词
(word <n>,<text>) 取<text>第n个单词
(word<n>,<text>)取<text>第n个单词 (wordlist ,,
单词个数统计函数:
(
w
o
r
d
s
<
t
e
x
t
>
)
首
单
词
函
数
:
(words <text>) 首单词函数:
(words<text>)首单词函数: (firstword
文件名操作函数:
取目录函数:
(
d
i
r
<
n
a
m
e
s
.
.
.
>
)
从
文
件
名
序
列
<
n
a
m
e
s
>
中
取
出
目
录
部
分
取
文
件
名
函
数
:
(dir <names...>) 从文件名序列 <names> 中取出目录部分 取文件名函数:
(dir<names...>) 从文件名序列<names>中取出目录部分取文件名函数: (notdir <names…>)
从文件名序列 中取出非目录部分
取后缀函数:
(
s
u
f
f
i
x
<
n
a
m
e
s
.
.
.
>
)
从
文
件
名
序
列
<
n
a
m
e
s
>
中
取
出
各
个
文
件
名
的
后
缀
部
分
取
前
缀
函
数
:
(suffix <names...>) 从文件名序列 <names> 中取出各个文件名的后缀部分 取前缀函数:
(suffix<names...>) 从文件名序列<names>中取出各个文件名的后缀部分取前缀函数: (basename <names…>)
从文件名序列 中取出各个文件名的前缀部分
加后缀函数:
(
a
d
d
s
u
f
f
i
x
<
s
u
f
f
i
x
>
,
<
n
a
m
e
s
.
.
.
>
)
把
后
缀
<
s
u
f
f
i
x
>
加
到
<
n
a
m
e
s
>
中
的
每
个
单
词
后
面
加
前
缀
函
数
:
(addsuffix <suffix>,<names...>) 把后缀 <suffix> 加到 <names> 中的每个单词后面 加前缀函数:
(addsuffix<suffix>,<names...>) 把后缀<suffix>加到<names>中的每个单词后面加前缀函数: (addprefix ,<names…>)
把前缀 加到 中的每个单词后面
连接函数: $(join ,)
把 中的单词对应地加到 的单词后面
origin函数:告诉变量从哪里来
shell函数
自动化变量
$@:目标文件名
$^:所有依赖的文件列表,使用空格分隔。如果目标文件是静态库文件,它所代表的只能是所有的库成员(.o文件)名。如果在依赖目标中有多个重复的,那么这个变量会去除重复的依赖目标,只保留一份。
$+:这个变量很像 $^ ,也是所有依赖目标的集合。只是它不去除重复的依赖目标
$<:第一个依赖的文件名
$?:所有比目标文件更新的依赖文件列表,空格分隔。如果目标文件是静态库文件,代表的是库文件
$%:当目标文件是一个静态库文件时,代表静态库的一个成员名
九、实例
作者:程序员良许
链接:https://www.zhihu.com/question/55488701/answer/600769671
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
VERSION = 1.0.0 #程序版本号
SOURCE = $(wildcard ./src/*.c) #获取所有的.c文件
OBJ = $(patsubst %.c, %.o, $(SOURCE)) #将.c文件转为.o文件
INCLUDES = -I./h #头文件路径
LIBS = -ldylib #库文件名字
LIB_PATH = -L./lib #库文件地址
DEBUG = -D_MACRO #宏定义
CFLAGS = -Wall -c #编译标志位
TARGET = app
CC = gcc
$(TARGET): $(OBJ)
@mkdir -p output/ #创建一个目录,用于存放已编译的目标
$(CC) $(OBJ) $(LIB_PATH) $(LIBS) -o output/$(TARGET).$(VERSION)
%.o: %.c
$(CC) $(INCLUDES) $(DEBUG) $(CFLAGS) $< -o $@
.PHONY: clean
clean:
rm -rf $(OBJ) output/