《Makefile 光學教程》之面向 Makefile 編程·Unit Test [CPL]

此教程將計劃以兩部分內容呈現(xiàn),目標是從零基礎到 GNU make 最本原的邏輯原理的掌握,這是第二部分內容,分按不同的工程類型分成多個示范項目來展示。零基礎可以先看第一部分:Basic Concepts:
??? Basic Concepts
??? Demo Projects
?? Scheme R6RS 語言規(guī)范文檔處理 [LaTeX]
?? Multi threaded Download?[Msys2 Packages]
?? C/C++ Project Templates [GLib Gobject]
?? Erlang Project Templates
?? Unit Test [CPL]
完整《Makefile 光學教程》以及 GNU M4 教程參考開源文檔:https://github.com/Jeangowhy/opendocs/blob/main/Makefile.md
為了在 Make 腳本中測試程序,需要了解各種 shell 環(huán)境中如何使用 Exit Code:
1. Windows 傳統(tǒng)命令行 CMD 使用 `echo %errorlevel%` 打印退出碼,不等于 0 就是錯誤退出;?
2. PowerShell 使用 $LASTEXITCODE 獲取退出碼,或者 $? 獲取布爾值,True 表示正常退出;
3. Bash 使用 $? 自動變量,比如 `echo $?` 顯示 127 表示 False,程序錯誤退出;
潛在的問題:Msys2 MinGW 編譯的程序出現(xiàn)非法指針時,bash 不能檢測到返回碼,-1073741819 0xC0000005 STATUS_ACCESS_VIOLATION。2.3.1 NTSTATUS Values。
使用 Msys2 沒有正確安裝編譯器版本,或者安裝在錯誤的平臺目錄下,也可能導致編譯出來的程序出現(xiàn)內存違規(guī)訪問。因此,要確保編譯器正常工作,編譯生成的程序能夠正常執(zhí)行。
注意:雖然反斜杠 \ 符號在 Makefile 中并不是功能符號,它和其它一般字符一樣對待,但是構建命令執(zhí)行時,將它輸入到 shell 中執(zhí)行就會有轉義字符的功能。所以,使用 make 命令時一般需要在 Makefile 當前目錄下執(zhí)行,如果不是,將使用斜杠 / 作為 Makefile 路徑的目錄分隔符。
另外,更重要的是不能使用 ifeq 或者 ifneq 對被程序的輸出結果進行判斷。因為條件指令是立即綁定模式,不能使用自動變量,或者 eval 函數(shù)設置的變量,只有 Makefile 中已有定義的全局變量才可以使用。
并且,內置函數(shù) if 只能做字符串是否為空值的判斷,不能做等值比較。Make v4.4 版本引入的 intcmp 函數(shù)才能比較數(shù)值的小于、等于、大于三種狀態(tài)。所以判斷結果是否相等,可以交給 shell 命令去判斷。
Make 還有一個做等值判斷的 “詭計”是使用做局部匹配的 findstring 函數(shù),其邏輯是:如果一個字符串 A 包含另一個字符串 B,并且這個 B 又包含 A,那么它們相等。我想可以發(fā)掘一些 GNU Make 沒有直接提供,并且又可以通過組合各種邏輯實現(xiàn)的功能,大概是這個構建工具在功能實現(xiàn)上如此克制的原因吧。為此,這些專用的功能函數(shù),可以使用專用腳本“保管”集中管理,需要引用就通過 include 指令加載,以下是 utilities.mk 函數(shù)庫參考:
以下是用于構建待測試程序,以及進行測試的腳本,功能說明如下:
1. 測試程序 type.c 代碼文件和 utilities.mk 函數(shù)庫與 Makefile 共同存放于 src 目錄;
2. 在任意目錄中執(zhí)行 make -f Makefile 進行編譯、測試,輸出存放于上一級 bin 目錄;
3. 使用 GCC 編譯器,DEBUG、RELEASE 兩套基本配置,使用 make DEBUG=true 激活調試配置;
4. 配置了一條 test_hello 用于測試的規(guī)則,配合 EXPACT PASS FAILED 等變量輸出相應測試信息;

為了讓編譯與測試更加自動化,可以使用 Node watch 模塊實時監(jiān)視并執(zhí)行命令:
Deno 也內置了 watch 功能,也可以直接通過命令行運行,但是它畢竟不是專用的 watch 工具,可以將以下功能編寫到 js 或 ts 腳本文件中再執(zhí)行,并且可以創(chuàng)造出一些自定義功能:
以下是作為一個要測試的使用的目標程序:
字節(jié)序 Endianness 是在處理多字節(jié)的數(shù)據(jù)時,不同的 CPU 構架使用不同的方式。
比如 0x1234 這個值(十進制 4660):
- Big endian 方式,PowerPC 或蘋果 CPU 構架使用。
- Little endian 方式,Intel x86 系統(tǒng)使用。
測試程序中,使用“123”字符串作為一個測試數(shù)據(jù),由于 C 語言中的字符串使用 \0 作為結束標志,即 null-terminated string 格式。在一個四字節(jié)的整形數(shù)值中,其中三個字節(jié)位置設置為 “123”,最后一個字節(jié)需要設置為 null,否則打印函數(shù)就會因為邊界失誤面導出內存違規(guī)訪問。通常,這個違規(guī)并不一定會發(fā)生,因為內存中有很多 null 值的位置,隨意遇到一個 null 就可以終結這個字符串。但是,從邏輯上這就是違規(guī)的內存訪問。
字節(jié)內部的比特位也有兩種序,Most Significant Bit (MSB),在二進制數(shù)中屬于最高有效位,MSB 是最高加權位。Least Significant Bit (LSB) 在二進制數(shù)中意為最低有效位。一般來說,按書寫習慣,MSB 位于二進制數(shù)的最左側,LSB 位于二進制數(shù)的最右側。
CPU 存儲數(shù)據(jù)操作的最小單位是一個字節(jié),至于字節(jié)內部的比特序如何,對于程序來說是一個黑盒子。
使用 C 語言定義結構體或聯(lián)合體時,實踐中通常和 typedef 一起使用,這樣方便定義結構體類型的變量。但是,至今我仍記得結構體定義與 typedef 關鍵字使用時出現(xiàn)的混亂狀態(tài),讓我畢生難忘??偟脕碚f,定義一個結構體類型和實例化結構體對象,它們有部分語法結構會因為 typedef 關鍵字的使用而出現(xiàn)重疊,這也是混亂的主要來源。
假設要定義一個 ByteChunk 結構體,以及其實體變量 byteChunk,各種形式如下,當然還可以使用花括號對實例進行初始化: