翻看到最前面的一篇文章:比手写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.c
和defs.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 _$@
今天的文章gnu makefile_word文档怎么加密分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/66951.html