最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

《Makefile 光學(xué)教程》之面向 Makefile 編程

2023-09-16 02:09 作者:緊果唄  | 我要投稿

此教程將計(jì)劃以兩部分內(nèi)容呈現(xiàn),目標(biāo)是從零基礎(chǔ)到 GNU make 最本原理的掌握:

  1. ??? Basic Concepts

  2. ??? Demo Projects

?? Basic Concepts

按照 GNU m4 宏編程經(jīng)驗(yàn), Macros 即代碼生成工具,輸入輸出都是字符串,輸入字符中所有宏符號(hào)都會(huì)被相應(yīng)的宏定義內(nèi)容替換。但是 make 作為一種宏編程工具,有些功能差異,它并不像 GNU m4 這種通用的宏編程工具,出于約束它的靈活性同時(shí)降低使用風(fēng)險(xiǎn),make 增加了許多約束條件,比如在 Target 規(guī)則之外不能使用宏輸出內(nèi)容。


GNU m4 作為一個(gè)通用的宏編程工具,它的核心概念就是字符串流,輸入輸出都是字符串流。宏定義的功能就是替換輸入流中匹配的字符串,再將替換后的數(shù)據(jù)發(fā)送到輸出流,這個(gè)過程稱之為宏展開 macro expannsion。

比如,以下是一個(gè) m4tutor.m4 腳本,即字符串文件,它將作為 GNU m4 宏處理器的輸入流:


宏處理器處理字符串輸入流唯一規(guī)則就是按宏定義進(jìn)行內(nèi)容替換,最簡單的宏定義就是使用 define 指令,或者直接通過命令行定義宏符號(hào),如下:


GNU M4 宏編程

那么這個(gè)執(zhí)行過程就是:讀取輸入流中的第一行,得到一個(gè)宏定義,最后可以輸出的只有換行符。然后讀取第二行內(nèi)容,是一個(gè)字符串,剛好 say_hello 這個(gè)字符串對(duì)應(yīng)一個(gè)宏定義,那么替換它得到 Hello World! 包含換行符,然后再輸出。最后讀取到一個(gè)字符串 cc,同樣它對(duì)應(yīng)一個(gè)宏定義,從命令行中傳入的宏定義,同樣要替換,得到 List(1,2,3)。整個(gè)宏腳本輸出內(nèi)容如下:

以上就是宏編程的一個(gè)基本流程概念:字符串替換!Makefile 腳本編程也適用這一基本原理。


Makefile 宏編程核心概念有兩個(gè),*Target* 和 *Rule*,其次是指令 directive 用于實(shí)現(xiàn) make 腳本功能的內(nèi)置宏。另外就是附加的一些宏腳本編程能力,比如變量、宏指令、宏參數(shù)、include 其它腳本、Secondary Expansion 二次展開,以及各種特殊功能符號(hào)等等,它們功能上都類似 GNU m4 的宏替換過程。Makefile 規(guī)則定義就是描述如何生成 Targer 之間的邏輯關(guān)系,也就是 Target 之間可以形成的依賴網(wǎng)絡(luò)。


默認(rèn)配置下,recipe 部分編寫的命令內(nèi)容必須使用 Tab 符號(hào)作為行首字符??梢允褂?.RECIPEPREFIX 內(nèi)置變量來定義。但這個(gè)功能不一定所有 make 都支持,在不支持的情況下就會(huì)提示錯(cuò)誤信息:missing separator.

Makefile 最基本的能力就是根據(jù) Taraget 所設(shè)置的依賴決定是否需要執(zhí)行 recipe 中編寫的編譯命令來生成最新的程序。這其中涉及了文件更新時(shí)間戳的檢測行為,包括 Target 命令匹配的文件,以及規(guī)則中冒號(hào)右側(cè)指定的依賴文件。


當(dāng)然,不是所有規(guī)則都需要對(duì)應(yīng)文件,像以上示范中的規(guī)則,一個(gè)命名為 all 的目標(biāo),沒有任何依賴文件,它本身也不對(duì)應(yīng)磁盤中的文件,這條規(guī)則只需要在命令行中執(zhí)行一條 echo 命令,打印一段字符串。


