make fclean && make all works, make re 不

make fclean && make all works, make re doesn't

我正在研究我在一本书中找到的高级 makefile。我里面有一些简单的规则: clean 删除二进制文件 fclean 也删除一些额外的文件(链接到 ln 生成的二进制文件) all 使所有 re 制作 fclean 然后制作 all

当我执行 make fclean 然后 make all 时,它完美地工作。当我执行 make re 时,出现错误:

error: unable to open output file '/Users/malberte/work/libft/bin/libft/common/ft_atoi.o':
      'No such file or directory'
1 error generated.

所以这是我的代码:

$(_MODULE_NAME)_OBJS := $(addsuffix $(_OBJEXT),$(addprefix $($(_MODULE_NAME)_OUTPUT)/,$(basename $(SRCS)))) $(DEPS)
$(_MODULE_NAME)_BINARY := $($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(BINARY_EXT)
$(_MODULE_NAME)_EXPOSE_BINARY := $(_ROOT)/$(BINARY)$(BINARY_EXT)

ifneq ($(_NO_RULES),T)
ifneq ($($(_MODULE_NAME)_DEFINED), T)

_CLEAN := clean-$(_MODULE_NAME)
_FCLEAN := fclean-$(_MODULE_NAME)
_ALL := all-$(_MODULE_NAME)
_RE := re-$(_MODULE_NAME)
_IGNORE := $(shell mkdir -p $($(_MODULE_NAME)_OUTPUT))

.PHONY: all re $(_ALL) $(_RE)
re: fclean all
# re: $(_RE)
# $(_RE): $(_FCLEAN) $(_ALL)

all: $(_ALL)
$(_ALL): $($(_MODULE_NAME)_BINARY)

.PHONY: $(_MODULE_NAME)
$(_MODULE_NAME): $($(_MODULE_NAME)_BINARY)

.PHONY: fclean clean $(_CLEAN)
fclean: $(_FCLEAN)
$(_FCLEAN): $(_CLEAN)
    rm -rf $($(patsubst fclean-%,%,$@)_EXPOSE_BINARY)
clean: $(_CLEAN)
$(_CLEAN):
    rm -rf $($(patsubst clean-%,%,$@)_OUTPUT)

$($(_MODULE_NAME)_OUTPUT)/%.o: $(_MODULE_PATH)/%.c
    @$(COMPILE.c) -o '$@' '$<'
$($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(_LIBEXT): $($(_MODULE_NAME)_OBJS)
    @if [ "$(LIBMERGE)" = "F" ]; \
    then \
        $(AR) r '$@' $^; \
        ranlib '$@'; \
    else \
        libtool -static -o '$@' $^; \
    fi
$($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(_EXEEXT): $($(_MODULE_NAME)_OBJS)
    $(LINK.c) $^ -o '$@'


$(_MODULE_NAME)_DEFINED := T
endif
endif

我已经尝试了很多东西,我真的不明白当我使用 make re 时发生了什么,它抛出了上面的错误。 有人有想法吗?

您的 makefile 中有这一行:

_IGNORE := $(shell mkdir -p $($(_MODULE_NAME)_OUTPUT))

在解析 makefile 时创建输出目录。然后你 运行 你的 clean 目标调用这个食谱:

rm -rf $($(patsubst clean-%,%,$@)_OUTPUT)

这会导致输出目录被删除。然后你 运行 你的 all 目标调用编译器并要求它将输出文件写入 $($(_MODULE_NAME)_OUTPUT)/%.o 但该目录不再存在。

所以编译器给你错误:

error: unable to open output file '...': No such file or directory

如果你 运行 make 两次,那么第一次清理并删除目录,然后当你 运行 make all 它会 运行 _IGNORE shell 命令并再次创建目录,这样它就会存在。

如果你 运行 make re 一次,那么 makefile 只被解析一次,输出目录只被创建一次(在它被删除之前)。

好的,非常感谢。这促使我思考 makefile 的真正工作原理,所以这里是我的基本解决方案,感谢您:

$(_MODULE_NAME)_OBJS := $(addsuffix $(_OBJEXT),$(addprefix $($(_MODULE_NAME)_OUTPUT)/,$(basename $(SRCS)))) $(DEPS)
$(_MODULE_NAME)_BINARY := $($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(BINARY_EXT)
$(_MODULE_NAME)_EXPOSE_BINARY := $(_ROOT)/$(BINARY)$(BINARY_EXT)

ifneq ($(_NO_RULES),T)
ifneq ($($(_MODULE_NAME)_DEFINED), T)

_OUTPUT_TREE := output-tree-$(_MODULE_NAME)
_CLEAN := clean-$(_MODULE_NAME)
_FCLEAN := fclean-$(_MODULE_NAME)
_ALL := all-$(_MODULE_NAME)
_RE := re-$(_MODULE_NAME)
# _IGNORE := $(shell mkdir -p $($(_MODULE_NAME)_OUTPUT))

.PHONY: all re $(_ALL) $(_RE)
re: fclean all
# re: $(_RE)
# $(_RE): $(_FCLEAN) $(_ALL)

all: $(_ALL)
$(_ALL): $($(_MODULE_NAME)_BINARY)


.PHONY: $(_MODULE_NAME)
$(_MODULE_NAME): $($(_MODULE_NAME)_BINARY)


.PHONY: fclean clean $(_CLEAN)
fclean: $(_FCLEAN)
$(_FCLEAN): $(_CLEAN)
    rm -rf $($(patsubst fclean-%,%,$@)_EXPOSE_BINARY)
clean: $(_CLEAN)
$(_CLEAN):
    rm -rf $($(patsubst clean-%,%,$@)_OUTPUT)

$($(_MODULE_NAME)_OUTPUT)/%.o: $(_MODULE_PATH)/%.c | $(_OUTPUT_TREE)
    @$(COMPILE.c) -o '$@' '$<'
$($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(_LIBEXT): $($(_MODULE_NAME)_OBJS)
    @if [ "$(LIBMERGE)" = "F" ]; \
    then \
        $(AR) r '$@' $^; \
        ranlib '$@'; \
    else \
        libtool -static -o '$@' $^; \
    fi
$($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(_EXEEXT): $($(_MODULE_NAME)_OBJS)
    $(LINK.c) $^ -o '$@'


.PHONY: output-tree $(_OUTPUT_TREE)
output-tree: $(_OUTPUT_TREE)
$(_OUTPUT_TREE):
    mkdir -p $($(_MODULE_NAME)_OUTPUT)

$(_MODULE_NAME)_DEFINED := T
endif
endif

我向原子目标添加了先决条件订单

$($(_MODULE_NAME)_OUTPUT)/%.o: $(_MODULE_PATH)/%.c | $(_OUTPUT_TREE)

规则如下:

.PHONY: output-tree $(_OUTPUT_TREE)
output-tree: $(_OUTPUT_TREE)
$(_OUTPUT_TREE):
    mkdir -p $($(_MODULE_NAME)_OUTPUT)

我会看看是否需要更多调整,但这似乎是正确的方法!