Makefile 8

您所在的位置:网站首页 makefile依赖关系树 Makefile 8

Makefile 8

2023-12-27 01:50| 来源: 网络整理| 查看: 265

1. 有多个不同的程序,由不同目录下的几个独立的Makefile来描述其重建规则。它们需要使用一组通用的变量定义或者模式规则。通用的做法是将这些共同使用的变量或者模式规则定义在一个文件中(没有具体的文件命名限制),在需要使用的Makefile中使用指示符“include”来包含此文件。2. 当根据源文件自动产生依赖文件时;我们可以将自动产生的依赖关系保存在另外一个文件中,主Makefile使用指示符“include”包含这些文件。这样的做法比直接在主Makefile中追加依赖文件的方法要明智的多。其它版本的make已经使用这种方式来处理。如 果 指 示 符 “ include ” 指 定 的 文 件 不 是 以 斜 线 开 始 ( 绝 对 路 径 , 如/usr/src/Makefile...),而且当前目录下也不存在此文件; make将根据文件名试图在以下几个目录下查找:首先,查找使用命令行选项“-I”或者“--include-dir”

指定的目录,如果找到指定的文件,则使用这个文件;否则继续依此搜索以下几个目录(如果其存在):“/usr/gnu/include”、“/usr/local/include”和“/usr/include”。当在这些目录下都没有找到“include”指定的文件时,make将会提示一个包含文件未找到的告警提示,但是不会立刻退出。而是继续处理Makefile的后续内容。当完成读取整个Makefile后,make将试图使用规则来创建通过指示符“include”指定的但未找到的文件(参考 3.7 makefile文件的重建 一节),当不能创建它时(没有创建这个文件的规则),make将提示致命错误并退出。

通常我们在 Makefile 中可使用“-include”来代替“include”,来忽略由于包含文件不存在或者无法创建时的错误提示(“-”的意思是告诉 make,忽略此操作的错误。make 继续执行)。

我们改成-include之后:

这样就没有提示找不到那个目录或文件了,但是我们必须确保有规则去创建include指定的内容,否则最后将出错。

make的执行过程如下:1. 依次读取变量“MAKEFILES”定义的makefile文件列表2. 读取工作目录下的makefile文件(根据命名的查找顺序“GNUmakefile”,“makefile”,“Makefile”,首先找到那个就读取那个)3. 依次读取工作目录makefile文件中使用指示符“include”包含的文件4. 查找重建所有已读取的makefile文件的规则(如果存在一个目标是当前读取的某一个makefile文件,则执行此规则重建此makefile文件,完成以后从第一步开始重新执行)5. 初始化变量值并展开那些需要立即展开的变量和函数并根据预设条件确定执行分支6. 根据“终极目标”以及其他目标的依赖关系建立依赖关系链表7. 执行除“终极目标”以外的所有的目标的规则(规则中如果依赖文件中任一个文件的时间戳比目标文件新,则使用规则所定义的命令重建目标文件)8. 执行“终极目标”所在的规则

知道了include优先于本Makefile的目标运行之后,来看我们的complicated项目:

1 .PHONY: all clean 2 3 MKDIR = mkdir 4 RM = rm 5 RMFLAGS = -rf 6 7 CC=gcc 8 9 DIR_OBJS=objs 10 DIR_EXES=exes 11 DIR_DEPS=deps 12 13 DIRS =$(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS) 14 15 EXE=complicated 16 EXE:=$(addprefix $(DIR_EXES)/,$(EXE)) 17 SRCS=$(wildcard *.c) 18 OBJS=$(SRCS:.c=.o) 19 OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS)) 20 DEPS=$(SRCS:.c=.dep) 21 DEPS:=$(addprefix $(DIR_DEPS)/,$(DEPS)) 22 23 all: $(EXE) 24 25 include $(DEPS) 26 27 $(DIRS): 28 $(MKDIR) $@ 29 $(EXE):$(DIR_EXES) $(OBJS) 30 $(CC) -o $@ $(filter %.o,$^) 31 $(DIR_OBJS)/%.o:$(DIR_OBJS) %.c 32 $(CC) -o $@ -c $(filter %.c,$^) 33 $(DIR_DEPS)/%.dep:$(DIR_DEPS) %.c 34 @echo "Creating $@ ..." 35 @set -e;\ 36 $(RM) $(RMFLAGS) [email protected];\ 37 $(CC) -E -MM $(filter %.c,$^) > [email protected];\ 38 sed 's,\(.*\)\.o[:]*,objs/\1.o:,g' $@;\ 39 $(RM) $(RMFLAGS) [email protected] 40 clean: 41 $(RM) $(RMFLAGS) $(DIRS)

