如何在bazel规则中获取工作空间目录

8
为了使用像 clang-formatclang-tidy 这样的 clang 工具,或生成像这个那样的 编译数据库,我需要知道 .bzl 文件中的 WORKSPACE 目录。怎么获取它呢?考虑以下例子,我只想打印工作区中所有 src 文件的完整路径:

# simple_example.bzl

def _impl(ctx):
  workspace_dir = // ---> what comes here? <---
  command = "\n".join([echo %s/%s" % (workspace_dir, f.short_path) 
                       for f in ctx.files.srcs])

  ctx.actions.write(
      output=ctx.outputs.executable,
      content=command,
      is_executable=True)


echo_full_path = rule(
    implementation=_impl,
    executable=True,
    attrs={
      "srcs": attr.label_list(allow_files=True),
    }
)

# BUILD

echo_full_path(
    name = "echo",
    srcs = glob(["src/**/*.cc"])
)

有没有更加简洁/优雅的方式来完成这个任务?

现在你所拥有的应该是一个脚本,其中包含相对于工作区的文件路径,因此只有在从工作区根目录执行脚本时才能正常工作。你是否试图规避这个限制?我想工作区目录在 skylark 规则实现中不可用,仅仅是因为还没有需要,但也会使编写不适用于远程执行或会污染构件缓存的规则更容易。 - ahumesky
@ahumesky 是的,这就是我正在尝试的。你说得对,这会违背远程执行的意图(我猜这里不需要工件缓存),但在这种情况下,使用bazel查找源文件非常简单,这将使事情变得更加容易。此外,我希望只用一个工具来完成所有操作,这样用户就不必在不同的工具之间切换。我想我将不得不用一个简单的shell脚本来实现这个功能。如果你愿意,你可以发表一个答案,这样我就可以接受它。 - Mike van Dyke
3个回答

5

您可以通过使用realpath来解决这个问题。例如:

def _impl(ctx):

  ctx.actions.run_shell(
    inputs = ctx.files.srcs,
    outputs = [ctx.outputs.executable],
    command = "\n".join(["echo echo $(realpath \"%s\") >> %s" % (f.path,
              ctx.outputs.executable.path) for f in ctx.files.srcs]),
    execution_requirements = {
        "no-sandbox": "1",
        "no-cache": "1",
        "no-remote": "1",
        "local": "1",
    },
  )

echo_full_path = rule(
    implementation=_impl,
    executable=True,
    attrs={
      "srcs": attr.label_list(allow_files=True),
    }
)

请注意execution_requirements,以解决我上面评论中可能出现的问题。

3
我修改了@ahumesky的规则,隐式地使用BUILD文件作为源,并且只写入一次工作区目录: workspace.bzl
def _write_workspace_dir_impl(ctx):
    src = ctx.files._src[0]
    out = ctx.actions.declare_file(ctx.label.name)
    ctx.actions.run_shell(
        inputs = ctx.files._src,
        outputs = [out],
        command = """
          full_path="$(readlink -f -- "{src_full}")"
          # Trim the src.short_path suffix from full_path. Double braces to
          # output literal brace for shell.
          echo "${{full_path%/{src_short}}}" >> {out_full}
        """.format(src_full = src.path, src_short = src.short_path, out_full = out.path),
        execution_requirements = {
            "no-sandbox": "1",
            "no-remote": "1",
            "local": "1",
        },
    )
    return [DefaultInfo(files = depset([out]))]

write_workspace_dir = rule(
    implementation = _write_workspace_dir_impl,
    attrs = {
        "_src": attr.label(allow_files = True, default = "BUILD"),
    },
    doc = "Writes the full path of the current workspace dir to a file.",
)

构建

load(":workspace.bzl", "write_workspace_dir")

write_workspace_dir(
    name = "workspace_dir",
)

样例输出

bazel build //build/bazel:workspace_dir
INFO: Analyzed target //build/bazel:workspace_dir 
INFO: Build completed successfully, 1 total action

cat bazel-bin/build/bazel/workspace_dir
/p/$MY_PROJECT

在2023年,这是可行的解决方案。 - Batato

2

如果你正在编写一个 repository_rule,那么你可以从 WORKSPACE.bazel 文件中传递 __workspace_dir__ 变量。 - xiay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接