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
|
def _go_template_impl(ctx):
input = ctx.files.srcs
output = ctx.outputs.out
args = ["-o=%s" % output.path] + [f.path for f in input]
ctx.actions.run(
inputs = input,
outputs = [output],
mnemonic = "GoGenericsTemplate",
progress_message = "Building Go template %s" % ctx.label,
arguments = args,
executable = ctx.executable._tool,
)
return struct(
types = ctx.attr.types,
opt_types = ctx.attr.opt_types,
consts = ctx.attr.consts,
opt_consts = ctx.attr.opt_consts,
deps = ctx.attr.deps,
file = output,
)
"""
Generates a Go template from a set of Go files.
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.
Args:
name: the name of the template.
srcs: the list of source files that comprise the template.
types: the list of generic types in the template that are required to be specified.
opt_types: the list of generic types in the template that can but aren't required to be specified.
consts: the list of constants in the template that are required to be specified.
opt_consts: the list of constants in the template that can but aren't required to be specified.
deps: the list of dependencies.
"""
go_template = rule(
implementation = _go_template_impl,
attrs = {
"srcs": attr.label_list(mandatory = True, allow_files = True),
"deps": attr.label_list(allow_files = True),
"types": attr.string_list(),
"opt_types": attr.string_list(),
"consts": attr.string_list(),
"opt_consts": attr.string_list(),
"_tool": attr.label(executable = True, cfg = "host", default = Label("//tools/go_generics/go_merge")),
},
outputs = {
"out": "%{name}_template.go",
},
)
def _go_template_instance_impl(ctx):
template = ctx.attr.template
output = ctx.outputs.out
# Check that all required types are defined.
for t in template.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 template.types) and (t not in template.opt_types):
fail("Type %s it not a parameter to %s" % (t, ctx.attr.template.label))
# Check that all required consts are defined.
for t in template.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 template.consts) and (t not in template.opt_consts):
fail("Const %s it not a parameter to %s" % (t, ctx.attr.template.label))
# Build the argument list.
args = ["-i=%s" % template.file.path, "-o=%s" % output.path]
args += ["-p=%s" % ctx.attr.package]
if len(ctx.attr.prefix) > 0:
args += ["-prefix=%s" % ctx.attr.prefix]
if len(ctx.attr.suffix) > 0:
args += ["-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()]
if ctx.attr.anon:
args += ["-anon"]
ctx.actions.run(
inputs = [template.file],
outputs = [output],
mnemonic = "GoGenericsInstance",
progress_message = "Building Go template instance %s" % ctx.label,
arguments = args,
executable = ctx.executable._tool,
)
# TODO: How can we get the dependencies out?
return struct(
files = depset([output]),
)
"""
Instantiates a Go template by replacing all generic types with concrete ones.
Args:
name: the name of the template instance.
template: the label of the template to be instatiated.
prefix: a prefix to be added to globals in the template.
suffix: a suffix to be added to global in the template.
types: the map from generic type names to concrete ones.
consts: the map from constant names to their values.
imports: the map from imports used in types/consts to their import paths.
package: the name of the package the instantiated template will be compiled into.
"""
go_template_instance = rule(
implementation = _go_template_instance_impl,
attrs = {
"template": attr.label(mandatory = True, providers = ["types"]),
"prefix": attr.string(),
"suffix": attr.string(),
"types": attr.string_dict(),
"consts": attr.string_dict(),
"imports": attr.string_dict(),
"anon": attr.bool(mandatory=False, default=False),
"package": attr.string(mandatory = True),
"out": attr.output(mandatory = True),
"_tool": attr.label(executable = True, cfg = "host", default = Label("//tools/go_generics")),
},
)
|