建立永久工作站

回報問題 查看來源

永久工作站可以加快建構速度。如果版本中有重複的動作,但該動作的啟動成本高,或適合使用跨動作快取,不妨考慮實作自己的永久工作站來執行這些動作。

Bazel 伺服器會使用 stdin/stdout 與工作站通訊,並支援使用通訊協定緩衝區或 JSON 字串。

工作站實作包含兩個部分:

建立 worker

永久工作站會遵循下列幾項需求:

  • 會從 stdin 讀取 WorkRequests
  • 並將 WorkResponses (且只有 WorkResponse) 寫入其 stdout
  • 並接受 --persistent_worker 旗標。包裝函式必須辨識 --persistent_worker 指令列旗標,且只有在傳遞該標記時才會讓自身持續保持不變,否則就必須執行一次性編譯和結束作業。

如果您的程式堅持這些要求,則可做為永久工作站使用!

工作要求

WorkRequest 包含給工作站的引數清單,包含代表工作站可存取輸入內容的路徑-摘要組合清單 (未強制執行,但您可以將這項資訊用於快取),以及要求 ID (針對單一複數工作站)。

注意:雖然通訊協定緩衝區規格使用「snake case」(request_id),但 JSON 通訊協定使用的是「駝峰式大小寫」(requestId)。本文件在 JSON 範例中使用駝峰式大小寫,但在提及欄位時,無論通訊協定為何,都請使用蛇案例。

{
  "arguments" : ["--some_argument"],
  "inputs" : [
    { "path": "/path/to/my/file/1", "digest": "fdk3e2ml23d"},
    { "path": "/path/to/my/file/2", "digest": "1fwqd4qdd" }
 ],
  "requestId" : 12
}

選用的 verbosity 欄位可用來向工作站要求額外偵錯輸出內容。完全由工作站決定輸出的內容和輸出方式。值越大,表示輸出越詳細。將 --worker_verbose 標記傳送至 Bazel 即可將 verbosity 欄位設為 10,不過更小或更大的值可以手動用於不同的輸出量。

選用的 sandbox_dir 欄位只有支援多工沙箱的工作站才會使用。

工作回應

WorkResponse 包含要求 ID、零或非零的結束代碼,以及輸出字串,用於說明處理或執行要求時發生的任何錯誤。output 欄位包含簡短說明;完整的記錄可能會寫入工作站的 stderr。由於 worker 只能將 WorkResponses 寫入 stdout,因此工作站通常會將任何工具的 stdout 重新導向至 stderr

{
  "exitCode" : 1,
  "output" : "Action failed with the following message:\nCould not find input
    file \"/path/to/my/file/1\"",
  "requestId" : 12
}

根據 Protobufs 的標準,所有欄位均為選填。不過,Bazel 必須使用 WorkRequest 和對應的 WorkResponse,要求 ID 必須相同,因此如果要求 ID 不是零,就必須指定要求 ID。這是有效的 WorkResponse

{
  "requestId" : 12,
}

request_id 為 0 表示「單複數」要求,在這項要求無法與其他要求同時處理時使用。伺服器會保證特定工作站收到的要求只有 request_id 0 或只有 request_id 大於零。單一複雜要求會以序列傳送,例如,如果伺服器在收到回應前,未傳送其他要求 (取消要求除外,請參閱下文)。

備忘錄

  • 每個通訊協定緩衝區的前面都會加上 varint 格式 (請參閱 MessageLite.writeDelimitedTo())。
  • JSON 要求和回應前面不會加上大小指標。
  • JSON 要求的結構與 protobuf 相同,但針對所有欄位名稱使用標準 JSON,並使用駝峰式大小寫。
  • 為了維持與 protobuf 相同的回溯和向前相容性屬性,JSON 工作站必須容許這些訊息中的不明欄位,並以 protobuf 預設值來處理遺漏的值。
  • Bazel 會將要求儲存為 protobufs,並使用通訊協定緩衝區的 JSON 格式將其轉換為 JSON 格式

取消

工作站可以選擇允許在工作完成前取消工作要求。這項功能特別適合用於動態執行,因為本機執行作業可透過較快的遠端執行方式定期中斷。如要允許取消,請將 supports-worker-cancellation: 1 新增至 execution-requirements 欄位 (如下所示),並設定 --experimental_worker_cancellation 標記。

