在使用makefile 自动生成头文件依赖是,大家多半使用了下面这个方法。
这个sed语句被称之为 "上帝的符号",可读性不言而喻。(PS:CSDN这个排版怎么也搞不好,只能用图片了。)
gcc的 -MMD 选项可以自动生成带有依赖规则的.d文件,为创建头文件依赖带来了方便。
示例如下:
CC = gcc
TARGET = main.out
LDLIBS = -m32 -Wl,--as-needed
CFLAGS = -m32 -g -MMD -MP -Wall -c
SRCS = main.c
ODIR = ./objs
OBJS = $(addprefix $(ODIR)/,$(SRCS:.c=.o))
DEPS = $(addprefix $(ODIR)/,$(SRCS:.c=.d))
.PHONY: all clean $(ODIR)
all: $(ODIR) $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(LDLIBS) $(OBJS) -o $@
$(ODIR)/%.o: %.c
$(CC) $(CFLAGS) -o $@ $<
-include $(DEPS)
$(ODIR):
@test -d $@ || mkdir -p $@
clean:
rm -rf $(ODIR)
rm -f $(TARGET)
测试代码:
/*main.c*/
#include <stdio.h>
#include "test.h"
int main(int argc, char *argv[])
{
printf("hello world! xtest=%d\n", xtest);
return 0;
}
/*test.h*/
#define xtest (1)
[root@ubuntu:makefile]# ll
总用量 20
drwxr-xr-x 2 root root 4096 Oct 26 16:42 ./
drwxr-xr-x 8 root root 4096 Oct 25 17:36 ../
-rw-r--r-- 1 root root 136 Oct 26 16:09 main.c
-rwxr--r-- 1 root root 488 Oct 26 16:18 makefile*
-rw-r--r-- 1 root root 20 Oct 26 16:09 test.h
[root@ubuntu:makefile]# make
gcc -m32 -g -MMD -MP -Wall -c -o objs/main.o main.c
gcc -m32 -Wl,--as-needed ./objs/main.o -o main.out
[root@ubuntu:makefile]# cat ./objs/main.d
objs/main.o: main.c test.h
test.h:
[root@ubuntu:makefile]# touch test.h
[root@ubuntu:makefile]# make
gcc -m32 -g -MMD -MP -Wall -c -o objs/main.o main.c
gcc -m32 -Wl,--as-needed ./objs/main.o -o main.out
[root@ubuntu:makefile]#
原理分析:
gcc -MMD -MP *.c 生成带有头文件依赖关系的*.d
展开这个makefile的关键部分
CC = gcc
TARGET = main.out
LDLIBS = -m32 -Wl,--as-needed
CFLAGS = -m32 -g -MMD -MP -Wall -c
SRCS = main.c
ODIR = ./objs
OBJS = $(addprefix $(ODIR)/,$(SRCS:.c=.o))
DEPS = $(addprefix $(ODIR)/,$(SRCS:.c=.d))
.PHONY: all clean $(ODIR)
all: $(ODIR) $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(LDLIBS) $(OBJS) -o $@
#$(ODIR)/%.o: %.c
objs/main.o: main.c
# $(CC) $(CFLAGS) -o $@ $<
gcc -m32 -g -MMD -MP -Wall -c -o objs/main.o main.c
#-include $(DEPS)
#-include main.d
objs/main.o: main.c test.h #-MMD选项生成
test.h: #-MP 选项生成
$(ODIR):
@test -d $@ || mkdir -p $@
clean:
rm -rf $(ODIR)
rm -f $(TARGET)
合并相同的目标,makefile的最终形式为:
CC = gcc
TARGET = main.out
LDLIBS = -m32 -Wl,--as-needed
CFLAGS = -m32 -g -MMD -MP -Wall -c
SRCS = main.c
ODIR = ./objs
OBJS = $(addprefix $(ODIR)/,$(SRCS:.c=.o))
DEPS = $(addprefix $(ODIR)/,$(SRCS:.c=.d))
.PHONY: all clean $(ODIR)
all: $(ODIR) $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(LDLIBS) $(OBJS) -o $@
objs/main.o: main.c test.h
gcc -m32 -g -MMD -MP -Wall -c -o objs/main.o main.c
test.h:
$(ODIR):
@test -d $@ || mkdir -p $@
clean:
rm -rf $(ODIR)
rm -f $(TARGET)
关于test.h:
如果没有这个目标,当头文件被删除或者忘了包含时,make报错如下:
[root@ubuntu:makefile]# make
gcc -m32 -g -MMD -Wall -c -o objs/main.o main.c
gcc -m32 -Wl,--as-needed ./objs/main.o -o main.out
[root@ubuntu:makefile]#
[root@ubuntu:makefile]#
[root@ubuntu:makefile]#
[root@ubuntu:makefile]# mv test.h test.x
[root@ubuntu:makefile]# make
make: *** 没有规则可以创建“objs/main.o”需要的目标“test.h”。 停止。
[root@ubuntu:makefile]#
加上test.h: make报错如下:
[root@ubuntu:makefile]# make
gcc -m32 -g -MMD -MP -Wall -c -o objs/main.o main.c
gcc -m32 -Wl,--as-needed ./objs/main.o -o main.out
[root@ubuntu:makefile]#
[root@ubuntu:makefile]# mv test.h test.x
[root@ubuntu:makefile]# make
gcc -m32 -g -MMD -MP -Wall -c -o objs/main.o main.c
main.c:3:18: fatal error: test.h: 没有那个文件或目录
#include "test.h"
^
compilation terminated.
make: *** [objs/main.o] 错误 1
[root@ubuntu:makefile]#
因为目标test.h 没有对应的规则,所以make会继续向下进行,从而准确的定位出产生错误的原因。
最后给出一个相对完善的makefile,主要支持如下功能:
1、支持c/c++混合编译
2、支持交叉编译
3、支持源文件放在不同的目录
4、自动生成头文件依赖关系
5、支持生成预编译文件
CC = gcc
CX = g++
CP = cp
MV = mv
RM = -rm -rf
JB = ${shell cat /proc/cpuinfo | grep "processor" | wc -l}
vpath %.h inc
vpath %.c src
vpath %.cpp src
SRCS = xmain.c xsha256.c xuart.c xutils.c xlibusb.c test.cpp
LIBS = -lpthread -lusb
ODIR = obj
OBJS = $(addprefix $(ODIR)/, $(patsubst %.c, %.o, $(filter %.c, $(SRCS))) $(patsubst %.cpp, %.o, $(filter %.cpp, $(SRCS))))
DEPS = $(addprefix $(ODIR)/, $(patsubst %.c, %.d, $(filter %.c, $(SRCS))) $(patsubst %.cpp, %.d, $(filter %.cpp, $(SRCS))))
PRES = $(addprefix $(ODIR)/, $(patsubst %.c, %.i, $(filter %.c, $(SRCS))) $(patsubst %.cpp, %.i, $(filter %.cpp, $(SRCS))))
TARGET = $(ODIR)/a.out
INCDIR = -I./inc
CFLAGS = -g3 -Wall -c -gdwarf-2
LFLAGS = -L./lib -Wl,-rpath=${shell pwd}/lib
ifeq ($(HOST), mips)
CC = mips-openwrt-linux-gcc
CX = mips-openwrt-linux-g++
ifeq (${shell file ./$(ODIR)/*.o | grep "MIPS" | wc -l}, 0)
${shell rm -rf ./$(ODIR)}
endif
else
ifeq (${shell file ./$(ODIR)/*.o | grep "x86-64" | wc -l}, 0)
${shell rm -rf ./$(ODIR)}
endif
endif
ifeq ($(filter %.cpp, $(SRCS)),)
LD = $(CC)
else
LD = $(CX)
endif
.PHONY: all clean $(ODIR)
all: $(ODIR) $(TARGET)
$(TARGET): $(OBJS) $(PRES)
@echo LD $@
@$(LD) $(LFLAGS) $(OBJS) $(LIBS) -o $@
@echo CP $@ ./
@$(CP) $@ ./
$(ODIR)/%.i: %.cpp
@$(CX) $(CFLAGS) -E $(INCDIR) -o $@ $<
$(ODIR)/%.o: %.cpp
@echo CX $<
@$(CX) $(CFLAGS) -MMD -MP $(INCDIR) -o $@ $<
$(ODIR)/%.i: %.c
@$(CC) $(CFLAGS) -E $(INCDIR) -o $@ $<
$(ODIR)/%.o: %.c
@echo CC $<
@$(CC) $(CFLAGS) -MMD -MP $(INCDIR) -o $@ $<
-include $(DEPS)
$(ODIR):
@test -d $@ || mkdir -p $@
clean:
@$(RM) $(ODIR)
@echo RM $(ODIR)
v2.0版本
CC = gcc
CX = g++
CP = cp
MV = mv
AR = ar
RM = -rm -rf
JB = ${shell cat /proc/cpuinfo | grep "processor" | wc -l}
LIBA = libx.a
LIBO = libx.so
DIRS = ./ ../
vpath %.c $(DIRS)
vpath %.cpp $(DIRS)
SRCS = a.cpp b.cpp c.cpp d.cpp e.cpp
ifneq ($(MAKECMDGOALS), $(LIBA))
SRCS += main.cpp
else
ifneq ($(MAKECMDGOALS), $(LIBO))
SRCS += main.cpp
endif
endif
LIBS = -lpthread
ODIR = obj
OBJS = $(addprefix $(ODIR)/, $(notdir $(patsubst %.c, %.o, $(filter %.c, $(SRCS))) $(patsubst %.cpp, %.o, $(filter %.cpp, $(SRCS)))))
DEPS = $(addprefix $(ODIR)/, $(notdir $(patsubst %.c, %.d, $(filter %.c, $(SRCS))) $(patsubst %.cpp, %.d, $(filter %.cpp, $(SRCS)))))
PRES = $(addprefix $(ODIR)/, $(notdir $(patsubst %.c, %.i, $(filter %.c, $(SRCS))) $(patsubst %.cpp, %.i, $(filter %.cpp, $(SRCS)))))
TARGET = $(ODIR)/test
INCDIR = $(addprefix -I, $(DIRS))
CFLAGS = -g3 -Wall -c -gdwarf-2 -fpic
XFLAGS = -std=c++11
LFLAGS = -L./lib -Wl,-rpath=${shell pwd}/lib -Wl,--as-needed
ifeq ($(host), arm)
CC = arm-linux-gnueabihf-gcc
CX = arm-linux-gnueabihf-g++
AR = arm-linux-gnueabihf-ar
ifeq (${shell file ./$(ODIR)/*.o | grep "ARM" | wc -l}, 0)
${shell rm -rf ./$(ODIR)}
endif
else
ifeq (${shell file ./$(ODIR)/*.o | grep "x86-64" | wc -l}, 0)
${shell rm -rf ./$(ODIR)}
endif
endif
ifeq ($(filter %.cpp, $(SRCS)),)
LD = $(CC)
else
LD = $(CX)
endif
.PHONY: all clean $(ODIR)
all: $(ODIR) $(TARGET) $(LIBA) $(LIBO)
$(TARGET): $(ODIR) $(OBJS) $(PRES)
@echo LD $@
@$(LD) $(LFLAGS) $(OBJS) $(LIBS) -o $@
@echo CP $@ ./
@$(CP) $@ ./
$(LIBA): $(ODIR) $(OBJS)
@echo AR $(ODIR)/$@
@$(AR) -rcs $(ODIR)/$@ $(OBJS)
$(LIBO): $(ODIR) $(OBJS)
@echo LD $(ODIR)/$@
@$(LD) $(LFLAGS) $(OBJS) $(LIBS) -shared -o $(ODIR)/$@
$(ODIR)/%.i: %.cpp
@$(CX) $(CFLAGS) $(XFLAGS) -E $(INCDIR) -o $@ $<
$(ODIR)/%.o: %.cpp
@echo CX $(notdir $<)
@$(CX) $(CFLAGS) $(XFLAGS) -MMD -MP $(INCDIR) -o $@ $<
$(ODIR)/%.i: %.c
@$(CC) $(CFLAGS) -E $(INCDIR) -o $@ $<
$(ODIR)/%.o: %.c
@echo CC $(notdir $<)
@$(CC) $(CFLAGS) -MMD -MP $(INCDIR) -o $@ $<
-include $(DEPS)
$(ODIR):
@test -d $@ || mkdir -p $@
clean:
@$(RM) $(ODIR)
@echo RM $(ODIR)
#SRCS = $(notdir $(foreach dir, $(DIRS), $(wildcard $(dir)*.cpp)))
#-Wl,-soname,$@
#-Wl,--whole-archive libx liby -Wl,--no-whole-archive
#-Wl,--start-group libx liby -Wl,--end-group