feat: add rule to run tool in BUILD_WORKSPACE_DIRECTORY (#60)
This PR adds a rule which is identical to `cwd`, except that it runs the
tool in `BUILD_WORKSPACE_DIRECTORY` instead of
`BUILD_WORKING_DIRECTORY`. There are some tools which should always be
run from the module/workspace root and I think it would be useful to add
a convenience rule for this.
diff --git a/examples/module/BUILD.bazel b/examples/module/BUILD.bazel
index 9e6b820..e143f37 100644
--- a/examples/module/BUILD.bazel
+++ b/examples/module/BUILD.bazel
@@ -15,3 +15,12 @@
],
data = ["@multitool//tools/target-determinator:cwd"],
)
+
+sh_test(
+ name = "integration_test_workspace_root",
+ srcs = ["integration_test.sh"],
+ args = [
+ "$(location @multitool//tools/target-determinator:workspace_root)",
+ ],
+ data = ["@multitool//tools/target-determinator:workspace_root"],
+)
diff --git a/examples/workspace/BUILD.bazel b/examples/workspace/BUILD.bazel
index 9e6b820..e143f37 100644
--- a/examples/workspace/BUILD.bazel
+++ b/examples/workspace/BUILD.bazel
@@ -15,3 +15,12 @@
],
data = ["@multitool//tools/target-determinator:cwd"],
)
+
+sh_test(
+ name = "integration_test_workspace_root",
+ srcs = ["integration_test.sh"],
+ args = [
+ "$(location @multitool//tools/target-determinator:workspace_root)",
+ ],
+ data = ["@multitool//tools/target-determinator:workspace_root"],
+)
diff --git a/multitool/private/BUILD.bazel b/multitool/private/BUILD.bazel
index 0df68e1..2f32f09 100644
--- a/multitool/private/BUILD.bazel
+++ b/multitool/private/BUILD.bazel
@@ -1,8 +1,8 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
exports_files([
- "cwd.template.bat",
- "cwd.template.sh",
+ "run_in.template.bat",
+ "run_in.template.sh",
])
bzl_library(
diff --git a/multitool/private/cwd.bzl b/multitool/private/cwd.bzl
index 3703836..803819c 100644
--- a/multitool/private/cwd.bzl
+++ b/multitool/private/cwd.bzl
@@ -1,30 +1,12 @@
"cwd: a rule for executing an executable in the BUILD_WORKING_DIRECTORY"
+load(":run_in.bzl", "run_in", "run_in_attrs")
+
def _cwd_impl(ctx):
- # This algorithm requires --enable_runfiles (enabled by default on non-windows)
- template = ctx.file._template_sh
- wrapper_name = ctx.label.name
- tool_short_path = ctx.file.tool.short_path
- if ctx.file.tool.extension == "exe":
- template = ctx.file._template_bat
- wrapper_name = wrapper_name + ".bat"
- tool_short_path = tool_short_path.replace("/", "\\")
- output = ctx.actions.declare_file(wrapper_name)
- ctx.actions.expand_template(
- template = template,
- output = output,
- substitutions = {
- "{{tool}}": tool_short_path,
- },
- )
- return [DefaultInfo(executable = output, runfiles = ctx.runfiles(files = [ctx.file.tool]))]
+ return run_in(ctx, "BUILD_WORKING_DIRECTORY")
cwd = rule(
implementation = _cwd_impl,
- attrs = {
- "tool": attr.label(mandatory = True, allow_single_file = True, executable = True, cfg = "exec"),
- "_template_sh": attr.label(default = "//multitool/private:cwd.template.sh", allow_single_file = True),
- "_template_bat": attr.label(default = "//multitool/private:cwd.template.bat", allow_single_file = True),
- },
+ attrs = run_in_attrs,
executable = True,
)
diff --git a/multitool/private/cwd.template.bat b/multitool/private/cwd.template.bat
deleted file mode 100644
index fcaf9d4..0000000
--- a/multitool/private/cwd.template.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-@set tool=%cd%\{{tool}}
-@cd %BUILD_WORKING_DIRECTORY%
-@%tool% %*
diff --git a/multitool/private/cwd.template.sh b/multitool/private/cwd.template.sh
deleted file mode 100644
index 0f6270f..0000000
--- a/multitool/private/cwd.template.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env bash
-
-tool="$PWD/{{tool}}"
-cd "$BUILD_WORKING_DIRECTORY" && exec "$tool" "$@"
diff --git a/multitool/private/hub_repo_tool_template/BUILD.bazel.template b/multitool/private/hub_repo_tool_template/BUILD.bazel.template
index ad27ef2..56e54db 100644
--- a/multitool/private/hub_repo_tool_template/BUILD.bazel.template
+++ b/multitool/private/hub_repo_tool_template/BUILD.bazel.template
@@ -1,6 +1,7 @@
# generated by multitool
load("@rules_multitool//multitool:cwd.bzl", "cwd")
+load("@rules_multitool//multitool:workspace_root.bzl", "workspace_root")
load(":tool.bzl", "tool")
toolchain_type(
@@ -18,3 +19,9 @@
tool = ":{name}",
visibility = ["//visibility:public"],
)
+
+workspace_root(
+ name = "workspace_root",
+ tool = ":{name}",
+ visibility = ["//visibility:public"],
+)
diff --git a/multitool/private/run_in.bzl b/multitool/private/run_in.bzl
new file mode 100644
index 0000000..619c523
--- /dev/null
+++ b/multitool/private/run_in.bzl
@@ -0,0 +1,39 @@
+"run_in provides a shared implementation for cwd and workspace_root execution"
+
+run_in_attrs = {
+ "tool": attr.label(mandatory = True, allow_single_file = True, executable = True, cfg = "exec"),
+ "_template_sh": attr.label(default = "//multitool/private:run_in.template.sh", allow_single_file = True),
+ "_template_bat": attr.label(default = "//multitool/private:run_in.template.bat", allow_single_file = True),
+}
+
+def run_in(ctx, env_var):
+ """
+ run_in implements a rule that runs a tool in a directory provided by env_var
+
+ Args:
+ ctx: rule ctx argument
+ env_var: env var string that contains the desired execution directory
+
+ Returns:
+ A list including the DefaultInfo provider for the created wrapper
+ executable
+ """
+
+ # This algorithm requires --enable_runfiles (enabled by default on non-windows)
+ template = ctx.file._template_sh
+ wrapper_name = ctx.label.name
+ tool_short_path = ctx.file.tool.short_path
+ if ctx.file.tool.extension == "exe":
+ template = ctx.file._template_bat
+ wrapper_name = wrapper_name + ".bat"
+ tool_short_path = tool_short_path.replace("/", "\\")
+ output = ctx.actions.declare_file(wrapper_name)
+ ctx.actions.expand_template(
+ template = template,
+ output = output,
+ substitutions = {
+ "{{tool}}": tool_short_path,
+ "{{env_var}}": env_var,
+ },
+ )
+ return [DefaultInfo(executable = output, runfiles = ctx.runfiles(files = [ctx.file.tool]))]
diff --git a/multitool/private/run_in.template.bat b/multitool/private/run_in.template.bat
new file mode 100644
index 0000000..0a2a83a
--- /dev/null
+++ b/multitool/private/run_in.template.bat
@@ -0,0 +1,3 @@
+@set tool=%cd%\{{tool}}
+@cd %{{env_var}}%
+@%tool% %*
diff --git a/multitool/private/run_in.template.sh b/multitool/private/run_in.template.sh
new file mode 100644
index 0000000..480412b
--- /dev/null
+++ b/multitool/private/run_in.template.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+tool="$PWD/{{tool}}"
+cd "${{env_var}}" && exec "$tool" "$@"
diff --git a/multitool/private/workspace_root.bzl b/multitool/private/workspace_root.bzl
new file mode 100644
index 0000000..4aba24b
--- /dev/null
+++ b/multitool/private/workspace_root.bzl
@@ -0,0 +1,12 @@
+"workspace_root: a rule for executing an executable in the BUILD_WORKSPACE_DIRECTORY"
+
+load(":run_in.bzl", "run_in", "run_in_attrs")
+
+def _workspace_root_impl(ctx):
+ return run_in(ctx, "BUILD_WORKSPACE_DIRECTORY")
+
+workspace_root = rule(
+ implementation = _workspace_root_impl,
+ attrs = run_in_attrs,
+ executable = True,
+)
diff --git a/multitool/workspace_root.bzl b/multitool/workspace_root.bzl
new file mode 100644
index 0000000..0c5fb30
--- /dev/null
+++ b/multitool/workspace_root.bzl
@@ -0,0 +1,5 @@
+"multitool workspace_root execution rule"
+
+load("@rules_multitool//multitool/private:workspace_root.bzl", _workspace_root = "workspace_root")
+
+workspace_root = _workspace_root
diff --git a/readme.md b/readme.md
index 7919a13..5ac45b4 100644
--- a/readme.md
+++ b/readme.md
@@ -72,6 +72,8 @@
To run a tool in the current working directory, use the convenience target `@multitool//tools/tool-name:cwd`.
+To run a tool in the Bazel module or workspace root, use the convenience target `@multitool/tools/tool-name:workspace_root`.
+
A common pattern we recommend to further simplify invoking tools for repository users it to:
1. Create a `tools/` directory