1、make和makefile

思考:什么是make,为什么需要make?

make是一个应用程序:

解析源程序之间的依赖关系,根据依赖关系自动维护编译工作,执行宿主操作系统中的各种命令。

makefile是一个描述文件:

定义了一系列的规则来指定源文件编译的先后顺序。

拥有特定的语法规则,支持函数定义和函数调用。

能够直接集成操作系统中的各种命令。

make和makefile之间的关系:

makefile中的描述用于指导make程序如何完成工作,make根据makefile中的规则执行命令,最后完成编译输出。

最简单的makefile示例:

hello:(目标)

            echo "hello makefile"(实现目标所需执行的命令)

注意:目标后的命令需要用Tab(‘\t’)隔开!

make 程序的使用示例 make -f mf.txt hello  //-f指定文件,表示后面的文件找到执行的动作mf.txt表示指导工作的文件

功能说明:以hello关键字作为目标查找mf.txt文件,并执行hello处的命令。

make程序的简写实例: make hello

功能说明:以hello关键字作为目标查找makefile或、Makefile文件,并执行hello处的命令。

make

功能说明:查找makefile或Makefile文件中最顶层目标,并执行最顶层目标的命令。

小结:

make只是一个特殊功能的应用程序。make用于根据指定的目标执行相应的命令。makefile用于定义目标和实现目标所需的命令。makefile有特定的语法规则,支持函数定义和调用。

2、初识makefile的结构

makefile的意义:makefile用于定义源文件间的依赖关系,makefile说明如何编译各个源文件并生成可执行文件。

依赖的定义:

target: prerequisites; command1  目标:依赖条件; 命令

'\t' command2 //命令写到下一行不要; 但要加tab键

依赖条件可以省略,只要执行后边的目标,目标就可以完成。 

makefile中的元素含义:

targets:通常是需要生成的目标文件名。make所需执行的命令名称。

prerequisite:当前目标所以依赖的其他目标或文件。

command:完成目标所需要执行的命令。gcc

规则中的注意事项:targets可以包含多个目标:使用空格对多个目标名进行分隔。

prerequisites可以包含多个依赖:使用空格对多个依赖进行分隔。

规则中的注意事项:

[Tab]键: ‘\t’

每一个命令行必须以[Tab]字符开始。

[Tab]字符告诉make此行是一个命令行。

续行符:\

可以将内容分开写到下一行,提高可读性。

一个makefile的依赖示例:

all:test

        echo "make all" //test成立,执行此行

test: 

        echo "make test" //test子目标没有依赖,只要执行此行命令,目标完成

all依赖于test,test是all的依赖条件,all完成,需要两个条件,一是依赖条件,二是命令

依赖规则:

当目标对应的文件不存在,执行对应命令。

当依赖在时间上比目标更新,执行对应命令。

当依赖关系连续发生时,对比依赖链上的每一个目标。确保都存在

小技巧:makefile中可以在命令前加上@符,作用为命令无回显。只打印结果(无回显字符)

all:test
@echo "make all"
test:

@echo "make test"  //执行,test完成

打印:make test   make all

第一个make的编译案例:

hello.out : main.o func.o

        gcc -o hello.out main.o func.o

main.o : main.c

        gcc -o main.o -c main.c

func.o : func.c  //func.c时间比func.o更晚的话,重新生成func.o

        gcc -o func.o -c func.c

小技巧:工程中可以将最终可执行文件名和all同时作为makefile中第一条规则的目标。

hello.out all: main.o func.o //

        gcc -o hello.out main.o func.o

hello.out all: func.o main.o //如果没有hello.out,依赖没有更新,但是每次都会执行下边的命令gcc -o hello.out main.o func.o,不用执行的,hello最新,不执行下边命令,如果强制编译的话,可以 make all,必然编译。只打make的话,make到makefile中找第一条依赖规则,然后找第一条依赖规则中的第一个目标,名字是hello.out,看看是不是最新的,如果是,没必要编译。

另一个应用:

小结:makefile用于定义源文件间的依赖关系。makefile说明如何编译各个源文件并生成可执行文件。makefile中的目标之间存在连续依赖关系。依赖存在并且命令执行成功是目标完成的充要条件。

3、伪目标的引入

mekefile中的目标究竟是什么?

默认情况下:make认为目标对应着一个文件。make比较目标文件和依赖文件的新旧关系,决定是否执行命令。make以文件处理作为第一优先级。

下面的代码有什么意义?

   clean:

        rm *.o hello.out  //删除 .o和.out文件

make clean

