Makefile基础入门

第1章 Makefile快速入门

基础概念

make :项目构建工具——用于管理文件的更新 Makefile:规则文件,告诉make如何进行编译与管理 Makefile(取名makefile也行)其实就是一个脚本文件。

快速入门

  1. 创建文本文档——文件名Makefile
  2. #代表注释
  3. 显示规则:一条显示规则由以下构成
目标文件:依赖文件
[Tab键]指令

示例: 1)首先我们新建一个文件夹,在该文件夹内写一个hello.cpp文件 2)我们新建一个Makefile文件。 3)在Makefile内第1个文件通常是我们的目标文件。 4)显然,在下面的文件中我们写上了4条显示规则。Makefile默认情况下会把第一条显示规则当作最终目标,也就是说,如果完成了第一条显示规则,那么Makefile是有可能不执行后面的显示规则的。可以通过ALL命令来指示最终目标:ALL hello,这样就无需将最终目标放在文件开头了。

hello:hello.o
	g++ hello.o -o hello

hello.o:hello.s
	g++ -c hello.s -o hello.o

hello.s:hello.i
	g++ -S hello.i -o hello.s

hello.i:hello.cpp
	g++ -E hello.cpp -o hello.i
  1. 伪目标 .PHONY:目标名(可以任意命名),使用伪目标指令就会执行目标后面的命令。
.PHONY: 
clean:
	rm  -rf hello.o hello.s hello.i

示例:执行clean命令

make clean 

变量的使用

变量=(替换) 变量+=(追加) 变量:=(常量) 使用变量——$(变量名)

TARGET=hello
OBJ=hello.o

$(TARGET):$(OBJ)
	g++ hello.o -o hello

hello.o:hello.s
	g++ -c hello.s -o hello.o

hello.s:hello.i
	g++ -S hello.i -o hello.s

hello.i:hello.cpp
	g++ -E hello.cpp -o hello.i 

.PHONY:
clean:
	-rm  -rf hello.o hello.s hello.i(rm前面加-表示执行过程中出错了也要继续往下执行)

隐含规则

%.c文件表示任意的.c文件,%.o表示任意的.o文件。

通配符(自动变量 )

$@ 表示所有的目标文件 
$^ 表示所有的依赖文件
$< 所有的依赖文件的第一个文件

第2章 Makefile详解

1个基本原则

  1. 若想生成目标,检查规则中的依赖条件是否存在,若不存在,则检查是否有规则用来生成该文件。
  2. 检查规则中的目标是否需要更新,必须先检查他的所有依赖,依赖中有任意一个被更新,则目标必须被更新(目标更新时间必须晚于依赖文件的更新时间,否则就会更新目标)。

2个函数

wildcard函数

