Makefile

1.Makefile作用

作用:组织管理程序。

为什么需要Makefile怎么高效地编译程序想达到什么样的效果?请参考 Visual Studio:修改源文件或头文件,只需要重新编译牵涉到的文件,就可以重新生成 APP。

我们使用Makefile可以为我们工程提供一套编译系统,也可以非常简单的去执行某些命令,就可以去编译文件,可以去清除文件。

2.Makefile的内部机制

Makefile的内部机制:预处理,编译,汇编,链接。

对于命令:

gcc -o test a.c b.c [-v]

a.c,b.c预处理编译为xxx.s,再汇编成xxx.o,最后一起链接成test.

该命令十分简单,但是有缺点,就是每执行一次,它就会对a.c和b.c依次进行预处理 编译 汇编这三个过程,最后再把它们链接在一起。(当我只修改a.c,b.c没有进行修改,执行该命令,它不仅仅对修改的a.c进行处理,也对没修改的b.c进行了处理,实际上没有必要)。

进行改进,应该先分别编译,最后在链接。

gcc -c -o a.o a.c                //编译a.c为a.o

gcc -c -o b.o b.c                //编译b.c为b.o

gcc -o test a.o b.o             //链接a.o,b.o为test

当我们只修改a.c后,我们可以只进行第一步和第三步,消去了对b.c的编译。

对于上面的命令,有个问题,那就是电脑如何判断哪些文件被修改了呢?

解决:比较时间

即:比较a.o与a.c的时间,比较b.o与b.c的时间,比较test与a.o,b.o的时间.

3 Makefile的规则

3.1 规则:是Makefile最根本元素

