当前位置: 首页 > news >正文

GNU make笔记

是看跟我一起写makefile做的一点笔记。

编译和链接

在Makefile中定义整个编译流程以及各个目标文件与源文件之间的依赖关系,并且只重新编译新的修改会影响到的部分,从而降低编译的时间。

编译只检查函数、变量是否被声明,并生成.o(Linux)或.obj(Windows)文件。由于编译生成的中间目标文件太多,因此通常会将其打包成为.a(Linux)或.lib(Windows)文件,以进行链接。

依赖关系

target ... : prerequisites ...command...
  • target:可以是一个目标文件,可执行文件,或一个标签。
  • prerequisites:target所依赖的文件和target。
  • command:该target要执行的shell命令。

注意makefile中的command必须以Tab开头。
另外,在执行有顺序性要求的命令时,不应该分写两行,而应该用分号隔开,例如:

	# 错误写法!pwd将在本目录执行cd ../pwd# 正确写法cd ../; pwd

makefile会检查文件的更新时间,当prerequisites中有文件比target新时,command所定义的命令就会被执行。

以1个.h文件 + 2个.c文件为例:

# myProcess是可执行文件
myProcess : main.o somec.ogcc -o myProcess main.o somec.omain.o : main.c somec.hgcc -c main.c
somec.o : somec.c somec.hgcc -c somec.cclean :rm myProcess main.o somec.o

注意,这里的clean是一个标签而非文件,使用类似make clean这样的形式来执行标签后的命令。

makefile会以完成第一个target为目标,逐次展开依赖,去更新涉及到的依赖的文件。

变量

将上例改为用变量的形式:

OBJ = main.o somec.o
TARGET = myProcess
CC = gcc$(TARGET) : $(OBJ)$(CC) -o $(TARGET) $(OBJ)main.o : main.c somec.h$(CC) -c main.c
somec.o : somec.c somec.h$(CC) -c somec.cclean :rm $(TARGET) $(OBJ)

也可以将clean改为下面这种写法:

.PHONY : clean
clean :-rm $(TARGET) $(OBJ)

其中,.PHONY显式地表示clean是一个伪目标,即表明clean不是需要检查依赖的目标。
另外,rm前的-表示执行时即使出错,也继续执行命令。

一般更推荐使用:=来赋值变量,与=的区别在于,前者立即展开,而后者在使用时才会展开。

定义空格变量

使用如下的方法定义空格变量:

nullstring :=
space := $(nullstring) #end of line

nullstring是空变量,其后加一个空格,在用注释表示结束,即可将space展开为一个空格变量。

变量的替换

例如:

var1 := a.o b.o
var2 := $(var1:.o=.c)

表示var2是将var1中的.o替换为.c,即var2为a.c b.c。

override

override强制修改变量,例如:

var := var1
override var1 := var2

多行变量

define multi_line_var
xxx
xxx
endef

目标变量

在目标后的依赖里写变量,表示不管全局上该变量如何,在此目标引发的行为中变量都是这样的,例如:

CFLAGS := -o
# 由prog引发的行为中CFLAGS都是-g
prog : CFLAGS = -g

自动变量

使用模式规则来定义一个隐含规则,需要在目标中使用%。目标中的%取决于依赖中的值。
而自动化变量,就会把模式中所定义的一系列的文件自动地挨个取出。

下面是一些常用的自动变量:

自动变量含义
$@规则中的目标文件名
$<第一个依赖文件
$^所有依赖文件
$*匹配通配符%的部分

make自动推导

make看到xx.o,可以自动地推导出它依赖同名的xx.c,并且gcc -c xx.c也会被自动加上。
由此,上例又可以省略为:

OBJ = main.o somec.o
TARGET = myProcess
CC = gcc$(TARGET) : $(OBJ)$(CC) -o $(TARGET) $(OBJ)main.o : somec.h
somec.o : somec.hclean :rm $(TARGET) $(OBJ)

包含其他makefile文件

默认情况下,make会寻找命名为makefile和Makefile的文件,当然也可以是其他任意命名的makefile文件,不过需要使用make -f <filename>来显式地指定。

在一个makefile中使用include <filenames>来包含其他makefile文件,

文件搜索

默认情况下,makefile只会在当前目录搜索文件。

  • 使用VPATH变量指明其他的目录用于搜索。
    例如VPATH = src:../headers,其中src和headers都是搜索目录,它们之间用:隔开。
  • 使用vpath关键字。
    例如vpath %.h ../headers表示在headers下搜索所有以.h结尾的文件。

一次生成多个程序

按基本的写法,makefile只会从第一行,即第一个生成程序开始查找依赖,如果后面还有程序要生成则可能被忽略。
可以使用伪目标来达成这一目的:

all : prog1 prog2
.PHONY : allprog1 : prog1.ogcc -o prog1 prog1.oprog2 : prog2.ogcc -o prog2 prog2.o

声明一个伪目标all,它依赖于两个程序,但由于伪目标不会被生成文件,因此all总是需要更新其依赖,也就总是生成这多个程序。

