-
Notifications
You must be signed in to change notification settings - Fork 199
/
dev_env_tool.bzl
336 lines (299 loc) · 10.8 KB
/
dev_env_tool.bzl
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@bazel_tools//tools/cpp:lib_cc_configure.bzl", "get_cpu_value")
load("@rules_sh//sh:posix.bzl", "posix")
def _create_build_content(rule_name, is_windows, tools, required_tools, win_paths, nix_paths):
content = """
# DO NOT EDIT: automatically generated BUILD file for dev_env_tool.bzl: {rule_name}
package(default_visibility = ["//visibility:public"])
filegroup(
name = "all",
srcs = glob(["**"]),
)
""".format(rule_name = rule_name)
for i in range(0, len(tools)):
if is_windows:
content += """
# Running tools with `bazel run` is not supported on Windows.
filegroup(
name = "{tool}",
srcs = ["{path}"],
)
""".format(
tool = tools[i],
path = win_paths[i],
)
else:
content += """
sh_binary(
name = "{tool}",
srcs = ["{path}"],
data = {dependencies},
)
""".format(
tool = tools[i],
dependencies = [":{}".format(dep) for dep in required_tools.get(tools[i], [])],
path = nix_paths[i],
)
content += """
config_setting(
name = "windows",
values = {"cpu": "x64_windows"},
visibility = ["//visibility:private"],
)
"""
return content
def dadew_where(ctx, ps):
ps = ctx.which("powershell")
ps_result = ctx.execute([ps, "-Command", "dadew where"], quiet = True)
if ps_result.return_code != 0:
fail("Failed to obtain dadew location.\nExit code %d.\n%s\n%s" %
(ps_result.return_code, ps_result.stdout, ps_result.stderr))
return ps_result.stdout.splitlines()[0]
def dadew_tool_home(dadew, tool):
return "%s\\scoop\\apps\\%s\\current" % (dadew, tool)
def _find_files_recursive(ctx, find, root):
find_result = ctx.execute([find, "-L", root, "-type", "f", "-print0"])
if find_result.return_code != 0:
fail("Failed to list files contained in '%s':\nExit code %d\n%s\n%s." %
(root, find_result.return_code, find_result.stdout, find_result.stderr))
return [
f[len(root) + 1:]
for f in find_result.stdout.split("\0")
if f and f.startswith(root)
]
def _symlink_files_recursive(ctx, find, source, dest):
files = _find_files_recursive(ctx, find, source)
for f in files:
ctx.symlink("%s/%s" % (source, f), "%s/%s" % (dest, f))
def _dadew_impl(ctx):
ctx.file("BUILD.bazel", executable = False)
if get_cpu_value(ctx) == "x64_windows":
ps = ctx.which("powershell")
dadew = dadew_where(ctx, ps)
ctx.file("dadew.bzl", executable = False, content = """
dadew = r"{dadew}"
def dadew_tool_home(tool):
return r"%s\\scoop\\apps\\%s\\current" % (dadew, tool)
""".format(dadew = dadew))
else:
ctx.file("dadew.bzl", executable = False, content = """
dadew = None
def dadew_tool_home(tool):
return None
""")
dadew = repository_rule(
implementation = _dadew_impl,
configure = True,
local = True,
)
def _dev_env_tool_impl(ctx):
is_windows = get_cpu_value(ctx) == "x64_windows"
if is_windows:
ps = ctx.which("powershell")
dadew = dadew_where(ctx, ps)
find = dadew_tool_home(dadew, "msys2") + "\\usr\\bin\\find.exe"
tool_home = dadew_tool_home(dadew, ctx.attr.win_tool)
for i in ctx.attr.win_include:
src = "%s\\%s" % (tool_home, i)
dst = ctx.attr.win_include_as.get(i, i)
if ctx.attr.prefix:
dst = "%s\\%s" % (ctx.attr.prefix, dst)
_symlink_files_recursive(ctx, find, src, dst)
else:
find = "find"
tool_home = "../%s" % ctx.attr.nix_label.name
for i in ctx.attr.nix_include:
src = "%s/%s" % (tool_home, i)
dst = i
if ctx.attr.prefix:
dst = "%s/%s" % (ctx.attr.prefix, dst)
_symlink_files_recursive(ctx, find, src, dst)
build_path = ctx.path("BUILD")
build_content = _create_build_content(
rule_name = ctx.name,
is_windows = is_windows,
tools = ctx.attr.tools,
required_tools = ctx.attr.required_tools,
win_paths = [
"%s/%s" % (ctx.attr.prefix, path)
for path in ctx.attr.win_paths
] if ctx.attr.prefix else ctx.attr.win_paths,
nix_paths = [
"%s/%s" % (ctx.attr.prefix, path)
for path in ctx.attr.nix_paths
] if ctx.attr.prefix else ctx.attr.nix_paths,
)
ctx.file(build_path, content = build_content, executable = False)
dev_env_tool = repository_rule(
implementation = _dev_env_tool_impl,
attrs = {
"tools": attr.string_list(
mandatory = True,
),
"required_tools": attr.string_list_dict(
mandatory = False,
default = {},
),
"win_tool": attr.string(
mandatory = True,
),
"win_include": attr.string_list(
mandatory = True,
),
"win_include_as": attr.string_dict(
mandatory = False,
default = {},
),
"win_paths": attr.string_list(
mandatory = False,
),
"nix_label": attr.label(
mandatory = False,
),
"nix_include": attr.string_list(
mandatory = True,
),
"nix_paths": attr.string_list(
mandatory = True,
),
"prefix": attr.string(),
},
configure = True,
local = True,
)
def _dadew_sh_posix_config_impl(repository_ctx):
ps = repository_ctx.which("powershell")
dadew = dadew_where(repository_ctx, ps)
msys2_usr_bin = dadew_tool_home(dadew, "msys2") + "\\usr\\bin"
commands = {}
for cmd in posix.commands:
for ext in [".exe", ""]:
path = "%s\\%s%s" % (msys2_usr_bin, cmd, ext)
if repository_ctx.path(path).exists:
commands[cmd] = path
repository_ctx.file("BUILD.bazel", executable = False, content = """
load("@rules_sh//sh:posix.bzl", "sh_posix_toolchain")
sh_posix_toolchain(
name = "dadew_posix",
cmds = {{
{commands},
}},
)
toolchain(
name = "dadew_posix_toolchain",
toolchain = "dadew_posix",
toolchain_type = "@rules_sh//sh/posix:toolchain_type",
exec_compatible_with = [
"@bazel_tools//platforms:x86_64",
"@bazel_tools//platforms:windows",
],
target_compatible_with = [
"@bazel_tools//platforms:x86_64",
"@bazel_tools//platforms:windows",
],
)
""".format(
commands = ",\n ".join([
'r"{cmd}": r"{path}"'.format(cmd = cmd, path = cmd_path).replace("\\", "/")
for (cmd, cmd_path) in commands.items()
if cmd_path
]),
))
_dadew_sh_posix_config = repository_rule(
implementation = _dadew_sh_posix_config_impl,
configure = True,
local = True,
)
def dadew_sh_posix_configure(name = "dadew_sh_posix"):
_dadew_sh_posix_config(name = name)
native.register_toolchains("@%s//:dadew_posix_toolchain" % name)
def _create_shim(repository_ctx, shim_exe, *, shim, path, extension):
"""Create a shim file for Scoop's shim.exe utility.
See [ScoopInstaller/Shim][shim-gh] for further information.
[shim-gh]: https://github.com/ScoopInstaller/Shim#readme
"""
if extension in [".exe", ".cmd", ".bat"]:
repository_ctx.file(shim + ".shim", executable = False, content = "path = " + str(path))
result = shim + ".exe"
# Will create a copy on Windows
repository_ctx.symlink(shim_exe, result)
else:
# We assume that files without standard extension are scripts (bash,
# perl, ...), e.g. automake. Scoop's shim doesn't work on these. So, we
# assume that, since they are shell scripts in the first place, they
# will only be called in a shell context and simply create a copy.
repository_ctx.symlink(path, shim)
result = shim + extension
return result
def _dadew_binary_bundle_impl(repository_ctx):
ps = repository_ctx.which("powershell")
dadew = dadew_where(repository_ctx, ps)
scoop_home = dadew_tool_home(dadew, "scoop")
shim_exe = repository_ctx.path(paths.join(scoop_home, "supporting/shimexe/bin/shim.exe"))
tool_home = dadew_tool_home(dadew, repository_ctx.attr.tool)
extensions = ["", ".exe", ".cmd", ".bat"]
missing = []
found = []
for path_str in repository_ctx.attr.paths:
parts = path_str.split(":", 1)
if len(parts) == 1:
[path_str] = parts
shim = path_str
elif len(parts) == 2:
[path_str, shim] = parts
else:
fail("Invalid path '{}': Expected 'PATH' or 'PATH:SHIM'.".format(path_str))
for ext in extensions:
path = repository_ctx.path(paths.join(tool_home, path_str + ext))
if path.exists:
break
else:
path = None
if path:
found.append(
_create_shim(repository_ctx, shim_exe, shim = shim, path = path, extension = ext),
)
else:
missing.append(path_str)
repository_ctx.file("BUILD.bazel", executable = False, content = """\
load("@com_github_digital_asset_daml//bazel_tools:bundle.bzl", "binary_bundle")
package(default_visibility = ["//visibility:public"])
binary_bundle(
name = "tools",
tools = {tools},
)
""".format(
tools = repr(found),
))
if missing:
fail("Missing paths in '{}': {}".format(tool_home, ", ".join(missing)))
dadew_binary_bundle = repository_rule(
_dadew_binary_bundle_impl,
attrs = {
"tool": attr.string(
mandatory = True,
doc = "Import binaries from this Scoop tool, i.e. an entry under the `dev-env/windows/manifests` folder.",
),
"paths": attr.string_list(
mandatory = True,
doc = """\
Import these binaries from the Scoop tool into Bazel.
Items can either be paths relative to the Scoop tool installation path, or colon (`:`) separated tuples where the first item is the path relative to the Scoop tool installation and the second item the name that it should be mapped to within Bazel.
""",
),
},
configure = True,
local = True,
doc = """\
Generate a `binary_bundle` containing a set of binaries provided by a Scoop tool.
Use this to import binaries provided by Scoop into the Bazel build in a
hermetic way. This generates [Scoop shims][scoop-shim] for the specified
binaries within Bazel's execution root and constructs a `binary_bundle` that
contains these shims. The generated PATH make variable will only cover tools
explicitly listed on this rule.
See [`binary_bundle`](../bundle.bzl) for more information on `binary_bundle`.
[scoop-shim]: https://github.com/ScoopInstaller/Shim#readme
""",
)