目标文件(target):依赖文件(prerequiries
       命令     
 

注:

(1)目标(target)通常是要生成的文件的名称,可以是可执行文件或OBJ文件, 也可以是一个执行的动作名称,诸如`clean’。

(2)依赖是用来产生目标的材料(比如源文件),一个目标经常有几个依赖。

(3)当“依赖文件”比“目标文件”新 时 或目标文件不存在时,执行命令。

3.2 将Makefile内部机制改写成Makefile文件

test : a.o b.o

        gcc -o test a.o b.o

a.o : a.c

        gcc -c -o a.o a.c

b.o :b.c

        gcc -c -o b.o b.c

Makefile文件就是名为Makefile的文件里放着这些内容,当我们像编译程序时,执行“make”即可

实际中该命令的执行情况

(1)执行“make”,要生成test,需要a.o,b.o,然后它发现没有a.o,b.o,它发现a.c比a.o新,会执行gcc -c -o a.o a.c命令,也发现b.c比b.o新,会执行gcc -c -o b.o b.c命令,然后生成test

(2)当修改a.c后,执行“make”,要生成test,要a.o,b.o,它发现,a.o依赖a.c,b.o依赖b.c,它发现a.c比a.o新,会执行gcc -c -o a.o a.c命令,最后因为新的a.o与旧的b.o其中有一个比test新,所以执行gcc -o test a.o b.o,生成test。

4 Makefile的语法

4.1 通配符:“ %.o ”

对于上面Makefile中的命令

test : a.o b.o

        gcc -o test a.o b.o

a.o : a.c

        gcc -c -o a.o a.c

b.o :b.c

        gcc -c -o b.o b.c

有一点缺点:就是,因为test依赖只有a.o,b.o,所以表面上看起来还可以,但是如果依赖有100个,1000个,那么就很麻烦了。所以我们引入了通配符。

对于上面的命令修改成

test:a.o b.o

        gcc -o test $^

%.o:%.c

        gcc -c -o $@ $<

注:

(1)所以的依赖用” $^ “表示。

(2)不可写成gcc -c -o %.o %.c。其中“ %.o ”用” $@ “代替,” %.c “用” $< “代替

总结:

$@  目标文件
 $<  依赖文件
$^ 所有依赖文件

4.2 即时变量,延时变量,expert

变量使用时格式为:

$(变量)

即时变量(简单变量)

A:=xxx                #A的值即刻被锁定

延时变量

B=xxx                #B的值使用时才确定

示例:

A := $(c)

B = $(c)

C = abc

all:

        @echo A=$(A)

        @echo B=(B)

结果:        A=

                B=abc

结果解析:

(1)因为A为即时变量,在定义时马上确定A的值,但是因为C为即时变量,而且在A后面定义,所有在确定A的值时,C还没被F值,所以A的值为空。

(2)因为B为延时变量,在使用到时才确定值,即echo B=$(B)时。所以B的值为abc。

补充1:

A := $(c)

B = $(c)

all:

        @echo A=$(A)

        @echo B=(B)

C = abc

当把C=abc放在@echo B=$(B)后面时,B的值也是abc,所以C=abc的位置对B的值无影响。

补充2:

A := $(c)

B = $(c)

C = abc

all:

        @echo A=$(A)

        @echo B=(B)

C = 123

结果:        A=

                   B=123 

结论:C原来的”abc“被”123“覆盖

补充3:

A := $(c)

B = $(c)

C = abc

all:

        @echo A=$(A)

        @echo B=(B)

C += 123

结果:        A=

                   B=abc123

总结:

:=                        #即时变量
=                        #延时变量
?=                        #延时变量,如果是第一次定义时才起效,如果前面变量已定义,则忽略这句
+=                        #附加,它是延时还是即时取决于前面的定义

4.3 假想目标

4.3.1 前言:如果我们”清除文件“怎么办?

1.如果使用命令:

rm *.o test

解释:“ *.o ”表示所有的.o文件。" test "代表test文件。

2.如果使用Makefile

test:a.o b.o

        gcc -o test $^

%.o:%.c

        gcc -c -o $@ $<

clean:

        rm *.o test

调用“ make clean”执行“ rm *.o test ”清除所有文件

注:

(1)通过“ make clean ”命令可以猜到,make后面带目标名,也可以不带。

(2)如果不带,它就会去生成第一个规则里面的第一个目标。

总结:

使用Makefile

make [目标]

若无目标,默认第一个目标。

4.3.2 假想目标出现的原因

因为有clean命令,但是如果Makefile编译的文件中有同名clean的文件时,执行“ make clean”时,会出现:’clean‘ is up to data.

问题解析:它说:clean文件已经更新,它根本没有执行“ clean ”删除命令。

出现问题解析:原因,一个规则可以执行的条件:“目标文件不存在”或“依赖比目标新”,现在我有名为“clean”的目标文件,但是它根本没有依赖文件,所以依赖不会比目标新。同时有同名的clean文件,导致目标文件存在,所以根本无法执行操作。

所以问题是:如果有同名的clean文件时,就无法执行" make clean "命令

解决方案:把”clean“目标定义为假想目标 : ".PHONY : clean"

test:a.o b.o

        gcc -o test $^

%.o:%.c

        gcc -c -o $@ $<

clean:

        rm *.o test

.PHONY : clean

5 Makefile函数

5.1 $(foreach Var , list , text)

对list中每一个元素,取出来赋值给Var,然后把Var改为test所描述的形式。

示例:

A = a b c

B = $(foreach f , A , $(f).o)

all:

        echo B = $(B)

执行make:

结果:B = a.o b.o c.o

5.2 $(filter pattern... , text) 和 $(filter-out pattern... , text)

$(filter pattern... , text)                      #在text中取出符合pattern格式的值。

$(filter-out pattern... , text)                #在text中取出不符合pattern的值

示例:

C =  a b c d/

D = $(filter %/ , $(C))                                #在C中寻找目录

E = $(filter-out %/ , $(C))                          #在C中寻找非目录的东西

all:

        @echo D = $(D)

        @echo E = $(E)

注:a,b,c为文件,d/为目录

执行:make

结果:D = d/

          E = a b c

5.3 $(wildcard pattern)

#pattern定义了文件名的格式

#wildcard取出其中存在的文件

示例1:

假设现有目录为:

a.c

b.c

c.c

Makefile

Makefile中:

files = $(wildcard *.c)

all:

        @echo files = $(files)

执行make:

结果:

files = a.c b.c c.c

示例2:

假设现有目录为:

a.c

b.c

c.c

Makefile

Makefile中:

files2 = a.c b.c c.c b.c e.c

files3 = $(wildcard $(files2))

all:

        echo file3 = $(files3)

执行make:

结果:files3 = a.c b.c c.c

结果分析:因为在目录中实际存在的文件为a.c,b.c,c.c,该函数可以找出真实存在的文件,所以d.c,e.c不会被找出。

5.4 $(patsubst pattern , replcement , $(Var))

#把Var中的文件取出来,把pattern替换为replcement。

示例:

files2 = a.c b.c c.c e.c abc

dep_files = $(patsubst %.c , %.d , $(files2r))

all:

        @echo dep_files =  $(dep_files)

执行make:

结果:dep_file = a.d b.d c.d d.d e.d abc

6 Makefile改进

6.1 支持头文件依赖

如果存在c.c和c.h函数

c.c中:

#include  <stdio.h>

#include "c.h"

void func_c()

{

        printf("this is C = %d\n",C);

}

在c.h中:

define C 1

Makefile中:

test : a.o b.o c.o

        gcc -o test a.o b.o c.o

%.o : %.c

        gcc -c -o $@ $<

clean :

        rm *.o test

.PHONY : clean

(1)执行make编译。

(2)执行 ./test 运行代码,成功 " C = 1 "。

(3)执行" vi c.h ",编辑c.h文件,改成" define C 2"

(4)执行make ,结果“make:'test' is up to data"

(5)执行 ./test 运行代码 “ C = 1”.

思考:为什么明明已经修改了c.h,运行 ./test 结果还是和原来一样

原因:在Makefile中,%.o依赖%.c,当我们修改c.h时,%.o不会更新,所以test也不会变,所以运行结果跟之前一样。

解决1:

Makefile中添加

c.o : c.c c.h

缺点:我们不容易确定一个文件有多少个.c和.h文件,手动写出不现实,我们需要自动来生成这些规则。

解决2:

gcc -M c.c                                          //打印出依赖

gcc -M -MF c.d c.c                            //把依赖写入c.d

gcc -c -o c.o c.c -MD -MF c.d           //编译c.o,把依赖写入文件c.d

修改Makjefile

test : a.o b.o c.o

        gcc -o test a.o b.o c.o

%.o : %.c

        gcc -c -o $@ $< -MD -MF .$@.d

执行make:

执行 ls -a

a.c a.o .a.o.d

b.c b.o .b.o.c

c.c c.d c.h clean c.o .c.o.d

Makefile

注:得到.%.o.d文件后,要判断是否为空,不为空才能包含依赖。

再次修改Makefile文件:

objs = a.o b.o c.o

dep_files := $(patsubst % , .%.d , $(objs))                #所以文件加上前缀. 和后缀 .d

dep_files := $(wildcard , $(dep_files))                       #判断生成的文件是否真实存在

test : $(objs)

        gcc -o test $^

ifneq ($(dep_files) , )                                                #判断dep_files不为空

include $(dep_files)                                                 #将dep_files文件包含进来

endif

%.o : %.c

        gcc -c -o $@ $< -MD -MF .$@.d

clean : 

        rm *.o test

distclean  :

        rm $(dep_files)                                                #删除所有文件

.PHONY : clean

6.2添加CFLAGS

CFLAGS编译参数

示例1:

CFLAGS = -Werror                                                #把所有warming变为error

注:gcc $(CFLAGS) -c -o $@ $< -MD -MF .$@.d

示例2:

CFLAGS = -I.                                        #指定头文件为当前目录

CFLAGS = -Iinclude                             #指定头文件目录为include目录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值