src=$(wildcard *.c)
或者
src=$(wildcard ./*.c)

src是Makefile文件的一个变量,Makefile只有一种变量类型字符串类型。 wildcard是一个函数,函数名意思是通配符。 以上函数语句的意思就是把当前目录下所有.c文件提取出来作为一个列表赋值给src。

patsubst函数

patsubst是patten substitude的缩写,匹配替代的意思。

obj=$(patsubts %.c, %.o, $(src))

将参数3中,包含参数1的部分,替换为参数2。 比如src = add.c sub.c div.c 则obj =add.o sub.o div.o

3个自动变量

$@:在规则的命令中,表示规则中的目标 $^:在规则的命令行中,表示所有的依赖条件 $<:在规则的命令中,表示第一个依赖条件。如果将该变量应用在模式规则中,它可将依赖条件中的依赖依次取出,套用模式规则。

注意:在一条规则中,$@只能用于命令行,而不能用于目标文件和依赖文件.看不懂这句话就先忽略

假设我们有Makefile文件内容如下:

src=$(wildcard *.c) # add.c sub.c div.c main.c
obj=$(patsubts %.c, %.o, $(src)) # add.o sub.o div.o main.o

ALL:a.out

a.out:$(obj)
    gcc $(obj) -o a.out

add.o:add.c
    gcc -c add.c -o add.o
sub.o:sub.c
    gcc -c sub.c -o sub.o
div.o:div.c
    gcc -c div.c -o div.o
main.o:main.c
    gcc -c main.c -o main.o

clean:
    -rm -rf $(obj) a.out

通过自动变量我们可以做出如下替换:

src=$(wildcard *.c) # add.c sub.c div.c main.c
obj=$(patsubts %.c, %.o, $(src)) # add.o sub.o div.o main.o

ALL:a.out

#在下面这条规则中,
#目标文件是a.out,所以在命令行中$@的值就是a.out
#依赖文件是$(obj)——值为add.o sub.o div.o main.o,所以在命令行中$^的值就是以上的4个文件
a.out:$(obj)   
    gcc $^ -o $@

#在下面的这条规则中,目标文件的值是add.o,依赖文件的值是add.c
#所以$<的值就是add.c,$@的值就是add.o,这里由于只有一个依赖文件,所以$<与$^的取值是一样的,我们使用$<
add.o:add.c
    gcc -c $< -o $@
sub.o:sub.c
    gcc -c $< -o $@
div.o:div.c
    gcc -c $< -o $@
main.o:main.c
    gcc -c $< -o $@

clean:
    -rm -rf $(obj) a.out

现在,你知道$@为什么不能用于目标文件和依赖文件的值了吧。因为以上三个自动变量的值就取决于目标文件和依赖文件的值。

模式规则

现在,我们要求再添加进一个功能,我们要添加mul.c文件实现乘法功能。 那么,我们现在也就必须要修改我们的Makefile文件。 但是我们想要实现的最终目标是,添加进新的文件时,不用修改我们的Makefile文件,这就需要用到模式规则。 %.c文件表示任意的.c文件,%.o表示任意的.o文件。 所以,在Makefile文件中,下面的几条规则我们可以进行替换

add.o:add.c
    gcc -c $< -o $@
sub.o:sub.c
    gcc -c $< -o $@
div.o:div.c
    gcc -c $< -o $@
main.o:main.c
    gcc -c $< -o $@

我们只用一条规则就可以替换上面的规则了

%.o:%.c
    gcc -c $< -o $@

请你思考,在这里为什么不能使用*.c而是必须使用%.c?为什么不能使用*.o而是必须使用%.o?也就是说,通配符*与%的区别是什么? 这样,我们添加新的mul.c文件时,通过模式规则我们就不用修改Makefile文件了。

静态模式规则

$(obj):%.o:%.c 相当于在模式规则的前面加上了个前缀,指定了(obj).o(obj)应该要套用的模式规则。 假设有以下场景:下面有两条模式规则都能产生.o文件,(obj)应该套用哪天模式规则呢?这里就产生了歧义。需要用到静态模式规则。

src=$(wildcard *.c) # add.c sub.c div.c main.c
obj=$(patsubts %.c, %.o, $(src)) # add.o sub.o div.o main.o

ALL:a.out

a.out:$(obj)
    gcc $(obj) -o a.out
%.o:%.c
    gcc -c $< -o $@
%.o:%.s
    gcc -S $< -o $@
clean:
    -rm -rf $(obj) a.out

修改如下:

src=$(wildcard *.c) # add.c sub.c div.c main.c
obj=$(patsubts %.c, %.o, $(src)) # add.o sub.o div.o main.o

ALL:a.out

a.out:$(obj)
    gcc $(obj) -o a.out
$(obj):%.o:%.c
    gcc -c $< -o $@
%.o:%.s
    gcc -S $< -o $@
clean:
    -rm -rf $(obj) a.out
全部评论

相关推荐

2025-12-24 15:25
已编辑
门头沟学院 前端工程师
是腾讯的csig腾讯云,前天晚上九点突然打电话约面,激动的通宵学了一晚上,第二天状态很差改了今天(以后再也不通宵学习了)感觉自己浪费了面试官一个半小时单纯手写+场景,无八股无项目无算法,打击真的很大,全是在面试官提醒的情况下完成的,自己技术方面真的还是有待提高,实力匹配不上大厂和已经面试的两个公司完全不一样,很注重编码能力和解决问题的能力,然而我这两个方面都很薄弱,面试官人很好很耐心的等我写完题目,遇到瓶颈也会提醒我,写不出题也会很耐心的跟我讲解好感动,到最后面试结束还安慰我打算把下周最后一场面试面完之后就不面啦,如果能去实习还是很开心,但是最重要的还是好好努力提高技术以下是面经第一题//&nbsp;实现一个解析&nbsp;url&nbsp;参数的函数function&nbsp;parseUrl(urlStr)&nbsp;{//&nbsp;TODO}parseUrl('*********************************************');//&nbsp;返回&nbsp;{a:&nbsp;1,&nbsp;b:&nbsp;2,&nbsp;c:&nbsp;3}追问:在链接里见过什么部分?用&nbsp;hash&nbsp;路由的话放在哪第二题//&nbsp;考虑有一个异步任务要执行,返回&nbsp;Promise,这个任务可能会失败,请实现&nbsp;retry&nbsp;方法,返回新方法,可以在失败后自动重试指定的次数。/***&nbsp;异步任务重试*&nbsp;@param&nbsp;task&nbsp;要执行的异步任务*&nbsp;@param&nbsp;times&nbsp;需要重试的次数,默认为&nbsp;3&nbsp;次*/function&nbsp;retry(task,&nbsp;times&nbsp;=&nbsp;3)&nbsp;{//&nbsp;TODO:&nbsp;请实现}//&nbsp;---------------测试示例&nbsp;----------------//&nbsp;原方法const&nbsp;request&nbsp;=&nbsp;async&nbsp;(data)&nbsp;=&gt;&nbsp;{//&nbsp;模拟失败if&nbsp;(Math.random()&nbsp;&lt;&nbsp;0.7)&nbsp;{throw&nbsp;new&nbsp;Error('request&nbsp;failed');}const&nbsp;res&nbsp;=&nbsp;await&nbsp;fetch(&#39;https://jsonplaceholder.typicode.com/posts&#39;,&nbsp;{method:&nbsp;'POST',body:&nbsp;JSON.stringify(data),});return&nbsp;res.json();}//&nbsp;新的方法const&nbsp;requestWithRetry&nbsp;=&nbsp;retry(request);//&nbsp;使用async&nbsp;function&nbsp;run()&nbsp;{const&nbsp;res&nbsp;=&nbsp;await&nbsp;requestWithRetry({&nbsp;body:&nbsp;'content'&nbsp;});console.log(res);}run();第三题就是给&nbsp;retry&nbsp;函数添加类型注释,用到泛型第四题:在组件库中将&nbsp;Alert&nbsp;用&nbsp;api&nbsp;的形式实现(应该就是&nbsp;message&nbsp;这个组件)怎么渲染到一个浮层里而不是原地渲染出来
不知道怎么取名字_:技术这个东西,太杂了,而且要下功夫的
查看5道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务