亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

自動生成依賴關系(十)

發布時間:2020-06-05 07:39:19 來源:網絡 閱讀:817 作者:上帝之子521 欄目:系統運維

        我們在之前的 makefile 學習中,其目標文件(.o)只依賴于源文件(.c)。那么如果在源文件中還包含有頭文件,此時編譯器如何編譯源文件和頭文件呢?我們來看看編譯行為帶來的缺陷:1、預處理器將頭文件中的代碼直接插入源文件;2、編譯器只通過預處理后的源文件產生目標文件;3、規則中以源文件為依賴,命令就可能無法執行。

        我們來看看下面的 makefile 有沒有問題


makefile 源碼

OBJS := func.o main.o

hello.out : $(OBJS)
    @gcc -o $@ $^
    @echo "Target File ==> $@"

$(OBJS) : %.o : %.c
    @gcc -o $@ -c $^


func.h 源碼

#ifndef _FUNC_H_
#define _FUNC_H_

#define HELLO "Hello D.T."

void foo();

#endif


func.c 源碼

#include <stdio.h>
#include "func.h"

void foo()
{
    printf("void foo() : %s\n", HELLO);
}


main.c 源碼

#include <stdio.h>
#include "func.h"

int main()
{
    foo();

    return 0;
}

        我們來看看編譯結果

自動生成依賴關系(十)

        我們看到已經正確實現了字符串的打印,那么我們接下來在 func.h 源文件中想要改掉這個字符串為 Software 呢?試試看能不能修改成功

自動生成依賴關系(十)

        我們看到在重新編譯的時候,它并沒有因為頭文件的改變而改變,我們在 makefile 中又沒有進行頭文件的相關添加,改掉頭文件中的內容肯定是不動的。下來我們在模式規則中加上頭文件,在 %.c 后加上 func.h,再來看看編譯結果

自動生成依賴關系(十)

        我們看到直接添加之后,編譯出錯了。因為 -c 后面的目標中含有頭文件,所以不能直接進行編譯。我們可以只編譯 %.o 后面的第一依賴 %.c,這樣就不會去編譯 func.h 頭文件了,將下面的 $^ 改為 $< ,我們來看看效果

自動生成依賴關系(十)

        我們看到已經正確改過來了。經過上面的實驗,我們看到:頭文件作為依賴條件出現于每個目標對應的規則中,當頭文件改動時,任何源文件都將會被重新編譯(編譯低效);當項目中頭文件巨大時,makefile 將很難維護。那么我們的頭腦中不禁會冒出這么個想法:通過命令對自動生成對頭文件的依賴;將生成的依賴自動包含進 makefile 中;當頭文件改動后,自動確認需要重新編譯的文件。那么此時我們還需要知道一個命令,Linux 中的 sed 命令。sed 是一個流編輯器,用于流文本的修改(增、刪、查、改);它可用于流文本中的字符串替換,其字符串替換方式為:sed 's:src:des:g',具體格式如下

自動生成依賴關系(十)

        sed 同樣也支持正則表達式,在 sed 中可以用正則表達式匹配替換目標,并且可以使用匹配的目標生成替換結果。格式如下

自動生成依賴關系(十)

        下來我們以代碼為例來看看 sed 命令是如何使用的

自動生成依賴關系(十)

        再來看看 gcc 關鍵編譯選項,獲取目標的完整依賴關系:gcc -M test.c;獲取目標的部分依賴關系:gcc -MM test.c。makefile 如下

.PHONY : test

test :
    gcc -M main.c

        編譯結果如下

自動生成依賴關系(十)

        我們看到 -M 是獲取了它的所有依賴關系,再來試試 -MM 呢

自動生成依賴關系(十)

        我們看到 -MM 后,它只依賴與 main.c func.h。我們可以拆分目標的依賴,即將目標的完整依賴差分為多個部分依賴。格式如下

自動生成依賴關系(十)

        我們來做個實驗

.PHONY : a b c

test : a b

test : b c

test :
    @echo "$^"

        我們來打印看看目標 test 的依賴都有哪些,編譯結果如下

自動生成依賴關系(十)

        那么我們思考下:如何將 sed 和 gcc -MM 用于 makefile,并自動生成依賴關系呢?

        我們再來看看 makefile 中的 include 關鍵字,它類似于 C 語言中的 include,是將其它文件的內容原封不動的搬入當前文件。make 對 include 關鍵字的處理方式是在當前目錄下搜索或指定搜索目標文件。如果搜索一成功,便將文件內容搬入當前 makefile 中;如果搜索失敗,將會產生警告,以文件名作為目標查找并執行對應規則,當文件名對應的規則不存在時,最終產生錯誤。格式如下

自動生成依賴關系(十)

        下來還是以代碼為例來進行說明

