IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> GNU make文档个人解读(详略得当) -> 正文阅读

[系统运维]GNU make文档个人解读(详略得当)

翻看到最前面的一篇文章:比手写makefile好用一万倍CMake!,CMake无疑是一个很好的自动编译链接工具,但是从某种程度上来说,用了别人封装好的东西,就会失去很多主动性,被束缚住了,因此,对于原装的make的了解,是必要的,我一直坚信,懂底层方为上。

如果在Windows环境下,如果你追求快速开发,可以跳过这篇文章,VS的内置编译环境和CMAKE和QMAKE诸如此类的应用封装的确实完整,我个人是不喜欢VS的,每次有问题看其源代码总是感觉怪怪的,可能它是P.J.Plauger版本?我也不懂,在QT下开发的话qmake和cmake已经很完善了,几乎不用拓展什么了。

本篇文章尽量在一个星期内更完,以GNU make英文文档为参考,我尽量做到详略得当。

有关于GNU的可参考GPL协议和GNU系统

引用比尔盖茨在2004年的一句话

“个人计算机是人类迄今为止创造的最强大工具,我认为这种说法并无偏颇”。摘自《计算机体系结构-量化研究方法》

慢慢更新哈~

写Makefile

包含其他Makefile文件

include告诉make暂停读取当前Makefile文件,而去读取其他Makefile文件。

include filename...

include的一种使用场景就是当有几个Makefile各自处理它们的程序时,需要使用一个相同的变量定义或者模块规则。

# my.mk
CF=lh

# Makefile
include my.mk

foo:
ifdef CF
	echo "yes"
else
	echo "no"
endif
# output yes

另一种使用场景是当想从源文件自从生成条件时。在Makefile中,依赖关系可能依赖一大堆文件,比如main.o依赖于main.cdefs.h,而在代码中我们可能这么写main.o:main.c,即.c改变时.o会被重新编译,但.h不会影响.o,所以需要建立.o.h的依赖,一般编译器在编译代码时会自动寻找,即加上-M或者-MM参数

cc -MM main.c                                                                                                                          [0]
main.o: main.c

所以一般我们这么做

sources = foo.c bar.c

include $(sources:.c=.d)

如下示例

cc -MMD main.c
cat main.d                                                                                                                             [0]
main.o: main.c def.h

当读取文件有错误的时候,可以使用-include不理那些错误

如何阅读一个Makefile文件

写规则

两种写法

# 第一种写法
targets : prerequisites
        recipe
        …
# 第二种写法
targets : prerequisites ; recipe
        recipe
        …

特殊目标

名称描述
.PHONY当他作为一个target的时候,make 会无条件的运行它,无论具有该名称的文件是否存在或其最后修改时间是什么。
.SUFFIXES用于检查后缀规则的后缀列表,过时了,不看了
.DEFAULT当木目标存在时,则使用他作为一个默认目标文件
.PRECIOUS所依赖的目标被给予以下特殊处理:当make运行中断,目标文件不会被删除,同时,如果目标文件是一个中间文件,也不会被删除
.INTERMEDIATE没影响
.SECONDARY所依赖的目标被视为中间文件,除了它们永远不会自动删除
.SECONDEXPANSION
.DELETE_ON_ERROR
.IGNORE
.LOW_RESOLUTION_TIME
.SILENT
.EXPORT_ALL_VARIABLES
.NOTPARALLEL
.ONESHELL
.POSIX

示例

# 由于 gao 是一个前提条件,但是 makefile中没有一个名字为 gao的目的。所以符合 .DEFAULT 目的的执行条件
all:gao
    @echo "final"

.DEFAULT:
	@echo "In default"

如何使用变量

变量定义方式

immediate = deferred  # 基本的赋值
immediate ?= deferred  # 如果没有被赋值过就赋予等号后面的值
immediate := immediate  # 覆盖之前的值
immediate ::= immediate  # 等于:=
immediate += deferred or immediate  # 添加等号后面的值
immediate != immediate  # 查看赋值的数是否不等

