Makefile 中的自动依赖处理
Automatic dependency processing in Makefile
我有一个简单的 makefile。
IDIR =./include
CC=gcc
CFLAGS=-I$(IDIR)
SRCDIR = ./src
ODIR=obj
LDIR =./lib
LIBS=-lm
SRC = hellomake hellofunc
OBJ = ${SRC:%=$(ODIR)/%.o}
_DEPS = hellomake.h
DEPS = ${_DEPS:%=$(IDIR)/%}
$(ODIR)/%.o: $(SRCDIR)/%.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: $(OBJ)
gcc -o $@ $^ $(CFLAGS) $(LIBS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ hellomake
我不喜欢这个 Makefile 的是依赖生成。
.c.o
是否可以教 make 文件 .c 在 src 目录中,而 .o 在 obj 目录中以制作这个简单的文件?
.c.o:
$(CC) -c -o $@ $< $(CFLAGS)
header依赖
是否可以教make file当c文件包含的header个文件发生变化时自动重新编译所有c文件?
IDIR = ./include
CC = gcc
CFLAGS = -I$(IDIR)
SRCDIR = src
ODIR = obj
LDIR = ./lib
LIBS = -lm
SRC = hellomake hellofunc
OBJ = ${SRC:%=$(ODIR)/%.o}
_DEPS = hellomake.h
DEPS = ${_DEPS:%=$(IDIR)/%}
%.o: ../$(SRCDIR)/%.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: $(OBJ) $(DEPS)
gcc -o $@ $^ $(CFLAGS) $(LIBS)
预赛
正在生成依赖关系
gcc 的 -MM -MG 可用于创建依赖项:gcc -MM -MG src/hellofunc.c -I./include
将创建 hellofunc.o: src/hellofunc.c include/hellomake.h
。但是,我们需要将 .d 文件本身包含到 .d 文件中,因此我们使用 sed
来实现该目标。
gcc -MM -MG src/hellofunc.c -I./include | sed -e 's@^\(.*\)\.o:@.d .o:@'
结果如下:
hellofunc.d hellofunc.o: src/hellofunc.c include/hellomake.h
当我们想要更改内容以包含不同的目录位置时,我们可以修改sed 脚本。
gcc -MM -MG src/hellofunc.c -I./include | sed -e 's@^\(.*\)\.o:@obj/.d obj/.o:@'
结果如下:
obj/hellofunc.d obj/hellofunc.o: src/hellofunc.c include/hellomake.h
包括
-include $(DEPS)
将包含 DEPS
目录中的文件,include 前面的 -
教导 make 在目录不存在时忽略。
模式匹配和替换
我们可以替换任何模式 (%) 或以 c (%.c) 结尾的模式,如下所示:
OBJDIRS := $(patsubst %, $(OBJDIR)/%, $(MODULES))
OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(SRCS))
我们也可以有shorthand表格。
DEPS := $(OBJS:.o=.d)
过滤器
我们可以select只过滤部分文件,这是一个例子:
OBJ := $(patsubst %.c, %.o, $(filter %.c, $(SRC)))
方法一
在这个方法中,我们指定了依赖(.d 依赖于.c),并用 include
包含创建的依赖文件。
.PHONY: clean depend
CC := gcc
CXX := g++
LD := g++
CP := cp
PROG := hellomake
MODULES := src
OBJDIR := obj
default: $(PROG)
OPTFLAGS := -g -O
CFLAGS += -Wall -Wno-unused-function $(OPTFLAGS) $(patsubst %, -I%, $(MODULES))
GARBAGE := core core.* *.stackdump ./tags $(PROG)
include $(patsubst %, %/module.make, $(MODULES))
OBJ := \
$(patsubst %.c, %.o, $(filter %.c, $(SRC)))
DEP := $(OBJ:.o=.d)
# implicit rules
%.d: %.c
./depends.sh $(CC) `dirname $*.c` $(CFLAGS) $*.c > $@
-include $(DEP)
# Actual targets
depend: $(DEP)
clean:
rm -rf $(PROG) $(OBJ) $(GARBAGE) $(DEP) depends
$(PROG): $(OBJ)
$(LD) -o $@ $^ $(LIBS)
每个模块都应包含 make 文件 (module.make) 以指定 SRC 变量中包含哪些文件。
`SRC += src/hellofunc.c \
src/hellomake.c`.
这是生成依赖文件的脚本:
#!/bin/sh
#echo "## Got: $*"
CC=""
DIR=""
shift 2
case "$DIR" in
"" | ".")
$CC -MM -MG "$@" | sed -e 's@^\(.*\)\.o:@.d .o:@'
;;
*)
$CC -MM -MG "$@" | sed -e "s@^\(.*\)\.o:@$DIR/.d $DIR/.o:@"
;;
esac
这个方法很简单,就是用这些语句在同一个src目录下创建了目标文件和依赖文件。
SRC += src/hellofunc.c src/hellomake.c
OBJ := $(patsubst %.c, %.o, $(filter %.c, $(SRC)))
DEP := $(OBJ:.o=.d)
方法二
此方法创建对象目录,并将所有生成的(中间)文件放入该目录。
它征集所有模块来指定目标文件和依赖文件生成的操作。
示例中,我们只有一个模块,但是如果有多个模块,我们需要重复语句。
obj/src/%.o: src/%.c
$(CC) -c $< -o $@ $(CFLAGS)
obj/src/%.d: src/%.c
gcc -MM -MG $< $(CFLAGS) | sed -e 's@^\(.*\)\.o:@obj/src/.d obj/src/.o:@' > $@
这是生成文件:
.SUFFIX = .o .c
.PHONY: clean
CC := gcc
LD := gcc
PROG := hellomake
OBJDIR = obj
MODULES := src
SRCS := src/hellofunc.c src/hellomake.c
OBJDIRS := $(patsubst %, $(OBJDIR)/%, $(MODULES))
OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(SRCS))
DEPS := $(OBJS:.o=.d)
default: $(PROG)
CFLAGS += -Wall -Wno-unused-function $(OPTFLAGS) $(patsubst %, -I%, $(MODULES))
CXXFLAGS += $(CFLAGS)
obj/src/%.o: src/%.c
$(CC) -c $< -o $@ $(CFLAGS)
$(PROG): $(OBJDIRS) $(OBJS)
$(LD) $(filter %.o, $^) -o $(PROG)
-include $(DEPS)
obj/src/%.d: src/%.c
gcc -MM -MG $< $(CFLAGS) | sed -e 's@^\(.*\)\.o:@obj/src/.d obj/src/.o:@' > $@
depend: $(DEPS)
GARBAGE := core core.* *.stackdump ./tags $(PROG)
clean:
rm -rf $(PROG) obj/*
$(OBJDIRS):
mkdir -p $@
参考资料
- How to place object files in separate subdirectory
- make deleting dependency files
- http://scottmcpeak.com/autodepend/autodepend.html
我有一个简单的 makefile。
IDIR =./include
CC=gcc
CFLAGS=-I$(IDIR)
SRCDIR = ./src
ODIR=obj
LDIR =./lib
LIBS=-lm
SRC = hellomake hellofunc
OBJ = ${SRC:%=$(ODIR)/%.o}
_DEPS = hellomake.h
DEPS = ${_DEPS:%=$(IDIR)/%}
$(ODIR)/%.o: $(SRCDIR)/%.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: $(OBJ)
gcc -o $@ $^ $(CFLAGS) $(LIBS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ hellomake
我不喜欢这个 Makefile 的是依赖生成。
.c.o
是否可以教 make 文件 .c 在 src 目录中,而 .o 在 obj 目录中以制作这个简单的文件?
.c.o:
$(CC) -c -o $@ $< $(CFLAGS)
header依赖
是否可以教make file当c文件包含的header个文件发生变化时自动重新编译所有c文件?
IDIR = ./include
CC = gcc
CFLAGS = -I$(IDIR)
SRCDIR = src
ODIR = obj
LDIR = ./lib
LIBS = -lm
SRC = hellomake hellofunc
OBJ = ${SRC:%=$(ODIR)/%.o}
_DEPS = hellomake.h
DEPS = ${_DEPS:%=$(IDIR)/%}
%.o: ../$(SRCDIR)/%.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
hellomake: $(OBJ) $(DEPS)
gcc -o $@ $^ $(CFLAGS) $(LIBS)
预赛
正在生成依赖关系
gcc 的 -MM -MG 可用于创建依赖项:gcc -MM -MG src/hellofunc.c -I./include
将创建 hellofunc.o: src/hellofunc.c include/hellomake.h
。但是,我们需要将 .d 文件本身包含到 .d 文件中,因此我们使用 sed
来实现该目标。
gcc -MM -MG src/hellofunc.c -I./include | sed -e 's@^\(.*\)\.o:@.d .o:@'
结果如下:
hellofunc.d hellofunc.o: src/hellofunc.c include/hellomake.h
当我们想要更改内容以包含不同的目录位置时,我们可以修改sed 脚本。
gcc -MM -MG src/hellofunc.c -I./include | sed -e 's@^\(.*\)\.o:@obj/.d obj/.o:@'
结果如下:
obj/hellofunc.d obj/hellofunc.o: src/hellofunc.c include/hellomake.h
包括
-include $(DEPS)
将包含 DEPS
目录中的文件,include 前面的 -
教导 make 在目录不存在时忽略。
模式匹配和替换
我们可以替换任何模式 (%) 或以 c (%.c) 结尾的模式,如下所示:
OBJDIRS := $(patsubst %, $(OBJDIR)/%, $(MODULES))
OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(SRCS))
我们也可以有shorthand表格。
DEPS := $(OBJS:.o=.d)
过滤器
我们可以select只过滤部分文件,这是一个例子:
OBJ := $(patsubst %.c, %.o, $(filter %.c, $(SRC)))
方法一
在这个方法中,我们指定了依赖(.d 依赖于.c),并用 include
包含创建的依赖文件。
.PHONY: clean depend
CC := gcc
CXX := g++
LD := g++
CP := cp
PROG := hellomake
MODULES := src
OBJDIR := obj
default: $(PROG)
OPTFLAGS := -g -O
CFLAGS += -Wall -Wno-unused-function $(OPTFLAGS) $(patsubst %, -I%, $(MODULES))
GARBAGE := core core.* *.stackdump ./tags $(PROG)
include $(patsubst %, %/module.make, $(MODULES))
OBJ := \
$(patsubst %.c, %.o, $(filter %.c, $(SRC)))
DEP := $(OBJ:.o=.d)
# implicit rules
%.d: %.c
./depends.sh $(CC) `dirname $*.c` $(CFLAGS) $*.c > $@
-include $(DEP)
# Actual targets
depend: $(DEP)
clean:
rm -rf $(PROG) $(OBJ) $(GARBAGE) $(DEP) depends
$(PROG): $(OBJ)
$(LD) -o $@ $^ $(LIBS)
每个模块都应包含 make 文件 (module.make) 以指定 SRC 变量中包含哪些文件。
`SRC += src/hellofunc.c \
src/hellomake.c`.
这是生成依赖文件的脚本:
#!/bin/sh
#echo "## Got: $*"
CC=""
DIR=""
shift 2
case "$DIR" in
"" | ".")
$CC -MM -MG "$@" | sed -e 's@^\(.*\)\.o:@.d .o:@'
;;
*)
$CC -MM -MG "$@" | sed -e "s@^\(.*\)\.o:@$DIR/.d $DIR/.o:@"
;;
esac
这个方法很简单,就是用这些语句在同一个src目录下创建了目标文件和依赖文件。
SRC += src/hellofunc.c src/hellomake.c
OBJ := $(patsubst %.c, %.o, $(filter %.c, $(SRC)))
DEP := $(OBJ:.o=.d)
方法二
此方法创建对象目录,并将所有生成的(中间)文件放入该目录。
它征集所有模块来指定目标文件和依赖文件生成的操作。
示例中,我们只有一个模块,但是如果有多个模块,我们需要重复语句。
obj/src/%.o: src/%.c
$(CC) -c $< -o $@ $(CFLAGS)
obj/src/%.d: src/%.c
gcc -MM -MG $< $(CFLAGS) | sed -e 's@^\(.*\)\.o:@obj/src/.d obj/src/.o:@' > $@
这是生成文件:
.SUFFIX = .o .c
.PHONY: clean
CC := gcc
LD := gcc
PROG := hellomake
OBJDIR = obj
MODULES := src
SRCS := src/hellofunc.c src/hellomake.c
OBJDIRS := $(patsubst %, $(OBJDIR)/%, $(MODULES))
OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(SRCS))
DEPS := $(OBJS:.o=.d)
default: $(PROG)
CFLAGS += -Wall -Wno-unused-function $(OPTFLAGS) $(patsubst %, -I%, $(MODULES))
CXXFLAGS += $(CFLAGS)
obj/src/%.o: src/%.c
$(CC) -c $< -o $@ $(CFLAGS)
$(PROG): $(OBJDIRS) $(OBJS)
$(LD) $(filter %.o, $^) -o $(PROG)
-include $(DEPS)
obj/src/%.d: src/%.c
gcc -MM -MG $< $(CFLAGS) | sed -e 's@^\(.*\)\.o:@obj/src/.d obj/src/.o:@' > $@
depend: $(DEPS)
GARBAGE := core core.* *.stackdump ./tags $(PROG)
clean:
rm -rf $(PROG) obj/*
$(OBJDIRS):
mkdir -p $@
参考资料
- How to place object files in separate subdirectory
- make deleting dependency files
- http://scottmcpeak.com/autodepend/autodepend.html