Makefile入门(超详细一文读懂)

article/2025/10/2 20:30:02

1、Makefile编译过程

在这里插入图片描述
  Makefile文件中的命令有一定规范,一旦该文件编写好以后在Linux命令行中执行一条make命令即可自动编译整个工程。不同厂家的make可能会稍有不同,并且语法上也有区别,不过基本思想都差不多,主要还是落在目标依赖上,最广泛使用的是GNUmake。

2、语法规则

目标 ... : 依赖 ...命令1命令2. . .

  Makefile的核心规则,类似于一位厨师做菜,目标就是做好一道菜,那么所谓的依赖就是各种食材,各种厨具等等,然后需要厨师好的技术方法类似于命令,才能作出一道好菜。
  同时这些依赖也有可能此时并不存在,需要现场制作,或者是由其他厨师做好,那么这个依赖就成为了其他规则的目标,该目标也会有他自己的依赖和命令。这样就形成了一层一层递归依赖组成了Makefile文件。
  Makefile并不会关心命令是如何执行的,仅仅只是会去执行所有定义的命令,和我们平时直接输入命令行是一样的效果。
1目标即要生成的文件。如果目标文件的更新时间晚于依赖文件更新时间,则说明依赖文件没有改动,目标文件不需要重新编译。否则会进行重新编译并更新目标文件。
2、默认情况下Makefile的第一个目标为终极目标。
3、依赖:即目标文件由哪些文件生成。
4、命令:即通过执行命令由依赖文件生成目标文件。注意每条命令之前必须有一个tab保持缩进,这是语法要求(会有一些编辑工具默认tab为4个空格,会造成Makefile语法错误)。
5、all:Makefile文件默认只生成第一个目标文件即完成编译,但是我们可以通过all 指定所需要生成的目标文件。例如下面的例子。

all: target1 target2 target3
target1:
# 编译规则1
target2:
# 编译规则2
target3:
# 编译规则3

all被设置为第一个目标,并且target1、target2和target3被列为all的依赖。当你在命令行中运行make时,make命令会寻找并执行all目标规则,这将依次执行target1、target2和target3的编译规则。
因此,通过在Makefile中设置all作为默认目标规则,你可以简化构建过程,只需运行make命令即可执行整个编译过程,无需显式指定目标

3、变量

$符号表示取变量的值,当变量名多于一个字符时,使用"( )"
$符的其他用法

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

SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))ALL: hello.outhello.out: $(OBJ)gcc $< -o $@$(OBJ): $(SRC)gcc -c $< -o $@

4、变量赋值

  1、"="是最普通的等号,在Makefile中容易搞错赋值等号,使用 “=”进行赋值,变量的值是整个Makefile中最后被指定的值。

VIR_A = A
VIR_B = $(VIR_A) B
VIR_A = AA

  经过上面的赋值后,最后VIR_B的值是AA B,而不是A B,在make时,会把整个Makefile展开,来决定变量的值
  2、“:=” 表示直接赋值,赋予当前位置的值。

VIR_A := A
VIR_B := $(VIR_A) B
VIR_A := AA

  最后BIR_B的值是A B,即根据当前位置进行赋值。因此相当于“=”,“:=”才是真正意义上的直接赋值
  3、“?=” 表示如果该变量没有被赋值,赋值予等号后面的值。

VIR ?= new_value

  如果VIR在之前没有被赋值,那么VIR的值就为new_value。

VIR := old_value
VIR ?= new_value

  这种情况下,VIR的值就是old_value
  4、"+="和平时写代码的理解是一样的,表示将符号后面的值添加到前面的变量上

5、预定义变量

CC:c编译器的名称,默认值为cc。cpp c预编译器的名称默认值为$(CC) -E

CC = gcc

回显问题,Makefile中的命令都会被打印出来。如果不想打印命令部分 可以使用@去除回显

@echo "clean done!"

6、函数

通配符

