Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: chdir_binary is an sh_binary that wraps a tool and does a cd first #251

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
5 changes: 5 additions & 0 deletions docs/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
40 changes: 40 additions & 0 deletions docs/wrap_binary.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions lib/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,17 @@ bzl_library(
deps = ["//lib/private:run_binary"],
)

bzl_library(
name = "wrap_binary",
srcs = ["wrap_binary.bzl"],
deps = [
":paths",
":utils",
"//lib/private:run_binary",
"@bazel_skylib//rules:write_file",
],
)

bzl_library(
name = "repo_utils",
srcs = ["repo_utils.bzl"],
Expand Down
25 changes: 25 additions & 0 deletions lib/tests/wrap_binary/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -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"],
)
8 changes: 8 additions & 0 deletions lib/tests/wrap_binary/needs-working-dir.sh
Original file line number Diff line number Diff line change
@@ -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
62 changes: 62 additions & 0 deletions lib/wrap_binary.bzl
Original file line number Diff line number Diff line change
@@ -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):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I'm wondering if the BUILD_WORKSPACE_DIRECTORY is intuitive. Most Bazel users are accustomed to binaries running either in the execroot or the runfiles root. I feel like defaulting to the source tree could confuse some people.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I agree, also at the use site it's better to make this value explicit.

"""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
)