首页 > 解决方案 > 如何不在每次调用时重建工件

问题描述

我想在工作区中下载和构建 ruby​​。我一直在尝试通过模仿 rules_go 来实现这一点。我有那部分工作。我遇到的问题是每次ruby_download_sdk调用它都会重建 openssl 和 ruby​​ 工件。在下面的代码中,下载工件被缓存,但 openssl 和 ruby​​ 的构建总是被执行。

def ruby_download_sdk(name, version = None):
    # TODO detect os and arch
    os, arch = "osx", "x86_64"

    _ruby_download_sdk(
        name = name, 
        version = version,
    )
    _register_toolchains(name, os, arch)

def _ruby_download_sdk_impl(repository_ctx):
    # TODO detect platform
    platform = ("osx", "x86_64")

    _sdk_build_file(repository_ctx, platform)
    _remote_sdk(repository_ctx)

_ruby_download_sdk = repository_rule(
    _ruby_download_sdk_impl,
    attrs = {
        "version": attr.string(),
    },
)

def _remote_sdk(repository_ctx):
    _download_openssl(repository_ctx, version = "1.1.1c")
    _download_ruby(repository_ctx, version = "2.6.3")

    openssl_path, ruby_path = "openssl/build", ""
    _build(repository_ctx, "openssl", openssl_path, ruby_path)
    _build(repository_ctx, "ruby", openssl_path, ruby_path)

def _build(repository_ctx, name, openssl_path, ruby_path):
    script_name = "build-{}.sh".format(name)
    template_name = "build-{}.template".format(name)

    repository_ctx.template(
        script_name,
        Label("@rules_ruby//ruby/private:{}".format(template_name)),
        substitutions = {
            "{ssl_build}": openssl_path,
            "{ruby_build}": ruby_path,
        }
    )
    repository_ctx.report_progress("Building {}".format(name))
    res = repository_ctx.execute(["./" + script_name], timeout=20*60)

    if res.return_code != 0:
        print("res %s" % res.return_code)
        print(" -stdout: %s" % res.stdout)
        print(" -stderr: %s" % res.stderr)

关于如何让 bazel 意识到它不会每次都重建这些构建工件的任何建议?

标签: bazel

解决方案


问题是,bazel 并没有真正构建你的 ruby​​ 和 openssl。当它准备你的构建树并运行存储库规则时,它只是按照指示执行一个 shell 脚本,这显然是构建的,但这个事实对 bazel 来说基本上是不透明的(而且它也发生在 bazel 本身甚至构建之前)。

可能还有其他,但我认为以下是您的选择:

  • 预先构建您的 ruby​​ 环境及其结果作为外部依赖项。明显的缺点(可能会或可能不会很痛苦)是您需要为您需要支持的所有平台这样做(包括确保正确检测和相应的下载)。好处是您实际上只构建一次(每个平台),并且还可以控制所有主机上使用的工具。这可能是我的主要选择。

  • 像任何其他 C 源代码一样构建 ssl 和 ruby​​,使它们成为另一个 bazel 目标。然而,这意味着您需要 bazelify 他们的构建(描述和维护其他 bazel 不知道项目的 bazel 构建)。

  • 您可以沿着您开始的路径继续前进,并且(有点)将 bazel 排除在外。即,对于这些构建扩展了魔法,并且在构建脚本中使用确定性位置和可能的清单文件(也可以减少损坏的可能性)使得可以确定构建确实已经发生并且您可以只是收集它以前的结果。


推荐阅读