条件判断

使用ifeq, ifneq, ifdef, ifndef来进行条件判断,并且都以endif结尾。
make读取Makefile时就计算条件表达式的值,并根据其值来选择语句,所以,不要把自动化变量放入条件表达式中,因为自动化变量是在运行时才有的。

函数

格式为$(function argument1,argument2)。函数和参数以空格隔开,参数见以逗号隔开。
下面介绍一些常用的函数。

字符串函数

  • subst
    $(subst from,to,text),把字符串text中的from替换为to。
  • patsubst
    $(patsubst pattern,replacement,text),模式替换,把text中的单词pattern替换为replacement。
  • strip
    $(strip string),去空格
  • filter
    $(filter pattern,text),只保留text中符合pattern模式的单词。
    例如```
sources := a.c b.o c.s
$(filter %.c %.o,$(sourecs))
返回a.c b.o
  • sort
    $(sort string),给string中的单词排序,注意会去重。
  • word
    $(word n,string),取string中的第n个单词。
  • words
    $(words string),统计单词个数。

文件函数

  • dir
    $(dir names),从文件名列表names中取出目录,例如$(dir /src/foo.c temp)返回/src/ ./
  • suffix
    $(suffix names),从names中取出各个文件的后缀。
  • basename
    同上,只不过取的是前缀名。

控制流函数

  • foreach
    $(foreach var,list,expr),把list中的单词拿出来赋给var,然后执行expr。expr每次的返回会以空格隔开,最后组成整个函数的返回值。
    例如:$(foreach n,a b,$(n).o)返回a.o b.o
    注意var是一个局部变量,其作用域只在foreach中。
  • if
    $(if condition,then-part,else-part),else-part可以省略。
  • shell
    $(shell expr),执行一个shell命令作为返回值。
  • error
    $(error text),停止make并报错text。
  • warning
    同上,不过不退出make而是继续执行。

总结

GNU make的执行步骤如下:

  1. 读入所有的Makefile。
  2. 读入被include的其它Makefile。
  3. 初始化文件中的变量。
  4. 推导隐式规则,并分析所有规则。
  5. 为所有的目标文件创建依赖关系链。
  6. 根据依赖关系,决定哪些目标要重新生成。
  7. 执行生成命令。

第三步并非一开始就将所有变量展开,而是其出现在依赖关系中且要被使用了才展开。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • open clip论文阅读摘要
  • MES系统防呆措施之具体场景学习
  • Kafka入门
  • 搭建WAMP网站教程(Windows+Apache+MySQL+PHP)
  • 【Python】11 Conda常用命令
  • 1.5 微信Native支付 - 申请退款、查询退款、退款通知、账单
  • vue3+setup 解决:this.$refs引用子组件报错 is not a function
  • 【数据结构】单链表
  • 【hcie-cloud】【2】华为云Stack解决方案介绍、缩略语整理 【下】
  • SpringCloudAlibaba系列之Nacos配置管理
  • 视频转码教程:轻松制作GIF动态图,一键高效剪辑操作
  • Luatos Air700 改变BL0942串口波特率
  • 深度学习理论知识入门【EM算法、VAE算法、GAN算法】和【RBM算法、MCMC算法、HMC算法】
  • 合并两个有序链表OJ
  • 浮点数保留指定位数的小数,小数位自动去掉多余的0
  • (三)从jvm层面了解线程的启动和停止
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • C++入门教程(10):for 语句
  • CSS 专业技巧
  • Docker入门(二) - Dockerfile
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • ReactNativeweexDeviceOne对比
  • 对象管理器(defineProperty)学习笔记
  • 新书推荐|Windows黑客编程技术详解
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • ​​​​​​​​​​​​​​Γ函数
  • #我与Java虚拟机的故事#连载08:书读百遍其义自见
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (12)目标检测_SSD基于pytorch搭建代码
  • (CVPRW,2024)可学习的提示:遥感领域小样本语义分割
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (done) 两个矩阵 “相似” 是什么意思?
  • (SpringBoot)第七章:SpringBoot日志文件
  • (vue)el-cascader级联选择器按勾选的顺序传值,摆脱层级约束
  • (windows2012共享文件夹和防火墙设置
  • (备份) esp32 GPIO
  • (附源码)ssm高校实验室 毕业设计 800008
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (顺序)容器的好伴侣 --- 容器适配器
  • (四)【Jmeter】 JMeter的界面布局与组件概述
  • (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
  • .NET BackgroundWorker
  • .NET WPF 抖动动画
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • .NET简谈设计模式之(单件模式)
  • .NET项目中存在多个web.config文件时的加载顺序
  • .project文件
  • [ C++ ] 继承
  • [ 隧道技术 ] cpolar 工具详解之将内网端口映射到公网
  • [2015][note]基于薄向列液晶层的可调谐THz fishnet超材料快速开关——
  • [AI]文心一言爆火的同时,ChatGPT带来了这么多的开源项目你了解吗
  • [ajaxupload] - 上传文件同时附件参数值