.PHONY : test

include test.txt

all :
    @echo "this is $@"

test.txt :
    @echo "test.txt"
    @touch test.txt

        我們在第 3 行包含 test.txt,可是當前目錄下并沒有 test.txt,然后觸發 test.txt 的規則。因而會打印出 test.txt,然后再創建 test.txt,我們來看看編譯結果

自動生成依賴關系(十)

        我們看到確實是創建了一個 test.txt 文件。那么在 makefile 中命令的執行是:1、規則中的每個命令默認是在一個新的進程中執行(Shell);2、可以通過接續符(;)將多個命令組合成一個命令;3、組合的命令依次在同一個進程中被執行;4、set -e 指定發生錯誤后立即退出執行。那么我們看看下面的代碼會實現想要的功能嗎?

.POHONY : all

all :
    mkdir test
    cd test
    mkdir subtest

        我們來看看編譯結果自動生成依賴關系(十)

        我們看到在當前目錄下創建了目錄,但是 subtest 目錄卻不是在 test 目錄下創建的,這是怎么回事呢?在第一條命令執行時創建了目錄 test,此時這個進程已經關閉了;在第二條命令執行時,執行的是另一個進程,雖然它已經進入到目錄 test 中,但是隨著這個進程的關閉,又回到了當前目錄;第三個進程是重新創建了目錄 subtest。那么如何解決這個問題呢?直接利用 set -e 和 接續符來解決

.PHONY : test

all :
    set -e; \
    mkdir test; \
    cd test; \
    mkdir subtest

        看看編譯結果

自動生成依賴關系(十)

        那么我們之前思考問題的初步思路是:1、通過 gcc -MM 和 sed 得到 .dep 依賴文件(目標的部分依賴),技術點是規則中命令的連續執行;2、通過 include 指令包含所有的 .dep 依賴文件。技術點是當 .dep 依賴文件不存在時,使用規則自動生成。下面我們來看看解決方案是怎樣的

ONY : all clean

MKDIR := mkdir
RM := rm -fr
CC := gcc

SRCS := $(wildcard *.c)
DEPS := $(SRCS:.c=.dep)

include $(DEPS)

all :
    @echo "all"
        
%.dep : %.c
    @echo "Creating $@ ..."
    @set -e; \
    $(CC) -MM -E $^ | sed 's,\(.*\)\.o[ :]*,objs/\1.o : ,g' > $@

clean :
    $(RM) $(DEPS)

        我們來看看編譯結果

自動生成依賴關系(十)

        我們先來分析下,在執行 make all 前,它先通過 include 包含 $(DEPS),通過 $(DEPS) 觸發模式規則,進而創建文件夾。我們看到在前面出現兩個沒有文件夾的信息,其實這條信息是可以隱藏的。我們在 include 前面加上 - 就 OK,來看看效果

自動生成依賴關系(十)

        我們看到并沒打印出前面的兩條信息了。那么我們再來思考下:如何組織依賴文件相關的規則與源碼編譯相關的規則,進而形成功能完整的 makefile  程序呢?我們如何在 makefile 中組織 .dep 文件到指定目錄呢?初步想法是當 include 發現 .dep 文件不存在時:1、通過規則和命令創建 deps 文件;2、將所有 .dep 文件創建到 deps 文件夾;3、.dep 文件中記錄目標文件的依賴關系

        我們下來看看初步的代碼設計是怎樣的

.PHONY : all clean

MKDIR := mkdir
RM := rm -rf
CC := gcc

DIR_DEPS := deps

SRCS := $(wildcard *.c)
DEPS := $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))

include $(DEPS)

all :
    @echo "all"

$(DIR_DEPS) :
    $(MKDIR) $@

$(DIR_DEPS)/%.dep : $(DIR_DEPS) %.c
    @echo "Creating $@ ..."
    @set -e; \
    $(CC) -MM -E $(filter %.c, $^) | sed 's,\(.*\)\.o[ :]*,objs/\1.o : ,g' > $@

clean :
    $(RM) $(DIR_DEPS)

         我們來看看編譯結果,是不是都將所有的 .dep 文件放入一個 deps 文件中

自動生成依賴關系(十)

        我們看到已經實現效果了。我們仔細看看 make 有一個警告,說 main.dep 被修改了,也就是說 main.dep 被重新創建了。那么我們來分析下,為什么一些 .dep 依賴文件會被重復創建多次呢?deps 文件夾的時間屬性會因為依賴文件創建而發生改變,make 發現 deps 文件夾比對應的目標更新,于是乎就觸發相應的規則重新解析和執行命令。那么我們知道了原因,此時這個方案該如何優化呢?我們可以使用 ifeq 動態決定 .dep 目標的依賴,具體 makefile 如下

.PHONY : all clean

