@@ -17,7 +17,37 @@ ShBinariesInfo = provider(
1717
1818_WINDOWS_EXE_EXTENSIONS = [".exe" , ".cmd" , ".bat" , ".ps1" ]
1919
20- def _sh_binaries_from_srcs (ctx , srcs , is_windows ):
20+ _POSIX_WRAPPER_TEMPLATE = """\
21+ #!/bin/sh
22+ if [[ -z ${{RUNFILES_MANIFEST_FILE+x}} && -z ${{RUNFILES_DIR+x}} ]]; then
23+ if [[ -f "{main_executable}.runfiles_manifest" ]]; then
24+ export RUNFILES_MANIFEST_FILE="{main_executable}.runfiles_manifest"
25+ elif [[ -d "{main_executable}.runfiles" ]]; then
26+ export RUNFILES_DIR="{main_executable}.runfiles"
27+ else
28+ echo "ERROR: Runfiles not found for bundle {main_executable}" >&2
29+ exit 1
30+ fi
31+ fi
32+ exec "{original_executable}" "$@"
33+ """
34+
35+ _WINDOWS_WRAPPER_TEMPLATE = """\
36+ @echo off
37+ if not defined RUNFILES_MANIFEST_FILE if not defined RUNFILES_DIR (
38+ if exist "{main_executable}.runfiles_manifest" (
39+ set RUNFILES_MANIFEST_FILE="{main_executable}.runfiles_manifest"
40+ ) else if exist "{main_executable}.runfiles" (
41+ set RUNFILES_DIR="{main_executable}.runfiles"
42+ ) else (
43+ echo ERROR: Runfiles not found for bundle {main_executable} >&2
44+ exit /b 1
45+ )
46+ )
47+ "{original_executable}" %*
48+ """
49+
50+ def _sh_binaries_from_srcs (ctx , srcs , is_windows , main_executable ):
2151 executable_files = []
2252 runfiles = ctx .runfiles ()
2353 executables_dict = dict ()
@@ -27,10 +57,10 @@ def _sh_binaries_from_srcs(ctx, srcs, is_windows):
2757 if src [DefaultInfo ].files_to_run == None or src [DefaultInfo ].files_to_run .executable == None :
2858 fail ("srcs must be executable, but '{}' is not." .format (src .label ))
2959
30- executable = src [DefaultInfo ].files_to_run .executable
31- name = executable .basename
60+ original_executable = src [DefaultInfo ].files_to_run .executable
61+ name = original_executable .basename
3262 if is_windows :
33- (noext , ext ) = paths .split_extension (executable .basename )
63+ (noext , ext ) = paths .split_extension (original_executable .basename )
3464 if ext in _WINDOWS_EXE_EXTENSIONS :
3565 name = noext
3666
@@ -41,8 +71,21 @@ def _sh_binaries_from_srcs(ctx, srcs, is_windows):
4171 src .label ,
4272 ))
4373
74+ if src [DefaultInfo ].default_runfiles :
75+ executable = ctx .actions .declare_file (ctx .label .name + ".path/" + (name + ".bat" if is_windows else name ))
76+ ctx .actions .write (
77+ executable ,
78+ (_WINDOWS_WRAPPER_TEMPLATE if is_windows else _POSIX_WRAPPER_TEMPLATE ).format (
79+ main_executable = main_executable .path ,
80+ original_executable = original_executable .path ,
81+ ),
82+ is_executable = True ,
83+ )
84+ executable_files .append (original_executable )
85+ runfiles = runfiles .merge (src [DefaultInfo ].default_runfiles )
86+ else :
87+ executable = original_executable
4488 executable_files .append (executable )
45- runfiles = runfiles .merge (src [DefaultInfo ].default_runfiles )
4689 executables_dict [name ] = executable
4790 executable_paths .append (executable .dirname )
4891
@@ -64,6 +107,9 @@ def _sh_binaries_from_deps(ctx, deps):
64107 fail ("deps must be sh_binaries targets, but '{}' is not." .format (dep .label ))
65108
66109 executable_files .append (dep [DefaultInfo ].files )
110+ # TODO: Handle tools with runfiles in deps correctly. They need to be wrapped in a new
111+ # wrapper script as in _sh_binaries_from_srcs since the dummy executable they reference
112+ # is no longer the sibling of the runfiles tree.
67113 runfiles = runfiles .merge (dep [DefaultInfo ].default_runfiles )
68114 executables_dict .update (dep [ShBinariesInfo ].executables )
69115 executable_paths .append (dep [ShBinariesInfo ].paths )
@@ -101,15 +147,16 @@ def _mk_sh_binaries_info(direct, transitive):
101147
102148def _sh_binaries_impl (ctx ):
103149 is_windows = ctx .attr ._is_windows [ConstantInfo ].value
104- direct = _sh_binaries_from_srcs (ctx , ctx .attr .srcs , is_windows )
150+ executable = ctx .actions .declare_file (ctx .label .name )
151+ direct = _sh_binaries_from_srcs (ctx , ctx .attr .srcs , is_windows , executable )
105152 transitive = _sh_binaries_from_deps (ctx , ctx .attr .deps )
106153 data_runfiles = _runfiles_from_data (ctx , ctx .attr .data )
107154
108155 sh_binaries_info = _mk_sh_binaries_info (direct , transitive )
109156 template_variable_info = mk_template_variable_info (ctx .label .name , sh_binaries_info )
110157 default_info = mk_default_info_with_files_to_run (
111158 ctx ,
112- ctx . label . name ,
159+ executable ,
113160 depset (direct = direct .executable_files , transitive = transitive .executable_files ),
114161 direct .runfiles .merge (transitive .runfiles ).merge (data_runfiles ),
115162 )
@@ -283,58 +330,5 @@ def _custom_rule_impl(ctx):
283330 ...
284331 )
285332```
286-
287- *Caveat: Using Binaries that require Runfiles*
288-
289- Note, support for binaries that require runfiles is limited due to limitations
290- imposed by Bazel's Starlark API, see [#15486][issue-15486]. In order for these
291- to work you will need to define the `RUNFILES_DIR` or `RUNFILES_MANIFEST_FILE`
292- environment variables for the action using tools from the bundle.
293-
294- (Use `RUNFILES_MANIFEST_FILE` if your operating system and configuration does
295- not support a runfiles tree and instead only provides a runfiles manifest file,
296- as is commonly the case on Windows.)
297-
298- You can achieve this in a `genrule` as follows:
299-
300- ```bzl
301- genrule(
302- name = "runfiles-genrule",
303- toolchains = [":a-bundle"],
304- cmd = "\\ n".join([
305- # The explicit RUNFILES_DIR/RUNFILES_MANIFEST_FILE is a workaround for
306- # https://github.com/bazelbuild/bazel/issues/15486
307- "RUNFILES_DIR=$(execpath :a-bundle).runfiles",
308- "RUNFILES_MANIFEST_FILE=$(execpath :a-bundle).runfiles_manifest",
309- "$(A_BUNDLE_BINARY_A)",
310- "$(A_BUNDLE_BINARY_B)",
311- ]),
312- outs = [...],
313- )
314- ```
315-
316- And in a custom rule as follows:
317-
318- ```bzl
319- def _custom_rule_impl(ctx):
320- tools = ctx.attr.tools[ShBinariesInfo]
321- (tools_inputs, tools_manifest) = ctx.resolve_tools(tools = [ctx.attr.tools])
322- # The explicit RUNFILES_DIR/RUNFILES_MANIFEST_FILE is a workaround for
323- # https://github.com/bazelbuild/bazel/issues/15486
324- tools_env = {
325- "RUNFILES_DIR": ctx.attr.tools[DefaultInfo].files_to_run.runfiles_manifest.dirname,
326- "RUNFILES_MANIFEST_FILE": ctx.attr.tools[DefaultInfo].files_to_run.runfiles_manifest.path,
327- }
328-
329- ctx.actions.run(
330- executable = tools.executables["binary-a"],
331- env = tools_env, # Pass the environment into the action.
332- inputs = tools_inputs,
333- input_manifests = tools_manifest,
334- ...
335- )
336- ```
337-
338- [issue-15486]: https://github.com/bazelbuild/bazel/issues/15486
339333""" ,
340334)
0 commit comments