Makefile笔记整理
- 什么是Makefile?
makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率 - make的作用?
Makefile制定的规则,由make工具来执行,make就是工程管理工具:帮助我们实现项目的自动编译 - Makefile的规则
粗略地Makefile的规则:
Target…:prerequisites…
[TAB]command
target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label).
prerequisites就是,要生成那个target所需要的文件或是目标。
command也就是make需要执行的命令
#其中第一条规则中的目标,将会成为终极目标,其他目标是为终极目标服务的,因为最终目的,就是为了生成这个目标.
#其他规则之间,没有必然顺序联系
4,Makefile的命名? - 默认的情况下,make命令会在当前文件夹下按顺序找寻文件名称为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名称中,最好使用 “Makefile”这个文件名称,由于,这个文件名称第一个字符为大写,这样有一种显目的感 觉。最好不要用“GNUmakefile”,这个文件是GNU的make识别的。有另外一些make仅仅对全小写的“makefile”文件名称敏感,可是基本上来说,大多数的make都支持“makefile”和“Makefile”这两种默认文件名称
- 当想要自己指定makefile文件名时候,例如my_mkfile 执行命令make -f my_mkfile
- Makefile中赋值方式
= 延时变量,只有被使用时才展开定义 也就是说,变量的值将会是整个makefile中最后被指定的值,如:x=fool,y= ( x ) b a r , x = x y z , 这 个 例 子 中 , y 的 最 终 值 是 x y z b a r , 而 不 是 f o o l b a r : = 立 即 变 量 , 定 义 时 的 赋 值 立 即 有 效 变 量 的 值 决 定 于 它 在 m a k e f i l e 中 的 位 置 , 而 不 是 整 个 m a k e f i l e 展 开 后 的 最 终 值 , 如 x = f o o l , y = (x)bar,x=xyz,这个例子中,y的最终值是xyzbar,而不是foolbar := 立即变量,定义时的赋值立即有效 变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值,如x = fool,y= (x)bar,x=xyz,这个例子中,y的最终值是xyzbar,而不是foolbar:= 立即变量,定义时的赋值立即有效变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值,如x=fool,y=(x)bar,x := xyz,y的最终值是foolbar
?= 条件变量,当变量为空时才赋值
+= 追加赋值,类似字符串string - 引用一个实例讲解接下来的知识点
1.print.h
#include<stdio.h>
Void printhello(); - print.c
#include”print.h”
Void printhello()
{
Printf(“hello,world”);
} - main.c
#include”print.h”
Int main()
{
Printhello();
return 0;
}
编译这个程序:gcc main.c print.c
如果是大工程,这样就会很繁琐
6.最简单的Makefile
Helloworld:main.c print.c
gcc -o helloworld main.c print.c
有一个问题就是,假如我们更改了main.c 或者print.c其中一个文件,重新make的时候会导致所有的文件跟着一起更新,如果是大工程,就会耗费系统资源和时间,对于这些源文件,我们应该分别处理,执行:预处理 编译 汇编 ,先分别编译它们,最后再把它们链接在一起
7.引入.o文件
Helloworld:main.o print.o
gcc –o main.o print.o
main.o:main.c
gcc –c main.c
print.c:print.o
gcc –c print.c
8,引入标签clean
每个Makefile中都应该写一个清空目标文件(.o和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。这是一个“修养”。
clean的规则不要放在文件的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”。
Helloworld:main.o print.o
gcc –o main.o print.o
main.o:main.c
gcc –c main.c
print.c:print.o
gcc –c print.c
clean:
rm –rf main.o print.o helloworld
9.引入伪目标
执行make的时候会将clean误认为是目标文件,所以如果在同目录下有clean文件,makeclean就不会执行成功,所以指定一下clean标签
Helloworld:main.o print.o
gcc –o main.o print.o
main.o:main.c
gcc –c main.c
print.c:print.o
gcc –c print.c
.PHONY:clean
clean:
rm –rf main.o print.o helloworld
好了,一个成型的makefile到此完成
10,引入变量
变量名字可以包含字符、数字(可以用在开头)、下划线,但不可以是: # = 或空字符。 变量是大小写敏感的。
如果源文件过多,我们每次添加或删除一个不必要的源文件可能或造成不必要的纰漏
Object = main.o print.o
Target = helloworld
( T a r g e t ) : (Target): (Target):(Object)
gcc –o helloworld main.o print.o
main.o:main.c
gcc –c main.c
print.c:main.c
gcc –c print.c
.PHONY:clean
Clean:
rm –f $(Target) $(Object)
11,引入特殊变量
$@:当前规则中的目标
$$:当前执行的进程编号
$^:当前规则中的所有依赖
$*:模式规则中的所有%匹配的部分
$<:当前依赖中的第一个
$?:模式规则中所有比所在规则中的目标更新的文件组成列表
Object = main.o print.o
Target = helloworld
( T a r g e t ) : (Target): (Target):(Object)
gcc $^ –o $@
main.o:main.c
gcc –c $<
print.c:main.c
gcc –c $<
.PHONY:clean
Clean:
rm –f $(Target) $(Object)
12,引入通配符
这样的Makefile还不算完善,因为我们每假如新的文件都要去修改Makefile,如果一下子加1000个.c文件就会很是繁琐
*:表示所有文件的前缀
%:%为Makefile规则通配符,一般用于规则描述
-:发生错误时继续执行
@使命令在被执行前不被回显。比如我们不想c语言中的printf被打印出来吧
Object = *.o
Target = helloworld
( T a r g e t ) : (Target): (Target):(Object)
@echo start -----
gcc $^ –o $@
@echo end
%.c:%.o
gcc –c $<
.PHONY:clean
Clean:
-rm –f $(Target) $(Object)
13,引入函数
Wildcard:获取工作目录下的所有.c文件
Notdir:去除所有的目录信息,src = $(notdir wildcarcd),src中文件名列表将只有文件名
Patsubst:src =
(
p
a
t
s
u
b
s
t
(patsubst%.c ,%.o ,
(patsubst(SRC)),意思是找到所有的.c结尾文件然后替换成.o
SRC = $(wildcard *.c)
File = $(notdir $(SRC))
Object =
(
p
a
t
s
u
b
s
t
(patsubst %c,%.o,
(patsubst(File))
Target = helloworld
(
T
a
r
g
e
t
)
:
(Target):
(Target):(object)
Gcc $^ -o $@
%.o:%.c
Gcc –c $<
.PHONY:clean
Clean:
-rm $(Target) $(object)
14,隐含规则
make会按照这种“惯例”心照不喧地来运行,那怕我们的Makefile中没有书写这样的规则。例如,把[.c]文件编译成[.o]文件这一规则,你根本就不用写出来,make会自动推导出这种规则,并生成我们需要的[.o]文件。
SRC = $(wildcard *.c)
File = $(notdir $(SRC))
Object =
(
p
a
t
s
u
b
s
t
(patsubst %c,%.o,
(patsubst(File))
Target = helloworld
(
T
a
r
g
e
t
)
:
(Target):
(Target):(object)
Gcc $^ -o $@
.PHONY:clean
Clean:
-rm $(Target) $(object)
可以使用make -p 打印出make的所有隐含规则
15.引入.d文件
在一个工程中,因为头文件被源文件所包含,所以头文件的更新应该使得它所依赖的文件也被更新,
使用gcc -MM可以查看到源文件生成对应的.o文件需要依赖那些文件
于是我们在Makefile中引入.d文件,其实文件名称是自己定义,无所谓
%.d:%.c
gcc -MM $< > $@
我们将依赖关系重定向到.d文件中,这样就可以实现改变头文件会导致所依赖的源文件重新编译
16,多目录Makefile
一般管理代码会将代码放入不同目录下进行管理,方便维护
文件存放说明:
bin: 存放编译生成的二进制文件
src: 存放源文件 (add.c multis.c sub.c main.c)
obj: 存放编译生成的目标文件
include: 存放头文件 (add.h multis.h sub.h)
Makefile 文件和 bin、src、include处于同一级目录
Makefile的写法
CUR_DIR = $(shell pwd)
INC_DIR = $(CUR_DIR)/include
BIN_DIR = $(CUR_DIR)/bin
SRC_DIR = $(CUR_DIR)/src
OBJ_DIR = $(CUR_DIR)/obj
SRC = $(wildcard $(SRC_DIR)/*.c)
OBJ =
(
p
a
t
s
u
b
s
t
(patsubst %.c,
(patsubst(OBJ_DIR)/%.o,$(notdir $(SRC)))
TARGET = main
BIN_TARGET =
(
B
I
N
D
I
R
)
/
(BIN_DIR)/
(BINDIR)/(TARGET)
CC = gcc
CFLAGS = -g –Wall –I(INC_DIR)
(
B
I
N
T
A
R
G
E
T
)
:
(BIN_TARGET):
(BINTARGET):(OBJ)
$(CC) $(OBJ) –o $@
@echo “Compile done”
(
O
B
J
D
I
R
)
/
(OBJ_DIR)/%.o:
(OBJDIR)/(SRC_DIR)/%.c
@echo “Compiling $< ==>”
$(CC) $(CFLAGS) –c $< -o $@
.PHONY:clean
Clean:
@rm –f $(OBJ)
@echo “clean object files done”
@rm –f *~
@echo “clean tempreator files done”
@rm –f $(TARGET)
@echo “Clean target files done”
@echo “Clean done”
注释:
CFLAGS:指定头文件路径
LIBS:告诉连接器要链接那些库文件
LDFLAGS:gcc等编译器会用到的一些优化参数也可以在里面指定库文件的位置
Export:导出变量将被子make继承,有点类似C语言中的全局变量
CROSS_COMPILE:指的是交叉编译器(或前缀),这里是arm-linux(-gcc)。可以在Makefile中指定,也可以在
make clean仅仅是清除之前编译的可执行文件及配置文件。
make distclean要清除所有生成的文件
CXXFLAGS用于C++代码
AR:归档文件
AS:汇编器
LD:连接器
obj-y += foo.o 该例子告诉Kbuild在这目录里,有一个名为foo.o的目标文件。foo.o将从foo.c 或foo.S文件编译得到
"obj-y += subdir/"表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir目录下的Makefile决定。
strip 去掉字符串中多余的空格符(若干单词,使用若干空字符分割) “STR”开头和结尾的空字符,并将其中多个连续空字符合并为一个空字符。
arm-linux-objcopy 复制选项,支持格式转换
arm-linux-objdump 反编译选项
o 编译及链接,会生成一个exe可执行文件
-Wall 指定产生全部的警告信息
-O/-O2/-O3 数字越高,代表优化的更多,可以使生成的执行文件的提高执行效率
-c 编译不链接,会生成一个*.obj文件,若后面加了-o,则表示指定输出文件名称
-static 静态链接,生成的文件会非常大, 好处在于不需要动态链接库,也可以运行
-S 只激活预处理和编译,就是指把文件编译成为汇编代码
-g选项是指可以用gdb调试