hello.out all: func.o main.o
gcc -o hello.out func.o main.o
func.o: func.c 
gcc -o func.o -c func.c  
main.o:main.c
gcc -o main.o -c main.c
clean :

rm *.o hello.out 

问题:如果文件夹中有clean文件,clean是最新的,不执行下边的命令。

解决方法:makefile中的伪目标:

通过.PHONY关键字声明一个伪目标,伪目标不对应任何实际的文件。不管伪目标的依赖是否更新,命令总是执行。

伪目标的语法:先声明,后使用。

本质:伪目标是make中特殊目标 .PHONY的依赖。

.PHONY:clean //clean是依赖条件

##注释##

clean:

    rm *.o hello.out

.PHONY:clean  //验证
clean :

rm *.o hello.out

伪目标的妙用:规则调用(函数调用)

.PHONY: clean rebuild all //声明三个伪目标

## other rules ##

rebuild:clean all

clean: 

    rm *.o hello.out

原理:当一个目标的依赖包含伪目标时,伪目标所定义的命令总是会被执行。

hello.out : func.o main.o
gcc -o hello.out func.o main.o
func.o: func.c 
gcc -o func.o -c func.c  
main.o:main.c
gcc -o main.o -c main.c
.PHONY:rebuild clean all
rebuild:clean all
all: hello.out
clean :
rm *.o hello.out

技巧二:绕开.PHONY关键字定义伪目标

原理:如果一个规则没有命令或者依赖,并且它的目标不是一个存在的文件名,在执行此规则时,目标总会被认为是最新的。

clean:FORCE

    rm *.o hello.out

FORCE:  //FORCE总认为最新的,clean就不是最新的,则clean之下的命令会执行

hello.out : func.o main.o
gcc -o hello.out func.o main.o
func.o: func.c 
gcc -o func.o -c func.c  
main.o:main.c
gcc -o main.o -c main.c
clean :FORCE
rm *.o hello.out

FORCE:

小结:默认情况下,make认为目标对应着一个文件。.PHONY用于声明一个伪目标,伪目标不对应实际的文件。伪目标的本质是make中特殊目标.PHONY的依赖。使用伪目标可以模拟“函数调用”。

4、变量和不同的赋值方式

makefile中支持程序设计语言中变量的概念。

makefile中变量只代表文本数据(字符串)。不需要什么类型

makefile中的变量名规则:

变量名可以包含字符,数字,下划线。不能包含“:”,“#”,“=”,或‘’

变量名大写敏感。

变量的定义和使用:

CC :=gcc

TARGET :=hello.out -->变量定义

变量使用

$(TARGET): func.o main.o  //hello.out : func.o main.o

        $(CC) -o $(TARGET) func.o main.o //  gcc -o hello.out func.o main.o

CC:=g++
TARGET:=hello-world.out
$(TARGET) : func.o main.o
$(CC) -o $(TARGET) func.o main.o
func.o: func.c 
$(CC) -o func.o -c func.c  
main.o:main.c
$(CC) -o main.o -c main.c
.PHONY:rebuild clean all
rebuild:clean all
all: $(TARGET)
clean :

rm *.o $(TARGET)

makefile中变量的赋值方式:

简单赋值(:=)

递归赋值(=)

条件赋值(?=) 第一次会赋值,之后不会

追加赋值(+=) 当前变量追加一个变量,字符串拼接

不同的赋值方式意义不同。

简单赋值:

程序设计语言中的通用的赋值方式,只针对当前语句的变量有效。

x :=foo

y :=$(x)b

x :=new==>x=>new  y=>foob

.PHONY:test

test:

    @echo "x=>$(x)"

    @echo "y=>$(y)"

递归赋值(=)

赋值操作可能影响多个其他变量。所有与目标变量相关的其它变量都将受到影响。

x :=foo

y :=$(x)b

x =new   ==>x=>new  y=>newb //与目标变量相关的其他变量都要受到影响。

.PHONY:test

test:

    @echo "x=>$(x)"

    @echo "y=>$(y)"

条件赋值(?=)

如果变量未定义,使用赋值符号中的值定义变量。如果变量已经定义,赋值无效。

x :=foo

y :=$(x)b

x ?=new    ==>x=>foo y=>foob

.PHONY:test

test:

    @echo "x=>$(x)"

    @echo "y=>$(y)"

第一次定义变量的时候,可以用条件赋值。

追加赋值(+=)

原变量值之后加上一个新值。原变量值与新值之间由空格隔开。

x :=foo

y :=$(x)b

x +=$(y)   ==>x=>foo foob   y=>foob

.PHONY:test

test:

    @echo "x=>$(x)"

    @echo "y=>$(y)"

