为什么makefile表现出非确定性行为?

时间:2017-05-25 05:05:24

标签: makefile

我有一个尝试执行以下操作的makefile:使用.c和.s扩展名识别当前目录下的所有文件(包括所有子目录),为每个文件编译一个非链接的目标文件并将其放入进入目录。所有C文件最终都在objects / c中,所有汇编文件最终都在objects / ass中。

makefile在第一次执行时始终按预期工作(所有命令都按正确的顺序调用),并且不会产生错误。

然而,如果我再次打电话给make,有一半时间我会“为'全部'做点什么。”这是您所期望的,因为没有文件被修改过。但另一半时间,make是选择一个随机的汇编文件并编译该文件。也就是说,如果我继续做“make”,我有时会编译file1.s有时是file2.s。并且它在组装文件之间随机交换添加无限(它永远不会达到“无所事事”)状态。

如何展示非确定性行为?

这是我可以制作的最小的makefile,它可以重现错误:

SRC_C = $(wildcard *.c) $(wildcard **/*.c)

SRC_ASS = $(wildcard *.s) $(wildcard **/*.s)

OBJECTS_C = $(addprefix $(OBJECT_DIR)c/, $(notdir $(SRC_C:.c=.o)))
OBJECTS_ASS = $(addprefix $(OBJECT_DIR)ass/, $(notdir $(SRC_ASS:.s=.o)))
OBJECTS = $(OBJECTS_C) $(OBJECTS_ASS)

OBJECT_DIR = objects/


all: $(OBJECTS)

%/:
    mkdir $@

$(OBJECTS_C): $(OBJECT_DIR) $(OBJECT_DIR)c/
    arm-none-eabi-gcc -O0 -march=armv8-a $(wildcard */$(@F:.o=.c)) -nostartfiles -c -o $@

$(OBJECTS_ASS): $(OBJECT_DIR) $(OBJECT_DIR)ass/
    arm-none-eabi-as -march=armv8-a $(wildcard */$(@F:.o=.s)) -c -o $@

.PHONY: clean
clean:
    rm -rf $(OBJECT_DIR)

1 个答案:

答案 0 :(得分:3)

这里有很多错误。

最大的概念是:通过将所有目标文件展平到一个目录中,无法使用模式规则表达正确的依赖关系,因此您的目标文件并不真正依赖于它们各自的源文件。我要说:不要那样做!拥有对象目录很好,但它们应该镜像源树的目录结构。

进一步的错误:

  • 直接取决于目录。这将不会按预期工作,目录应始终是仅订单依赖项,如注释
  • 中所述
  • Make不支持递归通配符 - 如果你真的需要它,你可以编写自己的函数,或者假设你总是在* nix上构建,只需调用find而不是
  • 创建目录的模式规则也不是最好的主意 - 我建议收集变量中所有需要的目录并循环遍历。

文体改进:

  • 使用:=
  • 分配不需要延期评估的变量
  • 使用?=分配影响构建过程的变量,以便用户可以在命令行覆盖它们
  • 使用"标准" CCASCROSS_COMPILE
  • 等变量
  • .PHONY
  • 中声明所有虚假目标

应用了这些更改的Makefile如下所示:

OBJECT_DIR ?= objects
C_OBJECT_DIR ?= $(OBJECT_DIR)/c
AS_OBJECT_DIR ?= $(OBJECT_DIR)/ass

SRC_C:= $(shell find -name \*.c)
SRC_ASS:= $(shell find -name \*.s)

OBJECTS_C:= $(addprefix $(C_OBJECT_DIR)/, $(SRC_C:.c=.o))
OBJECTS_ASS:= $(addprefix $(AS_OBJECT_DIR)/, $(SRC_ASS:.s=.o))
OBJECTS:= $(OBJECTS_C) $(OBJECTS_ASS)
OUTDIRS:= $(sort $(dir $(OBJECTS)))

CROSS_COMPILE ?= arm-none-eabi-
CC ?= gcc
AS ?= as
CFLAGS ?= -O0 -march=armv8-a -nostartfiles
ASFLAGS ?= -march=armv8-a

all: $(OBJECTS)


$(OUTDIRS):
    $(foreach _dir,$@,mkdir -p $(_dir);)

$(C_OBJECT_DIR)/%.o: %.c | $(OUTDIRS)
    $(CROSS_COMPILE)$(CC) -c -o $@ $(CFLAGS) $<

$(AS_OBJECT_DIR)/%.o: %.s | $(OUTDIRS)
    $(CROSS_COMPILE)$(AS) -c -o $@ $(ASFLAGS) $<

clean:
    rm -rf $(OBJECT_DIR)

.PHONY: all clean

请注意,缺少一件重要的事情:自动依赖。使用此Makefile,每个目标文件都依赖于其各自的源文件,但完全错过了包含的任何头文件。对于除了简单玩具之外的任何东西,你应该补充一点,google for&#34; gnu make gcc automatic dependencies&#34;或类似的东西(不是问题的范围)。