依賴關(guān)系鏈由 Target 與先決條件 prerequisites 之間的聯(lián)系產(chǎn)生,因?yàn)橄葲Q條件中的任何項(xiàng)都可以被定義為 Target,也就形成了 A_Target -> Prerequisite -> B_Target -> Prerequisite 這樣的鏈條。當(dāng)執(zhí)行 make A_Target 時(shí),根據(jù)依賴鏈,會(huì)一起遞歸到最尾端的 Target 并執(zhí)行其規(guī)則定義的 recipes,然后逐級(jí)返回執(zhí)行上一層的 recipes,直到 A_Target 的部分。


但是,只要這鏈條中間任何一環(huán)節(jié)破壞,Target 命名與上一層的先決條件名稱不匹配,那么后面的 Target 定義即失效。除非調(diào)用 make 命令時(shí),直接指定那些處于斷鏈狀態(tài)的 Target。依賴關(guān)系的判斷,是根據(jù)宏擴(kuò)展后的結(jié)果進(jìn)行的,所以定義規(guī)則時(shí),可以在規(guī)則中的 Targets 或先決條件中使用任意的宏函數(shù),來靈活地構(gòu)建依賴關(guān)系網(wǎng)絡(luò)。


本質(zhì)上,Makefile 就是一個(gè)描述依賴關(guān)系的腳本,例如如下一個(gè) `Makefile` 規(guī)則定義:

先拋開 $ 宏調(diào)用等特殊功能符號(hào),以上這個(gè) Makefile 它描述的是以下這樣的依賴關(guān)系,最終是構(gòu)建出 all 這個(gè)目標(biāo),它代表要鏈接各種目標(biāo)文件的可執(zhí)行程序。整個(gè)依賴關(guān)系網(wǎng)絡(luò)由規(guī)則定義,鏈接命令由依賴關(guān)系推斷。make 命令知道擴(kuò)展名為 .o 的目標(biāo)文件的處理,以及如何調(diào)用 C/C++ 編譯器和鏈接程序,通過 $(CC)?$(LEX) 分別調(diào)用 C/C++ 編譯器和 lex 詞法解釋器生成命令,兩個(gè)外部命令完成相應(yīng)的編譯工作。這種自動(dòng)推斷能力就是 make 的隱含能力,具有隱含功能的規(guī)則定義也就稱為 Implicit Rules,參考手冊(cè) 2.5 Letting make Deduce the Recipes。除非需要,開發(fā)者可以指定編譯器的各種參數(shù)以修正默認(rèn)的配置:


這里的 Makefile 編寫了兩條舊風(fēng)格的 suffix rule,即通過后綴識(shí)別行為/定義的規(guī)則,包含 double-suffixsingle-suffix 兩種。其中 .c .o .l 三個(gè)都是后綴,對(duì)應(yīng)了 C 語言源代碼、目標(biāo)文件和詞法規(guī)則分析器三種源文件。這種連續(xù)使用 source suffixtarget suffix 后綴的形式就是 double-suffix,也即是雙后綴形式的規(guī)則定義,從其執(zhí)行結(jié)果可以知道這種規(guī)則就是將前 source 文件處理成后 target 文件。Single-suffix 則是保留 source surfix 文件后綴。


Make 和 GNU m4 一樣默認(rèn)使用 # 作為注解符號(hào)。另外,如果行內(nèi)容超長,可以在先進(jìn)性行尾使用斜杠 \ 轉(zhuǎn)義換行符號(hào),便后一行內(nèi)容與前一行內(nèi)容拼接起來成為一行,即相當(dāng)于斷行連接。


一般的 Makefile 規(guī)則以冒號(hào)為分界,*Ordinary Rules*,左側(cè)表示輸出稱為 `Target`,可以有多個(gè)輸出,它本身就是一般的沒有隱含功能的字符串標(biāo)識(shí),右側(cè)表示輸入稱之為依賴或者先決條件。更復(fù)雜的規(guī)則可以參考官方文檔,Complex Makefile 示例中有完整的規(guī)則參考。語法中的 recipe 單詞為食譜、處方,也是規(guī)則實(shí)現(xiàn)、促使規(guī)則達(dá)成的意思,就是定義構(gòu)建目標(biāo)時(shí)要執(zhí)行的命令。像以上這種規(guī)則,因?yàn)?Target 部分字符具有特殊功能的規(guī)則,稱之為隱式規(guī)則 *Implict Rules*,與之對(duì)應(yīng)的就是顯式規(guī)則 *Explicit rules*,普通規(guī)則就是顯式規(guī)則。


