Clojure

開發補丁程式

如果您有興趣提供補丁程式或為 Clojure 貢獻,請參閱開發概觀

在編寫程式碼之前

如果您在開始編寫程式碼之前考慮以下事項,您將產生一個較容易評估且更有可能被接受的補丁程式

  • 您正在嘗試解決什麼問題?在任何補丁程式之前,您應該有一個問題陳述,並且應包含在補丁程式的說明中。

  • 您的解決方案是什麼?請在說明中清楚說明,以便不必從程式碼中推斷。

  • 您是否已與社群討論過您的想法?如果您能的話,請在 Slack 或郵件清單中與核心團隊成員討論。(對於像錯字之類的事情來說並非必要。)

  • 製作一個考慮的替代方案表,以及它們的權衡。說明為什麼選擇的解決方案是最佳選擇。

  • 您將如何向其他人證明您的修補程式有效?計畫納入測試。基於範例的測試是可以的,但建議採用產生式測試。

  • 在問題追蹤系統中記錄修補程式的變更。任何有助於審查員了解您所做的變更及其原因的事項都能讓他們的工作更輕鬆。

  • 不要做太多!提交處理特定問題的小修補程式,不要新增任何額外內容,甚至(尤其是!)不要「清理」周圍的程式碼。這只會讓修補程式的用意變得混淆。

在您的修補程式正在審查時

  • 隨著意見的提出,請保持說明的最新狀態。透過閱讀意見串來重建問題追蹤系統的目前狀態非常耗時。

  • 集結支持。投票很好,而其他使用者在審查問題追蹤系統時提出的意見甚至更好。

  • 隨著問題追蹤系統的增加,請務必清楚記錄哪些修補程式處於作用中。不要透過刪除舊修補程式來執行此動作,只要在意見中以名稱參照修補程式即可。

編碼

準備好製作您的程式碼後,您需要的第一件事是 Clojure 或適當儲存庫的複製。以下範例適用於 Clojure 專案,也就是提交給 Clojure 本身

$ git clone git://github.com/clojure/clojure.git
$ cd clojure

接著,為您自己建立一個新分支

$ git checkout -b fixbug42
Switched to a new branch "fixbug42"

現在,您可以開始駭客了。在開始作業前,請確定所有現有的回歸測試仍然通過,例如:對於 Clojure,請使用 Maven

$ mvn clean test
...lots of output...
test:
[INFO] Executed tasks
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:26 min
[INFO] Finished at: 2018-12-05T14:36:54-06:00
[INFO] ------------------------------------------------------------------------

完成變更後,您需要提交它們。請使用以 JIRA 編號 (CLJ-xyz) 開頭的提交訊息,並包含變更服務於的問題或增強功能的說明!

$ git commit -a -m "CLJ-932 fixed annoying bug"
Created commit 8f7c712: fixed annoying bug
1 files changed, 0 insertions(+), 1 deletions(-)

完成變更後,是時候將它們放入修補程式了。您需要更新儲存庫並修復您遇到的任何衝突。

$ git checkout master
Switched to branch "master"
$ git pull
...
$ git checkout fixbug42
Switched to branch "fixbug42"
$ git rebase master

修復所有衝突後,您就可以建立修補程式了

$ git format-patch master --stdout -U8 > clj-932-1.patch

新增修補程式

