Linux工程管理文件Makefile变量类型详解-进阶篇

1.立即变量和延迟变量

在Makefile中最容易让人引困惑的是立即变量和延迟变量。

立即变量和延迟变量是按展开时间来划分的。

立即变量使用 := 操作符进行赋值,在解析阶段就直接展开了,顾名思义,立即展开变量。

延迟变量则是使用 = 操作符进行赋值,在make解析Makefile阶段不会立即展开,而是等到实际使用这个变量时才展开,获得其真正的值。

复制代码
a = 1
b = 2
val_a := $(a)
val_b  = $(b)
a = 10
b = 20
test:    
	echo $(val_a)    
	echo $(val_b)

在上面的例子中,val_a是立即变量,当make解析到:=赋值符号时,会把(a)变量的值立即赋值给val_a,虽然后面a的值发生了变化,但val_a因为已经展开,所以值就不再发生变化。而val_b则不同,因为是延迟展开变量,所以,当make解析到 = 符号时,并没有立即把(b)的值赋值给val_b,而是在运行echo命令时才对其展开,因为此时b的值已经是20,所以$(val_b)的值也是20

复制代码
wit@pc:/home/makefile/# makeecho 11echo 2020

立即展开变量一般用在规则中的目标、目标依赖中。make在解析Makefile阶段,需要这些变量有确切的值来构建依赖关系树。

一个项目中的文件依赖关系在程序编译期间是固定不变的,因此需要立即变量在解析阶段就要有明确的值,立即展开。

延迟展开变量一般用在规则的命令行中,这些变量在make编译过程中被引用到才会展开,获得其实际的值。

2.自动变量

在makefile中,大家经常会见到类似@、^、$<这种类型的变量。这种变量一般称为自动变量,自动变量是局部变量,作用域范围在当前的规则内,它们分别代表不同的含义:

  • $@:目标
  • $^:所有目标依赖
  • $<:目标依赖列表中的第一个依赖
  • $?:所有目标依赖中被修改过的文件

有了这些自动变量,我们就可以改进下面的Makefile:

复制代码
.PHONY: clean
CC  = gcc
BIN = a.out
OBJS = hello.o
OBJS += module.o
$(BIN): $(OBJS)    

@echo "start compiling..."    
@echo $(OBJS)    
	$(CC) -o $(BIN) $(OBJS)    
@echo "compile done"hello.o: hello.c    
	$(CC) -c -o hello.o hello.c
module.o: module.c    
	$(CC) -c -o module.o module.c
clean:    
rm -f $(BIN) $(OBJS)

将这个Makefile中命令行中的目标和目标依赖分别使用@和^代替,Makefile就变成了下面的样子:

复制代码
.PHONY: clean
CC  = gcc
BIN = a.out
OBJS = hello.o module.o

$(BIN): $(OBJS)    
	@echo "start compiling..."    
	@echo $(OBJS)    
	$(CC) -o $@ $^    
	@echo "compile done"hello.o: hello.c    
	$(CC) -c -o $@ 
	$^module.o: module.c    
	$(CC) -c -o 
	$@ $^
clean:    
	rm -f $(BIN) $(OBJS)

除了上面几个常用的自动变量外,还有一些自动变量不太常用,但是大家在以后阅读Makefile时可能会遇到,比如:

  • %:当规则的目标是一个静态库文件时,%代表静态库的一个成员名
  • +:类似^,但是保留了依赖文件中重复出现的文件
  • \*:在模式匹配和静态模式规则中,代表目标模式中%的部分。比如hello.c,当匹配模式为%.c时,*表示hello
  • $(@D):表示目标文件的目录部分
  • $(@F):表示目标文件的文件名部分
  • $(*D):在模式匹配中,表示目标模式中%的目录部分
  • $(*F):在模式匹配中,表示目标模式中%的文件名部分
  • : :告诉make在编译时忽略所有的错误
  • @: :告诉make在执行命令前不要显示命令

3.环境变量

除了用户自定义的一些变量,make在解析Makefile中还会引入一些系统环境变量,如编译参数CFLAGS、SHELL、MAKE等。这些变量在make开始运行时被载入到Makefile文件中,因为是全局性的系统环境变量,所以这些变量对所有的Makefile都有效。若Makefile中有用户自定义的同名变量,系统环境变量将会被用户自定义的变量覆盖。若用户在命令行中传递跟系统环境变量同名的变量,系统环境变量也会被传递的同名变量覆盖。

复制代码
.PHONY:all
CFLAGS = -gall:    
@echo "CFLAGS = $(CFLAGS)"    
@echo "SHELL = $(SHELL)"   
@echo "MAKE = $(MAKE)"    
@echo "HOSTNAME = $(HOSTNAME)"

在上面的Makefile中,默认情况下,echo会打印各个系统变量的值:

复制代码
wit@pc:/home/makefile/demo
# makeCFLAGS = -g
SHELL = /bin/sh
MAKE = make
HOSTNAME =

如果我们在执行make命令时,给Makefile传递一个同名的变量HOSTNAME=zixue.com,系统环境变量就会被这个同名变量覆盖,Makefile实际打印的值就变成了传递的同名变量的值:

复制代码
wit@pc:/home/makefile/demo# make HOSTNAME=zixue.comCFLAGS = -gSHELL = /bin/shMAKE = makeHOSTNAME = zixue.com

4.变量替换

4.1字符串替换

复制代码
.PHONY: all
SRC := main.c sub.c
OBJ := $(SRC:.c=.o)all:    
@echo "SRC = $(SRC)"    
@echo "OBJ = $(OBJ)"

执行make命令,运行结果为:

复制代码
# makeSRC = main.c sub.cOBJ = main.o sub.o

4.2模式匹配替换

使用匹配符%匹配变量,使用 % 保留变量值中的指定字符串,然后其他部分使用指定字符串代替。

复制代码
.PHONY: all
SRC := main.c sub.c
OBJ := $(SRC:%.c=%.o)all:    
@echo "SRC = $(SRC)"    
@echo "OBJ = $(OBJ)"

执行make命令,运行结果为:

复制代码
# makeSRC = main.c sub.cOBJ = main.o sub.o
相关推荐
见闻小天地3 小时前
从晶片工艺到可靠性验证:晶威特TF-3215型RTC晶振全面解析
运维·业界资讯
沸速存储3 小时前
AI 机器人靠哪些内存与存储驱动整机运行?
服务器·科技·嵌入式硬件·电脑
码农小韩4 小时前
Linux应用开发(八)——TCP/IP网络编程基础
linux·嵌入式软件开发·linux操作系统·嵌入式操作系统·linux网络编程·linux应用
迷途之人不知返4 小时前
【进程】-5-进程优先级
linux
名字还没想好☜4 小时前
Docker 网络实战:bridge/host/none/自定义网络、容器互通与端口映射踩坑
运维·docker·kubernetes
fengyehongWorld4 小时前
Linux squid搭建基础代理服务器
linux·运维·服务器
木白CPP4 小时前
Linux DMA驱动详解(二)-----DMA的使用者
java·linux·运维
赵民勇5 小时前
systemd-socket-activate命令详解
linux·运维
跨境小彭5 小时前
Temu运营避坑:制造地点信息填写规范、后果及批量实操教程
大数据·运维·自动化·跨境电商·temu
Cx330❀5 小时前
【Linux网络】网络层 IP 协议:从网段划分到内核源码解析
大数据·linux·服务器·网络·tcp/ip·性能优化·langchain