Implict Rules vs. Explicit rules,弄清楚隱式規(guī)則與顯式規(guī)則的區(qū)別,是深入掌握各使用 make 的必要條件。


要區(qū)分什么規(guī)則是顯式或者是隱式,根本上來說有一個(gè)參考標(biāo)準(zhǔn):就是會(huì)不會(huì)觸發(fā) GNU make 內(nèi)置的規(guī)則,如果會(huì)觸發(fā)內(nèi)置規(guī)則,那么就可以認(rèn)定是隱式規(guī)則。內(nèi)置規(guī)則之所以稱為隱式規(guī)則,是因?yàn)樗鼈儾粫?huì)用戶在 Makefile 腳本中將這些規(guī)則編寫出來,make 會(huì)根據(jù)目標(biāo)的擴(kuò)展名或者磁盤對(duì)應(yīng)名稱的文件類型去調(diào)用相應(yīng)的內(nèi)置規(guī)則。這個(gè)過程是隱式的,不透明的(Makefile 腳本沒有相應(yīng)規(guī)則定義)。參考手冊(cè) 10.2 Catalogue of Built-In Rules。


所有內(nèi)置規(guī)則可以通過 make -p 命令查詢,它們涉及以下編程語言或者文件類型:

所有隱式規(guī)則涉及的文件擴(kuò)展名可以通過 .SUFFIXES 內(nèi)置變量中列表。


以下腳本演示了各種經(jīng)常出現(xiàn)的規(guī)則定義形式,在這里它們都可以認(rèn)為是顯式規(guī),除了最后一個(gè)注解掉規(guī)則。依賴關(guān)系鏈上沒有不清晰的環(huán)節(jié),所有依賴,從 all 到各個(gè) .lex 文件的依賴都有定義,所以不會(huì)觸發(fā) GNU make 內(nèi)置的規(guī)則,因此它們可認(rèn)為是顯式規(guī)則。


如果 all 只與最后一個(gè)注解掉規(guī)則 a.tex 搭配使用,那么就會(huì)觸發(fā) make 內(nèi)置規(guī)則,這就有兩種可能的結(jié)果:一是磁盤有相應(yīng)的 .lex 文件,make 自動(dòng)應(yīng)用隱式規(guī)則的命令;二是缺失對(duì)應(yīng)磁盤文件,Makefile 又不能提供完整的依賴關(guān)系,缺失了 b.lexc.tex 兩個(gè)依賴的目標(biāo)規(guī)則,所以提示錯(cuò)誤信息:No rule to make target 'b.tex' 等等。**因?yàn)槠渲幸粭l依賴構(gòu)建失敗,那么就會(huì)導(dǎo)致上層目標(biāo)構(gòu)建失敗,all 目標(biāo)中的命令就不會(huì)執(zhí)行。**


為何讓最后一條規(guī)則也“顯式”起來,那么可以搭配 %.tex 這樣的模式匹配規(guī)則、靜態(tài)模式匹配規(guī)則,或者使用變量等等方式來解決大量文件依賴關(guān)系的處理。注意:使用這些靈活的規(guī)則非常容易觸發(fā)隱式規(guī)則,并且 Makefile 可以使用 inlude 指令來引用更多的腳本文件,這會(huì)使得腳本變得異常復(fù)雜。


其中,Static Pertern Rules 是非常特別的一種模式匹配規(guī)則,它不像其它規(guī)則(包括一般的模式匹配規(guī)則)可以不指定依賴,靜態(tài)模式匹配規(guī)則就是為了批量處理依賴設(shè)計(jì)的。使用模式匹配符號(hào) % 可以將匹配到的內(nèi)容(stem)替換到依賴列表中的名稱中形成新的依賴列表。

另外一個(gè)問題是規(guī)則的優(yōu)先級(jí)問題,以下提供參考,其中前兩條是定義總結(jié):


