|
| 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