#ex1
#x:= foo
#y:=$(x)b
#x:=new
#ex2
#x=foo
#y=$(x)b
#x=new
#a=$(b)
#b=$(c)
#c=hello-makefile
#ex3
#x:= foo
#y:=$(x)b
#x?=new
#ex4
x:= foo
y:=$(x)b
x+=new
.PHONY:test
test:
@echo "x=>$(x)"
@echo "y=>$(y)"
@echo "a=>$(a)"
@echo "a=>$(b)"

@echo "a=>$(c)"

小结:

makefile中支持变量的定义和使用,makefile中存在四种变量的赋值方式。

简单赋值(:=)。递归赋值(=)。条件赋值(?=)。追加赋值(+=)。

5、预定义变量的使用

在makefile中存在一些预定义的变量

自动变量:$@ ,$^ ,$<

特殊变量:$(MAKE),$(MAKECMDGOALS),$(Makefile_LIST)

$(MAKE_VERSION),$(CURDIR),$(.VARIABLES)....

自动变量的意义:$@: 当前规则中触发命令被执行的目标。当前目标

$^ 当前规则中的所有依赖。

$< 当前规则中的第一个依赖。

自动变量的使用示例:

all:first second third

        @echo "\$$@=>$@"  //第一个$是转义,打印@值

        @echo "$$^=>$^"

        @echo "$$< =>$<"

make all

注意:1. “$”对于makefile有特殊含义:输出时需要加上一个“$”进行转义。

2.“$@”对于Vash Shell有特殊含义:输出时需要加上“\”进行转义。

.PHONY:all first second third
all:first second third
@echo "\$$@=>$@"
@echo "$$^=>$^"
@echo "$$< =>$<"
first:
second:

third:

打印:

delphi@delphi-vm:~$ make
$@=>all
$^=>first second third
$< =>first
delphi@delphi-vm:~$ ^C

delphi@delphi-vm:~$ 

改写:

CC:=g++

TARGET:=hello-world.out
$(TARGET) : func.o main.o 
$(CC) -o $@ $^     #$(TARGET) func.o main.o
func.o: func.c 
$(CC) -o $@ -c $^  #func.o func.c  
main.o:main.c
$(CC) -o $@ -c $^
.PHONY:rebuild clean all
rebuild:clean all
all: $(TARGET)
clean :

rm *.o $(TARGET)

一些特殊变量的含义:

$(MAKE):当前make解释器的文件名

$(MAKECMDGOALS):命令行中指定的目标名(make的命令行参数)

$(MAKEFILE_LIST):

make所需要处理的makefile文件列表。当前makefile的文件名总是位于列表的最后。文件名之间以空格进行分隔。

.PHONY: all out first second third
all out:
@echo "$(MAKE)"
@echo "$(MAKECMDGOALS)"

@echo "$(MAKEFILE_LIST)"

delphi@delphi-vm:~$ make
make

 makefile
delphi@delphi-vm:~$ make all
make
all
 makefile
delphi@delphi-vm:~$ make all out 
make
all out
 makefile
make
all out
 makefile

delphi@delphi-vm:~$

然后:

first :
@echo "first"
second :
@echo "second"
third :
@echo "third"
test:
$(MAKE) first
$(MAKE) second

$(MAKE) third

delphi@delphi-vm:~$ make test
make first
make[1]: 正在进入目录 `/home/delphi'
first
make[1]:正在离开目录 `/home/delphi'
make second
make[1]: 正在进入目录 `/home/delphi'
second
make[1]:正在离开目录 `/home/delphi'
make third
make[1]: 正在进入目录 `/home/delphi'
third

make[1]:正在离开目录 `/home/delphi'

一些特殊变量的含义:

$(MAKE_VERSION):当前make解释器的版本

$(CURDIR):当前make解释器的工作目录

$(.VARIABLES):所有已经定义的变量名列表(预定义变量和自定义变量)

.PHONY:test1 test2
TDelphi:=Delphi Tang
D.t.sofware:=D.T.
test1:
@echo "$(MAKE_VERSION)"
@echo "$(CURDIR)"

@echo "$(.VARIABLES)"

test2:

@echo "$(RM)"

打印出 #rm -f  ->RM表示强制删除的意思

小结:makefile提供了预定义变量供开发者使用。预定于变量的使用能够使得makefile的开发更高效。自动变量是makefile中最常见的元素。使用 $(.VARIABLES) 能够获取所有的特殊变量。

使用:先打印所有预定义变量,看看有没有自己需要的,复制出来,去gnu make解释器说明文档查找功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值