From a3ec19a20b5d2189c5c85f7c74fae53c9eee4fb1 Mon Sep 17 00:00:00 2001 From: Andres Cecilia Luque Date: Fri, 23 Oct 2026 02:57:43 +0400 Subject: [PATCH] fix: resolve rb_binary main scripts from any working directory The launcher resolves main scripts in the main repository relative to the working directory, which only matches bazel run's default cwd. Invocations that change the cwd (e.g. bazel --run_under="cd ...", or running the launcher binary directly) fail with a LoadError. Locate these scripts through runfiles instead, falling back to the relative path so behavior under plain bazel run is unchanged. Windows behavior is unchanged. --- .github/workflows/ci.yml | 7 +++++++ ruby/private/binary.bzl | 21 +++++++++++++++++---- ruby/private/binary/binary.sh.tpl | 7 ++++++- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a49768cd..191980f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,6 +95,13 @@ jobs: with: name: coverage-${{ matrix.os }}-${{ matrix.ruby }} path: examples/gem/bazel-testlogs/spec/add/coverage.dat + # rb_binary launchers must resolve the main script from any working + # directory, not only under `bazel run` defaults. + - run: | + launcher="$PWD/$(bazel cquery --output=starlark --starlark:expr='target.files_to_run.executable.path' lib/gem:add-numbers)" + cd / + "$launcher" 1 2 + if: matrix.os != 'windows-latest' - if: failure() && runner.debug == '1' uses: mxschmitt/action-tmate@v3 diff --git a/ruby/private/binary.bzl b/ruby/private/binary.bzl index 571032c5..e90ba12f 100644 --- a/ruby/private/binary.bzl +++ b/ruby/private/binary.bzl @@ -81,6 +81,7 @@ def generate_rb_binary_script(ctx, binary, bundler = False, args = [], env = {}, toolchain = ctx.attr.ruby[platform_common.ToolchainInfo] binary_path = "" + binary_fallback_path = "" locate_binary_in_runfiles = "" ruby_binary_name = toolchain.ruby.basename if binary and binary != toolchain.ruby: @@ -90,11 +91,22 @@ def generate_rb_binary_script(ctx, binary, bundler = False, args = [], env = {}, # so for now we skip locating the binary in runfiles. This only prevents # running binary scripts directly and should not affect normal `bazel run`. # See BATCH_RLOCATION_FUNCTION comments for more details. - if binary_path.startswith("../") and not _is_windows(ctx): - binary_path = _to_rlocation_path(ctx, binary) - locate_binary_in_runfiles = "true" - else: + if _is_windows(ctx): binary_path = _normalize_path(ctx, binary_path) + else: + binary_path = _to_rlocation_path(ctx, binary) + + # Binaries outside the main repository are always expected to be + # locatable through runfiles. Binaries in the main repository are + # resolved through runfiles when possible, so that they can be + # executed from any working directory (e.g. via --run_under or by + # running the launcher directly), and fall back to the relative + # path, which requires the default `bazel run` working directory. + if binary.short_path.startswith("../"): + locate_binary_in_runfiles = "true" + else: + binary_fallback_path = _normalize_path(ctx, binary.short_path) + locate_binary_in_runfiles = "fallback" # JRuby's .cmd stubs are plain batch files, so they must be executed # directly. MRI's are Ruby polyglots and only work via the interpreter. @@ -130,6 +142,7 @@ def generate_rb_binary_script(ctx, binary, bundler = False, args = [], env = {}, substitutions = { "{args}": args, "{binary}": binary_path, + "{binary_fallback}": binary_fallback_path, "{env}": _convert_env_to_script(ctx, environment), "{bundler_command}": bundler_command, "{jars_home_strip_suffix}": jars_home_strip_suffix, diff --git a/ruby/private/binary/binary.sh.tpl b/ruby/private/binary/binary.sh.tpl index c14bdd24..81244b9a 100644 --- a/ruby/private/binary/binary.sh.tpl +++ b/ruby/private/binary/binary.sh.tpl @@ -76,10 +76,15 @@ if [ -n "{bundler_command}" ]; then fi fi -if [ -n "{locate_binary_in_runfiles}" ]; then +if [ "{locate_binary_in_runfiles}" = "true" ]; then binary="$(rlocation "{binary}")" \ || (fail "Failed to locate {binary} in the runfiles." \ "Did you forget to add the binary to the deps?") +elif [ "{locate_binary_in_runfiles}" = "fallback" ]; then + binary="$(rlocation "{binary}" 2>/dev/null || true)" + if [ -z "$binary" ] || [ ! -f "$binary" ]; then + binary="{binary_fallback}" + fi else binary="{binary}" fi