blob: e2885e507880033cc6eaf4dd546ff383934f1607 [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 """
21 return {
22 "@io_bazel_rules_go//go/config:pure": True,
23 "@io_bazel_rules_go//go/config:static": True,
24 }
25
26build_pure_transition = transition(
27 implementation = _build_pure_transition_impl,
28 inputs = [],
29 outputs = [
30 "@io_bazel_rules_go//go/config:pure",
31 "@io_bazel_rules_go//go/config:static",
32 ],
33)
34
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +020035def _build_static_transition_impl(settings, attr):
36 """
37 Transition that enables static builds with CGo and musl for Go binaries.
38 """
39 return {
40 "@io_bazel_rules_go//go/config:static": True,
41 "//command_line_option:crosstool_top": "//build/toolchain/musl-host-gcc:musl_host_cc_suite",
42 }
43
44build_static_transition = transition(
45 implementation = _build_static_transition_impl,
46 inputs = [],
47 outputs = [
48 "@io_bazel_rules_go//go/config:static",
49 "//command_line_option:crosstool_top",
50 ],
51)
52
Serge Bazanski140bddc2020-06-05 21:01:19 +020053def _smalltown_initramfs_impl(ctx):
54 """
55 Generate an lz4-compressed initramfs based on a label/file list.
56 """
57
58 # Generate config file for gen_init_cpio that describes the initramfs to build.
59 cpio_list_name = ctx.label.name + ".cpio_list"
60 cpio_list = ctx.actions.declare_file(cpio_list_name)
61
62 # Start out with some standard initramfs device files.
63 cpio_list_content = [
64 "dir /dev 0755 0 0",
65 "nod /dev/console 0600 0 0 c 5 1",
66 "nod /dev/null 0644 0 0 c 1 3",
67 "nod /dev/kmsg 0644 0 0 c 1 11",
68 "nod /dev/ptmx 0644 0 0 c 5 2",
69 ]
70
71 # Find all directories that need to be created.
72 directories_needed = []
73 for _, p in ctx.attr.files.items():
74 if not p.startswith("/"):
75 fail("file {} invalid: must begin with /".format(p))
76
77 # Get all intermediate directories on path to file
78 parts = p.split("/")[1:-1]
79 directories_needed.append(parts)
80
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +020081 for _, p in ctx.attr.files_cc.items():
82 if not p.startswith("/"):
83 fail("file {} invalid: must begin with /".format(p))
84
85 # Get all intermediate directories on path to file
86 parts = p.split("/")[1:-1]
87 directories_needed.append(parts)
88
Serge Bazanski140bddc2020-06-05 21:01:19 +020089 # Extend with extra directories defined by user.
90 for p in ctx.attr.extra_dirs:
91 if not p.startswith("/"):
92 fail("directory {} invalid: must begin with /".format(p))
93
94 parts = p.split("/")[1:]
95 directories_needed.append(parts)
96
97 directories = []
98 for parts in directories_needed:
99 # Turn directory parts [usr, local, bin] into successive subpaths [/usr, /usr/local, /usr/local/bin].
100 last = ""
101 for part in parts:
102 last += "/" + part
103
104 # TODO(q3k): this is slow - this should be a set instead, but starlark doesn't implement them.
105 # For the amount of files we're dealing with this doesn't matter, but all stars are pointing towards this
106 # becoming accidentally quadratic at some point in the future.
107 if last not in directories:
108 directories.append(last)
109
110 # Append instructions to create directories.
111 # Serendipitously, the directories should already be in the right order due to us not using a set to create the
112 # list. They might not be in an elegant order (ie, if files [/foo/one/one, /bar, /foo/two/two] are request, the
113 # order will be [/foo, /foo/one, /bar, /foo/two]), but that's fine.
114 for d in directories:
115 cpio_list_content.append("dir {} 0755 0 0".format(d))
116
117 # Append instructions to add files.
118 inputs = []
119 for label, p in ctx.attr.files.items():
120 # Figure out if this is an executable.
121 is_executable = True
122
123 di = label[DefaultInfo]
124 if di.files_to_run.executable == None:
125 # Generated non-executable files will have DefaultInfo.files_to_run.executable == None
126 is_executable = False
127 elif di.files_to_run.executable.is_source:
128 # Source files will have executable.is_source == True
129 is_executable = False
130
131 # Ensure only single output is declared.
132 # If you hit this error, figure out a better logic to find what file you need, maybe looking at providers other
133 # than DefaultInfo.
134 files = di.files.to_list()
135 if len(files) > 1:
136 fail("file {} has more than one output: {}", p, files)
137 src = files[0]
138 inputs.append(src)
139
140 mode = "0755" if is_executable else "0444"
141
142 cpio_list_content.append("file {} {} {} 0 0".format(p, src.path, mode))
143
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +0200144 for label, p in ctx.attr.files_cc.items():
145 # Figure out if this is an executable.
146 is_executable = True
147
148 di = label[DefaultInfo]
149 if di.files_to_run.executable == None:
150 # Generated non-executable files will have DefaultInfo.files_to_run.executable == None
151 is_executable = False
152 elif di.files_to_run.executable.is_source:
153 # Source files will have executable.is_source == True
154 is_executable = False
155
156 # Ensure only single output is declared.
157 # If you hit this error, figure out a better logic to find what file you need, maybe looking at providers other
158 # than DefaultInfo.
159 files = di.files.to_list()
160 if len(files) > 1:
161 fail("file {} has more than one output: {}", p, files)
162 src = files[0]
163 inputs.append(src)
164
165 mode = "0755" if is_executable else "0444"
166
167 cpio_list_content.append("file {} {} {} 0 0".format(p, src.path, mode))
168
Serge Bazanski140bddc2020-06-05 21:01:19 +0200169 # Write cpio_list.
170 ctx.actions.write(cpio_list, "\n".join(cpio_list_content))
171
172 gen_init_cpio = ctx.executable._gen_init_cpio
173 savestdout = ctx.executable._savestdout
174 lz4 = ctx.executable._lz4
175
176 # Generate 'raw' (uncompressed) initramfs
177 initramfs_raw_name = ctx.label.name
178 initramfs_raw = ctx.actions.declare_file(initramfs_raw_name)
179 ctx.actions.run(
180 outputs = [initramfs_raw],
181 inputs = [cpio_list] + inputs,
182 tools = [savestdout, gen_init_cpio],
183 executable = savestdout,
184 arguments = [initramfs_raw.path, gen_init_cpio.path, cpio_list.path],
185 )
186
187 # Compress raw initramfs using lz4c.
188 initramfs_name = ctx.label.name + ".lz4"
189 initramfs = ctx.actions.declare_file(initramfs_name)
190 ctx.actions.run(
191 outputs = [initramfs],
192 inputs = [initramfs_raw],
193 tools = [savestdout, lz4],
194 executable = lz4.path,
195 arguments = ["-l", initramfs_raw.path, initramfs.path],
196 )
197
198 return [DefaultInfo(files = depset([initramfs]))]
199
200smalltown_initramfs = rule(
201 implementation = _smalltown_initramfs_impl,
202 doc = """
203 Build a Smalltown initramfs. The initramfs will contain a basic /dev directory and all the files specified by the
204 `files` attribute. Executable files will have their permissions set to 0755, non-executable files will have
205 their permissions set to 0444. All parent directories will be created with 0755 permissions.
206 """,
207 attrs = {
208 "files": attr.label_keyed_string_dict(
209 mandatory = True,
210 allow_files = True,
211 doc = """
212 Dictionary of Labels to String, placing a given Label's output file in the initramfs at the location
213 specified by the String value. The specified labels must only have a single output.
214 """,
Serge Bazanskic3ae7582020-06-08 17:15:26 +0200215 # Attach pure transition to ensure all binaries added to the initramfs are pure/static binaries.
216 cfg = build_pure_transition,
Serge Bazanski140bddc2020-06-05 21:01:19 +0200217 ),
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +0200218 "files_cc": attr.label_keyed_string_dict(
219 allow_files = True,
220 doc = """
221 Special case of 'files' for compilation targets that need to be built with the musl toolchain like
222 go_binary targets which need cgo or cc_binary targets.
223 """,
224 # Attach static transition to all files_cc inputs to ensure they are built with musl and static.
225 cfg = build_static_transition,
226 ),
Serge Bazanski140bddc2020-06-05 21:01:19 +0200227 "extra_dirs": attr.string_list(
228 default = [],
229 doc = """
230 Extra directories to create. These will be created in addition to all the directories required to
231 contain the files specified in the `files` attribute.
232 """,
233 ),
234
235 # Tools, implicit dependencies.
236 "_gen_init_cpio": attr.label(
237 default = Label("@linux//:gen_init_cpio"),
238 executable = True,
239 cfg = "host",
240 ),
241 "_lz4": attr.label(
242 default = Label("@com_github_lz4_lz4//programs:lz4"),
243 executable = True,
244 cfg = "host",
245 ),
246 "_savestdout": attr.label(
247 default = Label("//build/savestdout"),
248 executable = True,
249 cfg = "host",
250 ),
Serge Bazanskic3ae7582020-06-08 17:15:26 +0200251
252 # Allow for transitions to be attached to this rule.
253 "_whitelist_function_transition": attr.label(
254 default = "@bazel_tools//tools/whitelists/function_transition_whitelist",
255 ),
Serge Bazanski140bddc2020-06-05 21:01:19 +0200256 },
257)