如何避免这个递归 Makefile 重新链接?

How to avoid this recursive Makefile to relink?

今天我请求你帮助解决一个让我发疯的 Makefile。就是这样:

# Executable name
NAME = libft.a

# Compiler and archive linker settings
CC = gcc
AR = ar
CFLAGS = -Wall -Wextra -Werror -O3 -g3
ARFLAGS = -rsc
IFLAGS = -I./includes/

# Project layout
SRC_DIR = ./src/
INC_DIR = ./inc/
OBJ_DIR = ./obj/

OBJ = $(shell grep -r .o ./obj | awk '{print $}' | tr '\n' ' ')

.PHONY: all clean fclean re

#------------------------------------------------------------------------------#

all: $(OBJ_DIR) $(NAME)

$(OBJ_DIR):
    mkdir -p $(OBJ_DIR)

$(NAME): compile $(OBJ) $(INC_DIR)libft.h
    @echo "Linking library $(NAME).\n"
    @$(AR) $(ARFLAGS) $(NAME) $(OBJ)
    @echo "  ✧ $(AR) $(ARFLAGS) $(NAME) object files: OK! √\n"

compile:
    make -C src/io
    make -C src/lists
    make -C src/memory
    make -C src/strings
    make -C src/tests

我已经尝试了依赖项、规则等的多种组合,但我就是不明白。有时我让它停止重新链接,但在那些情况下它不会重新编译目标文件,因为 $(OBJ) 是空的并且在我 运行 编译后没有更新。

这个版本接近好,但每次我 运行 make 它执行配方 $(NAME) 并执行 ar -rsc %(OBJ) ..我怎么能把它们依赖于 $(NAME) ?

嗯,基本上你这里的整个方法都不会成功。仅举一个例子:您正在尝试使用 grep 查找目标文件(老实说,我根本不理解 shell 命令;从输出中打印 </code> 字是什么意思<code>grep -r do ??? 你不就是说 find $(OBJ_DIR) -name \*.o 吗?)这将扩展到在你的子目录中找到的所有目标文件。但是,shell 命令在您的顶级 makefile 被 解析 时运行,并且该解析发生在 before make 运行任何规则.. . 所以,还没有构建目标文件!所以,这个目标不依赖于任何东西。即使在构建了一些目标文件之后,它也只依赖于已经存在的目标文件,而不依赖于在构建过程中创建的目标文件。

真的,如果我是你,我会完全不同地做这件事。但是,使您的 makefile 正常工作的最简单方法是也使用递归 make 来构建 $(NAME);像这样更改您的 makefile:

all: compile

$(NAME): $(OBJ) $(INC_DIR)libft.h
        @echo "Linking library $(NAME).\n"
        @$(AR) $(ARFLAGS) $@ $^
        @echo "  ✧ $(AR) $(ARFLAGS) $@ object files: OK! √\n"

compile:
        mkdir -p $(OBJ_DIR)
        $(MAKE) -C src/io
        $(MAKE) -C src/lists
        $(MAKE) -C src/memory
        $(MAKE) -C src/strings
        $(MAKE) -C src/tests
        $(MAKE) $(NAME)

这里all不依赖$(NAME);相反,compile 步骤首先构建所有内容,然后在最后递归调用自身来构建 $(NAME);此时我们知道一切都是最新的,我们可以依赖现有的目标文件。

其他:注意我在这里使用了自动变量$^而不是$(OBJ);该变量是一个运行 shell 脚本的简单变量:它很昂贵!每次扩展 $(OBJ) 变量都会付出代价,所以你只想做一次。或者,您可以使用 := 来设置 OBJS,这样每个 make 实例只调用一次。这仍然比您需要的时间多一次,但避免这种情况会很痛苦。

我还将 mkdir 移到了 compile 规则中。它比 all.

的先决条件更干净

最后,您不应该直接使用 make 命令调用子 make。始终使用 $(MAKE) 变量,否则各种事情将无法正常工作。

这个问题很明显已经被前面的 post 解决了。

您需要使用 $(MAKE) 变量通过 $(NAME) 规则递归调用您的 make 文件,而不是在后续调用后将 $(NAME) 作为 all 依赖项再次使用 $(MAKE) 变量添加到您的底层 Makefile。