![](/img/trans.png)
[英]How can I create a custom bazel build rule that uses the runfiles path of another rule?
[英]How can I access the output of a bazel rule from another rule without using a relative path?
我正在嘗試使用 Bazel 編譯基於 dhall-kubernetes 的 dhall 程序以生成 Kubernetes YAML 文件。
使用簡單的 bazel 宏在沒有 dhall-kubernetes 的情況下進行基本 dhall 編譯可以正常工作。
我已經做了一個使用 dhall 的依賴解析來下載 dhall-kubernetes 的示例 - 請參見此處。 這也有效,但速度很慢(我認為是因為 dhall 分別下載每個遠程文件),並且對 bazel 規則執行引入了網絡依賴性,我希望避免這種情況。
我首選的方法是使用 Bazel 下載 dhall-kubernetes 的存檔發行版,然后讓規則在本地訪問它(請參閱此處)。 我的解決方案需要 Prelude.dhall 和 package.dhall 中的相對路徑,用於示例/k8s package 以引用 dhall-kubernetes。 雖然它有效,但我擔心這會破壞 Bazel 沙箱,因為它需要 Bazel 內部使用的文件夾結構的特殊知識。 有沒有更好的辦法?
Prelude.dhall:
../../external/dhall-kubernetes/1.17/Prelude.dhall
工作空間:
workspace(name = "dhall")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
DHALL_KUBERNETES_VERSION = "4.0.0"
http_archive(
name = "dhall-kubernetes",
sha256 = "0bc2b5d2735ca60ae26d388640a4790bd945abf326da52f7f28a66159e56220d",
url = "https://github.com/dhall-lang/dhall-kubernetes/archive/v%s.zip" % DHALL_KUBERNETES_VERSION,
strip_prefix = "dhall-kubernetes-4.0.0",
build_file = "@//:BUILD.dhall-kubernetes",
)
BUILD.dhall-kubernetes:
package(default_visibility=['//visibility:public'])
filegroup(
name = "dhall-k8s-1.17",
srcs = glob([
"1.17/**/*",
]),
)
示例/k8s/構建:
package(default_visibility = ["//visibility:public"])
genrule(
name = "special_ingress",
srcs = ["ingress.dhall",
"Prelude.dhall",
"package.dhall",
"@dhall-kubernetes//:dhall-k8s-1.17"
],
outs = ["ingress.yaml"],
cmd = "dhall-to-yaml --file $(location ingress.dhall) > $@",
visibility = [
"//visibility:public"
]
)
有一種方法可以讓dhall
進行“離線”構建,這意味着 package 管理器獲取所有 Dhall 依賴項,而不是 Dhall 獲取它們。
事實上,我為 Nixpkgs 實現了一些東西,你可以翻譯成 Bazel:
基本技巧是利用 Dhall 導入系統的一個特性,即如果緩存了受語義完整性檢查(即“語義哈希”)保護的 package,則 Dhall 將使用緩存而不是獲取 package。 您可以利用這個技巧讓 package 管理器通過這種方式注入依賴項來繞過 Dhall 的遠程導入。
你可以在這里找到與 Nix 相關的邏輯:
...但我會嘗試解釋它是如何以獨立於包管理器的方式工作的。
首先,使用 Nix 構建的 Dhall“包”的最終產品是具有以下結構的目錄:
$ nix-build --attr 'dhallPackages.Prelude'
…
$ tree -a ./result
./result
├── .cache
│ └── dhall
│ └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98
└── binary.dhall
2 directories, 2 files
這個目錄的內容是:
./cache/dhall/1220XXX…XXX
包含單個構建產品的 Dhall 的有效緩存目錄:解釋的 Dhall 表達式的二進制編碼。
您可以使用dhall encode
創建這樣的二進制文件,並且可以通過將上面的XXX…XXX
替換為表達式的sha256
編碼來計算文件名,您可以使用dhall hash
命令獲得。
./binary.dhall
一個方便的 Dhall 文件,其中包含missing sha256:XXX…XXX
的表達式。 僅當我們構建的與 hash sha256:XXX…XXX
匹配的表達式已被緩存時,解釋此表達式才會成功。
該文件被稱為binary.dhall
,因為它是“二進制”package 發行版的 Dhall 等價物,這意味着只能從二進制緩存中獲取導入,而不能從源中獲取和解釋。
可選: ./source.dhall
這是一個包含與緩存的表達式等效的完全 αβ 標准化表達式的文件。 默認情況下,除了頂級 package 之外的所有包都應該省略它,因為它包含存儲在./cache/1220XXX…XXX
內部的相同表達式,盡管效率較低(因為二進制編碼更緊湊)
該文件被稱為./source.dhall
,因為它是“源”package 發行版的 Dhall 等價物,它包含產生相同結果的有效源代碼。
用於構建 package 的 function 需要四個 arguments:
package 名稱
這對構建來說並不重要。 這只是命名,因為每個 Nix package 都必須有一個人類可讀的名稱。
構建的依賴項
這些依賴項中的每一個都是生成目錄樹的構建產品,就像我上面描述的那樣(即./cache
目錄、. ./binary.dhall
文件和可選的./source.dhall
文件)
一個 Dhall 表達式
這可以是任意的 Dhall 源代碼,只有一個警告:由表達式傳遞引用的所有遠程導入必須受到完整性檢查的保護,並且這些導入必須與此 Dhall package 的依賴項之一匹配(以便可以通過以下方式滿足導入緩存而不是 Dhall 運行時獲取 URL)
一個 boolean 選項指定是否保留./source.dhall
文件,默認為False
Dhall package 構建器的工作方式是:
首先,使用-f-with-http
標志構建 Haskell Dhall package
此標志編譯出對 HTTP 遠程導入的支持,這樣如果用戶忘記為遠程導入提供依賴項,他們將收到一條錯誤消息,指出Import resolution is disabled
我們將在所有后續步驟中使用這個可執行文件
在當前工作目錄中創建一個名為.cache/dhall
的緩存目錄
...並使用存儲在每個依賴項的./cache/
目錄中的二進制文件填充緩存目錄
配置解釋器使用我們創建的緩存目錄
...通過設置XDG_CACHE_HOME
指向我們剛剛在當前工作目錄中創建的.cache
目錄
解釋和 α 標准化 package 的 Dhall 源代碼
...使用dhall --alpha
命令。 將結果保存到$out/source.dhall
,其中$out
是存儲最終構建產品的目錄
獲取表達式的 hash
...使用dhall hash
命令。 我們將需要這個 hash 用於以下兩個步驟。
創建對應的二進制緩存文件
...使用dhall encode
命令並將文件保存到$out/cache/dhall/1220${HASH}
創建./binary.dhall
文件
...只需將包含missing sha256:${HASH}
的文本文件寫入$out/binary.dhall
可選:刪除./source.dhall
文件
...如果用戶沒有要求保留文件。 默認情況下省略此文件有助於節省 package 存儲中的空間,因為不會將相同的表達式存儲兩次(作為二進制文件和源代碼)。
一旦你有了這個 function,有幾個約定可以幫助簡化“大體上”的操作
默認情況下,package 應該構建項目的./package.dhall
文件
輕松覆蓋 package 版本
輕松覆蓋 package 中構建的文件
換句話說,如果用戶更喜歡導入單個文件,例如https://prelude.dhall-lang.org/List/map
而不是頂級./package.dhall
文件,那么他們應該有一種方法可以指定依賴像Prelude.override { file = "./List/map"; }
Prelude.override { file = "./List/map"; }
來獲得一個 package 來構建和緩存那個單獨的文件。
希望對您有所幫助,如果您對如何執行此操作有更多疑問,可以在這里提問,或者您也可以在我們的 Discourse 論壇上討論更多內容:尤其是在這個成語最初起源的線程上:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.