makefile 自动生成头文件依赖关系

在使用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


 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Makefile中实现多目录和头文件编译的关键是要将头文件的路径和源文件的路径分开,以便在编译时能够正确地找到这些文件。 以下是一个简单的Makefile示例,演示了如何实现多目录和头文件编译: ``` CC = gcc CFLAGS = -Wall -Wextra -Werror INCLUDES = -I./include SRCDIR = src OBJDIR = obj BINDIR = bin SOURCES = $(wildcard $(SRCDIR)/*.c) OBJECTS = $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SOURCES)) EXECUTABLE = $(BINDIR)/myprogram all: $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(OBJDIR)/%.o: $(SRCDIR)/%.c $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ clean: rm -f $(OBJECTS) $(EXECUTABLE) ``` 在此Makefile中,`SRCDIR`定义源文件的目录,`OBJDIR`定义对象文件的目录,`BINDIR`定义可执行文件的目录,`INCLUDES`定义头文件的路径。 我们使用`wildcard`函数来获取源文件的所有文件名,并使用`patsubst`函数将它们转换为对象文件的文件名。然后,我们定义了一个名为`all`的目标,它将生成可执行文件。我们使用`$(EXECUTABLE)`作为目标,并将它的依赖项设置为所有的对象文件。在`$(EXECUTABLE)`的规则中,我们链接了所有的对象文件以生成可执行文件。在对象文件的规则中,我们编译了每个源文件并将其转换为一个对象文件。 最后,我们定义了一个名为`clean`的目标,用于清除所有对象文件和可执行文件。 请注意,此Makefile假定源文件和头文件分别在`src`和`include`子目录中。您可以根据实际情况修改这些目录。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值