blob: 24f2af79177abd28d99d61d5e1791ad12b682bc6 [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.
Lorenz Brun54a5a052023-10-02 16:40:11 +020016load("@bazel_skylib//lib:paths.bzl", "paths")
Serge Bazanski140bddc2020-06-05 21:01:19 +020017
Serge Bazanskic3ae7582020-06-08 17:15:26 +020018def _build_pure_transition_impl(settings, attr):
19 """
20 Transition that enables pure, static build of Go binaries.
21 """
Tim Windelschmidt3a171d12024-12-09 23:51:23 +010022 race = settings["@io_bazel_rules_go//go/config:race"]
Serge Bazanski30021af2023-06-20 13:30:11 +020023 pure = not race
24
Serge Bazanskic3ae7582020-06-08 17:15:26 +020025 return {
Serge Bazanski30021af2023-06-20 13:30:11 +020026 "@io_bazel_rules_go//go/config:pure": pure,
Serge Bazanskic3ae7582020-06-08 17:15:26 +020027 "@io_bazel_rules_go//go/config:static": True,
Tim Windelschmidt3a171d12024-12-09 23:51:23 +010028 "//command_line_option:platforms": "//build/platforms:linux_amd64_static",
Serge Bazanskic3ae7582020-06-08 17:15:26 +020029 }
30
31build_pure_transition = transition(
32 implementation = _build_pure_transition_impl,
Serge Bazanski30021af2023-06-20 13:30:11 +020033 inputs = [
34 "@io_bazel_rules_go//go/config:race",
35 ],
Serge Bazanskic3ae7582020-06-08 17:15:26 +020036 outputs = [
37 "@io_bazel_rules_go//go/config:pure",
38 "@io_bazel_rules_go//go/config:static",
Tim Windelschmidt3a171d12024-12-09 23:51:23 +010039 "//command_line_option:platforms",
Serge Bazanskic3ae7582020-06-08 17:15:26 +020040 ],
41)
42
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +020043def _build_static_transition_impl(settings, attr):
44 """
45 Transition that enables static builds with CGo and musl for Go binaries.
46 """
47 return {
48 "@io_bazel_rules_go//go/config:static": True,
Leopoldbc93c2b2023-01-14 13:12:23 +010049 "//command_line_option:platforms": "//build/platforms:linux_amd64_static",
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +020050 }
51
52build_static_transition = transition(
53 implementation = _build_static_transition_impl,
54 inputs = [],
55 outputs = [
56 "@io_bazel_rules_go//go/config:static",
Leopoldbc93c2b2023-01-14 13:12:23 +010057 "//command_line_option:platforms",
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +020058 ],
59)
60
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +010061FSSpecInfo = provider(
62 "Provides parts of an FSSpec used to assemble filesystem images",
63 fields = {
64 "spec": "File containing the partial FSSpec as prototext",
65 "referenced": "Files (potentially) referenced by the spec",
Serge Bazanski140bddc2020-06-05 21:01:19 +020066 },
67)
Lorenz Brun6b13bf12021-01-26 19:54:24 +010068
Serge Bazanskia3938142022-04-04 17:04:47 +020069def _fsspec_core_impl(ctx, tool, output_file):
Lorenz Brun6b13bf12021-01-26 19:54:24 +010070 """
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +010071 _fsspec_core_impl implements the core of an fsspec-based rule. It takes
Serge Bazanskia3938142022-04-04 17:04:47 +020072 input from the `files`,`files_cc`, `symlinks` and `fsspecs` attributes
73 and calls `tool` with the `-out` parameter pointing to `output_file`
74 and paths to all fsspecs as positional arguments.
Lorenz Brun6b13bf12021-01-26 19:54:24 +010075 """
Lorenz Brun6b13bf12021-01-26 19:54:24 +010076 fs_spec_name = ctx.label.name + ".prototxt"
77 fs_spec = ctx.actions.declare_file(fs_spec_name)
78
79 fs_files = []
80 inputs = []
81 for label, p in ctx.attr.files.items() + ctx.attr.files_cc.items():
82 if not p.startswith("/"):
83 fail("file {} invalid: must begin with /".format(p))
84
85 # Figure out if this is an executable.
86 is_executable = True
87
88 di = label[DefaultInfo]
89 if di.files_to_run.executable == None:
90 # Generated non-executable files will have DefaultInfo.files_to_run.executable == None
91 is_executable = False
92 elif di.files_to_run.executable.is_source:
93 # Source files will have executable.is_source == True
94 is_executable = False
95
96 # Ensure only single output is declared.
97 # If you hit this error, figure out a better logic to find what file you need, maybe looking at providers other
98 # than DefaultInfo.
99 files = di.files.to_list()
100 if len(files) > 1:
101 fail("file {} has more than one output: {}", p, files)
102 src = files[0]
103 inputs.append(src)
104
105 mode = 0o555 if is_executable else 0o444
106 fs_files.append(struct(path = p, source_path = src.path, mode = mode, uid = 0, gid = 0))
107
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100108 fs_symlinks = []
109 for target, p in ctx.attr.symlinks.items():
110 fs_symlinks.append(struct(path = p, target_path = target))
111
Serge Bazanskia3938142022-04-04 17:04:47 +0200112 fs_spec_content = struct(file = fs_files, directory = [], symbolic_link = fs_symlinks)
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100113 ctx.actions.write(fs_spec, proto.encode_text(fs_spec_content))
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100114
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100115 extra_specs = []
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100116
117 for fsspec in ctx.attr.fsspecs:
Lorenz Brund1bc4a62022-09-12 16:45:18 +0000118 if FSSpecInfo in fsspec:
119 fsspecInfo = fsspec[FSSpecInfo]
120 extra_specs.append(fsspecInfo.spec)
121 for f in fsspecInfo.referenced:
122 inputs.append(f)
123 else:
124 # Raw .fsspec prototext. No referenced data allowed.
125 di = fsspec[DefaultInfo]
126 extra_specs += di.files.to_list()
Serge Bazanskia3938142022-04-04 17:04:47 +0200127
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100128 ctx.actions.run(
129 outputs = [output_file],
130 inputs = [fs_spec] + inputs + extra_specs,
131 tools = [tool],
132 executable = tool,
133 arguments = ["-out", output_file.path, fs_spec.path] + [s.path for s in extra_specs],
134 )
135 return
136
137def _node_initramfs_impl(ctx):
Lorenz Brun62f1d362023-11-14 16:18:24 +0100138 initramfs_name = ctx.label.name + ".cpio.zst"
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100139 initramfs = ctx.actions.declare_file(initramfs_name)
140
Serge Bazanskia3938142022-04-04 17:04:47 +0200141 _fsspec_core_impl(ctx, ctx.executable._mkcpio, initramfs)
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100142
143 # TODO(q3k): Document why this is needed
144 return [DefaultInfo(runfiles = ctx.runfiles(files = [initramfs]), files = depset([initramfs]))]
145
146node_initramfs = rule(
147 implementation = _node_initramfs_impl,
148 doc = """
149 Build a node initramfs. The initramfs will contain a basic /dev directory and all the files specified by the
150 `files` attribute. Executable files will have their permissions set to 0755, non-executable files will have
151 their permissions set to 0444. All parent directories will be created with 0755 permissions.
152 """,
153 attrs = {
154 "files": attr.label_keyed_string_dict(
155 mandatory = True,
156 allow_files = True,
157 doc = """
158 Dictionary of Labels to String, placing a given Label's output file in the initramfs at the location
159 specified by the String value. The specified labels must only have a single output.
160 """,
161 # Attach pure transition to ensure all binaries added to the initramfs are pure/static binaries.
162 cfg = build_pure_transition,
163 ),
164 "files_cc": attr.label_keyed_string_dict(
165 allow_files = True,
166 doc = """
167 Special case of 'files' for compilation targets that need to be built with the musl toolchain like
168 go_binary targets which need cgo or cc_binary targets.
169 """,
170 # Attach static transition to all files_cc inputs to ensure they are built with musl and static.
171 cfg = build_static_transition,
172 ),
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100173 "symlinks": attr.string_dict(
174 default = {},
175 doc = """
176 Symbolic links to create. Similar format as in files and files_cc, so the target of the symlink is the
177 key and the value of it is the location of the symlink itself. Only raw strings are allowed as targets,
178 labels are not permitted. Include the file using files or files_cc, then symlink to its location.
179 """,
180 ),
181 "fsspecs": attr.label_list(
182 default = [],
183 doc = """
Tim Windelschmidtc2290c22024-08-15 19:56:00 +0200184 List of file system specs (osbase.build.fsspec.FSSpec) to also include in the resulting image.
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100185 These will be merged with all other given attributes.
186 """,
187 providers = [FSSpecInfo],
Serge Bazanskia3938142022-04-04 17:04:47 +0200188 allow_files = True,
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100189 ),
190
191 # Tool
192 "_mkcpio": attr.label(
Tim Windelschmidtc2290c22024-08-15 19:56:00 +0200193 default = Label("//osbase/build/mkcpio"),
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100194 executable = True,
195 cfg = "exec",
196 ),
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100197 },
198)
199
200def _erofs_image_impl(ctx):
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100201 fs_name = ctx.label.name + ".img"
202 fs_out = ctx.actions.declare_file(fs_name)
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100203
Serge Bazanskia3938142022-04-04 17:04:47 +0200204 _fsspec_core_impl(ctx, ctx.executable._mkerofs, fs_out)
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100205
206 return [DefaultInfo(files = depset([fs_out]))]
207
208erofs_image = rule(
209 implementation = _erofs_image_impl,
210 doc = """
211 Build an EROFS. All files specified in files, files_cc and all specified symlinks will be contained.
212 Executable files will have their permissions set to 0555, non-executable files will have
213 their permissions set to 0444. All parent directories will be created with 0555 permissions.
214 """,
215 attrs = {
216 "files": attr.label_keyed_string_dict(
217 mandatory = True,
218 allow_files = True,
219 doc = """
220 Dictionary of Labels to String, placing a given Label's output file in the EROFS at the location
221 specified by the String value. The specified labels must only have a single output.
222 """,
223 # Attach pure transition to ensure all binaries added to the initramfs are pure/static binaries.
224 cfg = build_pure_transition,
225 ),
226 "files_cc": attr.label_keyed_string_dict(
227 allow_files = True,
228 doc = """
229 Special case of 'files' for compilation targets that need to be built with the musl toolchain like
230 go_binary targets which need cgo or cc_binary targets.
231 """,
232 # Attach static transition to all files_cc inputs to ensure they are built with musl and static.
233 cfg = build_static_transition,
234 ),
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100235 "symlinks": attr.string_dict(
236 default = {},
237 doc = """
238 Symbolic links to create. Similar format as in files and files_cc, so the target of the symlink is the
239 key and the value of it is the location of the symlink itself. Only raw strings are allowed as targets,
240 labels are not permitted. Include the file using files or files_cc, then symlink to its location.
241 """,
242 ),
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100243 "fsspecs": attr.label_list(
244 default = [],
245 doc = """
Tim Windelschmidtc2290c22024-08-15 19:56:00 +0200246 List of file system specs (osbase.build.fsspec.FSSpec) to also include in the resulting image.
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100247 These will be merged with all other given attributes.
248 """,
249 providers = [FSSpecInfo],
Serge Bazanskia3938142022-04-04 17:04:47 +0200250 allow_files = True,
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100251 ),
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100252
253 # Tools, implicit dependencies.
254 "_mkerofs": attr.label(
Tim Windelschmidtc2290c22024-08-15 19:56:00 +0200255 default = Label("//osbase/build/mkerofs"),
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100256 executable = True,
257 cfg = "host",
258 ),
Lorenz Brun6b13bf12021-01-26 19:54:24 +0100259 },
260)
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100261
262# VerityConfig is emitted by verity_image, and contains a file enclosing a
263# singular dm-verity target table.
264VerityConfig = provider(
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100265 "Configuration necessary to mount a single dm-verity target.",
266 fields = {
267 "table": "A file containing the dm-verity target table. See: https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/verity.html",
268 },
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100269)
270
271def _verity_image_impl(ctx):
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100272 """
273 Create a new file containing the source image data together with the Verity
274 metadata appended to it, and provide an associated DeviceMapper Verity target
275 table in a separate file, through VerityConfig provider.
276 """
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100277
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100278 # Run mkverity.
279 image = ctx.actions.declare_file(ctx.attr.name + ".img")
280 table = ctx.actions.declare_file(ctx.attr.name + ".dmt")
281 ctx.actions.run(
282 mnemonic = "GenVerityImage",
Tim Windelschmidt83f0aa42024-12-09 20:34:33 +0100283 progress_message = "Generating a dm-verity image: {}".format(image.short_path),
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100284 inputs = [ctx.file.source],
285 outputs = [
286 image,
287 table,
288 ],
289 executable = ctx.file._mkverity,
290 arguments = [
291 "-input=" + ctx.file.source.path,
292 "-output=" + image.path,
293 "-table=" + table.path,
294 "-data_alias=" + ctx.attr.rootfs_partlabel,
295 "-hash_alias=" + ctx.attr.rootfs_partlabel,
296 ],
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100297 )
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100298
299 return [
300 DefaultInfo(
301 files = depset([image]),
302 runfiles = ctx.runfiles(files = [image]),
303 ),
304 VerityConfig(
305 table = table,
306 ),
307 ]
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100308
309verity_image = rule(
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100310 implementation = _verity_image_impl,
311 doc = """
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100312 Build a dm-verity target image by appending Verity metadata to the source
313 image. A corresponding dm-verity target table will be made available
314 through VerityConfig provider.
315 """,
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100316 attrs = {
317 "source": attr.label(
318 doc = "A source image.",
319 allow_single_file = True,
320 ),
321 "rootfs_partlabel": attr.string(
322 doc = "GPT partition label of the rootfs to be used with dm-mod.create.",
Lorenz Brun35fcf032023-06-29 04:15:58 +0200323 default = "PARTLABEL=METROPOLIS-SYSTEM-X",
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100324 ),
325 "_mkverity": attr.label(
326 doc = "The mkverity executable needed to generate the image.",
Tim Windelschmidtc2290c22024-08-15 19:56:00 +0200327 default = "//osbase/build/mkverity",
Lorenz Brunb6a9d3c2022-01-27 18:56:20 +0100328 allow_single_file = True,
329 executable = True,
330 cfg = "host",
331 ),
332 },
Mateusz Zalegae2bf5742022-01-25 19:36:08 +0100333)