如何避免这个递归 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。
今天我请求你帮助解决一个让我发疯的 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。