現在您可以將該修補程式檔案附加到 JIRA 票證。在頁面頂端的「更多動作」功能表中,選擇「附加檔案」。撰寫有關您所附加修補程式的註解時,請閱讀並遵循以下建議。審查人員的審查時間有限。如果您能盡可能清楚表達,並盡可能有效利用他們的時間,您的修補程式就更有可能獲得核准。

  • 請使用 .patch 或 .diff (而非 .txt) 作為修補程式檔案的附檔字尾。

  • 附加修補程式檔案前,請先閱讀該檔案。如果您看到與您要修改的程式碼部分無關的空白變更,請編輯並移除那些變更,然後重新產生修補程式。此外,雖然在開發時進行多重提交並說明每個提交很不錯,但修補程式審查人員通常偏好將所有變更壓縮成單一提交以供審查。

  • 使用 'git add --patch' 來分段您的變更,將有助於避免提交不必要的變更。

  • 請使用與票證上所有現有附件不同的名稱。JIRA 允許您新增多個具有相同名稱的附件,但後面的附件不會取代前面的附件。這可能會在以名稱來參照修補程式時造成混淆。

  • 在任何參照修補程式的註解中,請包含檔案名稱和修補程式的日期。可以根據日期和時間將註解與修補程式配對,但這很繁瑣且容易出錯。

  • 若要取得票證更新的電子郵件通知,請按一下頁面右上角的「追蹤」字詞。這有助於您得知其他人對您的修補程式發表註解或建立新的修補程式等資訊。如果您要停止接收票證的更新電子郵件,請按一下「追蹤中」。您可能需要驗證自動電子郵件是否會被您的垃圾郵件過濾器攔截。電子郵件會傳送至與您的 JIRA 帳戶相關聯的地址,且會來自 jira@dev.clojure.org 這個地址。

  • 如果您建立一個包含一個或多個較早修補程式的修補程式,請將它們全部合併成一個修補程式檔案,並在您的註解中說明您已執行此動作(包含您取代的修補程式的檔案名稱和日期)。例外情況是當有多人做出顯著且獨立的貢獻時(例如,一人進行程式碼變更,另一人撰寫測試),且兩人都希望獲得肯定。在這種情況下,一個包含多個提交的單一修補程式檔案是可以的。但是,我們希望避免重複修改相同程式碼的多個修補程式。

透過編輯 [修補程式] 欄位,將此問題標記為已準備好進行篩選的修補程式。按一下問題頁面左上角附近的 [編輯] 按鈕。在下一頁中,找到標題為「修補程式」且旁邊有彈出式功能表的標題。從該功能表中選取「程式碼」或「程式碼和測試」,然後按一下頁面底部的 [更新] 按鈕。如果您在問題的頁面上看不到 [編輯] 按鈕,且您已簽署 CA,請在開發人員的電子郵件清單或 Clojurians Slack 中的 #clojure-dev 中提出要求,以取得編輯 Jira 問題的權限。

移除修補程式

若要移除修補程式(例如,因為它不再相關),請前往問題的頁面,並在 [說明] 文字下方尋找 [附件] 標題。最右邊有一個加號和一個三角形。按一下三角形,並從功能表中選取 [管理附件]。仔細考慮您要刪除哪一個,並按一下它旁邊的垃圾桶圖示。注意:大多數人都有權限移除自己的附件,但沒有權限移除其他人新增的附件。

一般來說,您不需要移除舊的修補程式。只要讓它們累積,並追蹤問題說明中最相關的修補程式即可。

更新過期的修補程式

過期的修補程式是指曾經可以順利套用到最新 Clojure 主版本,但由於在建立修補程式後提交的提交,而不再可以套用的修補程式。特別是,此指令的輸出

$ git am --keep-cr -s --ignore-whitespace < patch_file.patch

包含「修補程式失敗」和「若要還原原始分支並停止修補,請執行「git am --abort」」。您應該執行「git am --abort」以擺脫上述指令留下的失敗修補程式嘗試狀態。

「git am」非常「脆弱」,這表示如果使用一個版本的原始程式碼建立修補程式檔案,指令只要偵測到修補程式檔案中出現的任何背景行發生變更,就會失敗,即使它不是修補程式變更的行之一。這在包含單元測試的檔案中特別常見,因為人們通常會在這種檔案的結尾新增新的測試,因此如果兩個不同的修補程式在同一個檔案的結尾新增新的測試,新的測試之前的背景行就會變更。

要套用此類補丁,請使用 --reject 旗標

$ git apply --reject patch_file.patch

輸出將會提供一些提示,說明補丁檔案的每個「塊」是否成功或失敗。如果全部成功,那麼補丁檔案可能唯一的錯誤就是變更了少數幾個內容行。如果任何塊失敗,patch 會建立以「.rej」結尾的檔案,其中包含未套用的被拒絕塊,而你可以專注於這些地方,因為那裡面的原始碼可能變更幅度較大。類似這樣的指令會找出所有這些塊

$ find . -name '*.rej'

你需要檢視那些被拒絕的塊,或許思考一下它們,看看它們是否仍然適用以及如何適用,並透過手動編輯原始碼自行套用它們。

使用

$ git format-patch master --stdout -U8 > patch_file.patch

