blob: 1451f3d1cfa1b6153a6e5a49bd95454457188cf6 [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
Serge Bazanski140bddc2020-06-05 21:01:19 +020035def _smalltown_initramfs_impl(ctx):
36 """
37 Generate an lz4-compressed initramfs based on a label/file list.
38 """
39
40 # Generate config file for gen_init_cpio that describes the initramfs to build.
41 cpio_list_name = ctx.label.name + ".cpio_list"
42 cpio_list = ctx.actions.declare_file(cpio_list_name)
43
44 # Start out with some standard initramfs device files.
45 cpio_list_content = [
46 "dir /dev 0755 0 0",
47 "nod /dev/console 0600 0 0 c 5 1",
48 "nod /dev/null 0644 0 0 c 1 3",
49 "nod /dev/kmsg 0644 0 0 c 1 11",
50 "nod /dev/ptmx 0644 0 0 c 5 2",
51 ]
52
53 # Find all directories that need to be created.
54 directories_needed = []
55 for _, p in ctx.attr.files.items():
56 if not p.startswith("/"):
57 fail("file {} invalid: must begin with /".format(p))
58
59 # Get all intermediate directories on path to file
60 parts = p.split("/")[1:-1]
61 directories_needed.append(parts)
62
63 # Extend with extra directories defined by user.
64 for p in ctx.attr.extra_dirs:
65 if not p.startswith("/"):
66 fail("directory {} invalid: must begin with /".format(p))
67
68 parts = p.split("/")[1:]
69 directories_needed.append(parts)
70
71 directories = []
72 for parts in directories_needed:
73 # Turn directory parts [usr, local, bin] into successive subpaths [/usr, /usr/local, /usr/local/bin].
74 last = ""
75 for part in parts:
76 last += "/" + part
77
78 # TODO(q3k): this is slow - this should be a set instead, but starlark doesn't implement them.
79 # For the amount of files we're dealing with this doesn't matter, but all stars are pointing towards this
80 # becoming accidentally quadratic at some point in the future.
81 if last not in directories:
82 directories.append(last)
83
84 # Append instructions to create directories.
85 # Serendipitously, the directories should already be in the right order due to us not using a set to create the
86 # list. They might not be in an elegant order (ie, if files [/foo/one/one, /bar, /foo/two/two] are request, the
87 # order will be [/foo, /foo/one, /bar, /foo/two]), but that's fine.
88 for d in directories:
89 cpio_list_content.append("dir {} 0755 0 0".format(d))
90
91 # Append instructions to add files.
92 inputs = []
93 for label, p in ctx.attr.files.items():
94 # Figure out if this is an executable.
95 is_executable = True
96
97 di = label[DefaultInfo]
98 if di.files_to_run.executable == None:
99 # Generated non-executable files will have DefaultInfo.files_to_run.executable == None
100 is_executable = False
101 elif di.files_to_run.executable.is_source:
102 # Source files will have executable.is_source == True
103 is_executable = False
104
105 # Ensure only single output is declared.
106 # If you hit this error, figure out a better logic to find what file you need, maybe looking at providers other
107 # than DefaultInfo.
108 files = di.files.to_list()
109 if len(files) > 1:
110 fail("file {} has more than one output: {}", p, files)
111 src = files[0]
112 inputs.append(src)
113
114 mode = "0755" if is_executable else "0444"
115
116 cpio_list_content.append("file {} {} {} 0 0".format(p, src.path, mode))
117
118 # Write cpio_list.
119 ctx.actions.write(cpio_list, "\n".join(cpio_list_content))
120
121 gen_init_cpio = ctx.executable._gen_init_cpio
122 savestdout = ctx.executable._savestdout
123 lz4 = ctx.executable._lz4
124
125 # Generate 'raw' (uncompressed) initramfs
126 initramfs_raw_name = ctx.label.name
127 initramfs_raw = ctx.actions.declare_file(initramfs_raw_name)
128 ctx.actions.run(
129 outputs = [initramfs_raw],
130 inputs = [cpio_list] + inputs,
131 tools = [savestdout, gen_init_cpio],
132 executable = savestdout,
133 arguments = [initramfs_raw.path, gen_init_cpio.path, cpio_list.path],
134 )
135
136 # Compress raw initramfs using lz4c.
137 initramfs_name = ctx.label.name + ".lz4"
138 initramfs = ctx.actions.declare_file(initramfs_name)
139 ctx.actions.run(
140 outputs = [initramfs],
141 inputs = [initramfs_raw],
142 tools = [savestdout, lz4],
143 executable = lz4.path,
144 arguments = ["-l", initramfs_raw.path, initramfs.path],
145 )
146
147 return [DefaultInfo(files = depset([initramfs]))]
148
149smalltown_initramfs = rule(
150 implementation = _smalltown_initramfs_impl,
151 doc = """
152 Build a Smalltown initramfs. The initramfs will contain a basic /dev directory and all the files specified by the
153 `files` attribute. Executable files will have their permissions set to 0755, non-executable files will have
154 their permissions set to 0444. All parent directories will be created with 0755 permissions.
155 """,
156 attrs = {
157 "files": attr.label_keyed_string_dict(
158 mandatory = True,
159 allow_files = True,
160 doc = """
161 Dictionary of Labels to String, placing a given Label's output file in the initramfs at the location
162 specified by the String value. The specified labels must only have a single output.
163 """,
Serge Bazanskic3ae7582020-06-08 17:15:26 +0200164 # Attach pure transition to ensure all binaries added to the initramfs are pure/static binaries.
165 cfg = build_pure_transition,
Serge Bazanski140bddc2020-06-05 21:01:19 +0200166 ),
167 "extra_dirs": attr.string_list(
168 default = [],
169 doc = """
170 Extra directories to create. These will be created in addition to all the directories required to
171 contain the files specified in the `files` attribute.
172 """,
173 ),
174
175 # Tools, implicit dependencies.
176 "_gen_init_cpio": attr.label(
177 default = Label("@linux//:gen_init_cpio"),
178 executable = True,
179 cfg = "host",
180 ),
181 "_lz4": attr.label(
182 default = Label("@com_github_lz4_lz4//programs:lz4"),
183 executable = True,
184 cfg = "host",
185 ),
186 "_savestdout": attr.label(
187 default = Label("//build/savestdout"),
188 executable = True,
189 cfg = "host",
190 ),
Serge Bazanskic3ae7582020-06-08 17:15:26 +0200191
192 # Allow for transitions to be attached to this rule.
193 "_whitelist_function_transition": attr.label(
194 default = "@bazel_tools//tools/whitelists/function_transition_whitelist",
195 ),
Serge Bazanski140bddc2020-06-05 21:01:19 +0200196 },
197)