最近比较的系统学习了下makefile,网上查了很多资料,但是很多都是大同小异,没有一个称心的小型 c项目的比较通用的makefile模板。这里个人总结了一个 c项目的 makefile 模板,先放上模板,后面再具体解释几个关键点。
c makefile 参考模板
下面的模板比较短,源文件在 src 目录,编译输出在 output 目录。这个makefile文件保存在src、output同级的目录下,shell 中 cd 到这个目录执行 make 即可, 记得加上 -s 选项,不必要输出所有的命令。这里的src和output可以自己指定保存在相应的变量中。总体的思想就是利用源文件 .c
文件集合推导出所有的.d
、.o
文件, 利用 gcc 的 -MM
选项配合 makefile 的 -include
机制自动推到头文件依赖。
# c makefile template
SRC_DIR = src
OUTPUT_DIR = output
TARGETS = mytest.bin
SRCS = $(wildcard $(SRC_DIR)/*.c)
DEPS = $(addprefix $(OUTPUT_DIR)/, $(patsubst %.c, %.d, $(notdir $(SRCS))))
OBJS = $(addprefix $(OUTPUT_DIR)/, $(patsubst %.c, %.o, $(notdir $(SRCS))))
CFLAGS = -Wall -O2
RM = rm -f -v
.PHONY: all
all: $(OUTPUT_DIR)/$(TARGETS)
$(OUTPUT_DIR)/%.d: $(SRC_DIR)/%.c
$(CC) -MM $(CFLAGS) $< | sed 's,$*\.o[ :]*,$(basename $@).o $@: ,g' > $@; \
echo "compile .d file: $@"
-include $(DEPS)
$(OUTPUT_DIR)/%.o: $(SRC_DIR)/%.c
$(COMPILE.c) $(OUTPUT_OPTION) $<; \
echo "compile .o file: $@"
$(OUTPUT_DIR)/$(TARGETS): $(OBJS)
$(LINK.o) $(OUTPUT_OPTION) $^; \
echo "compile .bin file: $@ OK!"
.PHONY: clean
clean:
$(RM) output/*; \
echo "clean ok!"
自动处理头文件依赖
利用 makefile 的 include 机制配合 GCC 的 -MM 选项来实现。具体可以参考这篇文章自动处理头文件的依赖关系。makefile执行时,会先去include文件,如果没有会报warning,加上include
前面加上-
符号就不会告警了,然后make看是否有规则生成include文件,有就去生成,然后重新加载。有更新导致include的文件更新了也会重新加载。
这里生成依赖文件时好多资料都是先输出到一个临时文件中,个人感觉也可以不用生成临时文件,直接对字符流用管道重定向处理,最终生成类似这样的x.o x.d: x.c x.h a.h ...
这样的dependency 文件即可。这里主要用到了 sed
对指定的内容做了替换,生成.d
文件的这个命令显示吧makefile的自动化命令展开,然后执行这个 shell 命令。
makefile中的变量
首先看一下这篇介绍,makefile的变量。重点理解一下变量的定义和展开机制,以及一些内置变量的默认值,还有自动化变量的用法。这里只摘录过来一些重点:
-
$@
,表示规则中的目标。 -
$<
,表示规则中的第一个条件。 -
$?
,表示规则中所有比目标新的条件,组成一个列表,以空格分隔。 -
$^
,表示规则中的所有条件,组成一个列表,以空格分隔。 -
$*
, 表示目标模式中%
所代表的部分。
这里重点注意下 $*
是否有包含了目录名。不确定的地方可以自己在命令中 echo 出来,明确后再继续编写命令。个人在 mac os下测试如下: gcc -MM src/test.c
的输出是test.o: src/test.c src/test.h
,前面的test.o没有带目录名。
这条语句$(OUTPUT_DIR)/%.d: $(SRC_DIR)/%.c
下面使用的 $*
也没有包括目录名只有文件名。
一些内置变量的值如下:
-
CC
c编译器的名字,缺省值是cc
。 -
CFLAGS
c编译器的选项,没有定义。 -
LD
链接器的名字,缺省值是ld
。 -
LDFLAGS
链接器的选项,没有定义。 -
TARGET_ARCH
和目标平台相关的命令行选项,没有定义。 -
OUTPUT_OPTION
输出的命令行选项,缺省值是-o $@
。 -
COMPILE.c
编译.c文件的命令行,缺省值是$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
。 -
LINK.o
把.o文件链接在一起的命令行,缺省值是$(CC) $(LDFLAGS) $(TARGET_ARCH)
。 -
LINK.c
把.c文件链接在一起的命令行,缺省值是$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
。 -
RM
删除命令的名字,缺省值是rm -f
。
makefile 中的常用函数
常用的函数也不是很多,大都是字符和文件名操作。可以参考 GNU 官方手册,上面的例子主要使用了文件名操作的相关函数,这里给出官方的说明链接 File-Name-Functions。
有需要直接Google GUN的官方手册,也非常方便。
GCC常用选项
编写makefile的话,还需要熟悉GCC常用选项,再具体则可以查对应的官方的手册。