偽目標是這樣一個目標:它不代表一個真正的文件名,在執行make時可以指定這個目標來執行所在規則定義的命令,有時也可以將一個偽目標稱為標簽。偽目標通過PHONY來指明。
PHONY定義偽目標的命令一定會被執行,下面嘗試分析這種優點的妙處。
1、如果我們指定的目標不是創建目標文件,而是使用makefile執行一些特定的命令,例如:
clean:
rm *.o temp
我們希望,只要輸入”make clean“后,”rm *.o temp“命令就會執行。但是,當當前目錄中存在一個和指定目標重名的文件時,例如clean文件,結果就不是我們想要的了。輸入”make clean“后,“rm *.o temp” 命令一定不會被執行。
解決的辦法是:將目標clean定義成偽目標就成了。無論當前目錄下是否存在“clean”這個文件,輸入“make clean”后,“rm *.o temp”命令都會被執行。
注意:這種做法的帶來的好處還不止此,它同時提高了make的執行效率,因為將clean定義成偽目標后,make的執行程序不會試圖尋找clean的隱含規則。
2、PHONY可以確保源文件(*.c *.h)修改后,對應的目標文件會被重建。倘若缺少了PHONY,可以看到情況會很糟。
現在做一個實驗,實驗的目錄是/work,在這個目錄中,包含了四個目錄test、add、sub、include 和一個頂層目錄makefile文件。test、add、sub三個目錄分別包含了三個源程序test.c、add.c、sub.c和三個子目錄makefile,目錄include的是頭文件heads.h的目錄,分別展開四個目錄的內容如下。
test目錄
//test.c
#include <stdio.h>
#include "../include/heads.h"
int main()
{
int a=15,b=16;
printf("a+b=%d\n",add(a,b));
return 0;
}
makefile
test.o:test.c ../include/heads.h
gcc -c -o $@ $<
.PHONY: clean
clean:
rm -f *.o
add目錄
//add.c
#include "../include/heads.h"
int add(int a,int b)
{
return (a+b);
}
makefile
add.o :add.c ../include/heads.h
gcc -c -o $@ $<
.PHONY: clean
clean:
rm -f *.o
sub目錄
//sub.c
#include "../include/heads.h"
int sub(int a,int b)
{
return a-b;
}
makefile
sub.o:sub.c ../include/heads.h
gcc -c -o $@ $<
.PHONY: clean
clean:
rm -f *.o
inlcude目錄
//heads.h
#ifndef _HEAD_H_
#define _HEAD_H_
extern int add(int,int);
extern int sub(int,int);
#endif
頂層makefile文件
OBJS = ./add/add.o ./sub/sub.o ./test/test.o
program: $(OBJS)
gcc ./test/test.o ./add/add.o ./sub/sub.o -o program
$(OBJS):
make -C $(dir $@)
.PHONY: clean
clean:
make -C ./add clean
make -C ./sub clean
make -C ./test clean
rm -f program
編譯調試:當在/work目錄中,執行make后,編譯出了program應用程序。修改了任意一個源文件(test.c、sub.c、add.c、heads.h)例如test.c,重新在/work目錄中執行make,發現一直提示“make: `program' is up to date.” ,而不能重建test.o,更不用說重建program。
修改頂層makefile文件,添加紅色的一行
OBJS = ./add/add.o ./sub/sub.o ./test/test.o
program: $(OBJS)
gcc ./test/test.o ./add/add.o ./sub/sub.o -o program
.PHONY : $(OBJS)
$(OBJS):
make -C $(dir $@)
.PHONY: clean
clean:
make -C ./add clean
make -C ./sub clean
make -C ./test clean
rm -f program
加上偽目標修改后,問題就會解決。修改了任意一個源文件,執行make對應的目標文件就會重建,最后重建program。即使不修改源文件,執行make也會進入源文件目錄中執行子make,但不會更新目標文件,最后還要重建program。
原因分析:由於(*.c *.h)- - > (*.o)- - > (program),修改前的頂層目標(program)依賴於(*.o)。執行make時,檢查 (program)的依賴(*.o)是否比(program)新,而不會檢查(*.h *.c)是否比(program)新,(*.h *.c)不是(program)的依賴。顯然,(*.o)沒有program新,所以不用重建。
注意修改后的makefile,把./add/add.o ./sub/sub.o ./test/test.o當做三個偽目標,所以不會再檢查 (program)的依賴(*.o)是否比(program)新。而原來的makefile中把./add/add.o ./sub/sub.o ./test/test.o當做三個依賴文件。可以說加上“PHONY”后,make程序對./add/add.o ./sub/sub.o ./test/test.o的看法已經完全不一樣了。
修改后的makefile,強制執行./add/add.o ./sub/sub.o ./test/test.o這三個偽目標的命令,即進入相應的子目錄執行make,從而調用相應的子目錄makefile。由於子目錄中的makefile目標是(*.o),目標的依賴是(*.c heads.h),會檢查(*.c heads.h)是否比(*.o)新,從而有可能重建(*.o)。而在跳回到頂層makefile后,還要執行“ gcc ./test/test.o ./add/add.o ./sub/sub.o -o program”。
總結:PHONY偽目標可以解決源文件不是最終目標直接依賴(實際上可以認為是間接依賴)帶來的不能自動檢查更新規則。