Skip to content

Commit 2369dc2

Browse files
authored
Merge pull request #34 from rabbitmq/escript-rule
Add an internal rule for creating escripts
2 parents 22a898d + 5f6993e commit 2369dc2

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed

private/escript.bzl

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
load("//:erlang_home.bzl", "ErlangHomeProvider", "ErlangVersionProvider")
2+
load("//:erlang_app_info.bzl", "ErlangAppInfo", "flat_deps")
3+
load(
4+
"//:util.bzl",
5+
"BEGINS_WITH_FUN",
6+
"QUERY_ERL_VERSION",
7+
"path_join",
8+
)
9+
load(":util.bzl", "additional_file_dest_relative_path")
10+
11+
DEFAULT_HEADERS = [
12+
"shebang",
13+
"comment",
14+
]
15+
16+
def _build_erl_libs(ctx, dir = None):
17+
erlang_version = ctx.attr._erlang_version[ErlangVersionProvider].version
18+
19+
deps = flat_deps([ctx.attr.app])
20+
21+
entries = {}
22+
for dep in deps:
23+
lib_info = dep[ErlangAppInfo]
24+
if lib_info.erlang_version != erlang_version:
25+
fail("Mismatched erlang versions", erlang_version, lib_info.erlang_version)
26+
for src in lib_info.beam:
27+
if src.is_directory:
28+
fail("beam directories are not supported with this rule")
29+
archive_path = path_join(lib_info.app_name, "ebin", src.basename)
30+
dest = ctx.actions.declare_file(path_join(dir, archive_path))
31+
ctx.actions.symlink(output = dest, target_file = src)
32+
entries[archive_path] = dest
33+
for src in lib_info.priv:
34+
rp = additional_file_dest_relative_path(dep.label, src)
35+
archive_path = path_join(lib_info.app_name, rp)
36+
dest = ctx.actions.declare_file(path_join(dir, archive_path))
37+
ctx.actions.symlink(output = dest, target_file = src)
38+
entries[archive_path] = dest
39+
return entries
40+
41+
def _impl(ctx):
42+
name = ctx.attr.out if ctx.attr.out != "" else ctx.label.name
43+
44+
output = ctx.actions.declare_file(name)
45+
46+
file_entries = [
47+
"{{filename:basename(\"{p}\"), \"{p}\"}}".format(p = src.path)
48+
for src in ctx.files.srcs + ctx.files.beam
49+
]
50+
51+
app_entries = []
52+
app_files = []
53+
if ctx.attr.app != None:
54+
entries = _build_erl_libs(ctx, dir = ctx.label.name + "_deps")
55+
for path, file in entries.items():
56+
app_entries.append("{{\"{}\", \"{}\"}}".format(path, file.path))
57+
app_files.append(file)
58+
59+
entry_list = "[" + ", ".join(app_entries + file_entries) + "]"
60+
61+
script = """set -euo pipefail
62+
63+
{begins_with_fun}
64+
V=$("{erlang_home}"/bin/{query_erlang_version})
65+
if ! beginswith "{erlang_version}" "$V"; then
66+
echo "Erlang version mismatch (Expected {erlang_version}, found $V)"
67+
exit 1
68+
fi
69+
70+
"{erlang_home}"/bin/erl \\
71+
-noshell \\
72+
-eval 'io:format("Assembling {name} escript...~n", []),
73+
ArchiveEntries = [begin
74+
{{ok, Bin}} = file:read_file(Path),
75+
{{Name, Bin}}
76+
end || {{Name, Path}} <- {entry_list}],
77+
ok = escript:create("{output}",
78+
[{headers}
79+
{{archive, ArchiveEntries, []}}]),
80+
io:format("done.~n", []),
81+
halt().
82+
'
83+
""".format(
84+
begins_with_fun = BEGINS_WITH_FUN,
85+
query_erlang_version = QUERY_ERL_VERSION,
86+
erlang_home = ctx.attr._erlang_home[ErlangHomeProvider].path,
87+
erlang_version = ctx.attr._erlang_version[ErlangVersionProvider].version,
88+
name = name,
89+
headers = "".join(["{}, ".format(h) for h in ctx.attr.headers]),
90+
entry_list = entry_list,
91+
output = output.path,
92+
)
93+
94+
inputs = app_files + ctx.files.srcs + ctx.files.beam
95+
96+
ctx.actions.run_shell(
97+
inputs = inputs,
98+
outputs = [output],
99+
command = script,
100+
)
101+
102+
return [
103+
DefaultInfo(executable = output),
104+
]
105+
106+
escript_private = rule(
107+
implementation = _impl,
108+
attrs = {
109+
"_erlang_home": attr.label(default = Label("//:erlang_home")),
110+
"_erlang_version": attr.label(default = Label("//:erlang_version")),
111+
"out": attr.string(),
112+
"headers": attr.string_list(
113+
default = DEFAULT_HEADERS,
114+
),
115+
"srcs": attr.label_list(allow_files = [".erl"]),
116+
"beam": attr.label_list(allow_files = [".beam"]),
117+
"app": attr.label(providers = [ErlangAppInfo]),
118+
},
119+
executable = True,
120+
)

0 commit comments

Comments
 (0)