'bazel run from a running py_binary

I've got a py_binary and another runnable target. I want to bazel run //:the_inner the second target (which could be a py_binary as well) from the first target bazel run //:the_outer

py_binary(
  name="the_outer",
  srcs=["the_outer.py"]
)

py_binary(
  name="the_inner",
  srcs=["the_inner.py"]
)

A simple

rv = subprocess.run( "bazel run //:the_inner".split(), capture_output=True, text=True)

Gives me a error message, telling me I shouldn't do this and tells me the workspace I should have used. At this point I just parse the error message and call the second target again.

workspace = re.search( r"'(\.\w/)+'". rv.stderr ).group(1)
subprocess.run( "bazel run //:the_inner".split(), cwd=workspace)

It works, but the solution is extremly awkward. Is there a canonicial way of bazel running stuff from another bazel run, particularly for python? Solutions I have seen include

  • resolving the symlinks in the sandbox and hoping you end up in the workspace
  • find / -name WORKSPACE and hoping there is only one
  • wrapping everything in a shell script and passing bazel info workspace as a parameter

No, it shouldn't be done. No don't care - not if there are so many simple workarounds



Solution 1:[1]

I found what I was looking was in the docs: https://docs.bazel.build/versions/main/user-manual.html#run

subprocess.run( "bazel run //:the_inner".split(), cwd=os.environ.get("BUILD_WORKSPACE_DIRECTORY","."))

extra environment variables are also available to the binary:

  • BUILD_WORKSPACE_DIRECTORY: the root of the workspace where the build was run.
  • BUILD_WORKING_DIRECTORY: the current working directory where Bazel was run from.

Solution 2:[2]

The more typical approach is for the "outer" binary to depend on the "inner" binary via "data", and use the python runfiles library to find the inner binary.

https://github.com/bazelbuild/bazel/blob/master/tools/python/runfiles/runfiles.py

For example:

BUILD:

py_binary(
  name = "a",
  srcs = ["a.py"],
  deps = ["@bazel_tools//tools/python/runfiles:runfiles"],
  data = [":b"],
)

py_binary(
  name = "b",
  srcs = ["b.py"],
)

a.py:

from bazel_tools.tools.python.runfiles import runfiles
import subprocess

r = runfiles.Create()
rv = subprocess.run(r.Rlocation("__main__/b"), capture_output=True, text=True)
print("b says:")
print(rv.stdout)

b.py:

print("hello world")

running:

$ bazel run a
INFO: Analyzed target //:a (39 packages loaded, 296 targets configured).
INFO: Found 1 target...
Target //:a up-to-date:
  bazel-bin/a
INFO: Elapsed time: 0.791s, Critical Path: 0.01s
INFO: 8 processes: 8 internal.
INFO: Build completed successfully, 8 total actions
INFO: Build completed successfully, 8 total actions
b says:
hello world

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 gnight
Solution 2 ahumesky