forked from thought-machine/please
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsh_rules.build_defs
196 lines (174 loc) · 7.6 KB
/
sh_rules.build_defs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
""" Rules to 'build' shell scripts.
Note that these do pretty much nothing beyond collecting the files. In future we might
implement something more advanced (ala .sar files or whatever).
"""
_SH_BINARY_TMPL_PREFIX = """
unzip -qo $0 -d $(dirname $0)
""".replace('$', '\\\$').replace('"', '\\"')
_SH_BINARY_TMPL_SUFFIX = """
exit 0\n\
"""
def sh_library(name:str, src:str, deps:list=None, visibility:list=None, labels:list&features&tags=None):
"""Generates a shell script binary, essentially just the given source.
Note that these are individually executable so can only have one source file each.
This is a bit tedious and would be nice to improve sometime.
Args:
name (str): Name of the rule.
src (str): Source file for the rule.
deps (list): Dependencies of this rule.
visibility (list): Visibility declaration of the rule.
labels (list): List of labels.
"""
return filegroup(
name=name,
srcs=[src],
deps=deps,
visibility=visibility,
binary=True,
labels=labels,
)
def sh_binary(name:str, main:str|list&srcs, out:str="", deps:list=None, data:list=None, visibility:list=None,
labels:list&features&tags=None):
"""Generates a shell script binary.
It assumes that unzip is in your path.
The resulting script will contain three things:
1) Code necessary to unzip dependent files.
2) The user defined shell script.
3) The zipfile containing all dependent files.
Args:
name (str): Name of the rule
main (str): The script to execute after all files have been uncompressed
out (str): Name of the output file to create. Defaults to name + .sh.
data (list): Runtime data for this rule.
deps (list): Dependencies of this rule
visibility (list): Visibility declaration of the rule.
labels (list): List of labels.
"""
if isinstance(main, list):
assert len(main) == 1, "srcs must be a single-element list"
main = main[0]
# No need to go through zip/unzip and injecting code if there are no dependencies
if deps:
cmds = ' && '.join([
# If set, use the same shebang as the original script
'head -1 "$SRCS" | { grep "^#\!" || true; } > "$TMPDIR"/_tmp.txt',
# Inject bash code to untar the compressed files.
f'echo "{_SH_BINARY_TMPL_PREFIX}" >> "$TMPDIR"/_tmp.txt',
# Inject the user defined script.
'cat $SRCS >> "$TMPDIR"/_tmp.txt',
# Inject a final exit so it doesn't try to execute the zipfile contents.
f'echo "{_SH_BINARY_TMPL_SUFFIX}" >> "$TMPDIR"/_tmp.txt',
# Compress the dependent files and dump out into the bash script.
'"$TOOL" z -d -n -i . -o "$OUT" --preamble_file "$TMPDIR"/_tmp.txt --strip_prefix ./',
])
else:
cmds = 'cp "$SRC" "$OUT"'
return build_rule(
name = name,
srcs = [main],
outs = [out or f'{name}.sh'],
data = data,
tools = [CONFIG.JARCAT_TOOL],
cmd = cmds,
deps = deps,
binary = True,
needs_transitive_deps = True,
labels = labels,
visibility = visibility,
)
def sh_test(name:str, src:str=None, labels:list&features&tags=None, data:list|dict=None, deps:list=None, worker:str='',
size:str=None, visibility:list=None, flags:str='', flaky:bool|int=0, test_outputs:list=None, timeout:int=0,
sandbox:bool=None):
"""Generates a shell test. Note that these aren't packaged in a useful way.
Args:
name (str): Name of the rule
src (str): Test script file.
labels (list): Labels to apply to this test.
data (list): Runtime data for the test.
deps (list): Dependencies of this rule
worker (str): Reference to worker script, A persistent worker process that is used to set up the test.
size (str): Test size (enormous, large, medium or small).
visibility (list): Visibility declaration of the rule.
flags (str): Flags to apply to the test invocation.
timeout (int): Maximum length of time, in seconds, to allow this test to run for.
flaky (int | bool): True to mark this as flaky and automatically rerun.
test_outputs (list): Extra test output files to generate from this test.
sandbox (bool): Sandbox the test on Linux to restrict access to namespaces such as network.
"""
test_cmd = '$TEST %s' % flags
if worker:
test_cmd = f'$(worker {worker}) && {test_cmd} '
deps += [worker]
return build_rule(
name=name,
srcs=[src or test],
data=data,
deps=deps,
outs=[name + '.sh'],
cmd='cp "$SRC" "$OUT"',
test_cmd=test_cmd,
visibility=visibility,
labels=labels,
binary=True,
test=True,
no_test_output=True,
flaky=flaky,
requires=['test'],
test_outputs=test_outputs,
test_timeout=timeout,
size = size,
test_sandbox=sandbox,
)
def sh_cmd(name:str, cmd:str|dict|list, srcs:list|dict=[], data:list=[], out:str="", shell:str='/bin/sh',
labels:list&features&tags=[], deps:list=[], visibility:list=None, expand_env_vars:bool=True,
test_only:bool=False):
"""Generates a runnable shell script from a command.
This is doable with a genrule with a little effort but it's awkward enough to be nice
to have a builtin.
The command is subject to Please's usual variable expansion at build time. Note that if
you want `plz run` to transparently work and refer to other files, you may need to use
$(out_location ...) instead of $(location ...).
Args:
name (str): Name of the rule.
cmd (str | dict): Command to write into the output script file. The commands are subject to
shell expansion during build time by default. If that is to be avoided, set expand_env_vars to
False, or escape the variables with \\\\\\$, i.e. \\\\\\${@}.
expand_env_vars (bool): Whether to expand
srcs (list | dict): Source files. Can be consumed as env variables by the generated command (but are not
written into the output in any other way).
data (list): Runtime data for this rule.
out (str): Name of the output file to create. Defaults to name + .sh.
shell (str): Shell to invoke in, by default /bin/sh.
labels (list): Labels to apply to this rule.
deps (list): Any dependencies for this rule. These can be consumed as env variables in the cmd but are not
used in any other way.
visibility (list): Visibility declaration of the rule.
test_only (bool): If True, this rule can only be depended on by tests.
"""
cmd = ' && '.join(cmd) if isinstance(cmd, list) else cmd
cmd = cmd[CONFIG.BUILD_CONFIG] if isinstance(cmd, dict) else cmd
if not expand_env_vars:
return text_file(
name = name,
out = out or name + '.sh',
data = data,
labels = labels + ["sh_cmd"],
deps = deps + srcs, # There's no difference between these two as we're not expanding env vars as below
visibility = visibility,
binary = True,
test_only = test_only,
content = f"#!{shell}\n{cmd}\n",
)
cmd = f'{{ cat > "$OUT" << EOF\n#!{shell}\n{cmd}\nEOF\n}}'
return build_rule(
name = name,
outs = [out or name + '.sh'],
srcs = srcs,
data = data,
cmd = cmd,
labels = labels,
deps = deps,
visibility = visibility,
binary = True,
test_only = test_only,
)