MKDIR := mkdir
RM := rm -fr
CC := gcc

DIR_DEPS := deps

SRCS := $(wildcard *.c)
DEPS := $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))


all : 
    @echo "all"

ifeq ("$(MAKECMDGOALS)", "all")
-include $(DEPS)
endif

ifeq ("$(MAKECMDGOALS)", "")
-include $(DEPS)
endif

$(DIR_DEPS) :
    $(MKDIR) $@

ifeq ("$(wildcard $(DIR_DEPS))", "")
$(DIR_DEPS)/%.dep : $(DIR_DEPS) %.c
else
$(DIR_DEPS)/%.dep : %.c
endif
    @echo "Creating $@ ..."
    @set -e; \
    $(CC) -MM -E $(filter %.c, $^) | sed 's,\(.*\)\.o[ :]*,objs/\1.o : ,g' > $@
    
clean :
    $(RM) $(DIR_DEPS)

        我們再次編譯看看

自動生成依賴關系(十)

        我們看到它還是報了這樣的錯誤,有可能是編譯器的優化造成的。思路是正確的。下來我們來看看 include 的一些鮮為人知的秘密。

        A、 使用減號(-)不但關閉了 include 發出的警告,同時將關閉了錯誤;當錯誤發生時 make 將忽略這些錯誤! 以代碼為例來進行分析說明

.PHONY : all

include test.txt

all :
    @echo "this is all"

test :
    @echo "creating $@ ..."
    @echo "other : ; @echo "this is other" " > test.txt

        我們來編譯看看

自動生成依賴關系(十)

        我們看到不但發出警告,而且報錯了。下來我們來在 include 前面加上 - 試試

自動生成依賴關系(十)

        這樣它也不報錯了,直接就通過了,我們還以為 makefile 寫的對著呢。這便是第一個暗黑操作。下來看看第二個暗黑操作

        B、如果 include 觸發規則創建了文件,之后還會發生什么?以代碼為例來進行分析說明

.PHONY : all

include test.txt

all :
    @echo "this is all"

test.txt :
    @echo "creating $@ ..."
    @echo "other : ; @echo "this is other" " > test.txt

        看看編譯結果

自動生成依賴關系(十)

        我們進行直接 make 的時候,發現它輸出的 this is other,并不是我們所期望的 this is all。這是為什么呢?因為在 include 的時候,直接將 test.txt 鋪開在這,此時會觸發規則。makefile 就變成了下面這樣

.PHONY : all

other : 
    @echo "creating $@ ..."
    @echo "this is other"

all :
    @echo "this is all"

        我們在直接 make 的時候,它默認執行的是第一個目標,因此便會輸出 this is other,只有當我們 make all 的時候才會輸出 this is all。這便是 include 的第二個暗黑操作了,下面繼續看看第三個

        C、如果 include 包含的文件存在,之后會發生什么呢?以代碼為例來進行分析說明

.PHONY : all

-include test.txt

all :
    @echo "this is all"

test.txt : b.txt
    @echo "this is $@"

        在當前目錄下創建一個 b.txt 文件,看看編譯結果

自動生成依賴關系(十)

        我們看到同樣也執行了 test.txt 的相應的規則。看看下面這個 makefile 將會輸出什么

.PHONY : all

-include test.txt

all : 
    @echo "$@ : $^"
    
test.txt : b.txt
    @echo "creating $@ ..."
    @echo "all : c.txt" > test.txt

        看看結果

自動生成依賴關系(十)

        我們看到它最后輸出的 all 的依賴是 c.txt,不應該覺得奇怪嗎?我們明明在 all 后面沒有依賴啊。再來看看生成的 test.txt 文件,它的內容是 all : c.txt,因此輸出的結果是我們意想不到的。那么我們關于 include 便有了這幾條總結:1、當目標文件不存在時,以文件名查找規則并執行;2、當目標文件不存在時且查找到的規則中創建了目標文件,將創建成功的目標文件包含進當前的 makefile 中;3、當目標文件存在,將目標文件包含進當前 makefile,以目標文件名查找是否有相應規則,YES 的話則比較規則的依賴關系來決定是否執行規則的命令,NO 的話則 NULL(無操作)。4、當目標文件存在且目標名對應的規則被執行,規則中的命令更新了目標文件,make 重新包含目標文件,替換之前包含的內容。目標文件未被更新,便是 NULL(無操作)。

        經過了這么多的知識點的探索,此時已經具備實現之前的想法的能力了。想要實現的具體格式如下

自動生成依賴關系(十)

        下面我們就根據這個來編寫相關的 makefile。

func.h 源碼

#ifndef FUNC_H
#define FUNC_H

#define HELLO "hello Makefile"

#endif


func.c 源碼

#include <stdio.h>
#include "func.h"