=和:=的区别:make一般会将整个makefile展开后,再决定变量的值,变量的值将会是整个makefile中最后被指定的值,这个是’=‘,而’':="表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。

其中的?=等于

ifeq($(origin FOO), undefined)
	FOO=bar
endif

变量引用的高级功能

替换引用用您指定的更改替换变量的值

foo := a.o b.o l.a c.o
# 让.o的后缀结尾的都修改为.c后缀结尾的
bar := $(foo:.o=.c)  # bar=a.c b.c l.a c.c

计算变量名

重载一个变量

override var=value

未定义变量:如果要清除变量,将其值设置为空通常就足够了,但在origin中对于空和undefine 还是有区别的。

undefine foo
undefine bar

模板匹配变量值

此处要谈论的是%符号

特殊变量

MAKEFILE_LIST:包含每一个被make解析的Makefile文件名

name1:=$(lastword $(MAKEFILE_LIST))

all:
	echo $(name1)
# output
# echo Makefile
# Makefile

.DEFAULT_GOAL:如果没有定义target将使用默认的目标

ifeq ($(.DEFAULT_GOAL),)
$(warning no default goal is set)
endif

.PHONY: main
main:;@echo $@

$(warning default goal is $(.DEFAULT_GOAL))
# output
# Makefile:2: no default goal is set
# Makefile:8: default goal is main
# main
名称作用描述
MAKE_RESTARTS它将包含此实例重新启动的次数
MAKE_TERMOUT
MAKE_TERMERR这些值可用于来确定 make 本身是否正在写入终端
.RECIPEPREFIX
.VARIABLES
.FEATURES
.INCLUDE_DIRS
.EXTRA_PREREQS

Makefile的条件部分

控制语句

ifeq...else...endif

如下例子

lib=
foo:
ifeq ($(origin $(lib)), undefined)
	echo "yes" 
else
	echo "no"
endif
# output 
# echo yes
# yes

条件定义

ifdef var
else
endif

常见函数

函数这部分我不想写大量的篇幅去写,函数这部分可以说是一个API,是功能性的,是要大家通过不断的实践去摸索的,要用什么,怎么用,什么时候用,都要不断的实践。

函数语法

$(function arguments)
# or
${function arguments}

字符串替换函数

# 对文本文本执行文本替换:每次出现的 from 都替换为 to
$(subst from,to,text)

# 在文本中查找与模式匹配的空格分隔的单词并将它们替换为替换,同$(var:suffix=replacement)
$(patsubst pattern,replacement,text)

# 从字符串中删除前导和尾随空格,并用单个空格替换一个或多个空格字符的每个内部序列
$(strip string)

# 寻找某个值,如果存在,返回它,如果不存在,返回空
$(findstring find,in)

# 返回文本中匹配任何模式单词的所有空格分隔的单词,删除任何不匹配的单词
$(filter pattern…,text)

# 返回文本中所有不匹配任何模式单词的空格分隔单词,删除匹配一个或多个单词
$(filter-out pattern…,text)

# 按词汇顺序对列表中的单词进行排序,去除重复的单词
$(sort list)

# 返回文本的第 n 个单词。
$(word n,text)

# 返回文本中以单词 s 开头并以单词 e(包括)结尾的单词列表。
$(wordlist s,e,text)

# 返回文本中的单词数
$(words text)

# 返回参数名称的第一个参数
$(firstword names…)

# 返回参数名称的最后一个参数
$(lastword names…)

文件名相关函数

# 提取名称中每个文件名的目录部分
$(dir names…)

# 提取名称中每个文件名的目录部分以外的所有内容。
$(notdir names…)

# 提取names中每个文件名的后缀
$(suffix names…)

# 提取名称中每个文件名的后缀以外的所有内容。
$(basename names…)

# 给文件名加上一个后缀
$(addsuffix suffix,names…)

# 给文件名后缀加上一个前缀
$(addprefix prefix,names…)

# 逐字连接两个参数:连接的两个第一个词(每个参数一个)形成结果的第一个词,两个第二个词形成结果的第二个词,依此类推
$(join list1,list2)

# 通配符的结果是以空格分隔的与模式匹配的现有文件的名称列表
$(wildcard pattern)