取消要求是已設定 cancel 欄位的 WorkRequest (同樣地,取消回應也是已設定 was_cancelled 欄位的 WorkResponse)。取消要求或取消回應中唯一必須填寫的其他欄位是 request_id,指出要取消哪個要求。如果是單一複數工作站,request_id 欄位的值會是 0,而如果是多工工作站先前傳送的 WorkRequest,則會是非 0 request_id。伺服器可以針對工作站已回應的要求傳送取消要求,在這種情況下,您必須忽略取消要求。

無論是否取消,每個非取消的 WorkRequest 訊息都必須正確回答一次。伺服器傳送取消要求後,工作站可能會以 WorkResponse 回應,並將 request_idwas_cancelled 欄位設為 true。系統也接受傳送一般的 WorkResponse,但會忽略 outputexit_code 欄位。

傳送 WorkRequest 的回應後,工作站不得輕觸工作目錄中的檔案,您可以免費使用伺服器清理檔案,包括暫存檔案。

建立使用 worker 的規則

您也需要建立規則,產生工作站要執行的動作。建立一個使用工作站的 Starlark 規則,就如同建立任何其他規則一樣。

此外,規則必須包含 worker 參照,且其產生的動作也有一些需求條件。

參照工作站

使用工作站的規則必須包含參照 worker 的欄位,因此您必須建立 \*\_binary 規則的執行個體才能定義工作站。如果工作站呼叫了 MyWorker.Java,這可能是相關聯的規則:

java_binary(
    name = "worker",
    srcs = ["MyWorker.Java"],
)

這會建立「工作站」標籤,也就是工作站二進位檔。然後會定義「使用」 worker 的規則。這項規則應定義參照工作站二進位檔的屬性。

如果您建構的工作站二進位檔位於名為「work」的套件中,位於建構頂層,這可能是屬性定義:

"worker": attr.label(
    default = Label("//work:worker"),
    executable = True,
    cfg = "exec",
)

cfg = "exec" 表示工作站應建構在執行平台上,而不是在目標平台上執行 (也就是說,工作站在建構期間會當做工具使用)。

工作動作要求

使用工作站的規則為 worker 建立動作。這些動作有一些需求。

  • 「引數」欄位。這會取得字串清單,但最後一個是啟動時,會傳送至工作站的引數。「引數」清單中的最後一個元素是 flag-file (@-preceded) 引數。工作站會依據每個 WorkRequest 讀取指定旗標檔案中的引數。您的規則可以將工作站的非啟動引數寫入此旗標檔案。

  • "execution-requirements" 欄位:取得包含 "supports-workers" : "1" 和/或 "supports-multiplex-workers" : "1" 的字典。

    所有傳送至工作站的動作都必須具備「引數」和「執行要求」欄位。此外,JSON 工作站應執行的動作須在執行要求欄位中納入 "requires-worker-protocol" : "json""requires-worker-protocol" : "proto" 也是有效的執行要求,但由於是 proto 工作站,因此不需要用到。

    您也可以在執行需求中設定 worker-key-mnemonic。如果您要針對多個動作類型重複使用執行檔,且想要區分這個工作站的動作,這項功能可能就很實用。

  • 在這個動作過程中產生的暫存檔案應儲存至工作站的目錄。即可啟用沙箱功能。

假設規則定義包含上述的「worker」屬性,除了代表輸入內容的「srcs」屬性外,還有代表輸出內容的「output」屬性,以及代表工作站啟動引數的「args」屬性,對 ctx.actions.run 的呼叫可能是:

ctx.actions.run(
  inputs=ctx.files.srcs,
  outputs=[ctx.outputs.output],
  executable=ctx.executable.worker,
  mnemonic="someMnemonic",
  execution_requirements={
    "supports-workers" : "1",
    "requires-worker-protocol" : "json"},
  arguments=ctx.attr.args + ["@flagfile"]
 )

如需其他範例,請參閱「實作永久工作站」。

範例

除了用於整合測試使用的 JSON 工作站範例之外,Bazel 程式碼集也會使用 Java 編譯器工作站

您可以使用其鷹架,透過傳入正確的回呼將任何 Java 工具轉換為工作站。

如需使用 worker 的規則範例,請查看 Bazel 的工作站整合測試

外部貢獻者已實作多種語言的工作站;請查看 Bazel 永久工作站的 Polyglot 實作。您可以在 GitHub 找到更多範例