2

我正在尝试使用 Bazel 编译基于 dhall-kubernetes 的 dhall 程序以生成 Kubernetes YAML 文件。

使用简单的 bazel 宏在没有 dhall-kubernetes 的情况下进行基本dhall 编译可以正常工作。

我已经做了一个使用 dhall 的依赖解析来下载 dhall-kubernetes 的示例 - 请参见此处。这也有效,但速度很慢(我认为是因为 dhall 分别下载每个远程文件),并且对 bazel 规则执行引入了网络依赖性,我希望避免这种情况。

我首选的方法是使用 Bazel 下载 dhall-kubernetes 的存档发行版,然后让规则在本地访问它(请参阅此处)。我的解决方案需要 Prelude.dhall 和 package.dhall 中的相对路径,以便示例/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/**/*",
    ]),
)

示例/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"
    ]
)
4

1 回答 1

2

有一种方法可以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…XXXsha256dhall hash

  • ./binary.dhall

    一个方便的包含表达式的 Dhall 文件missing sha256:XXX…XXXsha256:XXX…XXX只有当我们构建的与哈希匹配的表达式已经被缓存时,解释这个表达式才会成功。

    调用该文件是binary.dhall因为这是“二进制”包分发的 Dhall 等价物,这意味着只能从二进制缓存中获取导入,而不能从源中获取和解释。

  • 可选的:./source.dhall

    这是一个包含与缓存的表达式等效的完全 αβ 标准化表达式的文件。默认情况下,除了顶级包之外的所有包都应该省略它,因为它包含存储在 中的相同表达式./cache/1220XXX…XXX,尽管效率较低(因为二进制编码更紧凑)

    之所以调用此文件,是./source.dhall因为它是“源”包分发的 Dhall 等价物,它包含产生相同结果的有效源代码。

用户界面

构建包的函数有四个参数:

  • 包名

    这对构建来说并不重要。这只是命名事物,因为每个 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文件

    ...只需写出一个文本文件来$out/binary.dhall包含missing sha256:${HASH}

  • 可选:删除./source.dhall文件

    ...如果用户没有要求保留文件。默认情况下省略此文件有助于通过不存储相同的表达式两次(作为二进制文件和源代码)来节省包存储中的空间。

包装约定

一旦你有了这个功能,有几个约定可以帮助简化“大”做事

  • 默认情况下,包应该构建项目的./package.dhall文件

  • 轻松覆盖包版本

  • 轻松覆盖包内构建的文件

    换句话说,如果用户喜欢导入单个文件https://prelude.dhall-lang.org/List/map而不是顶级./package.dhall文件,则应该有一种方法可以让他们指定依赖项,例如Prelude.override { file = "./List/map"; }获取构建和缓存该单个文件的包。

结论

我希望这会有所帮助!如果你有更多关于如何做到这一点的问题,你可以在这里问他们,或者你也可以在我们的 Discourse 论坛上讨论更多,特别是在这个成语最初起源的线程上:

于 2020-04-11T15:29:36.977 回答