makefile基本语法(已完成)

发布时间 2024-01-06 15:11:17作者: ciaoccna

makefile基本语法

一.makefile规则

一个简单的 Makefile 文件包含一系列的“规则”,其样式如下:

目标(target)…: 依赖(prerequiries)…
<tab>命令(command)

目标(target)通常是要生成的文件的名称,可以是可执行文件或OBJ文件, 也可以是一个执行的动作名称,诸如`clean’。 依赖是用来产生目标的材料(比如源文件),一个目标经常有几个依赖。

命令是生成目标时执行的动作,一个规则可以含有几个命令,每个命令占一 行。 注意:每个命令行前面必须是一个 Tab 字符,即命令行第一个字符是 Tab(有的时候执行makefile在命令出可能会出现空行以及一些莫名其妙的错误,要注意是不是makefile中是不是存在空行且该空行首个为Tab,makefile会把这样的空行当作命令执行)。

通常,如果一个依赖发生了变化,就需要规则调用命令以更新或创建目标。 但是并非所有的目标都有依赖,例如,目标“clean”的作用是清除文件,它没有依赖。 规则一般是用于解释怎样和何时重建目标。

make 首先调用命令处理依赖, 进而才能创建或更新目标。当然,一个规则也可以是用于解释怎样和何时执行一 个动作,即打印提示信息。 一个 Makefile 文件可以包含规则以外的其他文本,但一个简单的 Makefile 文件仅仅需要包含规则。虽然真正的规则比这里展示的例子复杂,但格式是完全 一样的。

对于上面的 Makefile,执行“make”命令时,仅当 hello.c 文件比 hello 文件新,才会执行命令“arm-linux-gcc –o hello hello.c”生成可执行文件 hello; 160 / 566 如果还没有 hello 文件,这个命令也会执行。 运行“make clean”时,由于目标 clean 没有依赖,它的命令“rm -f hello” 将被强制执行。

二.makefile常见符号

  • "@" 隐藏命令

这个符串通常用在“规则”行中,表示不显示命令本身,而只显示它的结果,例如以下例子:

a = 5
all:
	@echo a = $(a)

a = 5
all:
	echo a = $(a)

可以看到在第三行添加了"@"后,make执行的结果中本条命令就不可见了,去掉"@"后就会显示本条命令。

  • "#"注释

在makefile中,“#”表示注释,和我们平时写代码不一样,请注意,例子如上方代码所示。

  • "$"

美元符号$,主要扩展打开makefile中定义的变量,即对指定的变量会按照要求从中取出值,例子如下:

1)$@ --代表目标文件(target)

2)$^ --代表所有的依赖文件(components)

3)$< --代表第一个依赖文件(components中最左边的那个)。

4)$? --代表示比目标还要新的依赖文件列表。以空格分隔。

5)$(%) --表示从括号中的变量取值("%"表示为通配符)

  • "%"通配符

在makefile中,"%"表示通配符,即所有符合条件的符号,例如"%.c"就表示为所有文件中包含".c"的文件

三.变量的定义

在makefile中,定义变量大致可以分为4种,如下所示:

a := $(c)	 	#即使变量,也叫简单变量,立马赋值
b = $(c)		#延时变量,用到再赋值
c = abc	
d = hello	
d ?= No			#延时变量,如果是第一次定义才起效,如果已经定义过了就忽略这个指令
                #"+=" 附加,它是延时变量还是即时变量取决于前面的值,不太好演示就不举例了

all:
	@echo a = $(a)
	@echo b = $(b)
	@echo d = $(d)

image-20231002214953769

四.假想目标

在编写makefile时,很可能遇到这种情况:想要执行make clean,但是目录中已经有clean这个文件存在,此时怎么办?makefile提供了一个办法--假想目标".PHONY",可以理解成总是会执行。它的用法和平时我们编译我们类似,如以下所示:

test : a.c
	gcc -o test a.c

clean:
	rm -f test

image-20231002221214844

可以看到使用make clean时,如果有同名的文件,就会提示已存在文件,无法执行make clean命令,这是就需要用到们的".PHONY"了

image-20231002221409519

此时就可以正确删除了。当然,".PHONY"不仅仅可以用于多次执行make clean命令,也可以帮助我们重复编译文件,我们知道makefile编译形成可执行文件,一般是比较修改信息的,如果你的目标文件比原来的文件新,他就不会再编译,加上".PHONY"就可以重复编译文件,如下所示:

.PHONY:test
test : a.c
	gcc -o test a.c

.PHONY:clean
clean:
	rm -f test

image-20231002222012478

五.常见函数

在makefile中,有很多函数可以帮助我们实现功能,接下来我们介绍几个常见的函数(以下介绍的都是简单常用的函数,其余的函数感兴趣的可以看看官方文档)。

1.makefile函数调用的基本格式:

$(function arguments)

这里"function"是函数名,"arguments"是该函数的参数。参数和函数名之 间是用空格或 Tab 隔开,如果有多个参数,它们之间用逗号隔开。这些空格和逗号不是参数值的一部分。内核的 Makefile 中用到大量的函数,现在介绍一些常 用的。

2.$(patsubst pattern,replacement,text)

用"replacement"替换"text"变量中符合格式"pattern’的字符。 "pattern"和"replacement"中可以使用通配符。例子如下:

a = x.o obj.o
b = $(patsubst %.o,%.c, $(a))
c = $(patsubst %.c,%.o,x.c.c bar.c)

all:
	@echo a = $(a)
	@echo b = $(b)
	@echo c = $(c)

image-20231003160932928

3.$(findstring find,in)

在字符串"in"中寻找"find",如果找到,则返回值是"find",否则返回值 为空,例子如下:

makefilea = hello vscode
b = $(findstring hello, $(a))
c = $(findstring enen, $(a))

all:
	@echo a = $(a)
	@echo b = $(b)
	@echo c = $(c)

image-20231003161840085

4.$(filter pattern...,text)

返回在"text"中由空格隔开且符合参数"pattern"的字符串,去除不符合的字符串,例子如下:

a = hello.c vscode.o a.c b.s
b = $(filter %.c, $(a))
all:
	@echo a = $(b)

image-20231003162439102

5.$(filter-out pattern...,text)

返回在"text"中由空格隔开且不符合参数"pattern"的字符串,去除符合的字符串,例子如下:

a = hello.c vscode.o a.c b.s
b = $(filter %.c, $(a))
c = $(filter-out %.c, $(a))

all:
	@echo a = $(b)
	@echo c = $(c)

image-20231003162701794

6.$(wildcard text)

从变量test中找到真实存在的文件,如下:

a = hello.c vscode.o a.c b.s
b = $(wildcard $(a))
all:
	@echo b = $(b)

image-20231003163118896

7.$(sort list)

将‘list’中的字按字母顺序排序,并去掉重复的字。输出由单个空格隔开 的字的列表,如下:

a = hello.c vscode.o a.c b.s
b = $(sort $(a))
all:
	@echo b = $(b)

image-20231003192406409

六.编写makefile

前文我们已经介绍了makefile常见的语法了,接下来让我们自己编写几个makefile看看把。

1.单文件编译形成单个可执行程序

#include<stdio.h>

int main()
{
    printf("hello, can you see me?\n");
    return 0;
}
test : test.c	#想要生成test,test依赖于test.c
	gcc -o test test.c	#指令
.PHONY:clean
clean:
	rm -f test

image-20231002223458944

#以上就是我们编写makefile的基本格式
目标:源文件	#注意下方首个是tab键,不然不能执行指令
	指令

知道以上基本格式以后,我们再来编写几个复杂点的makefile

2.多文件编译形成多个可执行程序

编写一个文件的时候我们可以不用告诉makefile,它会默认执行,但是要一次性形成多个可执行程序时,就要说明。

all : test test2	//告诉makefile要生成哪几个程序
test : test.c
	gcc -o test test.c
test2 : test2.c
	gcc -o test2 test2.c
.PHONY:clean
clean:
	rm -f test
	rm -f test2

3.通用makefile模板

makefile对于我们而言,只是一个工具,针对大型项目的时候,没必要每次都自己造轮子,只要知道怎么用就行了,接下来介绍一个通用的makefile模板。

target=main   //想要生成的可执行文件名称可自定义,此处为main
src=$(wildcard *.c)
deps=$(wildcard *.h)
obj=$(patsubst %.c,%.o,$(src))

$(target):$(obj)
  gcc $^ -o $@ -Wall

%.o:%.c $(deps)
  gcc -c $< -o $@ -Wall

.PHONY:clean
clean:
  -rm -rf $(target) $(obj) 

对于生成多个目标文件的,只需要在上面修改一下即可