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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
|
:orphan:
.. _image_authoring:
Authoring System Images
=======================
This section forms a guide to creating system images with BuildStream.
Building a Linux image
----------------------
.. note::
This page does *not* list all files required for the project, it
merely comments on noteworthy sections. See `this repository
<https://gitlab.com/tlater/image-test>`_ for the full project.
Setting up the project
~~~~~~~~~~~~~~~~~~~~~~
To create an image, we will want to use the x86image plugin from the
``bst-external`` repository. The ``bst-external`` repository is a
collection of plugins that are either too niche or unpolished to
include as plugins in the main repository, but are useful for various
purposes.
If you have not already, install the latest version of this
repository:
.. code:: bash
git clone https://gitlab.com/BuildStream/bst-external.git
cd bst-external
pip3 install .
This should make bst-external plugins available to buildstream. To use
the x86image and docker plugins in our project, we need to set up
plugins in our ``project.conf``:
.. literalinclude:: image/project.conf
:caption: project.conf
:linenos:
:language: yaml
:lines: -17
We also set aliases for all project pages we will be fetching sources
from, should we later want to change their location (e.g. when we
decide that we want to mirror the files in our datacenter for
performance reasons):
.. literalinclude:: image/project.conf
:caption: project.conf (continued)
:language: yaml
:lineno-start: 17
:linenos:
:lines: 18-
Base System
~~~~~~~~~~~
The base system will be used to *build* the image and the project, but
it won't be a part of the final result. It should contain everything
that is required to build both the project and the tools required to
create an image.
The x86image plugin requires a specific set of tools to create an
image. To make using this plugin easier, we provide an alpine-based
base system using docker that contains all required tools:
.. literalinclude:: image/elements/base.bst
:caption: elements/base.bst
:language: yaml
:linenos:
This image only provides musl-libc and busybox for building, should
your project require GNU tools it will be simpler to create your own
and ensure that your base system includes the following packages:
* parted
* mtools
* e2fsprogs
* dosfstools
* syslinux
* nasm
* autoconf
* gcc
* g++
* make
* gawk
* bc
* linux-headers
Image Contents
~~~~~~~~~~~~~~
The final linux image will consist of several elements that can be
broadly summarized in four scopes:
1. A sysroot
2. An initramfs
3. A Linux kernel
4. Project-specific elements
A sysroot
+++++++++
Before we create the elements to make an actual image, we will set up
the sysroot that will form the user space. For this tutorial, we will
create a very simple system.
The sysroot must contain everything required to run a Linux system -
this will usually be `GNU coreutils
<https://www.gnu.org/software/coreutils/coreutils.html>`_ or `BusyBox
<https://www.busybox.net/about.html>`_ - and any runtime dependencies,
if these tools are not statically linked - often `glibc
<https://www.gnu.org/software/libc/>`_ or
`musl <https://www.musl-libc.org/>`_.
For this tutorial we will build a BusyBox + musl system:
.. code:: yaml
kind: stack
description: The image contents
depends:
- contents/busybox.bst
A keen eye may notice that this does not include a ``musl.bst``
dependency - this is because the busybox element itself is set to
run-depend on musl, which we use later to stage sysroot content, but
not the base system.
For the specifics of the included elements, refer to the accompanying
project repository.
An initramfs
++++++++++++
Now that we have defined the basic sysroot we can also set up an
`initramfs <https://en.wikipedia.org/wiki/Initial_ramdisk>`_ - we do
this now, because we have defined the most basic root file system that
will be booted into (for the sake of brevity we will not set up any
kernel modules, otherwise these should be built first and included in
the initramfs).
For our initramfs we will want an ``init`` and ``shutdown`` script,
and a copy of the sysroot created previously. We start with an
initramfs-scripts element:
.. literalinclude:: image/elements/image/initramfs/initramfs-scripts.bst
:caption: elements/image/initramfs/initramfs-scripts.bst
:language: yaml
:linenos:
This will simply place the ``init`` and ``shutdown`` scripts located
in ``files/initramfs-scripts`` in ``/sbin``, where they can later be
found and executed.
We then define our initramfs as the intersection between our
initramfs-scripts and sysroot content:
.. literalinclude:: image/elements/image/initramfs/initramfs.bst
:caption: elements/image/initramfs/initramfs.bst
:language: yaml
:linenos:
Finally we create an element that produces the cpio archive and
compress it using gzip:
.. literalinclude:: image/elements/image/initramfs/initramfs-gz.bst
:caption: elements/image/initramfs/initramfs-gz.bst
:language: yaml
:linenos:
A Linux kernel
++++++++++++++
Now that our final environment is set up, we create the Linux kernel
that will drive it. Setup for this is a little less intricate since it
only involves building a single project:
.. literalinclude:: image/elements/image/linux.bst
:caption: elements/image/linux.bst
:language: yaml
:linenos:
:lines: -16
.. literalinclude:: image/elements/image/linux.bst
:caption: elements/image/linux.bst (continued)
:language: yaml
:lineno-start: 272
:linenos:
:lines: 272-
The main complexity in compiling a Linux kernel is its configuration;
the 'correct' settings depend a lot on the project. The remaining
configuration should be quite portable to other builds, however, and
simply deals with placing files in the correct locations.
Project-specific elements
+++++++++++++++++++++++++
Finally, our project-specific files should be included. For a real
project, this may be an installer, 'rescue' applications such as
parted, distribution-specific files or similar.
In our case, we will include a ``hello`` script that simply prints
``Hello World!``, as is tradition:
.. literalinclude:: image/elements/contents/hello.bst
:caption: elements/contents/hello.bst
:language: yaml
:linenos:
We also update the ``contents.bst`` file to include our project
target:
.. literalinclude:: image/elements/contents.bst
:caption: elements/contents.bst
:language: yaml
:linenos:
While our ``hello`` element run-depends on busybox, our contents
*must* include a working set of coreutils - we make this explicit by
also depending on busybox.
Creating the image
~~~~~~~~~~~~~~~~~~
Now that all image content is defined, we can create the elements that
actually build the image. The x86image plugin requires two elements:
- A base system that contains the tools to create an image
- An element that contains the system we want to create an image of
We already have ``base.bst``, which conveniently contains all tools we
need (we could have used a separate base system to create the image),
but we still need to create a system element.
For the system element, we simply collect the image content elements
and their runtime dependencies:
.. literalinclude:: image/elements/image/system.bst
:caption: elements/image/system.bst
:language: yaml
:linenos:
We can now define our image element - we start by depending on the
above elements:
.. literalinclude:: image/elements/image-x86_64.bst
:caption: elements/image-x86_64.bst
:language: yaml
:linenos:
:lines: -12
We then set a few parameters to suit our system:
.. literalinclude:: image/elements/image-x86_64.bst
:caption: elements/image-x86_64.bst (continued)
:language: yaml
:lineno-start: 16
:linenos:
:lines: 16-31
The correct values for these parameters will depend on the specific
image created, but for this project the following values were used:
boot-size
The size of ``/boot`` as created by ``image/system.bst`` - the
system can be inspected using ``bst checkout``.
rootfs-size
The size of ``/`` as created by ``image/system.bst``.
sector-size
The default size of 512 should work in most cases, your
requirements may differ.
swap-size
The desired size for the swap partition.
kernel-args
The kernel arguments - the image plugin will by default create the
following ``/etc/fstab``:
.. code::
/dev/sda2 / ext4 defaults,rw,noatime 0 1
/dev/sda1 /boot vfat defaults 0 2
/dev/sda3 none swap defaults 0 0
Hence we specify ``root=/dev/sda2`` and ``rootfstype=ext4``.
``image/initramfs-scripts.bst`` defines our init script as
``/sbin/init``, hence we set ``init=/sbin/init``.
Finally, qemu (which we will use to try out this image)
requires our console to be on ttyS0, so we specify
``console=ttyS0``.
kernel-name
The default name of the kernel name as created by
``image/linux.bst`` is ``vmlinuz-4.14.3``, and since this is easier
than renaming it we specify that value.
The final configuration specifies which dependencies to use as the
base/system elements, and creates a script to launch our image using
qemu:
.. literalinclude:: image/elements/image-x86_64.bst
:caption: elements/image-x86_64.bst (continued)
:language: yaml
:lineno-start: 32
:linenos:
:lines: 32-
Building the image
~~~~~~~~~~~~~~~~~~
We have now defined everything required to build a basic Linux
image. With bst and the bst-external plugin repository installed, we
can now build and boot our image.
We first run ``bst track --deps all image-x86_64.bst`` to determine
references for all sources used to build this project. We then run
``bst build image-x86_64.bst`` to build and finally ``bst checkout
image-x86_64.bst image`` to retrieve our finalized image.
To test the result, simply run ``cd image/ && ./run-in-qemu.sh``
(perhaps as root), and the image should boot.
|