程序员开发实例大全宝库

网站首页 > 编程文章 正文

Makefile学习

zazugpt 2025-04-24 10:23:41 编程文章 9 ℃ 0 评论

权威参考:
http://www.gnu.org/software/make/manual

make是一个能自动的判断一个大型程序的哪些源代码需要重新编译的工具,并且能够根据判断结果自动调用编译器编译源代码,按照一定的顺序,将编译结果整合成可执行程序。

makefile不是一行一行顺序执行的

Makefile根据写的规则会构建一个依赖树,根据时间戳判断是否需要编译。

隐含规则:

Makefile会自动寻找.c文件生成对应的.o文件

基础知识

目标:依赖列表

命令列表(可以多条)

目标可以是文件,也可以是标签(ALL、clean等),

每一条规则中,目标后的依赖可以为空(目标不一定要有依赖),每一条规则后面也不一定需要命令。但是目标和依赖不能同时没有。

当想要自己指定Makefile文件名时,在执行时,需要使用make -f 文件名

在命令前加上@符号,可以取消打印这条命令本身@gcc test.c -o test

一般在echo语句前加@,加不加@区别如下:

src=$(wildcard ./*.c)#src=myadd.c mysub.c mymyth.c

obj=$(patsubst %.c,%.o,$(src))#obj =myadd.o mysub.o mymyth.o

ALL:mymath

mymath:$(obj)
	gcc $^ -o $@
	@echo "hello world"

$(obj):%.o:%.c
	gcc -c lt; -o $@


clean:
	-rm -rf $(obj) mymath

.PHONY:clean ALL

makefile中注释是#,但是不能在命令开始用#,不然他会连着#一起打出来

src=$(wildcard ./*.c)#src=myadd.c mysub.c mymyth.c

obj=$(patsubst %.c,%.o,$(src))#obj =myadd.o mysub.o mymyth.o

ALL:$(obj)
	gcc $^ 
	#echo "hello world"

$(obj):%.o:%.c
	gcc -c lt; -o $@


clean:
	-rm -rf $(obj) mymath

.PHONY:clean ALL

神奇的a.out,在上面的all命令语句中没有指定生成的目标,它就自己生成了a.out。

变量赋值方式:

A:=B:立即展开赋值,

A=B:延迟展开赋值

A?=B :条件赋值,若A之间没有被附过值,将B的值赋值给A,否则A保持原值

A+=B:追加赋值,将B的值连接在A之后,空格分开。

B:=$A 
A=10

all:
	@echo "A=$A"
	@echo "B=$B"
	@echo "C=$C"
	@echo "D=$D"
	@echo "E=$E"
C=$A 
A=20
D?=$A
E=5
E+=$A

特殊变量$@、$^、

特殊变量$@、$^、lt;、$、$*、$?

lt;、$、$*、$?

$:当前执行的进程的进程编号

all:f1 f2 f3
	@echo '$@ = ' $@
	@echo '$^ = ' $^
	@echo '$< = ' lt;
	@echo '$$ = ' $$ #单引号不进行解析
	@echo "$$ = " $$ #双引号进行解析
	@echo $@

f1:
	@touch f1
f2:
	@touch f2
f3:
	@touch f3

隐含规则

make -p:查看make的隐含规则

上面的规则其实就是隐含规则。

如果我们没有显示写明某些规则,make会去隐含规则中去查询是否有生成这个规则中的目标的规则存在,如果有就会调用,如果没有就会报错。

$(SRC:%.c=%.o)的含义:将SRC变量中所有以.c结尾的文件名替换成对应的以.o结尾的文件名,然后赋回给SRC

%可以省略:$(SRC:.c=.o)

SRC=a.c b.c c.h d.h
all:
	@echo $(SRC:.c=.o)
	@echo $(SRC)
	

头文件更新与include

Makefile中头文件的更新不会导致make重新编译,可以通过include把目标需要的依赖(gcc -MM -I 源文件.c)包含进Makefile中,让头文件更新后,make时,对应的目标文件重新编译生成。

Makefile中include作用

  1. 对于一些通用的变量定义、通用规则,写在一个文件中,任意目录结构中的makefile想要使用这些通用的变量或规则时,include指定的文件就好了,而不用在每个makefile中又重写一遍。
  2. 对于源文件自动生成依赖文件(makefile之目录搜索&自动依赖)时,将这些个依赖关系保存成文件,在需要使用时include进来,这样少了人为的干预,同时也减少的错误的发生。

包含的目标如果没有,会在Makefile中找对应的规则去生成。

例如查看main.o需要的头文件源文件依赖:记住要加-I指定头文件位置,不然会报错。

TARGET = mymath
SRC=$(wildcard ./*.c)#src=myadd.c mysub.c mymyth.c

#obj=$(patsubst %.c,%.o,$(src))#obj =myadd.o mysub.o mymyth.o


CUR_DIR = $(shell pwd)#指定当前目录(变量定义的时候注意不要在变量后面多输入空格,否则空格也会被当作变量的内容,好坑阿)
HEAD_DIR = $(CUR_DIR)/../inc  #头文件位置 
#HEAD_DIR = $(shell pwd)/../inc  #头文件位置

#CROSS_COMPILER = arm-linux- #指定交叉编译环境

CC = $(CROSS_COMPILER)gcc  #编译器
CFLAGS=-I$(HEAD_DIR) #编译参数


ALL:$(TARGET)

$(TARGET):$(SRC:.c=.o)
	gcc $^ -o $@ $(CFLAGS)

$(obj):%.o:%.c
	gcc -c lt; -o $@ $(CFLAGS)

%.d:%.c
#       @echo $(HEAD_DIR)
	$(CC) -MM -I$(HEAD_DIR) lt; >$@  #查看.c文件对应的.o文件所依赖的头文件,源文件,重定向到.d文件。

#下面这三行作用一样,把.o文件的依赖包含进来,显示指明了.o文件所依赖的头文件,所以头文件改变后,对应的目标文件会进行重新编译。
#include $(SRC:.c=.d)
#-include $(SRC:.c=.d)
sinclude $(SRC:.c=.d)

clean:
	-rm -rf $(SRC:.c=.o) $(SRC:.c=.d) $(TARGET)

.PHONY:clean ALL

当修改头文件myadd.h后,make时会进行对应的更新

include中包含的内容可能会影响最终生成的目标,如果include中包含了有目标内容的话。

https://blog.csdn.net/q_z_r_s/article/details/80790560

include包含的内容如下:

myadd.o:myadd.c
	gcc -c myadd.c -o myadd.o -I../inc
	@echo "helloworld"

Makefile中内容如下:

include test.d
all:mymath.o

mymath.o:mymath.c
	gcc  -c mymath.c -o mymath.o -I../inc

include放在all前面,就会生成myadd.o,放在all后面就会生成mymath.o

Makefile中变量定义时空格问题

在Makefile中定义变量的时候,注意不要在变量值后面敲空格,否则,它会把空格也当成变量内容的一部分。

a = helloworld
b = helloworld #这里我敲了一个空格
all:
	@echo $(a)/
	@echo $(b)/

Makefile中变量变量前括号问题

MakeFile中的变量定义 一般在我们书写Makefile时,各部分变量引用的格式我们建议如下:

1. make变量(Makefile中定义的或者是make的环境变量)的引用使用“$(VAR)”格式。

2. 出现在规则命令行中shell变量(一般为执行命令过程中的临时变量,它不属于Makefile变量,而是一个shell变量)引用使用shell的“$tmp”格式。

3. 对出现在命令行中的make变量我们同样使用“$(CMDVAR)” 格式来引用。

层级Makefile

主Makefile:制定规则来说明如何在当前目录下生成最终目标文件。

加一个伪目标clean SUB _DIR

很奇怪,为什么上面程序中$(.^:=/$(SUB_TGT)),不是将原来的值覆盖了,而是接在了后面。

sub=/hello
test=/hello
test:=/world

all:$(sub)
	@echo $(^:=/world)
	@echo $(sub:=/world)
	@echo $(test)

$(sub):
	@echo "123"
#顶层Makefile
TARGET = a.out#指定最终生成的目标文件

SUB_DIR = mymath myadd#指定子目录

export SUB_TGT = built-in.o#指定子目标

export TOP_DIR = $(shell pwd)#当前工作目录

export HEAD_DIR = $(TOP_DIR)/inc#指定头文件路径

#export CROSS_COMPILER = arm-linux-#指定交叉编译环境

export CC = $(CROSS_COMPILER)gcc#指定编译器

export CFLAGS = -I$(HEAD_DIR) -Wall#指定编译参数

export LD = ld#指定链接器

export LDFLAGS =#指定链接选项

#生成终极目标规则
$(TARGET):$(SUB_DIR)
	$(CC) $(CFLAGS) $(^:=/$(SUB_TGT))

#子目标生成规则,告诉make,进入到对应的子目录中去生成字目标
$(SUB_DIR):
	make -C $@
#-C,让make进入到后面的指定目录中

clean:
	-rm -vf $(TARGET)
	for dir in $(SUB_DIR); do \
		make -C $dir clean;\
	done

.PHONY: clean $(SUB_DIR)#记得加上子目标,不然子目录已经存在,可能不会去生成子目标



#mymath-Makefile
SRCS = mymath.c
SUB_DIR =

#生成当前目录下子目标规则,由当前目录下的.c文件生成的.o文件和当前目录下的子目录中的目标生成
$(SUB_TGT):$(SRCS:.c=.o) $(SUB_DIR)
	$(LD) $(LDFLAGS) $(SRCS:.c=.o) $(SUB_DIR:=/$(SUB_TGT))\
		-r -o $@

%.o:%.c
	$(CC) $(CFLAGS) lt; -c

%.d:%.c
	$(CC) $(CFLAGS) lt; -MM > $@
sinclude $(SRCS:.c=.d)

$(SUB_DIR):
	make -C $@

clean:
	-rm -vf $(SUB_TGT) $(SRCS:.c=.o) $(SRCS:.c=.d)
	for dir in $(SUB_DIR); do\
		make -C $dir clean;\
	done
.PHONY: clean $(SUB_DIR)


#myadd-Makefile
SRCS = myadd.c
SUB_DIR = mysub

#生成当前目录下子目标规则,由当前目录下的.c文件生成的.o文件和当前目录下的子目录中的目标生成
$(SUB_TGT):$(SRCS:.c=.o) $(SUB_DIR)
	$(LD) $(LDFLAGS) $(SRCS:.c=.o) $(SUB_DIR:=/$(SUB_TGT))\
		-r -o $@

%.o:%.c
	$(CC) $(CFLAGS) lt; -c

%.d:%.c
	$(CC) $(CFLAGS) lt; -MM > $@
sinclude $(SRCS:.c=.d)

$(SUB_DIR):
	make -C $@

clean:
	-rm -vf $(SUB_TGT) $(SRCS:.c=.o) $(SRCS:.c=.d)
	for dir in $(SUB_DIR); do\
		make -C $dir clean;\
	done
.PHONY: clean $(SUB_DIR)



#mysub-Makefile
SRCS = mysub.c
SUB_DIR =

#生成当前目录下子目标规则,由当前目录下的.c文件生成的.o文件和当前目录下的子目录中的目标生成
$(SUB_TGT):$(SRCS:.c=.o) $(SUB_DIR)
	$(LD) $(LDFLAGS) $(SRCS:.c=.o) $(SUB_DIR:=/$(SUB_TGT))\
		-r -o $@

%.o:%.c
	$(CC) $(CFLAGS) lt; -c

%.d:%.c
	$(CC) $(CFLAGS) lt; -MM > $@
sinclude $(SRCS:.c=.d)

$(SUB_DIR):
	make -C $@

clean:
	-rm -vf $(SUB_TGT) $(SRCS:.c=.o) $(SRCS:.c=.d)
	for dir in $(SUB_DIR); do\
		make -C $dir clean;\
	done
.PHONY: clean $(SUB_DIR)

Makefile中export作用

Makefile中有两种export,一种是Makefile中自己的,一种是shell的。

Makefile中自己的export就是用来export变量,让子Makefile中可以使用这个变量,同级的Makefile中不能使用

export varibale=hello#或者

varibale=hello

export variable

export导入系统环境变量?

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表