本頁說明以任務為基礎的建構系統、其運作方式,以及 使用以工作為基礎的系統可能發生的小工具。殼層指令碼之後 以任務為基礎的建構系統是建構的下一波建構邏輯演變。
瞭解以任務為基礎的建構系統
在以任務為基礎的建構系統中,工作的基本單位是工作。每項 任務是可以執行任何類型邏輯的指令碼,工作則指定其他 做為依附元件,這些工作必須先執行使用中的多數主要建構系統 例如 Ant、Maven、Gradle、Grunt 和 Rake,都會是工作的基礎。而不是 殼層指令碼,大多數現代化建構系統都需要工程師建立建構檔案 說明如何執行建構作業
我們從 Ant 使用手冊:
<project name="MyProject" default="dist" basedir=".">
<description>
simple example build file
</description>
<!-- set global properties for this build -->
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init"
description="compile the source">
<!-- Compile the Java code from ${src} into ${build} -->
<javac srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends="compile"
description="generate the distribution">
<!-- Create the distribution directory -->
<mkdir dir="${dist}/lib"/>
<!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
<jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
</target>
<target name="clean"
description="clean up">
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>
buildfile 是以 XML 編寫,並定義了一些有關建構作業的簡單中繼資料
以及工作清單 (XML 中的 <target>
標記)。(Ant 使用
target 表示「工作」,並使用 task 這個字詞來表示
commands)。每項工作都會執行 Ant 定義的可用指令清單。
包括建立及刪除目錄、執行 javac
建立 JAR 檔案這組指令可由使用者提供
來涵蓋任何類型的邏輯。每項工作也可以定義
取決於取決於 屬性這些依附元件形成非循環圖
如圖 1 所示。
圖 1. 顯示依附元件的循環圖
使用者藉由將工作提供給 Ant 的指令列工具來執行建構作業。例如:
當使用者輸入 ant dist
時,Ant 會執行下列步驟:
- 在目前的目錄中載入名為
build.xml
的檔案,並剖析為 建立如圖 1 所示的圖表結構。 - 尋找指令列上提供且名為
dist
的工作,並 發現其對名為compile
的工作具有依附元件。 - 尋找名為
compile
的工作,並發現其具有依附元件 名為init
的工作 - 尋找名為
init
的工作,並發現其沒有依附元件。 - 執行
init
工作中定義的指令。 - 基於上述所有考量,執行
compile
工作中定義的指令 任務的依附元件已執行完畢 - 基於上述所有考量,執行
dist
工作中定義的指令 任務的依附元件已執行完畢
最後,Ant 在執行 dist
工作時執行的程式碼相等
加入以下殼層指令碼:
./createTimestamp.sh
mkdir build/
javac src/* -d build/
mkdir -p dist/lib/
jar cf dist/lib/MyProject-$(date --iso-8601).jar build/*
若去除語法,建構檔案和建構指令碼實際上
原則上不過這種做法已大幅提升。我們可以
在其他目錄中建立新的建構檔案,然後將這些檔案相互連結。您可以輕鬆
以任意且複雜的方式新增依賴現有工作的新工作。三
只需要將單一工作的名稱傳遞至 ant
指令列工具
以決定需要執行的所有功能
Ant 是 2000 年發行的老舊軟體,其他工具,例如 在 Ant 的過渡時期,Maven 和 Gradle 都獲得進步,整體上來說 做法是新增自動管理外部 IP 位址的功能 和不含 XML 的簡潔語法。但這些新技術的性質 系統還是一樣:讓工程師將建構指令碼編寫成 有原則和模組化的方式做為任務,並提供執行這些任務的工具 以及管理依附元件之間的依附元件
以任務為基礎的建構系統黑暗面
這些工具基本上可讓工程師將任何指令碼定義為工作 功能極為強大,不論你想到什麼,都能做到 看似有益或無害的 AI 用途 也可能夾帶問題或相關風險但這種效能固然有缺點 而以任務為基礎的建構系統 會變得難以使用 這類系統的問題是其實會提供太多電力, 也無法提供足夠的電力因為系統不知道 指令碼執行的工作和效能會受到負面影響,因為必須保持保守 如何排程和執行建構步驟當然,系統也不可能 確保每個指令碼都能發揮效果 而最後是需要偵錯的另一個東西
難以平行處理建構步驟
現代開發工作站功能強大,搭載多個核心 能夠同時執行多個建構步驟。但以任務為基礎的系統 通常無法平行執行任務,即使看起來像是 能夠達成的目標假設工作 A 依附工作 B 和 C。由於工作 B 和 C 彼此之間沒有依附元件 如果同時執行這些程式碼 才能更快完成任務 A?或許只要他們一按就能 相同資源的但或許不是,兩者都使用同一個檔案來追蹤 狀態和同時執行都會造成衝突。沒有 系統才能知道,所以 (造成建構問題很少,但難以偵錯),或者必須 整個建構作業只能在單一程序的單一執行緒中執行。 這可能會浪費大量效能強大的開發人員機器 將建構分散到多部機器上的可能性
難以執行漸進式建構作業
優質的建構系統可讓工程師執行可靠的漸進式建構作業, 進行小幅變更時,不需要從整個程式碼集重新建構整個程式碼集 。如果建構系統速度緩慢且無法達成目標,就必須特別注意這點 針對上述原因平行處理建構步驟。但很抱歉 任務式建構系統同樣也是一大挑戰。由於工作可以執行任何作業 但通常無法確認這些圖片是否已完成。許多工作 只要擷取一組來源檔案並執行編譯器,即可建立一組 二進位檔;因此如果基礎來源檔案已經存在,也不必重新執行 全都沒有改變不過,如果缺少額外資訊,系統將無法確認這項資訊 也許任務是下載可能已變更的檔案 這些寫入的時間戳記可能在每次執行作業時都不同。保證 一般而言,系統會在每次建構期間重新執行每個工作。只有部分通知 建構系統透過允許工程師指定 持續執行工作的條件這個做法有時可行 但問題通常比表面上還難。比方說 例如允許其他檔案直接納入檔案的 C++ 無法判斷必須監控變更所需的整組檔案 不必剖析輸入來源工程師經常會變換快速鍵 這些快速鍵可能會導致工作結果的罕見問題, 或不當重複使用這種情況經常出現 到每次版本執行前先做好清理的習慣,才能取得全新狀態 一開始就執行漸進式建構作業,完全沒有作用 。如何判斷何時需要重新執行工作並不意外 機器能比人類更妥善地處理工作
難以維護指令碼及偵錯
最後,以工作為基礎的建構系統所施加的建構指令碼通常僅 難以管理雖然他們通常較不會仔細審查,但可以建構指令碼 這些程式碼就像建構系統一樣,容易隱藏錯誤。 以下列舉幾項常見的錯誤 任務式建構系統:
- 工作 A 仰賴工作 B 產生特定檔案做為輸出。擁有者 的工作 B 不知道其他工作需要依賴這個狀況 因此工作 會產生位於不同位置的輸出內容其他人必須等待參與者才能偵測到這個狀態 嘗試執行任務 A,並發現失敗了。
- 工作 A 依附於工作 B,這取決於工作 C 做為工作 A 所需的輸出內容工作 B 的擁有者 判定不需要再依附工作 C,這會造成工作 這樣就算工作 B 完全不在意工作 C 也沒關係!
- 新工作的開發人員不小心假設了 例如工具的位置或 特定環境變數工作可以在自己的電腦上運作,但失敗 每當有其他開發人員嘗試使用時
- 工作包含非確定性元件,例如下載檔案 或是為建構作業新增時間戳記現在,使用者可享 每次執行建構作業時的結果都可能不同 工程師無法重現並修正其他人失敗的問題 或自動化建構系統發生的失敗情形
- 具有多個依附元件的工作可能會產生競爭狀況。如果工作 A 工作 B 和工作 C 都需要執行,而工作 B 和 C 都會修改相同的程式碼 工作 A 也會根據工作 B 和 C 中的任一個工作,取得不同的結果 就在結束之前
要解決這些效能、正確性或 這裡列出以工作為基礎的架構中的可維護性問題。再見 因為工程師可以編寫在建構期間執行的任何程式碼 可能沒有足夠的資訊隨時能快速執行建構作業 正確。為瞭解決這個問題,我們必須設法讓 把它放回系統去,重新構思 而不是執行任務,而是產生構件。
這種做法推動了建立 Blaze 等以構件為基礎的建構系統 和 Bazel