SRC = $(wildcard ./*.c)

匹配目录下所有.c 文件,并将其赋值给SRC变量。

OBJ = $(patsubst %.c, %.o, $(SRC))

这个函数有三个参数,意思是取出SRC中的所有值,然后将.c 替换为.o 最后赋值给OBJ变量。
示例:如果目录下有很多个.c 源文件,就不需要写很多条规则语句了,而是可以像下面这样写

SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))ALL: hello.outhello.out: $(OBJ)gcc $(OBJ) -o hello.out$(OBJ): $(SRC)gcc -c $(SRC) -o $(OBJ)

这里先将所有.c 文件编译为 .o 文件,这样后面更改某个 .c 文件时,其他的 .c 文件将不在编译,而只是编译有更改的 .c 文件,可以大大提高大项目中的编译速度。

7、伪目标 .PHONY

伪目标只是一个标签,clean是个伪目标没有依赖文件,只有用make来调用时才会执行
当目录下有与make 命令 同名的文件时 执行make 命令就会出现错误。
解决办法就是使用伪目标

SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))ALL: hello.outhello.out: $(OBJ)gcc $< -o $@$(OBJ): $(SRC)gcc -c $< -o $@clean:rm -rf $(OBJ) hello.out.PHONY: clean ALL

通常也会把ALL设置成伪目标

8、其他常用功能

代码清理clean
我们可以编译一条属于自己的clean语句,来清理make命令所产生的所有文件,列如

SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))ALL: hello.outhello.out: $(OBJ)gcc $< -o $@$(OBJ): $(SRC)gcc -c $< -o $@clean:rm -rf $(OBJ) hello.out

9、嵌套执行Makefile

  在一些大工程中,会把不同模块或不同功能的源文件放在不同的目录中,我们可以在每个目录中都写一个该目录的Makefile这有利于让我们的Makefile变的更加简洁,不至于把所有东西全部写在一个Makefile中。
  列如在子目录subdir目录下有个Makefile文件,来指明这个目录下文件的编译规则。外部总Makefile可以这样写

subsystem:cd subdir && $(MAKE)
其等价于:
subsystem:$(MAKE) -C subdir

  定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较有利于维护。两个例子意思都是先进入"subdir"目录,然后执行make命令
  我们把这个Makefile叫做总控Makefile,总控Makefile的变量可以传递到下级的Makefile中,但是不会覆盖下层Makefile中所定义的变量,除非指定了 "-e"参数。
  如果传递变量到下级Makefile中,那么可以使用这样的声明
  export
  如果不想让某些变量传递到下级Makefile,可以使用
  unexport

export variable = value
等价于
variable = value
export variable
等价于
export variable := value
等价于
variable := value
export variable
如果需要传递所有变量,那么只要一个export就行了。后面什么也不用跟,表示传递所有变量

10、指定头文件路径

一般都是通过"-I"(大写i)来指定,假设头文件在:

/home/develop/include

则可以通过-I指定:

-I/home/develop/include

将该目录添加到头文件搜索路径中
在Makefile中则可以这样写:

CFLAGS=-I/home/develop/include

然后在编译的时候,引用CFLAGS即可,如下

yourapp:*.cgcc $(CFLAGS) -o yourapp

11、指定库文件路径

与上面指定头文件类似只不过使用的是"-L"来指定

LDFLAGS=-L/usr/lib -L/path/to/your/lib

告诉链接器要链接哪些库文件,使用"-l"(小写L)如下:

LIBS = -lpthread -liconv

简单的Makefile实例

目录结构
在这里插入图片描述
在这里插入图片描述

include

myinclude.h

#include <stdio.h>
void print1() ;
void print2() ;

f1

f1.c

#include "../include/myinclude.h"                                                                              void print1()  
{  printf("Message f1.c\n");  return;  
} 

Makefile
目标前面的路径,意思是将目标生成到指定的目录下

../$(OBJS_DIR)/f1.o:f1.c                                                                                       @$(CC) -c $^ -o $@  

f2

f2.c

#include "../include/myinclude.h"                                                                              void print2()  
{  printf("Message f2.c\n");  return;  
} 

Makefile

../$(OBJS_DIR)/f2.o:f2.c                                                                                       @$(CC) -c $^ -o $@ 

main

main.c

#include "../include/myinclude.h"                                                                                            int main(int argc, char const *argv[])
{print1();  print2();  return 0;
}

Makefile

../$(OBJS_DIR)/main.o:main.c                                                                                   @$(CC) -c $^ -o $@  

obj

此目录用来存放相关生成的目标文件

Makefile

../$(BIN_DIR)/$(BIN) : $(OBJS)@$(CC) $^ -o $@ 

主Makefile

#预定义变量
CC = gcc
#预定义编译目录
SUBDIRS = f1 \f2 \main \obj
#预定义目标
OBJS = f1.o f2.o main.o
BIN = myapp
OBJS_DIR = obj
BIN_DIR = bin
#传递预定义参数
export CC OBJS BIN OBJS_DIR BIN_DIRall:CHECK_DIR $(SUBDIRS)
CHECK_DIR:@mkdir -p $(BIN_DIR)
$(SUBDIRS):ECHO@make -C $@ ECHO:@echo $(SUBDIRS)@echo begin compile
clean:@$(RM) $(OBJS_DIR)/*.o@rm -rf $(BIN_DIR)

bin

此文件用来存放生成的二进制文件

执行结果

主Makefile执行过程
在这里插入图片描述
生成.o文件
在这里插入图片描述
生成二进制文件执行结果
在这里插入图片描述


http://chatgpt.dhexx.cn/article/8cuJDoLW.shtml

相关文章

刘玉真先生语录

或问&#xff1a;“古今之法门多矣&#xff0c;何以此教独名‘净明忠孝’ ? ” 先生曰&#xff1a;“别无他说&#xff0c;‘净明’只是正心诚意&#xff0c;‘忠孝’只是扶植纲常。但世人习闻此语&#xff0c;多是忽略过去&#xff0c;此间却务真践实履。” 先生曰&#xff1…

俞敏洪用20年的经验笑谈人生:不要在穷的时候假装崇高

转载于: https://www.huxiu.com/article/174774/1.html 虎嗅注&#xff1a;本文是新东方创始人、洪泰基金联合创始人俞敏洪&#xff0c;于12月1日在麻省理工学院跟学生做的一个主题演讲&#xff0c;言辞幽默&#xff0c;充满智慧&#xff0c;有太多人生的道理。本文由微信公众号…

每日言论:『模仿他人是人生陷阱』

公众号关注 「奇妙的 Linux 世界」 设为「星标」&#xff0c;每天带你玩转 Linux &#xff01; 最近&#xff0c;我们建立了一个技术交流微信群。目前群里已加入了不少行业内的大神&#xff0c;有兴趣的同学可以加入和我们一起交流技术&#xff0c;在 「奇妙的 Linux 世界」 公…

38岁,外企技术经理,失业:职场遇到瓶颈,你可能掉进了“能力陷阱”!

作者| Mr.K 编辑| Emma 来源| 技术领导力(ID&#xff1a;jishulingdaoli) 一位读者小R&#xff0c;给我讲述了他的职场经历。 小R&#xff0c;2008年通信专业硕士毕业。先去了华为&#xff0c;做了1年觉得有点苦&#xff0c;就去学了1年英语&#xff0c;后来跳槽到摩托罗拉。…

复旦大学教师 于娟博士《为啥是我得癌症?》

复旦女教师于娟已经去世半年多了&#xff0c;但这篇《为啥是我得癌症&#xff1f;》值得每个人认真阅读。 于娟&#xff0c;女&#xff0c;32岁&#xff0c;祖籍山东济宁&#xff0c;海归&#xff0c;博士&#xff0c;复旦大学优秀青年教师&#xff0c;一个两岁孩子的母亲&a…

《思考致富》不应该指望不经历“暂时的失败”便能发财

目录 作者简介 经典摘录 机遇有个狡猾的习惯&#xff0c;喜欢从后门悄悄溜进来&#xff0c;往往还喜欢以灾难或暂时失败的方式乔装露面 离金矿仅有三英尺远 欲望&#xff1a;成就一切的起点&#xff08;通往致富之路的第一步&#xff09; 信念&#xff1a;在脑海里目睹并坚…

复旦女博士于娟:为啥是我得癌症?

复旦女博士于娟&#xff1a;为啥是我得癌症&#xff1f;【请所有的朋友看看此文】 复旦女教师于娟已经去世半年多了&#xff0c;但这篇《为啥是我得癌症&#xff1f;》值得每个人认真阅读。不要再瞎吃八吃、暴饮暴食、嗜荤如命&#xff0c;不要再拼命工作、天天熬夜&#xff0…

[转载]复旦女博士于娟——为啥是我得癌症? (转)

[转载]复旦女博士于娟——为啥是我得癌症? (转) (2012-07-30 13:47:00) 转载▼ 标签&#xff1a; 转载 黄帝内经之类。就此引用一段话&#xff1a; 下午5--7点酉时 肾经当令 晚上7--9点戌时 心包经当令 9-11点亥时 三焦经当令 11-1点子时 胆经当令 凌晨1--3点丑时 肝经当令 3…

38岁,外企技术经理,失业:职场遇到瓶颈,你可能掉进了“能力陷阱”

一位读者小R&#xff0c;给我讲述了他的职场经历。 小R&#xff0c;2008年通信专业硕士毕业。先去了华为&#xff0c;做了1年觉得有点苦&#xff0c;就去学了1年英语&#xff0c;后来跳槽到摩托罗拉。 呆了12年&#xff0c;自己也熬到了技术经理的岗位。其实&#xff0c;2013年…

于娟《生命日记》——复旦大学教师于娟对大学生健康的建议

下文很长&#xff0c;需要你花点耐心&#xff0c;总结只有一句&#xff1a;珍惜、享受。 “在生死临界点的时候&#xff0c;你会发现&#xff0c;任何的加班&#xff08;长期熬夜等于慢性自杀&#xff09;&#xff0c;给自己太多的压力&#xff0c;买房买车的需求&#xff0c;这…

此生未完成 --- 于娟

文章目录 图书外观好句摘录Part1 病隙日记01 “为啥是我得癌症”的非学术报告02 我的二〇一〇 Part2 人间烟火03 写给我的宝贝04 碎落在身后的时光05 远在天涯06 生为女人 Part3 唯念芳辰07 刹那芳华&#xff1a;于娟的诗 图书外观 好句摘录 Part1 病隙日记 01 “为啥是我得癌…

于娟的忠告----生命只有一次,活着才是王道啊

复旦女教师于娟已经去世半年多了,但这篇《为啥是我得癌症?》值得每个人认真阅读。 于娟,女,32岁,祖籍山东济宁,海归,博士,复旦大学优秀青年教师,一个两岁孩子的母亲,乳腺癌晚期患者。 2009年12月被确诊患上了乳腺癌,2010年1月2日于娟被进一步确诊乳腺癌晚期, 2011年…

于娟的忠告----生命只有一次,活着才是王道啊!!!

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴! 复旦女教师于娟已经去世半年多了,但这篇《为啥是我得癌症?》值得每个人认真阅读。 于…

于娟临终前的忠告(二)

复旦大学女教师于娟已经去世一年多了&#xff0c;但这篇《为啥是我得癌症&#xff1f;》值得每个人认真阅读。 于娟&#xff0c;女&#xff0c;32岁&#xff0c;祖籍山东济宁&#xff0c;海归&#xff0c;博士&#xff0c;复旦大学优秀青年教师&#xff0c;一个两岁孩子的母亲&…

inode和软硬链接

文章目录&#xff1a; 一、理解文件系统1.1 什么是inode1.2 磁盘了解1.2.1磁盘的硬件结构1.2.2 磁盘的分区1.2.3 EXT2文件系统 二、软硬链接2.1 软链接2.2 硬链接 一、理解文件系统 1.1 什么是inode inodes 是文件系统中存储文件元数据的数据结构。每个文件或目录都有一个唯一…

服务器inode满了怎样删除文件,inode满了

inode满了 内容精选 换一换 可能原因:客户端缓冲区(output buffer)占用过多的内存空间。解决方案:Redis-cli客户端连接实例后,执行大key扫描命令:redis-cli --bigkeys,然后执行info,查看output buffer占用情况。 系统每30秒周期性检测磁盘Inode使用率,并把实际Inode使用…

一、Inode

文章目录 一、Inode概述1.1、文件数据包括元信息与实际数据1.2、inode的特殊作用1.3、Linux系统文件三个主要的时间属性1.4、目录文件的结构1.5、inode的号码 二、inode的大小三、链接文件四、恢复EXT类型的文件五、恢复XFS类型的文件xfsdump命令格式六、xfsdump使用限制七、日…

inode节点(详解)

首先&#xff0c;要明确理解inode是理解Linux/Unix文件系统和硬盘存储的基础。 1.什么是inode&#xff1f; 理解inode&#xff0c;要从文件存储说起。文件存储在硬盘上&#xff0c;硬盘的最小存储单位叫做“扇区”。每个扇区能存储512字节&#xff08;相当于0.5KB&#xff09…

Linux inode 详解

目录 1 inode 和 block 概述 2 inode 内容 2.1 stat 2.2 file 2.3 inode 号码 2.4 inode 大小 2.5 特有现象 2.6 inode 耗尽故障 3 硬链接与软链接 3.1 硬链接 3.2 软链接 1 inode 和 block 概述 文件是存储在硬盘上的&#xff0c;硬盘的最小存储单位叫做扇区 secto…

文件系统之inode

注&#xff1a;本文分析基于linux-4.18.0-193.14.2.el8_2内核版本&#xff0c;即CentOS 8.2 1 inode 内存中&#xff0c;每个文件都有一个inode&#xff0c;一切皆文件&#xff0c;其实应该是说一切皆inode。inode保存了文件系统中一个文件的属性描述&#xff0c;比如文件类型…