1"""Internal rules for building upb."""
2
3load(":upb_proto_library.bzl", "GeneratedSrcsInfo")
4
5def _librule(name):
6    return name + "_lib"
7
8def _get_real_short_path(file):
9    # For some reason, files from other archives have short paths that look like:
10    #   ../com_google_protobuf/google/protobuf/descriptor.proto
11    short_path = file.short_path
12    if short_path.startswith("../"):
13        second_slash = short_path.index("/", 3)
14        short_path = short_path[second_slash + 1:]
15    return short_path
16
17def _get_real_root(file):
18    real_short_path = _get_real_short_path(file)
19    return file.path[:-len(real_short_path) - 1]
20
21def _get_real_roots(files):
22    roots = {}
23    for file in files:
24        real_root = _get_real_root(file)
25        if real_root:
26            roots[real_root] = True
27    return roots.keys()
28
29def lua_cclibrary(name, srcs, hdrs = [], deps = [], luadeps = []):
30    lib_rule = name + "_lib"
31    so_rule = "lib" + name + ".so"
32    so_file = _remove_prefix(name, "lua/") + ".so"
33
34    native.cc_library(
35        name = _librule(name),
36        hdrs = hdrs,
37        srcs = srcs,
38        deps = deps + [_librule(dep) for dep in luadeps] + ["@lua//:liblua_headers"],
39    )
40
41    native.cc_binary(
42        name = so_rule,
43        linkshared = True,
44        deps = [_librule(name)],
45        linkopts = select({
46            ":darwin": [
47                "-undefined dynamic_lookup",
48            ],
49            "//conditions:default": [],
50        }),
51    )
52
53    native.genrule(
54        name = name + "_copy",
55        srcs = [":" + so_rule],
56        outs = [so_file],
57        cmd = "cp $< $@",
58    )
59
60    native.filegroup(
61        name = name,
62        data = [so_file],
63    )
64
65def _remove_prefix(str, prefix):
66    if not str.startswith(prefix):
67        fail("%s doesn't start with %s" % (str, prefix))
68    return str[len(prefix):]
69
70def _remove_suffix(str, suffix):
71    if not str.endswith(suffix):
72        fail("%s doesn't end with %s" % (str, suffix))
73    return str[:-len(suffix)]
74
75def lua_library(name, srcs, strip_prefix, luadeps = []):
76    outs = [_remove_prefix(src, strip_prefix + "/") for src in srcs]
77    native.genrule(
78        name = name + "_copy",
79        srcs = srcs,
80        outs = outs,
81        cmd = "cp $(SRCS) $(@D)",
82    )
83
84    native.filegroup(
85        name = name,
86        data = outs + luadeps,
87    )
88
89def make_shell_script(name, contents, out):
90    contents = contents.replace("$", "$$")
91    native.genrule(
92        name = "gen_" + name,
93        outs = [out],
94        cmd = "(cat <<'HEREDOC'\n%s\nHEREDOC\n) > $@" % contents,
95    )
96
97def _lua_binary_or_test(name, luamain, luadeps, rule):
98    script = name + ".sh"
99
100    make_shell_script(
101        name = "gen_" + name,
102        out = script,
103        contents = """
104BASE=$(dirname $(rlocation upb/upb_c.so))
105export LUA_CPATH="$BASE/?.so"
106export LUA_PATH="$BASE/?.lua"
107$(rlocation lua/lua) $(rlocation upb/tools/upbc.lua) "$@"
108""",
109    )
110
111    rule(
112        name = name,
113        srcs = [script],
114        data = ["@lua//:lua", luamain] + luadeps,
115    )
116
117def lua_binary(name, luamain, luadeps = []):
118    _lua_binary_or_test(name, luamain, luadeps, native.sh_binary)
119
120def lua_test(name, luamain, luadeps = []):
121    _lua_binary_or_test(name, luamain, luadeps, native.sh_test)
122
123def generated_file_staleness_test(name, outs, generated_pattern):
124    """Tests that checked-in file(s) match the contents of generated file(s).
125
126    The resulting test will verify that all output files exist and have the
127    correct contents.  If the test fails, it can be invoked with --fix to
128    bring the checked-in files up to date.
129
130    Args:
131      name: Name of the rule.
132      outs: the checked-in files that are copied from generated files.
133      generated_pattern: the pattern for transforming each "out" file into a
134        generated file.  For example, if generated_pattern="generated/%s" then
135        a file foo.txt will look for generated file generated/foo.txt.
136    """
137
138    script_name = name + ".py"
139    script_src = "//:tools/staleness_test.py"
140
141    # Filter out non-existing rules so Blaze doesn't error out before we even
142    # run the test.
143    existing_outs = native.glob(include = outs)
144
145    # The file list contains a few extra bits of information at the end.
146    # These get unpacked by the Config class in staleness_test_lib.py.
147    file_list = outs + [generated_pattern, native.package_name() or ".", name]
148
149    native.genrule(
150        name = name + "_makescript",
151        outs = [script_name],
152        srcs = [script_src],
153        testonly = 1,
154        cmd = "cat $(location " + script_src + ") > $@; " +
155              "sed -i.bak -e 's|INSERT_FILE_LIST_HERE|" + "\\\n  ".join(file_list) + "|' $@",
156    )
157
158    native.py_test(
159        name = name,
160        srcs = [script_name],
161        data = existing_outs + [generated_pattern % file for file in outs],
162        deps = [
163            "//:staleness_test_lib",
164        ],
165    )
166
167# upb_amalgamation() rule, with file_list aspect.
168
169SrcList = provider(
170    fields = {
171        "srcs": "list of srcs",
172    },
173)
174
175def _file_list_aspect_impl(target, ctx):
176    if GeneratedSrcsInfo in target:
177        srcs = target[GeneratedSrcsInfo]
178        return [SrcList(srcs = srcs.srcs + srcs.hdrs)]
179
180    srcs = []
181    for src in ctx.rule.attr.srcs:
182        srcs += src.files.to_list()
183    for hdr in ctx.rule.attr.hdrs:
184        srcs += hdr.files.to_list()
185    for hdr in ctx.rule.attr.textual_hdrs:
186        srcs += hdr.files.to_list()
187    return [SrcList(srcs = srcs)]
188
189_file_list_aspect = aspect(
190    implementation = _file_list_aspect_impl,
191)
192
193def _upb_amalgamation(ctx):
194    inputs = []
195    for lib in ctx.attr.libs:
196        inputs += lib[SrcList].srcs
197    srcs = [src for src in inputs if src.path.endswith("c")]
198    ctx.actions.run(
199        inputs = inputs,
200        outputs = ctx.outputs.outs,
201        arguments = [ctx.bin_dir.path + "/"] + [f.path for f in srcs] + ["-I" + root for root in _get_real_roots(inputs)],
202        progress_message = "Making amalgamation",
203        executable = ctx.executable.amalgamator,
204    )
205    return []
206
207upb_amalgamation = rule(
208    attrs = {
209        "amalgamator": attr.label(
210            executable = True,
211            cfg = "host",
212        ),
213        "libs": attr.label_list(aspects = [_file_list_aspect]),
214        "outs": attr.output_list(),
215    },
216    implementation = _upb_amalgamation,
217)
218
219def licenses(*args):
220    # No-op (for Google-internal usage).
221    pass
222