1. Literal 字面量定義方式優(yōu)先于 Double-Colon Rules,優(yōu)先于 Pattern Rules

2. 同類形規(guī)則,Target 命名使用的字面量字符越多越優(yōu)先,即信息精細(xì)度高;

3. 單冒號(hào)規(guī)則中,新定義會(huì)覆蓋舊定義,但保留依賴列表,只替換命令塊;

4. 雙冒號(hào)規(guī)則各自獨(dú)立執(zhí)行不存在定義覆蓋,但是 Target 不能同時(shí)定義單、雙冒號(hào)規(guī)則;


多目標(biāo)規(guī)則與單目標(biāo)規(guī)則不同,多目標(biāo)規(guī)則中不能混用多種匹配方式,只使用字面量匹配,或者只使用模式匹配。否則就會(huì)得到 mixed implicit and normal rules 警告提示。


因?yàn)?Static Pertern Rules 會(huì)形成新的依賴列表,單獨(dú)考慮。雙冒號(hào)規(guī)則參考手冊(cè) 4.13 Double-Colon Rules。


如果腳本中規(guī)則依賴已經(jīng)存在定義,但是沒有被執(zhí)行,那么最有可能的原因有二:


1. 一是可能是優(yōu)先級(jí)沒處理好。

2. 二是依賴的目標(biāo)文件已經(jīng)存在對(duì)應(yīng)的磁盤文件。


解決方法就是重新整理規(guī)則定義,對(duì)于磁盤文件已經(jīng)存在,但是還要執(zhí)行構(gòu)建命令的規(guī)則(通常不需要這樣做),就可以使用 .PHONY 內(nèi)置目標(biāo)規(guī)則,它會(huì)忽略磁盤文件的狀態(tài)信息,無條件地執(zhí)行命令塊。


隱式規(guī)則中涉及文件搜索的算法過程參考手冊(cè):


10.4 Chains of Implicit Rules

10.8 Implicit Rule Search Algorithm


對(duì)于復(fù)雜的 Makefile 腳本的調(diào)用有兩大法器:


1. 注解!使用注解可以最大程序地縮小 Makefile 的復(fù)雜度;

2. 清空!將腳本運(yùn)行目錄下的文件清空(使用新卡件夾)再運(yùn)行;

3. 調(diào)試!GNU make 使用 -d 或者 --debug[=FLAGS] 參數(shù)打印解釋器運(yùn)行狀態(tài);


調(diào)試信息輸出參考如下,它們完整打印從 make 加載隱式規(guī)則到目標(biāo)文件檢測的解釋過程:


Make 在處理 makefiles 腳本依賴關(guān)系(include)期間,會(huì)觸發(fā) Remaking,涉及 MAKE_RESTARTS 內(nèi)置變量值的更新。然后再處理構(gòu)建目標(biāo)的規(guī)則定義與執(zhí)行命令塊,這就是 make 命令執(zhí)行的兩個(gè)主要階段。


提煉一下調(diào)試器輸出信息,大體可以總結(jié)出以下幾個(gè)步驟,以下展示一個(gè)目標(biāo)及其一個(gè)依賴目標(biāo)的構(gòu)建過程,從文件搜索檢測、到依賴目標(biāo)的搜索檢測,最后創(chuàng)建新進(jìn)程構(gòu)建目標(biāo):



很巧合,打印調(diào)試信息時(shí)出來一個(gè) 731,這是多么令人恐怖的數(shù)字?。?/p>


《Makefile 光學(xué)教程》之面向 Makefile 編程的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
福海县| 竹溪县| 东港市| 南郑县| 洛浦县| 荃湾区| 天气| 宁武县| 汾阳市| 诸城市| 屏南县| 象山县| 临猗县| 永安市| 东海县| 曲沃县| 礼泉县| 台东市| 满城县| 同江市| 巴里| 丰都县| 漯河市| 周口市| 永顺县| 祁门县| 曲周县| 大港区| 屏东县| 石楼县| 吴桥县| 碌曲县| 视频| 双城市| 曲松县| 钟山县| 社旗县| 梅河口市| 潼关县| 神农架林区| 类乌齐县|