Linux C项目makefile模板

时间:2022-06-09 08:54:26

最近比较的系统学习了下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常用选项,再具体则可以查对应的官方的手册。