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