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

为什么makefile表现出不确定性行为?,makefile,Makefile,我有一个makefile,它试图执行以下操作:使用.c和.s扩展名标识当前目录(包括所有子目录)下的所有文件,为每个文件编译一个未链接的对象文件并将其放入目录中。所有C文件都以对象/C结尾,所有部件文件都以对象/ass结尾 makefile在第一次执行时始终按预期工作(所有命令都按正确的顺序调用),并且不会产生错误 然而,如果我再打电话给make,有一半的时间我会“对‘所有人’什么都不做”。这是您所期望的,因为没有修改任何文件。但另一半的时间,make是选择一个随机的程序集文件并编译该文件。也就

我有一个makefile,它试图执行以下操作:使用.c和.s扩展名标识当前目录(包括所有子目录)下的所有文件,为每个文件编译一个未链接的对象文件并将其放入目录中。所有C文件都以对象/C结尾,所有部件文件都以对象/ass结尾

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

然而,如果我再打电话给make,有一半的时间我会“对‘所有人’什么都不做”。这是您所期望的,因为没有修改任何文件。但另一半的时间,make是选择一个随机的程序集文件并编译该文件。也就是说,如果我继续做“make”,我有时编译file1.s,有时编译file2.s。并且它在程序集文件之间保持随机交换add infinity(它永远不会达到“无事可做”)状态

如何做出令人兴奋的非确定性行为

这是我能创建的最小的生成文件,它会复制错误:

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)

你这里有很多错误

最大的问题是概念上的问题:通过将所有对象文件放在一个目录中,无法使用模式规则表达适当的依赖关系,因此对象文件并不真正依赖于它们各自的源文件。我会说:别那么做!拥有对象目录很好,但它们应该镜像源树的目录结构

其他错误:

  • 直接依赖于目录。这将不会像预期的那样工作,目录应该始终是顺序依赖项,正如注释中所述
  • Make不支持递归通配符——如果确实需要,您可以编写自己的函数,或者,假设您总是在*nix上构建,只需调用
    find
  • 创建目录的模式规则也不是最好的主意——我建议在一个变量中收集所有需要的目录,并循环使用
风格改进:

  • 使用
    :=
  • 使用
    ?=
    分配影响生成过程的变量,以便用户可以在命令行中重写它们
  • 使用“标准”变量,如
    CC
    AS
    CROSS\u 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
OBJECT\u DIR?=对象
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))
对象:=$(对象C)$(对象ASS)
OUTDIRS:=$(排序$(目录$(对象)))
交叉编译?=arm none eabi-
CC?=gcc
作为
CFLAGS?=-O0-march=armv8-a-nostartfiles
ASFLAGS?=-march=armv8-a
全部:$(对象)
$(OUTDIRS):
$(foreach_dir,$@,mkdir-p$(_dir);)
$(C_OBJECT_DIR)/%.o:%.C|$(OUTDIRS)
$(交叉编译)$(CC)-c-o$@$(CFLAGS)$<
$(AS_OBJECT_DIR)/%.o:%.s |$(OUTDIRS)
$(交叉编译)$(AS)-c-o$@$(ASFLAGS)$<
清洁:
rm-rf$(对象目录)
.骗子:都是干净的

注意,缺少一件重要的事情:自动依赖关系。使用此Makefile,每个对象文件都依赖于其各自的源文件,但完全忽略了包含的任何头文件。除了一个简单的玩具之外,你还应该加上一句,谷歌搜索“gnu make gcc automatic dependencies”或类似的东西(不是这个问题的范围)。

使用
--trace
开关(假设gnu make)让
make
显示其逻辑,以便在你仍然有问题时决定是否重建,尝试生成一个实际显示问题的最小生成文件根据跟踪,它在第一次成功编译后再次编译的原因是因为目录对象?您没有指定除目录以外的任何依赖项,在任何情况下都应该指定目录。您似乎还在将每种类型的所有源文件编译为单个目标文件,这是有意的吗?^这很不寻常,通常每个.c文件都有一个.o文件