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
|
"""Generics support via go_generics.
A Go template is similar to a go library, except that it has certain types that
can be replaced before usage. For example, one could define a templatized List
struct, whose elements are of type T, then instantiate that template for
T=segment, where "segment" is the concrete type.
"""
TemplateInfo = provider(
"Information about a go_generics template.",
fields = {
"unsafe": "whether the template requires unsafe code",
"types": "required types",
"opt_types": "optional types",
"consts": "required consts",
"opt_consts": "optional consts",
"deps": "package dependencies",
"template": "merged template source file",
},
)
def _go_template_impl(ctx):
srcs = ctx.files.srcs
template = ctx.actions.declare_file(ctx.label.name + "_template.go")
args = ["-o=%s" % template.path] + [f.path for f in srcs]
ctx.actions.run(
inputs = srcs,
outputs = [template],
mnemonic = "GoGenericsTemplate",
progress_message = "Building Go template %s" % ctx.label,
arguments = args,
executable = ctx.executable._tool,
)
return [TemplateInfo(
types = ctx.attr.types,
opt_types = ctx.attr.opt_types,
consts = ctx.attr.consts,
opt_consts = ctx.attr.opt_consts,
deps = ctx.attr.deps,
template = template,
)]
go_template = rule(
implementation = _go_template_impl,
attrs = {
"srcs": attr.label_list(doc = "the list of source files that comprise the template", mandatory = True, allow_files = True),
"deps": attr.label_list(doc = "the standard dependency list", allow_files = True, cfg = "target"),
"types": attr.string_list(doc = "the list of generic types in the template that are required to be specified"),
"opt_types": attr.string_list(doc = "the list of generic types in the template that can but aren't required to be specified"),
"consts": attr.string_list(doc = "the list of constants in the template that are required to be specified"),
"opt_consts": attr.string_list(doc = "the list of constants in the template that can but aren't required to be specified"),
"_tool": attr.label(executable = True, cfg = "exec", default = Label("//tools/go_generics/go_merge")),
},
)
def _go_template_instance_impl(ctx):
info = ctx.attr.template[TemplateInfo]
output = ctx.outputs.out
# Check that all required types are defined.
for t in info.types:
if t not in ctx.attr.types:
fail("Missing value for type %s in %s" % (t, ctx.attr.template.label))
# Check that all defined types are expected by the template.
for t in ctx.attr.types:
if (t not in info.types) and (t not in info.opt_types):
fail("Type %s is not a parameter to %s" % (t, ctx.attr.template.label))
# Check that all required consts are defined.
for t in info.consts:
if t not in ctx.attr.consts:
fail("Missing value for constant %s in %s" % (t, ctx.attr.template.label))
# Check that all defined consts are expected by the template.
for t in ctx.attr.consts:
if (t not in info.consts) and (t not in info.opt_consts):
fail("Const %s is not a parameter to %s" % (t, ctx.attr.template.label))
# Build the argument list.
args = ["-i=%s" % info.template.path, "-o=%s" % output.path]
if ctx.attr.package:
args.append("-p=%s" % ctx.attr.package)
if len(ctx.attr.prefix) > 0:
args.append("-prefix=%s" % ctx.attr.prefix)
if len(ctx.attr.suffix) > 0:
args.append("-suffix=%s" % ctx.attr.suffix)
args += [("-t=%s=%s" % (p[0], p[1])) for p in ctx.attr.types.items()]
args += [("-c=%s=%s" % (p[0], p[1])) for p in ctx.attr.consts.items()]
args += [("-import=%s=%s" % (p[0], p[1])) for p in ctx.attr.imports.items()]
args += [("-in-substr=%s=%s" % (p[0], p[1])) for p in ctx.attr.input_substrs.items()]
args += [("-out-substr=%s=%s" % (p[0], p[1])) for p in ctx.attr.substrs.items()]
if ctx.attr.anon:
args.append("-anon")
ctx.actions.run(
inputs = [info.template],
outputs = [output],
mnemonic = "GoGenericsInstance",
progress_message = "Building Go template instance %s" % ctx.label,
arguments = args,
executable = ctx.executable._tool,
)
return [DefaultInfo(
files = depset([output]),
)]
go_template_instance = rule(
implementation = _go_template_instance_impl,
attrs = {
"template": attr.label(doc = "the label of the template to be instantiated", mandatory = True),
"prefix": attr.string(doc = "a prefix to be added to globals in the template"),
"suffix": attr.string(doc = "a suffix to be added to globals in the template"),
"types": attr.string_dict(doc = "the map from generic type names to concrete ones"),
"consts": attr.string_dict(doc = "the map from constant names to their values"),
"imports": attr.string_dict(doc = "the map from imports used in types/consts to their import paths"),
"input_substrs": attr.string_dict(doc = "the map from sub-strings to their replacements, applied just after reading the template code"),
"substrs": attr.string_dict(doc = "the map from sub-strings to their replacements, applied just before writing the template instance code"),
"anon": attr.bool(doc = "whether anoymous fields should be processed", mandatory = False, default = False),
"package": attr.string(doc = "the package for the generated source file", mandatory = False),
"out": attr.output(doc = "output file", mandatory = True),
"_tool": attr.label(executable = True, cfg = "exec", default = Label("//tools/go_generics")),
},
)
|