# 对于名称中的每个文件名,返回一个不包含任何文件的绝对名称
$(abspath names…)

# 对于名称中的每个文件名,返回规范的绝对名称。
$(realpath names…)

条件控制相关函数

$(if condition,then-part[,else-part])

$(or condition1[,condition2[,condition3…]])

$(and condition1[,condition2[,condition3…]])

循环相关函数

$(foreach var,list,text)

示例如下

dirs := a b c d

# 遍历dirs,取出每个元素dir并获取每个目录下的文件名
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))

文件相关函数

file 函数允许makefile 写入或读取文件。有两种写方式:覆盖写(override)和添加写(append),默认情况下当文件不存在时会自动创建。

$(file op filename[,text])

调用相关函数

$(call variable,param,param,…)

这里的函数的一个模拟的概念,如下一个函数和使用

reverse = $(2) $(1)  # 类似于反转函数

foo = $(call reverse,a,b) # 调用反转函数

其他函数

origin :不修改变量的值,只是查看变量的信息

$(origin variable)
  • undefined:未定义变量
  • default:默认变量
  • environment:环境变量
  • environment override:环境重载变量
  • file:这个变量被定义在Makefile文件内部
  • override:这个变量在一个Makefile被重载
  • automatic:自动定义变量

flavor :类似于origin 函数,

$(flavor variable)
  • undefined:未定义变量
  • recursive:递归扩展变量
  • simple:简单扩展变量

管理make行为函数

# 发出error警告
$(error text…)

# 发出warning警告
$(warning text…)

# 打印相关信息
$(info text…)

脚本函数

$(shell str...)

隐式规则

自动生成变量

变量描述
$@表示这个目标文件
$%仅是函数库中,表示规则中的目标成员名
$<条件序列的第一个条件
$?所有比目标新的依赖目标的集合,以空格分割
$^条件序列
$+可获取在条件序列中的重复条件
$按空格分割条件序列
$*表示目标中’%'及其之前的部分

make常见错误

make中产生致命错误的一般前面会有***

实践例子

例1:使用通配符编译所有的.c文件为.o文件和可执行文件

f=$(wildcard *.c)
fo=$(f:.c=.o)
fp=$(fo:.o=)

all: $(fo) $(fp)

%.o: %.c
	${CC} -c $^ -o $@ 
%: %.o
	${CC} $^ -o _$@

例2:从.d、.c、.h、.o生成顺序来生成目标文件

f=$(wildcard *.c)  # .o文件
fo=$(f:.c=.o)  # .o文件
.PHONY: all
all: $(fo)

%.o: %.c
	gcc -c -g -Wall $< -o $@ -MD -MF $*.d -MP
	
.PHONY: clean
clean:
	rm -f *.d *.o *.out

注意此时的.d文件使用位置是在gcc中的,也可以放在依赖文件中,但是考虑到编译位置,所以最好还是使用$*.d

参数说明:

  • -M:生成文件的依赖关系,同时也把一些标准库的头文件包含了进来,在GNU下是-MM
  • -MG:要求把缺失的头文件按存在对待,并且假定他们和源文件在同一目录下,必须和 ‘-M’ 选项一起用。
  • -MF:当使用了 “-M” 或者 “-MM” 选项时,则把依赖关系写入名为 “File” 的文件中。若同时也使用了 “-MD” 或 “-MMD”,“-MF” 将覆写输出的依赖文件的名称
  • -MD:等同于 -M -MF File,但是默认关闭了 -E 选项
  • -MP:生成的依赖文件里面,依赖规则中的所有 .h 依赖项都会在该文件中生成一个伪目标,其不依赖任何其他依赖项。
  • -MMD:类似于 “-MD”,但是输出的依赖文件中,不包含标准头文件

-E选项是GCC的生成预处理文件的选项。

当然,如果需要每个.o文件都生成一个可执行文件

ff=$(f:.c=)
.PHONY: all
all: $(fo) $(ff)

%:%.o
	gcc $^ -o _$@
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 12:01:58  更:2022-05-05 12:02:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 17:46:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码