如何用Makefile逐个编译C源文件?批量与逐个编译效果是否一致?
问题解答
1. 是否可以通过Makefile实现逐个编译C源文件
完全可以。你当前的Makefile之所以会一次性编译全部源文件,是因为规则写法有误:
- 你写的
$(OBJECTS): $(SRCS)规则表示每一个.o目标文件都依赖所有的.c源文件,只要任意一个源文件改动,所有.o都会被判定为过期需要重新生成 - 规则下的编译命令直接将所有SRCS传入gcc,自然会触发单条命令全量编译,完全没有用到Make的增量编译能力
修正方案
Make本身内置了从.c文件生成同名.o文件的隐式编译规则,会自动逐个执行gcc $(CFLAGS) -c 对应源文件.c -o 对应目标文件.o,你只需要删掉错误的全量编译规则,正确声明依赖即可,修正后的完整Makefile参考如下:
NAME = libft.a SRCS = ft_isalnum.c ft_isalpha.c ft_isascii.c ft_isdigit.c ft_isprint.c ft_strlen.c ft_bzero.c ft_memcpy.c ft_memset.c ft_memmove.c ft_strlcpy.c ft_strlcat.c ft_toupper.c ft_tolower.c ft_strchr.c ft_strrchr.c ft_strncmp.c ft_memchr.c ft_memcmp.c ft_strnstr.c ft_atoi.c ft_calloc.c ft_strdup.c ft_substr.c ft_strjoin.c ft_strtrim.c ft_split.c ft_itoa.c ft_strmapi.c ft_striteri.c ft_putchar_fd.c ft_putstr_fd.c ft_putendl_fd.c ft_putnbr_fd.c SRC_BONUS = ft_lstnew.c ft_lstadd_front.c ft_lstsize.c ft_lstlast.c ft_lstadd_back.c ft_lstdelone.c ft_lstclear.c ft_lstiter.c ft_lstmap.c OBJECTS = $(SRCS:.c=.o) OBJ_BONUS = $(SRC_BONUS:.c=.o) CFLAGS = -Wall -Wextra -Werror # 替换为你实际的头文件名,让所有目标文件依赖头文件,改头文件时自动触发重编 HEADER = libft.h all: $(NAME) $(NAME): $(OBJECTS) ar rc $(NAME) $(OBJECTS) # 所有目标文件依赖公共头文件 $(OBJECTS) $(OBJ_BONUS): $(HEADER) bonus: $(OBJECTS) $(OBJ_BONUS) ar rc $(NAME) $(OBJECTS) $(OBJ_BONUS) # 补充常用的清理规则 clean: rm -f $(OBJECTS) $(OBJ_BONUS) fclean: clean rm -f $(NAME) re: fclean all
如果你想显式声明编译规则而不使用内置隐式规则,可以添加模式规则实现逐个编译,效果完全一致:
%.o: %.c gcc $(CFLAGS) -c $< -o $@
其中$<是自动变量,代表当前规则的第一个依赖(即对应的单个.c源文件),$@代表当前要生成的目标(即对应的单个.o文件)。
这种写法下,Make会自动按需逐个编译源文件:如果只修改了某一个.c文件,下次执行make时只会重编译这一个文件,不会全量重编所有源文件,编译效率会高很多。
2. 一次性全量编译和逐个编译生成的目标文件效果是否一致
只要使用的编译选项、编译器版本、源码内容完全一致,两种方式生成的.o文件功能上完全等价,最终打包出来的静态库运行效果没有任何差异。
两者仅存在使用层面的区别:
- 一次性全量编译时,gcc单次进程处理所有传入的源文件,只要任意一个文件存在语法错误,整个编译流程会直接终止,不会生成任何.o文件
- 逐个编译时,gcc会单独处理每个源文件,哪个文件存在错误就只终止对应文件的编译,其他无错误的源文件可以正常生成.o文件
- 逐个编译可以配合Make的增量编译能力,日常开发时只重编修改过的文件,大幅减少不必要的重复编译耗时,是C项目Makefile的标准写法。
内容的提问来源于stack exchange,提问作者JOKITON
相关产品推荐
相关产品推荐

