1. 程式人生 > >make 的隱式規則(十一)

make 的隱式規則(十一)

覆蓋 wildcard toolbar fff .com 包含 TE 經驗總結 帶來

如果我們將同一個目標的命令拆分的寫到不同地方,會發生什麽呢?我們來看看下面的代碼

.PHONY : all

all :
    @echo "command-1"

VAR := test
        
all :
    @echo "all : $(VAR)"

我們來分析下,這份代碼中有兩個目標 all,那麽我們在執行 make 的時候。它到底是執行哪個呢?一個可能是兩個都執行,另一個就是執行第一個,因為默認的是執行第一個目標。下來我們來看看執行結果

技術分享圖片

我們看到它說 all 重復了,便忽略了前面的 all 命令。最終執行的是最後一個 all 命令。因此,當 makefile 中出現同名目標時,會將所有的依賴合並在一起,成為目標的最終依賴;當多處出現同一目標的命令時,make 發出警告,所有之前定義的命令被最後定義的命令取代

註意:當使用 include 關鍵字包含其他文件時,需要確保被包含文件中的同名目標只要依賴,沒有命令;否則,同名目標的命令將被覆蓋!我們還是來看看,再建立一個新的 makefile.1


makefile.1 源碼

all :
    @echo "this is command from makefile.1"


makefile 源碼

.PHONY : all

VAR := test

all :
    @echo "all : $(VAR)"

include makefile.1

我們來看看編譯結果,看看打印出的是不是 all : test

技術分享圖片

我們看到輸出的是 "this is command from makefile.1,並不是我們所期望的 all : test。其實也不難理解,因為我們包含的 makefile.1 中也包含了 all 命令,因此將上面的 all 給替換了。換句話說,這個現象有可能會給我們帶來意想不到的結果。那麽這也就屬於 makefile 中的一條隱式規則了,什麽是隱式規則呢?在 make 中,它提供了一些常用的,例行的規則實現;當相應目標的規則未提供時,make 嘗試使用隱式規則。那麽我們來看看下面這個 makefile 能編譯成功嗎?

.PHONY : all

SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)

app.out : $(OBJS)
    $(CC) -o $@ $^
    $(RM) $^
    @echo "Target ==> $@"

我們看看編譯的結果,是否會報錯?

技術分享圖片

我們看到已經正確的編譯了,而且結果也是對的。那麽我們並沒有在裏面定義相應的規則啊,為什麽就能正確編譯呢?我們按照它的格式寫個規則,再將 cc 換成 gcc 試試,看看結果是否會相同?

.PHONY : all

CC := gcc

SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)

app.out : $(OBJS)
    $(CC) -o $@ $^
    $(RM) $^
    @echo "Target ==> $@"

%.o : %.c
    @echo "my rule"
    $(CC)   -c -o $@ $^

編譯結果如下

技術分享圖片

結果和之前是一樣的,只不過是輸出了我們自定義的語句,將 cc 換成了 gcc。那麽 cc 是什麽呢?為何能編譯源文件呢?cc 第一個 c 是 C 語言,第二個是 compiler 編譯器的意思。那麽這個 cc 編譯器是哪來的呢?在原來的 Unix 系統中是有 cc 編譯器的,因為它是商業版的,需要收費,因此在 Linux 系統中也是要支持 cc 的,不過此 cc 非彼 cc,我們來看看 cc 最後的原型是什麽

技術分享圖片

我們看到 cc 最後指向的是 gcc,因此使用 cc 進行編譯工作其實也是和使用 gcc 進行編譯是一樣的。那麽上面的模式規則是誰來實現的呢?這個便是 makefile 中的隱式規則了。make 提供了生成目標文件的隱式規則,隱式規則會使用預定義變量完成編譯工作;改變預定義變量將部分改變隱式規則的行為,當存在自定義規則時,不再使用隱式規則。當 make 發現目標的依賴不存在時,嘗試通過依賴名逐一查找隱式規則,並且通過依賴名推導可能需要的源文件,如下

技術分享圖片

既然隱式編譯這麽強大,我們是不是就不用自己編寫相關規則了呢?其實不是的,根據前輩們的經驗總結。在實際的項目中,我們還是有必要禁止 makefile 中的隱式規則的,因為隱式規則有副作用。具體表現在:a> 編譯行為難以控制,大量使用隱式規則可能產生意想不到的編譯行為;b> 編譯效率低下,make 從隱式規則和自定義規則中選擇最終使用的規則。

