現在我們再對complicated項目做一些更改,增加程序文件間依賴關系的復雜度。
1 /× main.c ×/ 2 #include"foo.h" 3 int main(void) 4 { 5 foo(); 6 return 0; 7 } 8 9 /* foo.c */ 10 #include<stdio.h> 11 #include"foo.h" 12 void foo(void) 13 { 14 printf("%s,This is foo()!\n",HELLO); 15 } 16 17 18 /* foo.h */ 19 #ifndef __FOO_H 20 #define __FOO_H 21 #include "define.h" 22 void foo(void); 23 24 #endif /*__FOO_H*/ 25 26 /* define.h */ 27 #ifndef __DEFINE_H 28 #define __DEFINE_H 29 30 #define HELLO "hello" 31 32 #endif/*__DEFINE_H*/
在之前的Makefile不做更改的情況下,我們make一下:
在這次成功編譯的基礎上,我們再做一些改動,注意在這之前不要執行make clean,否則不能發現新問題。
1 /× define.h */ 2 #ifndef __DEFINE_H 3 #define __DEFINE_H 4 5 #include "other.h" 6 7 #endif/*__DEFINE_H*/ 8 9 10 /* other.h */ 11 #ifndef __OTHER_H 12 #define __OTHER_H 13 14 #define HELLO "hello" 15 16 #endif /*__OTHER_H*/
從結果看,盡管foo.c和main.c都被重新編譯了,但依賴關系卻沒有重新構建。運行complicated結果,其打印結果是我們所希望的“hello”。
現在,我們對other.h文件進行修改把hello改成hi,從下面運行結果來看,項目並沒有因為更改了other文件而重新編譯。
1 #ifndef __OTHER_H 2 #define __OTHER_H 3 4 #define HELLO "hi" 5 6 #endif /*__OTHER_H*/
更改Makefile如下,更改部分紅色標出了,其實只增加了一個$@:
1 .PHONY: all clean 2 3 MKDIR = mkdir 4 RM = rm 5 RMFLAGS = -rf 6 7 CC=gcc 8 9 DIR_OBJS=objs 10 DIR_EXES=exes 11 DIR_DEPS=deps 12 13 DIRS =$(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS) 14 15 EXE=complicated 16 EXE:=$(addprefix $(DIR_EXES)/,$(EXE)) 17 SRCS=$(wildcard *.c) 18 OBJS=$(SRCS:.c=.o) 19 OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS)) 20 DEPS=$(SRCS:.c=.dep) 21 DEPS:=$(addprefix $(DIR_DEPS)/,$(DEPS)) 22 23 ifeq ($(wildcard $(DIR_OBJS)),) 24 DEP_DIR_OBJS :=$(DIR_OBJS) 25 endif#dir_objs 26 ifeq ($(wildcard $(DIR_EXES)),) 27 DEP_DIR_EXES :=$(DIR_EXES) 28 endif#dir_exes 29 ifeq ($(wildcard $(DIR_DEPS)),) 30 DEP_DIR_DEPS :=$(DIR_DEPS) 31 endif#dir_deps 32 all: $(EXE) 33 34 include $(DEPS) 35 36 $(DIRS): 37 $(MKDIR) $@ 38 $(EXE):$(DEP_DIR_EXES) $(OBJS) 39 $(CC) -o $@ $(filter %.o,$^) 40 $(DIR_OBJS)/%.o:$(DEP_DIR_OBJS) %.c 41 $(CC) -o $@ -c $(filter %.c,$^) 42 $(DIR_DEPS)/%.dep:$(DEP_DIR_DEPS) %.c 43 @echo "Creating $@ ..." 44 @set -e;\ 45 $(RM) $(RMFLAGS) $@.tmp;\ 46 $(CC) -E -MM $(filter %.c,$^) > $@.tmp;\ 47 sed 's,\(.*\)\.o[:]*,objs/\1.o $@:,g' <$@.tmp >$@;\ 48 $(RM) $(RMFLAGS) $@.tmp 49 50 clean: 51 $(RM) $(RMFLAGS) $(DIRS)
這樣之后,Makefile就可以知道other.h的更改了。因為$@表示的是依賴關系的文件名,這個問題(Makefile 6隨筆中要把foo.h加在依賴關系中的更改一樣)和之前的那個問題解決方案一樣,用sed命令可以做到,問題的根本在於,我們應該為依賴文件增加依賴關系,這樣才能將整個項目全局聯系在一起。
但是,這樣之后,連續執行兩次make clean:
第一次clean時,沒什么問題,也是我們期望的,第二次clean時,make會先構建依賴項,緊接着又把目錄刪除。為什么第二次clean時,make會重新構建依賴文件?因為我們有一個include指令,他會優先於目標執行!!!
為了解決這個問題,我們再運用條件語法,並且用到我們之前提到的MAKECMDGOALS變量。更改后的Makefile如下:
1 .PHONY: all clean 2 3 MKDIR = mkdir 4 RM = rm 5 RMFLAGS = -rf 6 7 CC=gcc 8 9 DIR_OBJS=objs 10 DIR_EXES=exes 11 DIR_DEPS=deps 12 13 DIRS =$(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS) 14 15 EXE=complicated 16 EXE:=$(addprefix $(DIR_EXES)/,$(EXE)) 17 SRCS=$(wildcard *.c) 18 OBJS=$(SRCS:.c=.o) 19 OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS)) 20 DEPS=$(SRCS:.c=.dep) 21 DEPS:=$(addprefix $(DIR_DEPS)/,$(DEPS)) 22 23 ifeq ($(wildcard $(DIR_OBJS)),) 24 DEP_DIR_OBJS :=$(DIR_OBJS) 25 endif#dir_objs 26 ifeq ($(wildcard $(DIR_EXES)),) 27 DEP_DIR_EXES :=$(DIR_EXES) 28 endif#dir_exes 29 ifeq ($(wildcard $(DIR_DEPS)),) 30 DEP_DIR_DEPS :=$(DIR_DEPS) 31 endif#dir_deps 32 33 all: $(EXE) 34 ifneq ($(MAKECMDGOALS),clean) 35 include $(DEPS) 36 endif#clean 37 38 $(DIRS): 39 $(MKDIR) $@ 40 $(EXE):$(DEP_DIR_EXES) $(OBJS) 41 $(CC) -o $@ $(filter %.o,$^) 42 $(DIR_OBJS)/%.o:$(DEP_DIR_OBJS) %.c 43 $(CC) -o $@ -c $(filter %.c,$^) 44 $(DIR_DEPS)/%.dep:$(DEP_DIR_DEPS) %.c 45 @echo "Creating $@ ..." 46 @set -e;\ 47 $(RM) $(RMFLAGS) $@.tmp;\ 48 $(CC) -E -MM $(filter %.c,$^) > $@.tmp;\ 49 sed 's,\(.*\)\.o[:]*,objs/\1.o $@:,g' <$@.tmp >$@;\ 50 $(RM) $(RMFLAGS) $@.tmp 51 52 clean: 53 $(RM) $(RMFLAGS) $(DIRS)
這樣就解決了。
再看一個例子:
1 all: 2 @echo "command of rule" 3 4 all: dep 5 6 dep: 7 @echo "prerequisite of rule"
這個make的特性說明了一個問題:在生成的依賴關系文件中,其中的規則只描述了依賴關系,而沒有任何的命令,make是怎么知道使用哪些命令進行目標構建的呢?
當一個Makefile中存在構建同一目標的不同規則時,make會將這些規則合在一起,合並的內容包括先決條件和命令。盡管在自動生成的依賴關系文件中只存在目標和先決條件,但是由於Makefile中已經定義了.o 和.dep文件的生成規則,因此make會將這兩部分結合在一起,從而形成最終針對一個(類)構建目標的規則。上面的那個例子可以很好地幫助我們理解這個make特性。