如何在不使用相对路径的情况下从另一个规则访问 bazel 规则的输出?
How can I access the output of a bazel rule from another rule without using a relative path?
我正在尝试使用Bazel编译基于dhall-kubernetes的dhall程序生成Kubernetes YAML文件。
没有 dhall-kubernetes 使用简单的 bazel 宏的 basic dhall compile 工作正常。
我已经制作了一个使用 dhall 的依赖解析来下载 dhall-kubernetes 的示例 - 请参阅 here。这也有效,但速度很慢(我认为是因为 dhall 分别下载每个远程文件),并引入了对 bazel 规则执行的网络依赖性,我希望避免这种情况。
我的首选方法是使用 Bazel 下载 dhall-kubernetes 的归档发布版本,然后让规则在本地访问它(参见 here)。我的解决方案需要 Prelude.dhall 和 package.dhall 中的相对路径,以便 examples/k8s 包引用 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/**/*",
]),
)
examples/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
进行“离线”构建,这意味着包管理器获取所有 Dhall 依赖项而不是 Dhall 获取它们。
事实上,我为 Nixpkgs 实现了完全相同的功能,您可以将其翻译成 Bazel:
高级解释
基本技巧是利用 Dhall 导入系统的一个特性,即如果缓存了受语义完整性检查(即“语义哈希”)保护的包,那么 Dhall 将使用缓存而不是取包裹。您可以利用此技巧让包管理器通过以这种方式注入依赖项来绕过 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
。仅当我们构建的匹配散列 sha256:XXX…XXX
的表达式已被缓存时,解释此表达式才会成功。
文件被称为 binary.dhall
因为这是 Dhall 等同于“二进制”包分发,这意味着导入只能从二进制缓存中获取,不能从源中获取和解释。
可选:./source.dhall
这是一个包含与缓存表达式等效的完全 αβ 规范化表达式的文件。默认情况下,除了顶层包之外的所有包都应该省略它,因为它包含存储在 ./cache/1220XXX…XXX
中的相同表达式,尽管效率较低(因为二进制编码更紧凑)
此文件被称为 ./source.dhall
因为这是 Dhall 等同于“源”包分发,它包含产生相同结果的有效源代码。
用户界面
构建包的函数有四个参数:
包名
这不是 material 构建。这只是为了命名,因为每个 Nix 包都必须有一个人类可读的名称。
构建的依赖关系
这些依赖项中的每一个都是生成目录树的构建产品,就像我上面描述的那样(即 ./cache
目录、./binary.dhall
文件和可选的 ./source.dhall
文件)
Dhall 表达式
这可以是任意的 Dhall 源代码,只有一个警告:表达式传递引用的所有远程导入必须受到完整性检查的保护,并且这些导入必须与此 Dhall 包的依赖项之一匹配(以便可以通过缓存而不是 Dhall 运行时获取 URL)
来满足导入
布尔值选项,指定是否保留./source.dhall
文件,默认为False
实施
Dhall 包生成器的工作方式是:
首先,使用-f-with-http
标志构建Haskell Dhall包
这个标志编译出对 HTTP 远程导入的支持,这样如果用户忘记为远程导入提供依赖项,他们将收到一条错误消息说 Import resolution is disabled
我们将在所有后续步骤中使用此可执行文件
在当前工作目录中创建一个名为.cache/dhall
的缓存目录
... 并使用存储在每个依赖项的 ./cache/
目录
中的二进制文件填充缓存目录
配置解释器使用我们创建的缓存目录
...通过设置 XDG_CACHE_HOME
指向我们刚刚在当前工作目录中创建的 .cache
目录
为我们的包解释和 α-规范化 Dhall 源代码
... 使用 dhall --alpha
命令。将结果保存到 $out/source.dhall
,其中 $out
是将存储最终构建产品的目录
获取表达式的哈希值
... 使用 dhall hash
命令。我们将需要此哈希来执行以下两个步骤。
创建对应的二进制缓存文件
... 使用 dhall encode
命令并将文件保存到 $out/cache/dhall/1220${HASH}
创建 ./binary.dhall
文件
...只需将包含 missing sha256:${HASH}
的文本文件写入 $out/binary.dhall
可选:删除 ./source.dhall
文件
...如果用户没有请求保留文件。默认情况下省略此文件有助于在包存储中保存 space,因为不会将相同的表达式存储两次(作为二进制文件和源代码)。
打包约定
拥有此功能后,有一些约定可以帮助简化“大范围”的操作
默认情况下,包应该构建项目的 ./package.dhall
文件
轻松覆盖包版本
轻松覆盖包内构建的文件
换句话说,如果用户更喜欢导入像 https://prelude.dhall-lang.org/List/map
这样的单个文件而不是顶级 ./package.dhall
文件,那么应该有一种方法可以让他们指定像 [=48] 这样的依赖项=] 以获得构建和缓存该单个文件的包。
结论
希望对您有所帮助!如果您对如何执行此操作有更多疑问,可以在这里提问,也可以在我们的 Discourse 论坛上进行更多讨论,尤其是在这个成语最初起源的线程上:
我正在尝试使用Bazel编译基于dhall-kubernetes的dhall程序生成Kubernetes YAML文件。
没有 dhall-kubernetes 使用简单的 bazel 宏的 basic dhall compile 工作正常。
我已经制作了一个使用 dhall 的依赖解析来下载 dhall-kubernetes 的示例 - 请参阅 here。这也有效,但速度很慢(我认为是因为 dhall 分别下载每个远程文件),并引入了对 bazel 规则执行的网络依赖性,我希望避免这种情况。
我的首选方法是使用 Bazel 下载 dhall-kubernetes 的归档发布版本,然后让规则在本地访问它(参见 here)。我的解决方案需要 Prelude.dhall 和 package.dhall 中的相对路径,以便 examples/k8s 包引用 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/**/*",
]),
)
examples/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
进行“离线”构建,这意味着包管理器获取所有 Dhall 依赖项而不是 Dhall 获取它们。
事实上,我为 Nixpkgs 实现了完全相同的功能,您可以将其翻译成 Bazel:
高级解释
基本技巧是利用 Dhall 导入系统的一个特性,即如果缓存了受语义完整性检查(即“语义哈希”)保护的包,那么 Dhall 将使用缓存而不是取包裹。您可以利用此技巧让包管理器通过以这种方式注入依赖项来绕过 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
。仅当我们构建的匹配散列sha256:XXX…XXX
的表达式已被缓存时,解释此表达式才会成功。文件被称为
binary.dhall
因为这是 Dhall 等同于“二进制”包分发,这意味着导入只能从二进制缓存中获取,不能从源中获取和解释。可选:
./source.dhall
这是一个包含与缓存表达式等效的完全 αβ 规范化表达式的文件。默认情况下,除了顶层包之外的所有包都应该省略它,因为它包含存储在
./cache/1220XXX…XXX
中的相同表达式,尽管效率较低(因为二进制编码更紧凑)此文件被称为
./source.dhall
因为这是 Dhall 等同于“源”包分发,它包含产生相同结果的有效源代码。
用户界面
构建包的函数有四个参数:
包名
这不是 material 构建。这只是为了命名,因为每个 Nix 包都必须有一个人类可读的名称。
构建的依赖关系
这些依赖项中的每一个都是生成目录树的构建产品,就像我上面描述的那样(即
./cache
目录、./binary.dhall
文件和可选的./source.dhall
文件)Dhall 表达式
这可以是任意的 Dhall 源代码,只有一个警告:表达式传递引用的所有远程导入必须受到完整性检查的保护,并且这些导入必须与此 Dhall 包的依赖项之一匹配(以便可以通过缓存而不是 Dhall 运行时获取 URL)
来满足导入布尔值选项,指定是否保留
./source.dhall
文件,默认为False
实施
Dhall 包生成器的工作方式是:
首先,使用
-f-with-http
标志构建Haskell Dhall包这个标志编译出对 HTTP 远程导入的支持,这样如果用户忘记为远程导入提供依赖项,他们将收到一条错误消息说
Import resolution is disabled
我们将在所有后续步骤中使用此可执行文件
在当前工作目录中创建一个名为
的缓存目录.cache/dhall
... 并使用存储在每个依赖项的
中的二进制文件填充缓存目录./cache/
目录配置解释器使用我们创建的缓存目录
...通过设置
XDG_CACHE_HOME
指向我们刚刚在当前工作目录中创建的.cache
目录为我们的包解释和 α-规范化 Dhall 源代码
... 使用
dhall --alpha
命令。将结果保存到$out/source.dhall
,其中$out
是将存储最终构建产品的目录获取表达式的哈希值
... 使用
dhall hash
命令。我们将需要此哈希来执行以下两个步骤。创建对应的二进制缓存文件
... 使用
dhall encode
命令并将文件保存到$out/cache/dhall/1220${HASH}
创建
./binary.dhall
文件...只需将包含
的文本文件写入missing sha256:${HASH}
$out/binary.dhall
可选:删除
./source.dhall
文件...如果用户没有请求保留文件。默认情况下省略此文件有助于在包存储中保存 space,因为不会将相同的表达式存储两次(作为二进制文件和源代码)。
打包约定
拥有此功能后,有一些约定可以帮助简化“大范围”的操作
默认情况下,包应该构建项目的
./package.dhall
文件轻松覆盖包版本
轻松覆盖包内构建的文件
换句话说,如果用户更喜欢导入像
https://prelude.dhall-lang.org/List/map
这样的单个文件而不是顶级./package.dhall
文件,那么应该有一种方法可以让他们指定像 [=48] 这样的依赖项=] 以获得构建和缓存该单个文件的包。
结论
希望对您有所帮助!如果您对如何执行此操作有更多疑问,可以在这里提问,也可以在我们的 Discourse 论坛上进行更多讨论,尤其是在这个成语最初起源的线程上: