4.makefile自動推導目的碼配置和偽目標clean清理
4.1 make和makefile
- makefile檔案主要包含了5部分內容:
- 顯式規則。說明了如何生成一個或多個目標檔案。由makefile檔案的創作者指出,包括要生成的檔案、檔案的依賴檔案、生成的命令。
- 隱式規則。由於make有自動推導的功能,所以隱式的規則可以比較粗糙地簡略書寫makefile檔案,這是由make所支援的。
- 變數定義。在makefile檔案中要定義一系列的變數,變數一般都是字串,這與C語言中的宏有些類似。當makefile檔案執行時,其中的變數都會擴充套件到相應的引用位置上。
- 檔案指示。其包括3個部分,一個是在一個makefile檔案中引用另一個makefile檔案;另一個是指根據某些情況指定makefile檔案中的有效部分;還有就是定義一個多行的命令。
- 註釋。makefile檔案中只有行註釋,其註釋用“#”字元。如果要在makefile檔案中使用“#”字元,可以用反斜框進行轉義,如:“#”。
make 是一個功能強大的構建工具,雖然 make 需要根據 makefile 中指定的規則來完成原始檔的編譯。我們編寫 makefile 的時候難免寫的不是那麼嚴謹從而漏寫一些構建規則,但是會發現程式還是會被編譯成功。這是因為 make 有自動推導的能力,不會完全依據makefile。
4.2 自動推導
如果說某個依賴項是由它能推匯出來,它自己可以生成編譯程式碼,自動推導涉及.o檔案
# first_make
# $^ 依賴 不重複
# $@ 目標
# @ 不顯示命令執行 -失敗不停止
TARGET=first_make
LIBS=-lpthread
$(TARGET):first_make.o xdata.o
@#-rm test
@echo "begin build $(TARGET)"
$(CXX) $^ -o $@ $(LIBS)
@echo "$(TARGET) build success!"
root@cmt-virtual-machine:/home/cmt/src/first_make# ls
first_make.cpp makefile xdata.cpp xdata.h
root@cmt-virtual-machine:/home/cmt/src/first_make# make
g++ -c -o first_make.o first_make.cpp
g++ -c -o xdata.o xdata.cpp
begin build first_make
g++ first_make.o xdata.o -o first_make -lpthread
first_make build success!
root@cmt-virtual-machine:/home/cmt/src/first_make#
g++針對.o檔案生成,這裡涉及了自動推導。
但是自動推導這裡有一個問題,如果說自動推導涉及標頭檔案的引用,
//first_make.cpp
#include <iostream>
#include <thread>
#include "xdata.h"
#include "test.h"
using namespace std;
void ThreadMain()
{
cout << "Thread Main" << endl;
}
int main(int argc, char *argv[])
{
thread th(ThreadMain);
cout << "test make" << endl;
th.join();
XData d;
return 0;
}
test.h
//../test_gcc
#define CONF_PATH "/user/local/xcj/"
root@cmt-virtual-machine:/home/cmt/src/first_make# ls
first_make first_make.cpp first_make.o makefile xdata.cpp xdata.h xdata.o
root@cmt-virtual-machine:/home/cmt/src/first_make# make
g++ -c -o first_make.o first_make.cpp
first_make.cpp:5:10: fatal error: test.h: 沒有那個檔案或目錄
5 | #include "test.h"
| ^~~~~~~~
compilation terminated.
make: *** [<內建>:first_make.o] 錯誤 1
root@cmt-virtual-machine:/home/cmt/src/first_make#
編譯時找不標頭檔案
手動做
root@cmt-virtual-machine:/home/cmt/src/first_make# g++ -c -o first_make.o first_make.cpp -I../test_gcc
自動推導如何加這個命令?
# first_make
# $^ 依賴 不重複
# $@ 目標
# @ 不顯示命令執行 -失敗不停止
TARGET=first_make
LIBS=-lpthread
OBJS=first_make.o xdata.o
CXXFLAGS=-I../test_gcc
$(TARGET):$(OBJS)
@#-rm test
@echo "begin build $(TARGET)"
$(CXX) $^ -o $@ $(LIBS)
@echo "$(TARGET) build success!"
root@cmt-virtual-machine:/home/cmt/src/first_make# make
begin build first_make
g++ first_make.o xdata.o -o first_make -lpthread
first_make build success!
在 Makefile
中新增一個清理目標(clean
target)是一個常見的做法,用於自動清理編譯過程中生成的中間檔案或可執行檔案。這樣可以避免手動清理的麻煩。
清理和編譯目標有一個區別,所謂的目標項都有一個對應的目標式檔案,新增清理,沒有目標也沒有依賴項
(1)偽目標
偽目標(Phony Targets)是在 Makefile
中一種特殊的目標,它們並不對應於實際的檔案,而是用於執行特定的命令。使用偽目標的主要目的是避免檔名和目標名衝突,並確保目標總是被執行。
①偽目標的定義
偽目標通常透過在 Makefile
中使用 .PHONY
來宣告。下面是一個例子:
.PHONY: clean
在這個例子中,clean
被定義為一個偽目標。無論當前目錄下是否存在名為 clean
的檔案,make clean
命令都會執行對應的命令(如刪除檔案),而不會試圖將其視為一個檔案目標。
②為什麼使用偽目標?
-
避免衝突:
如果你有一個目標名與檔名相同,而你不希望make
把它當成一個檔案目標(即不會嘗試透過規則去生成這個檔案),你可以將其定義為偽目標。例如:clean: rm -f *.o first_make .PHONY: clean
如果沒有
.PHONY
,且當前目錄中有一個名為clean
的檔案,make
將認為clean
已經是最新的,並且不會執行rm -f *.o first_make
這條命令。 -
確保執行:
偽目標總是被認為需要執行,而不會受制於檔案的時間戳。即使在不需要重新構建的情況下,你仍然希望某些命令被執行,如清理操作。 -
提高可讀性:
偽目標通常用於表示一個動作或一組命令,而非生成某個檔案。例如,clean
、install
、test
等動作就是常見的偽目標。定義偽目標可以使Makefile
更具可讀性。
③多個偽目標
你可以在 .PHONY
中定義多個偽目標,用空格分隔:
.PHONY: all clean install
在這個例子中,all
、clean
和 install
都被定義為偽目標。
④使用 .PHONY
的例子
一個簡單的 Makefile
示例:
.PHONY: all clean install
all: program
program: main.o lib.o
gcc -o program main.o lib.o
main.o: main.c
gcc -c main.c
lib.o: lib.c
gcc -c lib.c
clean:
rm -f *.o program
install:
cp program /usr/local/bin/
在這個例子中:
all
、clean
和install
都是偽目標。make clean
會執行rm -f *.o program
,無論當前目錄中是否存在clean
檔案。make install
會將program
複製到/usr/local/bin/
,無論當前目錄中是否存在install
檔案。
總結來說,偽目標是 Makefile
中一種重要的機制,可以確保某些命令總是被執行,同時避免與檔名的衝突,從而增強 Makefile
的靈活性和可靠性。
# first_make
# $^ 依賴 不重複
# $@ 目標
# @ 不顯示命令執行 -失敗不停止
TARGET=first_make
LIBS=-lpthread
OBJS=first_make.o xdata.o
CXXFLAGS=-I../test_gcc
$(TARGET):$(OBJS)
@#-rm test
@echo "begin build $(TARGET)"
$(CXX) $^ -o $@ $(LIBS)
@echo "$(TARGET) build success!"
clean:
$(RM) $(OBJS) $(TARGET)
.PHONY: clean *clean
root@cmt-virtual-machine:/home/cmt/src/first_make# ls
first_make first_make.cpp makefile xdata.cpp xdata.h
root@cmt-virtual-machine:/home/cmt/src/first_make# make
g++ -I../test_gcc -c -o first_make.o first_make.cpp
g++ -I../test_gcc -c -o xdata.o xdata.cpp
begin build first_make
g++ first_make.o xdata.o -o first_make -lpthread
first_make build success!
root@cmt-virtual-machine:/home/cmt/src/first_make# vi makefile
root@cmt-virtual-machine:/home/cmt/src/first_make# make clean
rm -f first_make.o xdata.o first_make
root@cmt-virtual-machine:/home/cmt/src/first_make# make clean
rm -f first_make.o xdata.o first_make
root@cmt-virtual-machine:/home/cmt/src/first_make# make
g++ -I../test_gcc -c -o first_make.o first_make.cpp
g++ -I../test_gcc -c -o xdata.o xdata.cpp
begin build first_make
g++ first_make.o xdata.o -o first_make -lpthread
first_make build success!
root@cmt-virtual-machine:/home/cmt/src/first_make#
每次想重新編譯就進行一下清理
(2)為什麼涉及到清理?
因為用了時間戳的問題可能涉及到大家程式碼的複製、從版本庫上拿到程式碼時間上有時候會產生問題。一般會清理掉重新編譯一下。
參考資料來源:夏曹俊