blob: e0aa548ccf50e331f046a102d82013281872525b [file] [log] [blame]
Serge Bazanski140bddc2020-06-05 21:01:19 +02001# Copyright 2020 The Monogon Project Authors.
2#
3# SPDX-License-Identifier: Apache-2.0
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Serge Bazanskic3ae7582020-06-08 17:15:26 +020017def _build_pure_transition_impl(settings, attr):
18 """
19 Transition that enables pure, static build of Go binaries.
20 """
Serge Bazanski30021af2023-06-20 13:30:11 +020021 race = settings['@io_bazel_rules_go//go/config:race']
22 pure = not race
23
Serge Bazanskic3ae7582020-06-08 17:15:26 +020024 return {
Serge Bazanski30021af2023-06-20 13:30:11 +020025 "@io_bazel_rules_go//go/config:pure": pure,
Serge Bazanskic3ae7582020-06-08 17:15:26 +020026 "@io_bazel_rules_go//go/config:static": True,
27 }
28
29build_pure_transition = transition(
30 implementation = _build_pure_transition_impl,
Serge Bazanski30021af2023-06-20 13:30:11 +020031 inputs = [
32 "@io_bazel_rules_go//go/config:race",
33 ],
Serge Bazanskic3ae7582020-06-08 17:15:26 +020034 outputs = [
35 "@io_bazel_rules_go//go/config:pure",
36 "@io_bazel_rules_go//go/config:static",
37 ],
38)
39
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +020040def _build_static_transition_impl(settings, attr):
41 """
42 Transition that enables static builds with CGo and musl for Go binaries.
43 """
44 return {
45 "@io_bazel_rules_go//go/config:static": True,
Leopoldbc93c2b2023-01-14 13:12:23 +010046 "//command_line_option:platforms": "//build/platforms:linux_amd64_static",
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +020047 }
48
49build_static_transition = transition(
50 implementation = _build_static_transition_impl,
51 inputs = [],
52 outputs = [
53 "@io_bazel_rules_go//go/config:static",
Leopoldbc93c2b2023-01-14 13:12:23 +010054 "//command_line_option:platforms",
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +020055 ],
56)
57
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +010058FSSpecInfo = provider(
59 "Provides parts of an FSSpec used to assemble filesystem images",
60 fields = {
61 "spec": "File containing the partial FSSpec as prototext",
62 "referenced": "Files (potentially) referenced by the spec",
Serge Bazanski140bddc2020-06-05 21:01:19 +020063 },
64)
Lorenz Brun6b13bf12021-01-26 19:54:24 +010065
Serge Bazanskia3938142022-04-04 17:04:47 +020066def _fsspec_core_impl(ctx, tool, output_file):
Lorenz Brun6b13bf12021-01-26 19:54:24 +010067 """
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +010068 _fsspec_core_impl implements the core of an fsspec-based rule. It takes
Serge Bazanskia3938142022-04-04 17:04:47 +020069 input from the `files`,`files_cc`, `symlinks` and `fsspecs` attributes
70 and calls `tool` with the `-out` parameter pointing to `output_file`
71 and paths to all fsspecs as positional arguments.
Lorenz Brun6b13bf12021-01-26 19:54:24 +010072 """
Lorenz Brun6b13bf12021-01-26 19:54:24 +010073 fs_spec_name = ctx.label.name + ".prototxt"
74 fs_spec = ctx.actions.declare_file(fs_spec_name)
75
76 fs_files = []
77 inputs = []
78 for label, p in ctx.attr.files.items() + ctx.attr.files_cc.items():
79 if not p.startswith("/"):
80 fail("file {} invalid: must begin with /".format(p))
81
82 # Figure out if this is an executable.
83 is_executable = True
84
85 di = label[DefaultInfo]
86 if di.files_to_run.executable == None:
87 # Generated non-executable files will have DefaultInfo.files_to_run.executable == None
88 is_executable = False
89 elif di.files_to_run.executable.is_source:
90 # Source files will have executable.is_source == True
91 is_executable = False
92
93 # Ensure only single output is declared.
94 # If you hit this error, figure out a better logic to find what file you need, maybe looking at providers other
95 # than DefaultInfo.
96 files = di.files.to_list()
97 if len(files) > 1:
98 fail("file {} has more than one output: {}", p, files)
99 src = files[0]
100 inputs.append(src)
101
102 mode = 0o555 if is_executable else 0o444
103 fs_files.append(struct(path = p, source_path = src.path, mode = mode, uid = 0, gid = 0))
104
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100105 fs_symlinks = []
106 for target, p in ctx.attr.symlinks.items():
107 fs_symlinks.append(struct(path = p, target_path = target))
108
Serge Bazanskia3938142022-04-04 17:04:47 +0200109 fs_spec_content = struct(file = fs_files, directory = [], symbolic_link = fs_symlinks)
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100110 ctx.actions.write(fs_spec, proto.encode_text(fs_spec_content))
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100111
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100112 extra_specs = []
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100113
114 for fsspec in ctx.attr.fsspecs:
Lorenz Brund1bc4a62022-09-12 16:45:18 +0000115 if FSSpecInfo in fsspec:
116 fsspecInfo = fsspec[FSSpecInfo]
117 extra_specs.append(fsspecInfo.spec)
118 for f in fsspecInfo.referenced:
119 inputs.append(f)
120 else:
121 # Raw .fsspec prototext. No referenced data allowed.
122 di = fsspec[DefaultInfo]
123 extra_specs += di.files.to_list()
Serge Bazanskia3938142022-04-04 17:04:47 +0200124
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100125 ctx.actions.run(
126 outputs = [output_file],
127 inputs = [fs_spec] + inputs + extra_specs,
128 tools = [tool],
129 executable = tool,
130 arguments = ["-out", output_file.path, fs_spec.path] + [s.path for s in extra_specs],
131 )
132 return
133
134def _node_initramfs_impl(ctx):
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100135 initramfs_name = ctx.label.name + ".cpio.lz4"
136 initramfs = ctx.actions.declare_file(initramfs_name)
137
Serge Bazanskia3938142022-04-04 17:04:47 +0200138 _fsspec_core_impl(ctx, ctx.executable._mkcpio, initramfs)
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100139
140 # TODO(q3k): Document why this is needed
141 return [DefaultInfo(runfiles = ctx.runfiles(files = [initramfs]), files = depset([initramfs]))]
142
143node_initramfs = rule(
144 implementation = _node_initramfs_impl,
145 doc = """
146 Build a node initramfs. The initramfs will contain a basic /dev directory and all the files specified by the
147 `files` attribute. Executable files will have their permissions set to 0755, non-executable files will have
148 their permissions set to 0444. All parent directories will be created with 0755 permissions.
149 """,
150 attrs = {
151 "files": attr.label_keyed_string_dict(
152 mandatory = True,
153 allow_files = True,
154 doc = """
155 Dictionary of Labels to String, placing a given Label's output file in the initramfs at the location
156 specified by the String value. The specified labels must only have a single output.
157 """,
158 # Attach pure transition to ensure all binaries added to the initramfs are pure/static binaries.
159 cfg = build_pure_transition,
160 ),
161 "files_cc": attr.label_keyed_string_dict(
162 allow_files = True,
163 doc = """
164 Special case of 'files' for compilation targets that need to be built with the musl toolchain like
165 go_binary targets which need cgo or cc_binary targets.
166 """,
167 # Attach static transition to all files_cc inputs to ensure they are built with musl and static.
168 cfg = build_static_transition,
169 ),
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100170 "symlinks": attr.string_dict(
171 default = {},
172 doc = """
173 Symbolic links to create. Similar format as in files and files_cc, so the target of the symlink is the
174 key and the value of it is the location of the symlink itself. Only raw strings are allowed as targets,
175 labels are not permitted. Include the file using files or files_cc, then symlink to its location.
176 """,
177 ),
178 "fsspecs": attr.label_list(
179 default = [],
180 doc = """
181 List of file system specs (metropolis.node.build.fsspec.FSSpec) to also include in the resulting image.
182 These will be merged with all other given attributes.
183 """,
184 providers = [FSSpecInfo],
Serge Bazanskia3938142022-04-04 17:04:47 +0200185 allow_files = True,
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100186 ),
187
188 # Tool
189 "_mkcpio": attr.label(
190 default = Label("//metropolis/node/build/mkcpio"),
191 executable = True,
192 cfg = "exec",
193 ),
194
195 # Allow for transitions to be attached to this rule.
196 "_whitelist_function_transition": attr.label(
197 default = "@bazel_tools//tools/whitelists/function_transition_whitelist",
198 ),
199 },
200)
201
202def _erofs_image_impl(ctx):
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100203 fs_name = ctx.label.name + ".img"
204 fs_out = ctx.actions.declare_file(fs_name)
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100205
Serge Bazanskia3938142022-04-04 17:04:47 +0200206 _fsspec_core_impl(ctx, ctx.executable._mkerofs, fs_out)
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100207
208 return [DefaultInfo(files = depset([fs_out]))]
209
210erofs_image = rule(
211 implementation = _erofs_image_impl,
212 doc = """
213 Build an EROFS. All files specified in files, files_cc and all specified symlinks will be contained.
214 Executable files will have their permissions set to 0555, non-executable files will have
215 their permissions set to 0444. All parent directories will be created with 0555 permissions.
216 """,
217 attrs = {
218 "files": attr.label_keyed_string_dict(
219 mandatory = True,
220 allow_files = True,
221 doc = """
222 Dictionary of Labels to String, placing a given Label's output file in the EROFS at the location
223 specified by the String value. The specified labels must only have a single output.
224 """,
225 # Attach pure transition to ensure all binaries added to the initramfs are pure/static binaries.
226 cfg = build_pure_transition,
227 ),
228 "files_cc": attr.label_keyed_string_dict(
229 allow_files = True,
230 doc = """
231 Special case of 'files' for compilation targets that need to be built with the musl toolchain like
232 go_binary targets which need cgo or cc_binary targets.
233 """,
234 # Attach static transition to all files_cc inputs to ensure they are built with musl and static.
235 cfg = build_static_transition,
236 ),
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100237 "symlinks": attr.string_dict(
238 default = {},
239 doc = """
240 Symbolic links to create. Similar format as in files and files_cc, so the target of the symlink is the
241 key and the value of it is the location of the symlink itself. Only raw strings are allowed as targets,
242 labels are not permitted. Include the file using files or files_cc, then symlink to its location.
243 """,
244 ),
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100245 "fsspecs": attr.label_list(
246 default = [],
247 doc = """
248 List of file system specs (metropolis.node.build.fsspec.FSSpec) to also include in the resulting image.
249 These will be merged with all other given attributes.
250 """,
251 providers = [FSSpecInfo],
Serge Bazanskia3938142022-04-04 17:04:47 +0200252 allow_files = True,
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100253 ),
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100254
255 # Tools, implicit dependencies.
256 "_mkerofs": attr.label(
257 default = Label("//metropolis/node/build/mkerofs"),
258 executable = True,
259 cfg = "host",
260 ),
261
262 # Allow for transitions to be attached to this rule.
263 "_whitelist_function_transition": attr.label(
264 default = "@bazel_tools//tools/whitelists/function_transition_whitelist",
265 ),
266 },
267)
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100268
269# VerityConfig is emitted by verity_image, and contains a file enclosing a
270# singular dm-verity target table.
271VerityConfig = provider(
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100272 "Configuration necessary to mount a single dm-verity target.",
273 fields = {
274 "table": "A file containing the dm-verity target table. See: https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/verity.html",
275 },
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100276)
277
278def _verity_image_impl(ctx):
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100279 """
280 Create a new file containing the source image data together with the Verity
281 metadata appended to it, and provide an associated DeviceMapper Verity target
282 table in a separate file, through VerityConfig provider.
283 """
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100284
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100285 # Run mkverity.
286 image = ctx.actions.declare_file(ctx.attr.name + ".img")
287 table = ctx.actions.declare_file(ctx.attr.name + ".dmt")
288 ctx.actions.run(
289 mnemonic = "GenVerityImage",
290 progress_message = "Generating a dm-verity image",
291 inputs = [ctx.file.source],
292 outputs = [
293 image,
294 table,
295 ],
296 executable = ctx.file._mkverity,
297 arguments = [
298 "-input=" + ctx.file.source.path,
299 "-output=" + image.path,
300 "-table=" + table.path,
301 "-data_alias=" + ctx.attr.rootfs_partlabel,
302 "-hash_alias=" + ctx.attr.rootfs_partlabel,
303 ],
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100304 )
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100305
306 return [
307 DefaultInfo(
308 files = depset([image]),
309 runfiles = ctx.runfiles(files = [image]),
310 ),
311 VerityConfig(
312 table = table,
313 ),
314 ]
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100315
316verity_image = rule(
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100317 implementation = _verity_image_impl,
318 doc = """
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100319 Build a dm-verity target image by appending Verity metadata to the source
320 image. A corresponding dm-verity target table will be made available
321 through VerityConfig provider.
322 """,
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100323 attrs = {
324 "source": attr.label(
325 doc = "A source image.",
326 allow_single_file = True,
327 ),
328 "rootfs_partlabel": attr.string(
329 doc = "GPT partition label of the rootfs to be used with dm-mod.create.",
330 default = "PARTLABEL=METROPOLIS-SYSTEM",
331 ),
332 "_mkverity": attr.label(
333 doc = "The mkverity executable needed to generate the image.",
334 default = "//metropolis/node/build/mkverity",
335 allow_single_file = True,
336 executable = True,
337 cfg = "host",
338 ),
339 },
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100340)