void foo()
{
    printf("void foo() : %s\n", HELLO);
}


main.c 源碼

#include <stdio.h>
#include "func.h"

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


makefile 源碼

.PHONY : all clean

MKDIR := mkdir
RM := rm -rf
CC := gcc

DIR_DEPS := deps
DIR_OBJS := objs
DIR_EXES := exes

DIRS := $(DIR_DEPS) $(DIR_EXES) $(DIR_OBJS)

EXE := app.out
EXE := $(addprefix $(DIR_EXES)/, $(EXE))

SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
DEPS := $(SRCS:.c=.dep)
DEPS := $(addprefix $(DIR_DEPS)/, $(DEPS))

all : $(DIR_OBJS) $(DIR_EXES) $(EXE)

ifeq ("$(MAKECMDGOALS)", "all")
-include $(DEPS)
endif

ifeq ("$(MAKECMDGOALS)", "")
-include $(DEPS)
endif

$(EXE) : $(OBJS)
    $(CC) -o $@ $^
    @echo "Success! Target => $@"

$(DIR_OBJS)/%.o : %.c
    $(CC) -o $@ -c $^

$(DIRS) :
    $(MKDIR) $@

ifeq ("$(wildcard $(DIR_DEPS))", "")
$(DIR_DEPS)/%.dep : $(DIR_DEPS) %.c
else
$(DIR_DEPS)/%.dep : %.c
endif
    @echo "Creating $@ ..."
    @set -e; \
    $(CC) -MM -E $(filter %.c, $^) | sed 's,\(.*\)\.o[ :]*,objs/\1.o  $@ : ,g' > $@
                
clean :
    $(RM) $(DIRS)

        編譯結果如下

自動生成依賴關系(十)

        我們看到已經自動生成了,并且最后的結果也是我們想要的,那么我們如果在 func.h 中改變字符串,看看結果是否也會改變

自動生成依賴關系(十)

        我們看到在編譯的時候報錯了,原因是只能編譯 .c 文件,.h 頭文件不參與編譯,這時我們便要用到預定義函數 filter 了。因此我們需要在 makefile 第37 行將它改為 $(CC) -o $@ -c $(filter %.c, $^);再來看看效果

自動生成依賴關系(十)

        我們看到也成功的替換掉了。這時我們基本上已經完成我們之前的想法了,那么在實際開發中,肯定需要時不時的添加頭文件,我們再來在 func.h 中包含一個頭文件 define.h,在 define.h 文件中定義字符串 hello-makefile,看看結果是否會跟著改變

自動生成依賴關系(十)

        我們看到字符串并沒有發生改變,再來看看 func.dep 和 main.dep 中是否包含了 define.h

自動生成依賴關系(十)

        也沒有包含,按理說不應該,因為我們在 func.h 中包含了 define.h,那么在 func.c 和 main.c 中肯定也就包含了 define.h。下來我們來分析下這個,當 .dep 文件生成后,如果動態的改變頭文件間的依賴關系,那么 make 可能無法檢測到這個改變,進而做出錯誤的編譯決策。解決方案便是:1、將依賴文件名作為目標加入自動生成的依賴關系中;2、通過 include 加載依賴文件時判斷是否執行規則;3、在規則執行時重新生成依賴關系文件;4、最后加載新的依賴文件。解決方法是在 sed 命令后加上 $@,看看編譯效果,順便我們再來加上 rebuild。

自動生成依賴關系(十)

        我們看到已經正確實現了,我們來看看在 deps 文件下的 .dep 文件是否包含 define.h 呢?

自動生成依賴關系(十)

        確實是包含了 define.h,我們再來加上 new.h,看看是否還會有效

自動生成依賴關系(十)

        我們看到 new.h 同樣也包含進去了。通過對綜合示例的學習,總結如下:1、makefile 中可以將目標的依賴拆分寫到不同的地方;2、include 關鍵字能夠觸發相應規則的執行;3、如果規則的執行導致依賴更新,可能導致再次解釋執行相應規則;4、依賴文件也需要依賴于源文件得到正確的編譯決策;5、自動生成文件間的依賴關系能夠提高 makefile 的移植性


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

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

婺源县| 龙游县| 商南县| 芦山县| 阳泉市| 台北县| 雷州市| 沙河市| 洛川县| 黔东| 塘沽区| 鹤壁市| 桑植县| 界首市| 文登市| 红原县| 安多县| 株洲县| 武宣县| 通州市| 荃湾区| 温州市| 越西县| 阳曲县| 肇东市| 泸州市| 马尔康县| 永昌县| 集贤县| 鱼台县| 泗阳县| 鄱阳县| 旬阳县| 榆中县| 崇左市| 满城县| 永顺县| 阿巴嘎旗| 治多县| 马鞍山市| 水城县|