那麽我們下來來看看隱式規則鏈。當依賴的目標不存在時,make 會極力組合各種隱式規則對目標進行創建,進而產生意料之外的編譯行為!如:需要名為 N.o 的目標:N.y --> N.c --> N.o。我們還是以代碼為例來進行分析說明


main.c 源碼

#include <stdio.h>

extern void greeting();

int main()
{
    greeting();
    
    return 0;
}


func.p 源碼(這只是一個測試的代碼,用的是 Pascal 語言)

unit Func;

interface

procedure Greeting(); attribute (name = 'greeting');

implementation

procedure Greeting();
begin
  WriteLn('Hello, Pascal!');
end;

end.


makefile 源碼

app.out : main.o func.o
    $(CC) -lstdc++ -o $@ $^

那麽我們此時想要用 func.c 實現某個功能,但是現在沒有 func.c,所以編譯時肯定會出錯

技術分享圖片

我們看到 func.o 竟然會利用 func.p 來生成,不過最終還是出錯了。出現這樣的錯誤,我們是不是很納悶呢?明明是沒有對應的源文件,但是卻報的是沒有 pc 命令。這個便是 makefile 中的隱式規則了,那麽 make 究竟提供了多少隱式規則?應如何查看查看隱式規則呢?查看隱式規則的方法是:查看所有的是 make -p;查看具體的規則是 make -p | grep "XXX"。下來我們來看看 make 中的隱式規則

技術分享圖片

因為所有的規則非常多,我們只截取了其中的一段,下來看看 %.o 對應的規則

技術分享圖片

我們看到它默認的是支持好多格式的,其中就包括 .c 和 .p 文件,因此它會將將我們之前的測試代碼當成源文件進行編譯了。那麽我們應該如何避免它的隱式規則呢?在局部禁用的話,是直接在 makefile 中自定義規則或者在 makefile 中定義模式(如:%.o : %.p);全局禁用的話則使用 make -r。下來我們先來使用下局部禁用的方式,直接在 makefile 中定義模式,但是不做具體處理。

技術分享圖片

它直接就報錯了,說沒有相應的源文。我們再來看看使用全局禁用的方式

技術分享圖片

我們看到使用全局禁用的方式後,連 main.o 的文件也不能生成了。下來我們來說說後綴規則,它是舊式的“模式規則”,可以通過後綴描述的方式自定義規則。格式如下

技術分享圖片

後綴規則分為雙後綴規則和單後綴規則。雙後綴規則是指定義一對文件後綴(依賴文件後綴和目標文件後綴),如:.cpp.o <==> %.o : %.cpp;單後綴規則是指定義單個文件後綴(源文件後綴),如:.c <==> % : %.c。後綴規則有這麽幾個註意事項:1、後綴規則中不允許有依賴;2、後綴規則必須有命令,否則無意義;3、後綴規則將逐步被模式規則所取代。

下來我們還是以代碼為例來進行分析說明,我們新建一個 func.c 文件用於說明問題


func.c 源碼

#include <stdio.h>

void greeting()
{
    printf("void greeting : %s\n", "hello makefile!");
}


makefile 源碼

app.out : main.o func.o
    $(CC) -lstdc++ -o $@ $^

.c.o :
    @echo "my suffix rule"
    $(CC) -o $@ -c $^

.c :
    @echo "my suffix rule"
    $(CC) -o $@ -c $^

我們來看看編譯結果

技術分享圖片

我們看到已經正確實現了。不過在現在的工程項目中,我們一般都會摒棄掉後綴規則,采用的都是模式規則。通過對 makefile 中隱式規則的學習,總結如下:1、當多處出現同一目標的命令時,只有最後定義的命令才有效;2、make 提供了一系列的隱式規則可使用,當 makefile 中未定義相關規則時,將嘗試使用隱式規則;3、隱式規則中可能使用 make 中的預定義變量,改變預定義變量可部分改變預定義規則的行為;4、隱式規則可能造成意想不到的編譯行為,在實際工程項目中盡量不使用隱式規則;5、後綴規則是一種舊式的模式規則,它正逐步被模式規則所取代。


歡迎大家一起來學習 makefile,可以加我QQ:243343083

make 的隱式規則(十一)