建立新的 git 補丁時,它會將你的姓名和目前日期置於檔案的頂端附近。如果你所做的唯一變更是在內容行中,請複製你開始時使用的原始補丁中的姓名和日期,以保持原始作者的功勞完整,然後上傳該補丁。

如果你在原始補丁中撰寫單元測試(原始補丁中沒有單元測試),但並未修改原始補丁,而且你希望你的姓名出現在提交記錄中,請建立一個包含你姓名的測試新增補丁,並將原始作者的姓名保留在更新的補丁中。

篩選補丁

如果你是一個正在測試補丁的篩選者,你可以建立一個新的分支並套用補丁來開始使用它

$ git checkout -b testxyz
$ git am --keep-cr -s --ignore-whitespace < patch_file.patch

而且你可以在用完後丟棄那個分支

$ git checkout master
$ git branch -D testxyz

如何執行所有 Clojure 測試

$ mvn clean test

若要將偽隨機產生的生成式測試的持續時間從 60 秒縮短至 1 秒(例如),請編輯檔案 src/script/run_test_generative.clj 並變更數字 60000。只要小心不要在提交的任何補丁中包含此類變更即可。(在 Clojure 1.6.0 及更早版本中,檔案名稱為 src/scripts/run_tests.clj)

執行個別測試

首先,在不執行任何測試的情況下建置最新的 Clojure

$ mvn -Dmaven.test.skip=true clean package
# If no compilation errors, mvn command above creates target/clojure-VERSION-master-SNAPSHOT.jar

以上指令會建置一個 Clojure jar 檔案,但不會編譯或執行測試。

建立一個 deps.edn 檔案,說明你可能需要的相依性

{:paths ["test"]
 :deps
 {org.clojure/clojure {:mvn/version "RELEASE"}
  org.clojure/test.check {:mvn/version "0.9.0"}
  org.clojure/test.generative {:mvn/version "0.5.2"}}
 :aliases
 {:dbg {:classpath-overrides {org.clojure/clojure "target/classes"}
        :extra-deps {criterium/criterium {:mvn/version "0.4.4"}}}}}

使用 clj 啟動一個 repl,並從中執行個別測試

$ clj -A:dbg
Clojure ...
;; We're testing with clojure.test
=> (require 'clojure.test)
nil
;; Load a test file
user=> (require 'clojure.test-clojure.data)
nil
;; Run it
user=> (clojure.test/run-tests 'clojure.test-clojure.data)

Testing clojure.test-clojure.data
Ran 1 tests containing 17 assertions.
0 failures, 0 errors.
{:type :summary, :pass 17, :test 1, :error 0, :fail 0}

啟動一個 repl,並從中執行一個生成式測試

生成式測試使用額外的測試 jar(在你執行 ./antsetup.sh 時安裝)。因此,你需要一些額外的類別路徑,而 antsetup.sh 會將其保留在 maven-classpath 檔案中。如果你使用的是 *nix,最簡單的方法是利用這個檔案

$ clj -A:dbg
Clojure ...
;; Install some clojure.test extensions
user=> (require 'clojure.test-helper)
nil
;; Load a test file that uses test.generative
user=> (require 'clojure.test-clojure.reader)
nil
;; Load the test.generative runner ns
user=> (use 'clojure.test.generative.runner)
nil
;; Test a specification on 1 thread for 200 ms
user=> (run 1 200 #'clojure.test-clojure.reader/types-that-should-roundtrip)
{:iter 60, :seed 1255541066, :test clojure.test-clojure.reader/types-that-should-roundtrip}
nil

建置 Clojure 的其他選項

不使用直接連結來建構 Clojure

預設情況下,Clojure 會在啟用直接連結的情況下建構。儘管這會提升效能,但這表示如果函式 A 在 Clojure 內部呼叫函式 B,那麼使用 spec 來記錄 B 將會讓 A 仍然呼叫原始函式 B,而不是記錄過的版本。如果你希望記錄 B 並讓 Clojure 中的其他函式呼叫記錄過的版本,其中一種方法是停用直接連結來建構 Clojure。

編輯 build.xml 檔案,將下列行中的「true」替換為「false」,該行位於以「target name="compile-clojure"」開頭的區段內

<sysproperty key="clojure.compiler.direct-linking" value="true"/>

然後使用你偏好的方法從原始碼建構 Clojure,例如

$ mvn -Dmaven.test.skip=true clean install