makefile
首先,如何用makefile执行一个我们熟悉的语句
gcc main.c -o main
目标项:依赖项
main: main.c
gcc main.c -o main
那么,如果不止一个c文件呢
main: main.o hello.o gcc main.o hello.o -o main main.o: main.c gcc -c main.c -o main.o hello.o: hello.c gcc -c hello.c -o hello.o
如果我有很多个源文件呢?
这样显然比较复杂,也不利于日后的修改,因为一旦我改了名字,或者删掉了某个c文件,那么整个makefile就要重写
所以,我们用变量替代main.c hello.c等文件名
下面介绍几种变量
变量 |
说明 |
$@ |
当前规则的目标文件 |
$< |
当前规则的第一个依赖文件 |
$^ |
当前规则的所有依赖文件,以逗号分隔 |
$? |
规则中日期新于目标文件的所有相关文件列表,逗号分隔 |
$(@D) |
目标文件的目录名部分 |
$(@F) |
目标文件的文件名部分 |
使用一下 $@ 和 $<
和上述代码相同,用$@ 和 $< 替代main main.c
main: main.o hello.o gcc $^ -o $@ main.o: main.c gcc -c $< -o main.o hello.o: hello.c gcc -c $< -o hello.o
看起来简洁了很多,但是,仍然不能解决我们前面的问题——当有多个源文件需要编译,甚至还不确定是否都有需要的时候,怎么办?
这里,我们可以将main.c hello.c iic.c adc.c等等等赋值给一个变量
比如这样
EXE := main SRC := main.c hello.c INC := hello.h main.h OBJ := main.o hello.o main: main.o hello.o gcc $^ -o $@ main.o: main.c gcc -c $< -o main.o hello.o: hello.c gcc -c $< -o hello.o
这里解释一下赋值号的问题
有这么几种
= 这种赋值,你可以先使用变量,后面再去声明,比如 B=A A=20 A的值是后来才赋上的,但是B仍然可以得到20
:= 这种比较正经,跟C语言中 = 的使用基本差不多
?= 在赋值的时候他要判断一下,如果没被赋值,则赋值
+= 类似于A = A + 1
我声明了变量之后如何使用呢 用$( )
还是刚才那个例子
EXE := main SRC := main.c hello.c INC := hello.h main.h OBJ := main.o hello.o $(EXE): $(OBJ) gcc $^ -o $@ main.o: main.c gcc -c $< -o main.o hello.o: hello.c gcc -c $< -o hello.o
顺便再说一下,其实后面两句gcc可以不写,因为他会自动识别出你需要的o文件,并同同名文件中自动编译,哦,还有头文件也不需要写进去,你自己知道include 过了就可以了
这样似乎轻松了很多,因为我们可以把各种文件归类了,以后编译什么新的源文件,只需要把xxx.c写进makefile就可以了
然鹅,还是不够简单。linux中可以用*或者?当作通配符,makefile中也可以这样用
上例中 o文件的变量和c文件的变量内容有高度一致性
所以我们
EXE := main SRC := main.c hello.c INC := hello.h main.h OBJ := $(SRC:%.c=%.o) $(EXE): $(OBJ) gcc $^ -o $@
OBJ := $(SRC:%.c=%.o)这句话就表示,把SRC中所有以.c为结尾的文件名改成以.o为结尾 并把结果赋值给OBJ 其中%当作通配符用
那么 SRC是否也可以像OBJ那样,也使用一个什么方法,自动提取出所有的.c文件呢
答案必然是有的,这里用到shell函数
EXE := main SRC := $(wildcard *.c) OBJ := $(SRC:%.c=%.o) $(EXE): $(OBJ) gcc $^ -o $@
SRC := $(wildcard *.c)表示在本文件夹下,所有的.c文件,并把这个值返回给变量SRC
这样看起来终于好多了
其实我们也可以用下面两个函数表达同样的意思:
SRC := $(shell echo *.c)
SRC := $(shell ls *.c)
接下来,把刚才的内容完善一下
这是一些预定义变量,什么意思呢?就是系统给你定了的变量,你可以通过修改他们稍微的改动一下makefile的效果
AR:库文件打包程序默认为ar
AS:汇编程序,默认为as
CC:c编译器默认为cc
CPP:c预编译器,默认为$(CC) -E
CXX:c++编译器,默认为g++
RM:删除,默认为rm -f
ARFLAGS:库选项,无默认
ASFLAGS:汇编选项,无默认
CFLAGS:c编译器选项,无默认
CPPFLAGS:c预编译器选项,无默认
CXXFLAGS:c++编译器选项
EXE := main SRC := $(wildcard *.c) OBJ := $(SRC:%.c=%.o) CFLAGS := -Wall -O2 -fpic $(EXE): $(OBJ) $(CC) $^ -o $@
比如本例中,$(CC) 是编译器 和gcc一样用法
CFLAGS是编译器的选项,Wall 可以发送警报 -O 进行一些优化 -fpic 为了节约内存 需要经常更新而且还要高效运行需要加的参数
同样的,我们也可以使用通配符,把刚才被我们删掉的main.o : main.c表示出来
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
再完善一点,加上一个伪目标clean
EXE := main SRC := $(wildcard *.c) OBJ := $(SRC:%.c=%.o) CFLAGS := -Wall -O2 -fpic $(EXE): $(OBJ) $(CC) $^ -o $@ .PHONY: clean clean: @rm -f *.o
.PHONY: clean类似于伪目标的声明,当然你不声明也没什么影响。。
@rm -f *.o @放在命令前面表示这行命令执行的时候不用打印再终端上,自己默默执行就好了
所以这个命令的意思就是:
只有当我们输入 make clean 时,执行一个命令,删除所有的.o文件,并且不需要通知我。
当然也可以写成这个样子 因为RM命令默认就是不提示的 所以不用-f了
@$(RM) $(OBJ)
类似地,也可以加入一个rebuild重载
EXE := main SRC := $(wildcard *.c) OBJ := $(SRC:%.c=%.o) CFLAGS := -Wall -O2 -fpic $(EXE): $(OBJ) $(CC) $^ -o $@ .PHONY: clean rebuild clean: @rm -f *.o rebuild: @make clean @make $(EXE)
执行make build 后,先清理一下.o文件,然后再重新编译一遍。
下面时执行make常用的选项
选项名 |
说明 |
-C dir |
读取指定目录的makefile |
-f fille |
读取当前目录的file文件做为Makefile |
-i |
忽略所有命令的执行错误 |
-I dir |
指定被包含的Makefile所在目录 |
-n |
只打印要执行的命令,但是不执行这些命令 |
-p |
显示make 变量数据库和隐含规则 |
-s |
在执行命令时候不显示命令 |
-w |
如果在执行过程中改变目录,则打印当前目录名 |