4.makefile自動推導目的碼配置和偽目標clean清理

CodeMagicianT發表於2024-08-10

4.makefile自動推導目的碼配置和偽目標clean清理

4.1 make和makefile

  • makefile檔案主要包含了5部分內容:
  1. 顯式規則。說明了如何生成一個或多個目標檔案。由makefile檔案的創作者指出,包括要生成的檔案、檔案的依賴檔案、生成的命令。
  2. 隱式規則。由於make有自動推導的功能,所以隱式的規則可以比較粗糙地簡略書寫makefile檔案,這是由make所支援的。
  3. 變數定義。在makefile檔案中要定義一系列的變數,變數一般都是字串,這與C語言中的宏有些類似。當makefile檔案執行時,其中的變數都會擴充套件到相應的引用位置上。
  4. 檔案指示。其包括3個部分,一個是在一個makefile檔案中引用另一個makefile檔案;另一個是指根據某些情況指定makefile檔案中的有效部分;還有就是定義一個多行的命令。
  5. 註釋。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 命令都會執行對應的命令(如刪除檔案),而不會試圖將其視為一個檔案目標。

②為什麼使用偽目標?

  1. 避免衝突
    如果你有一個目標名與檔名相同,而你不希望 make 把它當成一個檔案目標(即不會嘗試透過規則去生成這個檔案),你可以將其定義為偽目標。例如:

    clean:
        rm -f *.o first_make
    
    .PHONY: clean
    

    如果沒有 .PHONY,且當前目錄中有一個名為 clean 的檔案,make 將認為 clean 已經是最新的,並且不會執行 rm -f *.o first_make 這條命令。

  2. 確保執行
    偽目標總是被認為需要執行,而不會受制於檔案的時間戳。即使在不需要重新構建的情況下,你仍然希望某些命令被執行,如清理操作。

  3. 提高可讀性
    偽目標通常用於表示一個動作或一組命令,而非生成某個檔案。例如,cleaninstalltest 等動作就是常見的偽目標。定義偽目標可以使 Makefile 更具可讀性。

③多個偽目標

你可以在 .PHONY 中定義多個偽目標,用空格分隔:

.PHONY: all clean install

在這個例子中,allcleaninstall 都被定義為偽目標。

④使用 .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/

在這個例子中:

  • allcleaninstall 都是偽目標。
  • 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)為什麼涉及到清理?

因為用了時間戳的問題可能涉及到大家程式碼的複製、從版本庫上拿到程式碼時間上有時候會產生問題。一般會清理掉重新編譯一下。

參考資料來源:夏曹俊

相關文章