diff --git a/README.md b/README.md index 750e744..24a0dd5 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,10 @@ Installation instructions are included on each release: - [run_binary](docs/run_binary.md) Like skylib's run_binary but adds directory output support. - [stamping](docs/stamping.md) Support version stamping in custom rules. +## Running programs + +- [wrap_binary](docs/wrap_binary.md) Compatibility shims which wrap binaries to adapt to the Bazel execution environment. + ## Generating documentation - [docs](docs/docs.md) Rules for generating docs and stamping tests to ensure they are up to date. diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel index 4a3ccb7..3aff3c2 100644 --- a/docs/BUILD.bazel +++ b/docs/BUILD.bazel @@ -73,6 +73,11 @@ stardoc_with_diff_test( bzl_library_target = "//lib:run_binary", ) +stardoc_with_diff_test( + name = "wrap_binary", + bzl_library_target = "//lib:wrap_binary", +) + stardoc_with_diff_test( name = "transitions", bzl_library_target = "//lib:transitions", diff --git a/docs/paths.md b/docs/paths.md index ab2d6bc..869e585 100644 --- a/docs/paths.md +++ b/docs/paths.md @@ -2,27 +2,6 @@ Public API - - -## chdir_binary - -
-chdir_binary(name, binary, chdir, kwargs) -- -Wrap a *_binary to be executed under a given directory. - -**PARAMETERS** - - -| Name | Description | Default Value | -| :------------- | :------------- | :------------- | -| name | Name of the rule. | none | -| binary | Label of an executable target to wrap. | none | -| chdir | Argument for the
cd
command, the default is commonly used with bazel run
to run the program in the root of the Bazel workspace, in the source tree. | "$BUILD_WORKSPACE_DIRECTORY"
|
-| kwargs | Additional named arguments for the resulting sh_binary rule. | none |
-
-
## relative_file
diff --git a/docs/wrap_binary.md b/docs/wrap_binary.md
new file mode 100644
index 0000000..e648f99
--- /dev/null
+++ b/docs/wrap_binary.md
@@ -0,0 +1,40 @@
+
+
+Wraps binary rules to make them more compatible with Bazel.
+
+Currently supports only Bash as the wrapper language, not cmd.exe.
+
+Future additions might include:
+- wrap a binary such that it sees a tty on stdin
+- manipulate arguments or environment variables
+- redirect stdout/stderr, e.g. to silence buildspam on success
+- intercept exit code, e.g. to make an "expect_fail"
+- change user, e.g. to deal with containerized build running as root, but tool requires non-root
+- intercept signals, e.g. to make a tool behave as a Bazel persistent worker
+
+
+
+
+## chdir_binary
+
++chdir_binary(name, binary, chdir, kwargs) ++ +Wrap a *_binary to be executed under a given working directory. + +Note: under `bazel run`, this is similar to the `--run_under "cd $PWD &&"` trick, but is hidden +from the user so they don't need to know about that flag. + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| name | Name of the rule. | none | +| binary | Label of an executable target to wrap. | none | +| chdir | Argument for the
cd
command, the default is commonly used with bazel run
to run the program in the root of the Bazel workspace, in the source tree. | "$BUILD_WORKSPACE_DIRECTORY"
|
+| kwargs | Additional named arguments for the resulting sh_binary rule. | none |
+
+
diff --git a/lib/BUILD.bazel b/lib/BUILD.bazel
index 7a55d8a..fe74388 100644
--- a/lib/BUILD.bazel
+++ b/lib/BUILD.bazel
@@ -48,10 +48,7 @@ bzl_library(
bzl_library(
name = "paths",
srcs = ["paths.bzl"],
- deps = [
- "//lib/private:paths",
- "@bazel_skylib//rules:write_file",
- ],
+ deps = ["//lib/private:paths"],
)
bzl_library(
@@ -126,6 +123,16 @@ bzl_library(
deps = ["//lib/private:run_binary"],
)
+bzl_library(
+ name = "wrap_binary",
+ srcs = ["wrap_binary.bzl"],
+ deps = [
+ ":paths",
+ "//lib/private:run_binary",
+ "@bazel_skylib//rules:write_file",
+ ],
+)
+
bzl_library(
name = "repo_utils",
srcs = ["repo_utils.bzl"],
diff --git a/lib/paths.bzl b/lib/paths.bzl
index 76932a4..c8e1601 100644
--- a/lib/paths.bzl
+++ b/lib/paths.bzl
@@ -1,6 +1,5 @@
"Public API"
-load("@bazel_skylib//rules:write_file.bzl", "write_file")
load("//lib/private:paths.bzl", "paths")
relative_file = paths.relative_file
@@ -23,44 +22,3 @@ source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/
{ echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
# --- end runfiles.bash initialization v2 ---
"""
-
-def chdir_binary(name, binary, chdir = "$BUILD_WORKSPACE_DIRECTORY", **kwargs):
- """Wrap a *_binary to be executed under a given directory.
-
- Args:
- name: Name of the rule.
- binary: Label of an executable target to wrap.
- chdir: Argument for the `cd` command, the default is commonly used with `bazel run`
- to run the program in the root of the Bazel workspace, in the source tree.
- **kwargs: Additional named arguments for the resulting sh_binary rule.
- """
-
- script = "_{}_chdir.sh".format(name)
-
- # It's 2022 and java_binary still cannot be told to cd to the source directory under bazel run.
- write_file(
- name = "_{}_wrap".format(name),
- out = script,
- content = [
- "#!/usr/bin/env bash",
- BASH_RLOCATION_FUNCTION,
- # Remove external/ prefix that is included in $(rootpath) but not supported by $(rlocation)
- "bin=$(rlocation ${1#external/})",
- # Consume the first argument
- "shift",
- # Fix the working directory
- "cd " + chdir,
- # Replace the current process
- "exec $bin $@",
- ],
- is_executable = True,
- )
-
- native.sh_binary(
- name = name,
- srcs = [script],
- args = ["$(rootpath {})".format(binary)] + kwargs.pop("args", []),
- data = [binary],
- deps = ["@bazel_tools//tools/bash/runfiles"],
- **kwargs
- )
diff --git a/lib/tests/wrap_binary/BUILD.bazel b/lib/tests/wrap_binary/BUILD.bazel
new file mode 100644
index 0000000..eb5bfe2
--- /dev/null
+++ b/lib/tests/wrap_binary/BUILD.bazel
@@ -0,0 +1,25 @@
+load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
+load("@bazel_skylib//rules:build_test.bzl", "build_test")
+load("@aspect_bazel_lib//lib:wrap_binary.bzl", "chdir_binary")
+
+sh_binary(
+ name = "fixture",
+ srcs = ["needs-working-dir.sh"],
+)
+
+chdir_binary(
+ name = "wrap",
+ binary = "fixture",
+ chdir = package_name(),
+)
+
+run_binary(
+ name = "assert",
+ outs = ["thing"],
+ tool = "wrap",
+)
+
+build_test(
+ name = "test",
+ targets = ["assert"],
+)
diff --git a/lib/tests/wrap_binary/needs-working-dir.sh b/lib/tests/wrap_binary/needs-working-dir.sh
new file mode 100755
index 0000000..6f1d0f0
--- /dev/null
+++ b/lib/tests/wrap_binary/needs-working-dir.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -o errexit -o nounset -o pipefail
+
+if [ "$(dirname $(pwd))" != "wrap_binary" ]; then
+ echo >&2 "this program must be run with the working directory inside the package, but was $(pwd)"
+ exit 1
+fi
\ No newline at end of file
diff --git a/lib/wrap_binary.bzl b/lib/wrap_binary.bzl
new file mode 100644
index 0000000..ee75d4a
--- /dev/null
+++ b/lib/wrap_binary.bzl
@@ -0,0 +1,62 @@
+"""Wraps binary rules to make them more compatible with Bazel.
+
+Currently supports only Bash as the wrapper language, not cmd.exe.
+
+Future additions might include:
+- wrap a binary such that it sees a tty on stdin
+- manipulate arguments or environment variables
+- redirect stdout/stderr, e.g. to silence buildspam on success
+- intercept exit code, e.g. to make an "expect_fail"
+- change user, e.g. to deal with containerized build running as root, but tool requires non-root
+- intercept signals, e.g. to make a tool behave as a Bazel persistent worker
+"""
+
+load(":paths.bzl", "BASH_RLOCATION_FUNCTION")
+load(":utils.bzl", "to_label")
+load("@bazel_skylib//rules:write_file.bzl", "write_file")
+
+def chdir_binary(name, binary, chdir = "$BUILD_WORKSPACE_DIRECTORY", **kwargs):
+ """Wrap a *_binary to be executed under a given working directory.
+
+ Note: under `bazel run`, this is similar to the `--run_under "cd $PWD &&"` trick, but is hidden
+ from the user so they don't need to know about that flag.
+
+ Args:
+ name: Name of the rule.
+ binary: Label of an executable target to wrap.
+ chdir: Argument for the `cd` command.
+ By default, supports using the binary under `bazel run` by running program in the
+ root of the Bazel workspace, in the source tree.
+ **kwargs: Additional named arguments for the resulting sh_binary rule.
+ """
+
+ script = "_{}_chdir.sh".format(name)
+ binary = to_label(binary)
+
+ # It's 2022 and java_binary still cannot be told to cd to the source directory under bazel run.
+ write_file(
+ name = "_{}_wrap".format(name),
+ out = script,
+ content = [
+ "#!/usr/bin/env bash",
+ BASH_RLOCATION_FUNCTION,
+ # Remove external/ prefix that is included in $(rootpath) but not supported by $(rlocation)
+ "bin=$(rlocation ${1#external/})",
+ # Consume the first argument
+ "shift",
+ # Fix the working directory
+ "cd " + chdir,
+ # Replace the current process
+ "exec $bin $@",
+ ],
+ is_executable = True,
+ )
+
+ native.sh_binary(
+ name = name,
+ srcs = [script],
+ args = ["$(rootpath {})".format(binary)] + kwargs.pop("args", []),
+ data = [binary],
+ deps = ["@bazel_tools//tools/bash/runfiles"],
+ **kwargs
+ )