diff --git a/sh/private/defs.bzl b/sh/private/defs.bzl index 558c76f..e477b63 100644 --- a/sh/private/defs.bzl +++ b/sh/private/defs.bzl @@ -31,12 +31,11 @@ def mk_template_variable_info(name, sh_binaries_info): }, )) -def mk_default_info_with_files_to_run(ctx, name, files, runfiles): +def mk_default_info_with_files_to_run(ctx, executable, files, runfiles): # Create a dummy executable to trigger the generation of a FilesToRun # provider which can be used in custom rules depending on this bundle to # input the needed runfiles into build actions. # This is a workaround for https://github.com/bazelbuild/bazel/issues/15486 - executable = ctx.actions.declare_file(name) ctx.actions.write(executable, "", is_executable = True) return DefaultInfo( executable = executable, diff --git a/sh/sh.bzl b/sh/sh.bzl index 10bc40d..c56f9a5 100644 --- a/sh/sh.bzl +++ b/sh/sh.bzl @@ -17,7 +17,33 @@ ShBinariesInfo = provider( _WINDOWS_EXE_EXTENSIONS = [".exe", ".cmd", ".bat", ".ps1"] -def _sh_binaries_from_srcs(ctx, srcs, is_windows): +_POSIX_WRAPPER_TEMPLATE = """\ +#!/bin/sh +if [[ -f "{main_executable}.runfiles_manifest" ]]; then + export RUNFILES_MANIFEST_FILE="{main_executable}.runfiles_manifest" +elif [[ -d "{main_executable}.runfiles" ]]; then + export RUNFILES_DIR="{main_executable}.runfiles" +else + echo "ERROR: Runfiles not found for bundle {main_executable}" >&2 + exit 1 +fi +exec "{original_executable}" "$@" +""" + +_WINDOWS_WRAPPER_TEMPLATE = """\ +@echo off +if exist "{main_executable}.runfiles_manifest" ( + set RUNFILES_MANIFEST_FILE={main_executable}.runfiles_manifest +) else if exist "{main_executable}.runfiles" ( + set RUNFILES_DIR={main_executable}.runfiles +) else ( + echo ERROR: Runfiles not found for bundle {main_executable} >&2 + exit /b 1 +) +"{original_executable}" %* +""" + +def _sh_binaries_from_srcs(ctx, srcs, is_windows, main_executable): executable_files = [] runfiles = ctx.runfiles() executables_dict = dict() @@ -27,10 +53,10 @@ def _sh_binaries_from_srcs(ctx, srcs, is_windows): if src[DefaultInfo].files_to_run == None or src[DefaultInfo].files_to_run.executable == None: fail("srcs must be executable, but '{}' is not.".format(src.label)) - executable = src[DefaultInfo].files_to_run.executable - name = executable.basename + original_executable = src[DefaultInfo].files_to_run.executable + name = original_executable.basename if is_windows: - (noext, ext) = paths.split_extension(executable.basename) + (noext, ext) = paths.split_extension(original_executable.basename) if ext in _WINDOWS_EXE_EXTENSIONS: name = noext @@ -41,8 +67,21 @@ def _sh_binaries_from_srcs(ctx, srcs, is_windows): src.label, )) + if src[DefaultInfo].default_runfiles: + executable = ctx.actions.declare_file(ctx.label.name + ".path/" + (name + ".bat" if is_windows else name)) + ctx.actions.write( + executable, + (_WINDOWS_WRAPPER_TEMPLATE if is_windows else _POSIX_WRAPPER_TEMPLATE).format( + main_executable = main_executable.path, + original_executable = original_executable.path, + ), + is_executable = True, + ) + executable_files.append(original_executable) + runfiles = runfiles.merge(src[DefaultInfo].default_runfiles) + else: + executable = original_executable executable_files.append(executable) - runfiles = runfiles.merge(src[DefaultInfo].default_runfiles) executables_dict[name] = executable executable_paths.append(executable.dirname) @@ -101,7 +140,8 @@ def _mk_sh_binaries_info(direct, transitive): def _sh_binaries_impl(ctx): is_windows = ctx.attr._is_windows[ConstantInfo].value - direct = _sh_binaries_from_srcs(ctx, ctx.attr.srcs, is_windows) + executable = ctx.actions.declare_file(ctx.label.name) + direct = _sh_binaries_from_srcs(ctx, ctx.attr.srcs, is_windows, executable) transitive = _sh_binaries_from_deps(ctx, ctx.attr.deps) data_runfiles = _runfiles_from_data(ctx, ctx.attr.data) @@ -109,7 +149,7 @@ def _sh_binaries_impl(ctx): template_variable_info = mk_template_variable_info(ctx.label.name, sh_binaries_info) default_info = mk_default_info_with_files_to_run( ctx, - ctx.label.name, + executable, depset(direct = direct.executable_files, transitive = transitive.executable_files), direct.runfiles.merge(transitive.runfiles).merge(data_runfiles), )