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
相关推荐
Yana.nice17 分钟前
openssl将证书从p7b转换为crt格式
java·linux
AI逐月22 分钟前
tmux 常用命令总结:从入门到稳定使用的一篇实战博客
linux·服务器·ssh·php
想逃离铁厂的老铁24 分钟前
Day55 >> 并查集理论基础 + 107、寻找存在的路线
java·服务器
小白跃升坊1 小时前
基于1Panel的AI运维
linux·运维·人工智能·ai大模型·教学·ai agent
跃渊Yuey1 小时前
【Linux】线程同步与互斥
linux·笔记
杨江1 小时前
seafile docker安装说明
运维
舰长1151 小时前
linux 实现文件共享的实现方式比较
linux·服务器·网络
好好沉淀1 小时前
Docker开发笔记(详解)
运维·docker·容器
zmjjdank1ng1 小时前
Linux 输出重定向
linux·运维
路由侠内网穿透.1 小时前
本地部署智能家居集成解决方案 ESPHome 并实现外部访问( Linux 版本)
linux·运维·服务器·网络协议·智能家居