这里增加了filter函数,具体可以看前面函数那一篇随笔。正如前面所提及的,当make看到include指令时会师徒去构建所需包含进来的依赖文件,这样就不必在显式地让all目标依赖它了。这也是我举第一个例子的原因,有了include,make会自动去 构建 依赖。所以,在complicated项目中,我们在每一个依赖项之前都添加了一个先决条件,这个先决条件就是每一个依赖的目录。

 

需要指出地是,上面的代码可能会无限循环。

如果你的编译器安装在FAT32文件系统上,将可以运行不会无限循环,但是如果是在NTFS文件系统上,会死循环。笔者的Linux上是无限循环了。

出现无限循环的原因和文件系统有关,有的文件系统当目录中的文件被更改时,目录时间戳随之更改,由于在Makefile中创建依赖关系时,制定了deps目录是其第一个先决条件,于是,deps目录时间戳地改变使得make又一次使用规则再次创建main.dep 和foo.dep,这样造成了无限循环。

既然发现了问题,证明我们这个Makefile存在bug,需要更改,基本思路是:

如果deps目录不存在,则让deps目录成为规则的第一个先决条件;

如果deps目录已经存在,则不让deps目录出现在规则的先决条件中。

沿着这个思想走下去,需要用到Makefile中的条件语法。

 关键字“ ifeq”此关键字用来判断参数是否相等,格式如下:`ifeq (ARG1, ARG2)`ifeq 'ARG1' 'ARG2''`ifeq "ARG1" "ARG2"'`ifeq "ARG1" 'ARG2''`ifeq 'ARG1' "ARG2"'替换展开“ ARG1”和“ ARG1”后,对它们的值进行比较。如果相同则(条件为真)将“ TEXT-IF-TRUE”作为 make 要执行的一部分,否则将“ TEXT-IF-FALSE”作为 make 要执行的一部分(上边的第二种格式)。

还有ifdef,ifndef和ifeq,ifneq用法类似。

关键字“ ifdef”关键字“ ifdef”用来判断一个变量是否已经定义。格式为:`ifdef VARIABLE-NAME'如果变量“ VAEIABLE_NAME”的值非空(在 Makefile 中没有定义的变量的值为空),那么表达式为真,将“ TEXT-IF-TRUE”作为 make 要执行的一部分。否则,表达式为假,如果存在“ TEXT-IF-FALSE”,就将它作为 make 要执行一部分。当一个变量没有被定义时,它的值为空。“ VARIABLE-NAME”可以是变量或者函数的引用。对于“ ifdef”需要说明的是: ifdef 只是测试一个变量是否有值,不会对变量进行替 换 展 开 来 判 断 变 量 的 值 是 否 为 空 。 对 于 变 量 “ VARIABLE-NAME ” , 除 了“ VARIABLE-NAME=”这种情况以外,使用其它方式对它的定义都会使“ ifdef”返回真。就是说,即使我们通过其它方式(比如,定义它的值引用了其它的变量)给它赋了一个空值,“ ifdef”也会返回真。我们来看一个例子:例1:bar =foo = $(bar)ifdef foofrobozz = yeselsefrobozz = noendif例 2:foo =ifdef foofrobozz = yeselsefrobozz = noendif例 1 中的结果是:“ frobozz = yes”;而例 2 的结果是:“ frobozz = no”。其原因就是在例 1 中,变量“ foo”的定义是“ foo = $(bar)”。虽然变量“ bar”的值为空,但是“ ifdef”判断的结果是真。因此当我们需要判断一个变量的值是否为空的情况时,需要使用“ ifeq”(或者“ ifneq”)而不是“ ifdef”。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3