密封建構動作

Fuchsia 的建構系統使用工具來追蹤 建構動作,偵測建構動作是否正確且完整狀態 以及相關輸入內容和輸出內容

如果遇到類似下方的錯誤,請繼續閱讀本指南:

Unexpected file accesses building //some/target:label ...
(FileAccessType.READ /path/to/file/not/declared/as/input)

或者,如果您正在查看 action()action_foreach() 目標如下:

action("foo") {
  ...
  hermetic_deps = false
}

建構圖形正確性

建構定義為有向非循環圖,因此動作具有 輸入到機器學習的輸入資料 以及其輸出內容舉例來說 編譯 .cc 檔案至 .o 檔案中的動作,來源檔案會是 做為輸出內容和物件檔案編譯中使用的任何 .h 標頭 都視為相同動作的輸入來源

這個圖表呈現方式可確保建構系統能正確執行 漸進式建構。漸進式建構作業 但後來完成了一些動作輸入內容已變更 要求重新建構應用程式在漸進式建構作業中 盡可能執行最低限度的工作,只重建具有 無論是使用者修改原始碼,還是 因為其他動作的輸出內容有所變更,所以必須重新執行。

對於建構圖表中的任何動作,所有輸入和輸出內容都必須 能讓建構圖正確且指示動作 。不過,基礎建構系統 Ninja 不會對其進行驗證。 建構動作會在使用者的本機環境中執行,並具備 整個檔案系統,包括原始碼樹狀結構和 out/ 中的所有檔案 目錄,因此就不會採用沙箱機制,而且可隨處存取。

