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

CCc编译器默认为cc

CPPc预编译器,默认为$(CC) -E

CXXc++编译器,默认为g++

RM:删除,默认为rm -f

ARFLAGS:库选项,无默认

ASFLAGS:汇编选项,无默认

CFLAGSc编译器选项,无默认

CPPFLAGSc预编译器选项,无默认

CXXFLAGSc++编译器选项

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

如果在执行过程中改变目录,则打印当前目录名

 

 

 




 
 

 
posted @ 2019-10-16 20:55  祁峰_1024  阅读(290)  评论(0编辑  收藏  举报