如未宣告輸入內容,會導致無法重新執行動作 ( 更新文字內容。未宣告輸出內容 形成另一個動作的輸入內容,會產生 動作,在這類動作中,單一建構叫用可能會錯過時間戳記更新。 指出無法在單一叫用中進行彙整 (請參閱 Ninja no-op)。

如果您閱讀這段文字,代表的建構動作可能不是 完整狀態的一或多項輸入內容或輸出內容。

使用自訂動作擴充建構

開發人員可以使用 GN 中繼建構系統,在自家的 BUILD.gn 個檔案。方法是使用 actionaction_foreach。自訂動作可讓開發人員叫用 建構時的自訂工具,並將這些工具連結至依附元件圖表,例如: 此工具可在建構期間叫用,並對 漸進式建構作業

動作會使用下列參數來狀態輸入內容:

  • script:要執行的工具。這通常是 Python 指令碼,但也可以是 程式語言,以在主機上執行。
  • inputs:做為工具資料輸入的檔案。舉例來說, 這項工具會壓縮檔案,接著要壓縮的檔案就會列為 輸入內容
  • sources:這與 inputs 相同。只有 因為 sources 通常用於 工具的 script,例如依附的 Python 或指令碼程式庫

動作會使用下列參數來狀態輸出內容:

  • outputs:每個動作都必須產生至少一個輸出檔案。可執行的操作 舉例來說,如果執行個體動作可以驗證 確保資料正確性,通常會產生「載入檔案」 表示動作已執行,可以空白。

Depfiles

如果在執行動作前無法得知要輸入的部分輸入值, 然後,動作可以指定 depfile。Depfiles 清單 在執行階段找到的一或多個輸出內容輸入動作的輸入值。 格式為一行或多行,如下所示:

[output_file1] [output_file2...]: [input_file1] [input_file2...]

depfile 中的所有路徑都必須與 root_build_dir (設為 執行動作的工作目錄)。另請參閱: 偏好來自 rebase_path() 的相對路徑

編譯器等工具應 (且確實) 支援 該檔案在編譯過程中使用,且格式為 depfile。

用於偵測非密封動作的檔案系統操作追蹤

Fuchsia 建構系統使用檔案系統動作追蹤工具,偵測 動作所讀取或寫入的檔案未列為輸入或輸出, 如上所示,在 BUILD.gn 檔案或 depfile 中明確指定。完成了 以取代執行動作的沙箱,以及作為各種類型的執行階段消毒器。

如果您正在閱讀本頁內容,可能是發生了以下錯誤: 這個系統。錯誤會精確地列出讀取或讀取的檔案 但是並未在 BUILD.gn 或 depfile 中指定為輸入/輸出。 您應該修正這些遺漏的內容,並嘗試重新建構,直到錯誤發生為止 因為

如要在本機建構作業中重現這個錯誤,您需要確保 動作追蹤已啟用:

fx set what --args=build_should_trace_actions=true

執行 fx args、新增一行 build_should_trace_actions=true、 儲存並結束。

請注意,如果動作未經過嚴格定義,且尚未修正 在嘗試重建動作時 錯誤。由於這個動作沒有完全定義,因此可能不正確 並從漸進式建構作業中收集 (也就是 嘗試解決的問題)。如要強制執行所有建構動作,請清除 您的建構作業輸出快取

fx clean

根據預設,CQ 會對所有變更執行這些完整性檢查。是 使用上述的 build_should_trace_actions=true 引數,因此 開發人員可在本機重現完全相同的追蹤版本。

抑制隱密動作檢查

為目前不合格的動作設有下列參數:

action("foo") {
  ...
  # TODO(https://fxbug.dev/xxxxx): delete the line below and fix this
  hermetic_deps = false
}

這樣會略過上述檢查作業。如果您發現有動作 則應移除抑制,嘗試重現 並修正問題。

如果不想立即修正問題,請回報錯誤,並將錯誤標題 「[Hermetic]」並在回應中納入失敗建構動作的追蹤輸出內容 例如說明如果知道存取權違規問題,請留言說明 以及訓練資料

常見問題及修正方法

缺少輸入/輸出內容

有時在建構期間就知道輸入/輸出內容,但並未指定。 或是您指定的值不正確這些常見問題通常可以輕鬆解決。 例如:

動作執行階段才會知道輸入內容

如前文所述,有時並非所有輸入內容都會在建構時間得知,因此 不能在 BUILD.gn 定義中指定。這就是 depfiles 重點

您可以在下方找到修正建構動作以產生 Depfile 的範例:

輸入/輸出內容中缺少動作引數

建構動作通常是使用特定檔案路徑做為引數的指令碼。

action("foo") {
  script = "concatenate.py"
  outputs = [ "$target_out_dir/file1_file2.txt" ]
  args = [
    "--concat-from",
    rebase_path("data/file1.txt", root_build_dir),
    rebase_path("data/file2.txt", root_build_dir),
    "--output",
  ] + outputs
}

在上述情況中,你會看到動作追蹤程式錯誤訊息 concatenate.py 讀取自 data/file1.txtdata/file2.txt。這些錯誤很容易察覺 因為您可以看到這些路徑是以引數形式傳遞至指令碼,但 並未列為輸入或輸出內容雖然就技術上來說 不會實際讀取/寫入這些路徑的指令碼 可能性不高。

修正方法如下:

action("foo") {
  script = "concatenate.py"
  sources = [
    "data/file1.txt",
    "data/file2.txt",
  ]
  outputs = [ "$target_out_dir/file1_file2.txt" ]
  args = [
    "--concat-from",
  ] + rebase_path(sources, root_build_dir) + [
    "--output",
  ] + outputs
}

展開檔案中的引數

在 Python 指令碼中,有一個常見模式會使用這個模式來擴充 做為引數 (也稱為「回應檔案」)。在「BUILD.gn」中 你會看到:

action("foo") {
   script = "myaction.py"
   args = [ "@" + rebase_path(args_file, root_build_dir) ]
   ...
}

接著,您可以在相關聯的 Python 檔案 myaction.py 中找到 使用 fromfile_prefix_chars 的引數剖析器:

def main():
    parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
    args = parser.parse_args()
    ...

上述問題在於 Python 會在執行階段讀取 args_file 指令碼,而且必須指定為輸入內容。修正方法如下:

action("foo") {
   script = "myaction.py"
   inputs = [ args_file ]
   args = [ "@" + rebase_path(args_file, root_build_dir) ]
   ...
}

如需快速從 GN 的清單填入此類檔案,可以使用 write_file():

action("foo") {
  args_file = "${target_gen_dir}/${target_name}.args"
  write_file(args_file, a_very_long_list_of_args)
  args = [ "@" + rebase_path(args_file, root_build_dir) ]
  ...
}

請注意,GN 提供 response_file_contents 做為 以便使用便捷的替代方法 (而不是 write_file)。但前提是: 針對源自 Ninja 的錯誤,我們目前不允許 我們建構中的 response_file_contents

建立及刪除暫存檔

這是在建構動作中建立暫存檔案的常見模式。 只要執行相同的動作,我們便不允許將暫存檔案列為輸出內容。 建立暫存檔案時,暫存檔案也會在返回前刪除。

暫存檔案應儲存在 target_out_dirtarget_gen_dir 底下。 使用全域臨時儲存空間 (例如 /tmp$TMPDIR),或任何讀取和 不建議您寫入結帳或輸出目錄以外的地方,因為 因此更難排解建構失敗的問題,因為系統可能需要大量檔案 以便從檔案系統的其他位置復原 錯誤。

建立及刪除臨時目錄

有時需要在臨時目錄中建立暫存檔案。 同樣地,只要建立暫存目錄的動作 系統也會在傳回之前 以遞迴方式刪除物件

shutil.rmtree 是用於刪除臨時儲存空間的常用函式 目錄然而,由於追蹤器的限制,有時可能會 造成非預期的讀取作業另請參閱:問題 75057:妥善處理 從動作追蹤器的 goil.rmtree 中刪除目錄

如要突破這項限制,其中一種方法是只建立暫存檔案,而非 暫存目錄暫存檔案應寫入 target_out_dir 底下 或 target_gen_dir

有時候無法這麼做,例如當暫存目錄是 您無法修改由外部建構工具所建立。在這種情況下 另一種方法是為臨時目錄取一個特殊名稱 __untraced_foo_tmp_outputs__,並加入許可清單 「動作追蹤器」。這個檔案的存取權 追蹤程式會忽略這個特殊目錄。因此, 功能不應謹慎使用

舉例來說,假設 bar.py 一律會刪除 --tmp-dir 中的所有檔案 然後重新填入:

action(target_name) {
  script = "bar.py"
  args = [
    "--tmp-dir"
    rebase_path("${target_gen_dir}/${target_name}/__untraced_bar_tmp_outputs__", root_build_dir)
  ]
  ...
}

然後在動作追蹤器的 ignored_path_parts 中新增項目:

ignored_path_parts = {
  # Comment with clear explanation on why this is necessary,
  # preferably with a link to an associated bug for more context.
  "__untraced_bar_tmp_outputs__",
  ...
}

CQ 回報的錯誤無法在本機上重現

首先,請確認您使用的是建構引數 build_should_trace_actions=true,因為 相同。

如果 CQ 回報 action_tracer.py 意外讀取 Python 檔案,但您無法 在本機重現這個問題,原因可能是經過編譯的 Python 檔案 整個樹狀結構的 __pycache__ 目錄 (例如 find third_party -type d -name __pycache__)。 快速的解決方法是刪除這些目錄中的所有 *.pyc 檔案。原因如下: 偽陰性是指檔案系統一律不會開啟原始 .py 檔案,因此 不會回報為觸碰,因此不會觸發失敗的語意檢查。

Python 以外的檔案類型可能會因為類似原因而無法重現。

另請參閱:開啟專案中的裝置化動作