blob: 9076f0fcd49293fe5cbc50cb49edc407b56d5dbc [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
2"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
3
4<appendix id='hello-world-example'>
5 <title>Hello World Example</title>
6
7 <section id='bitbake-hello-world'>
8 <title>BitBake Hello World</title>
9
10 <para>
11 The simplest example commonly used to demonstrate any new
12 programming language or tool is the
13 "<ulink url="http://en.wikipedia.org/wiki/Hello_world_program">Hello World</ulink>"
14 example.
15 This appendix demonstrates, in tutorial form, Hello
16 World within the context of BitBake.
17 The tutorial describes how to create a new project
18 and the applicable metadata files necessary to allow
19 BitBake to build it.
20 </para>
21 </section>
22
23 <section id='example-obtaining-bitbake'>
24 <title>Obtaining BitBake</title>
25
26 <para>
27 See the
28 "<link linkend='obtaining-bitbake'>Obtaining BitBake</link>"
29 section for information on how to obtain BitBake.
30 Once you have the source code on your machine, the BitBake directory
31 appears as follows:
32 <literallayout class='monospaced'>
33 $ ls -al
34 total 100
35 drwxrwxr-x. 9 wmat wmat 4096 Jan 31 13:44 .
36 drwxrwxr-x. 3 wmat wmat 4096 Feb 4 10:45 ..
37 -rw-rw-r--. 1 wmat wmat 365 Nov 26 04:55 AUTHORS
38 drwxrwxr-x. 2 wmat wmat 4096 Nov 26 04:55 bin
39 drwxrwxr-x. 4 wmat wmat 4096 Jan 31 13:44 build
40 -rw-rw-r--. 1 wmat wmat 16501 Nov 26 04:55 ChangeLog
41 drwxrwxr-x. 2 wmat wmat 4096 Nov 26 04:55 classes
42 drwxrwxr-x. 2 wmat wmat 4096 Nov 26 04:55 conf
43 drwxrwxr-x. 3 wmat wmat 4096 Nov 26 04:55 contrib
44 -rw-rw-r--. 1 wmat wmat 17987 Nov 26 04:55 COPYING
45 drwxrwxr-x. 3 wmat wmat 4096 Nov 26 04:55 doc
46 -rw-rw-r--. 1 wmat wmat 69 Nov 26 04:55 .gitignore
47 -rw-rw-r--. 1 wmat wmat 849 Nov 26 04:55 HEADER
48 drwxrwxr-x. 5 wmat wmat 4096 Jan 31 13:44 lib
49 -rw-rw-r--. 1 wmat wmat 195 Nov 26 04:55 MANIFEST.in
Patrick Williamsc124f4f2015-09-15 14:41:29 -050050 -rw-rw-r--. 1 wmat wmat 2887 Nov 26 04:55 TODO
51 </literallayout>
52 </para>
53
54 <para>
55 At this point, you should have BitBake cloned to
56 a directory that matches the previous listing except for
57 dates and user names.
58 </para>
59 </section>
60
61 <section id='setting-up-the-bitbake-environment'>
62 <title>Setting Up the BitBake Environment</title>
63
64 <para>
65 First, you need to be sure that you can run BitBake.
66 Set your working directory to where your local BitBake
67 files are and run the following command:
68 <literallayout class='monospaced'>
69 $ ./bin/bitbake --version
70 BitBake Build Tool Core version 1.23.0, bitbake version 1.23.0
71 </literallayout>
72 The console output tells you what version you are running.
73 </para>
74
75 <para>
76 The recommended method to run BitBake is from a directory of your
77 choice.
78 To be able to run BitBake from any directory, you need to add the
79 executable binary to your binary to your shell's environment
80 <filename>PATH</filename> variable.
81 First, look at your current <filename>PATH</filename> variable
82 by entering the following:
83 <literallayout class='monospaced'>
84 $ echo $PATH
85 </literallayout>
86 Next, add the directory location for the BitBake binary to the
87 <filename>PATH</filename>.
88 Here is an example that adds the
89 <filename>/home/scott-lenovo/bitbake/bin</filename> directory
90 to the front of the <filename>PATH</filename> variable:
91 <literallayout class='monospaced'>
92 $ export PATH=/home/scott-lenovo/bitbake/bin:$PATH
93 </literallayout>
94 You should now be able to enter the <filename>bitbake</filename>
95 command from the command line while working from any directory.
96 </para>
97 </section>
98
99 <section id='the-hello-world-example'>
100 <title>The Hello World Example</title>
101
102 <para>
103 The overall goal of this exercise is to build a
104 complete "Hello World" example utilizing task and layer
105 concepts.
106 Because this is how modern projects such as OpenEmbedded and
107 the Yocto Project utilize BitBake, the example
108 provides an excellent starting point for understanding
109 BitBake.
110 </para>
111
112 <para>
113 To help you understand how to use BitBake to build targets,
114 the example starts with nothing but the <filename>bitbake</filename>
115 command, which causes BitBake to fail and report problems.
116 The example progresses by adding pieces to the build to
117 eventually conclude with a working, minimal "Hello World"
118 example.
119 </para>
120
121 <para>
122 While every attempt is made to explain what is happening during
123 the example, the descriptions cannot cover everything.
124 You can find further information throughout this manual.
125 Also, you can actively participate in the
126 <ulink url='http://lists.openembedded.org/mailman/listinfo/bitbake-devel'></ulink>
127 discussion mailing list about the BitBake build tool.
128 </para>
129
130 <note>
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500131 This example was inspired by and drew heavily from
132 <ulink url="http://www.mail-archive.com/yocto@yoctoproject.org/msg09379.html">Mailing List post - The BitBake equivalent of "Hello, World!"</ulink>.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500133 </note>
134
135 <para>
136 As stated earlier, the goal of this example
137 is to eventually compile "Hello World".
138 However, it is unknown what BitBake needs and what you have
139 to provide in order to achieve that goal.
140 Recall that BitBake utilizes three types of metadata files:
141 <link linkend='configuration-files'>Configuration Files</link>,
142 <link linkend='classes'>Classes</link>, and
143 <link linkend='recipes'>Recipes</link>.
144 But where do they go?
145 How does BitBake find them?
146 BitBake's error messaging helps you answer these types of questions
147 and helps you better understand exactly what is going on.
148 </para>
149
150 <para>
151 Following is the complete "Hello World" example.
152 </para>
153
154 <orderedlist>
155 <listitem><para><emphasis>Create a Project Directory:</emphasis>
156 First, set up a directory for the "Hello World" project.
157 Here is how you can do so in your home directory:
158 <literallayout class='monospaced'>
159 $ mkdir ~/hello
160 $ cd ~/hello
161 </literallayout>
162 This is the directory that BitBake will use to do all of
163 its work.
164 You can use this directory to keep all the metafiles needed
165 by BitBake.
166 Having a project directory is a good way to isolate your
167 project.
168 </para></listitem>
169 <listitem><para><emphasis>Run Bitbake:</emphasis>
170 At this point, you have nothing but a project directory.
171 Run the <filename>bitbake</filename> command and see what
172 it does:
173 <literallayout class='monospaced'>
174 $ bitbake
175 The BBPATH variable is not set and bitbake did not
176 find a conf/bblayers.conf file in the expected location.
177 Maybe you accidentally invoked bitbake from the wrong directory?
178 DEBUG: Removed the following variables from the environment:
179 GNOME_DESKTOP_SESSION_ID, XDG_CURRENT_DESKTOP,
180 GNOME_KEYRING_CONTROL, DISPLAY, SSH_AGENT_PID, LANG, no_proxy,
181 XDG_SESSION_PATH, XAUTHORITY, SESSION_MANAGER, SHLVL,
182 MANDATORY_PATH, COMPIZ_CONFIG_PROFILE, WINDOWID, EDITOR,
183 GPG_AGENT_INFO, SSH_AUTH_SOCK, GDMSESSION, GNOME_KEYRING_PID,
184 XDG_SEAT_PATH, XDG_CONFIG_DIRS, LESSOPEN, DBUS_SESSION_BUS_ADDRESS,
185 _, XDG_SESSION_COOKIE, DESKTOP_SESSION, LESSCLOSE, DEFAULTS_PATH,
186 UBUNTU_MENUPROXY, OLDPWD, XDG_DATA_DIRS, COLORTERM, LS_COLORS
187 </literallayout>
188 The majority of this output is specific to environment variables
189 that are not directly relevant to BitBake.
190 However, the very first message regarding the
191 <filename>BBPATH</filename> variable and the
192 <filename>conf/bblayers.conf</filename> file
193 is relevant.</para>
194 <para>
195 When you run BitBake, it begins looking for metadata files.
196 The
197 <link linkend='var-BBPATH'><filename>BBPATH</filename></link>
198 variable is what tells BitBake where to look for those files.
199 <filename>BBPATH</filename> is not set and you need to set it.
200 Without <filename>BBPATH</filename>, Bitbake cannot
201 find any configuration files (<filename>.conf</filename>)
202 or recipe files (<filename>.bb</filename>) at all.
203 BitBake also cannot find the <filename>bitbake.conf</filename>
204 file.
205 </para></listitem>
206 <listitem><para><emphasis>Setting <filename>BBPATH</filename>:</emphasis>
207 For this example, you can set <filename>BBPATH</filename>
208 in the same manner that you set <filename>PATH</filename>
209 earlier in the appendix.
210 You should realize, though, that it is much more flexible to set the
211 <filename>BBPATH</filename> variable up in a configuration
212 file for each project.</para>
213 <para>From your shell, enter the following commands to set and
214 export the <filename>BBPATH</filename> variable:
215 <literallayout class='monospaced'>
216 $ BBPATH="<replaceable>projectdirectory</replaceable>"
217 $ export BBPATH
218 </literallayout>
219 Use your actual project directory in the command.
220 BitBake uses that directory to find the metadata it needs for
221 your project.
222 <note>
223 When specifying your project directory, do not use the
224 tilde ("~") character as BitBake does not expand that character
225 as the shell would.
226 </note>
227 </para></listitem>
228 <listitem><para><emphasis>Run Bitbake:</emphasis>
229 Now that you have <filename>BBPATH</filename> defined, run
230 the <filename>bitbake</filename> command again:
231 <literallayout class='monospaced'>
232 $ bitbake
233 ERROR: Traceback (most recent call last):
234 File "/home/scott-lenovo/bitbake/lib/bb/cookerdata.py", line 163, in wrapped
235 return func(fn, *args)
236 File "/home/scott-lenovo/bitbake/lib/bb/cookerdata.py", line 173, in parse_config_file
237 return bb.parse.handle(fn, data, include)
238 File "/home/scott-lenovo/bitbake/lib/bb/parse/__init__.py", line 99, in handle
239 return h['handle'](fn, data, include)
240 File "/home/scott-lenovo/bitbake/lib/bb/parse/parse_py/ConfHandler.py", line 120, in handle
241 abs_fn = resolve_file(fn, data)
242 File "/home/scott-lenovo/bitbake/lib/bb/parse/__init__.py", line 117, in resolve_file
243 raise IOError("file %s not found in %s" % (fn, bbpath))
244 IOError: file conf/bitbake.conf not found in /home/scott-lenovo/hello
245
246 ERROR: Unable to parse conf/bitbake.conf: file conf/bitbake.conf not found in /home/scott-lenovo/hello
247 </literallayout>
248 This sample output shows that BitBake could not find the
249 <filename>conf/bitbake.conf</filename> file in the project
250 directory.
251 This file is the first thing BitBake must find in order
252 to build a target.
253 And, since the project directory for this example is
254 empty, you need to provide a <filename>conf/bitbake.conf</filename>
255 file.
256 </para></listitem>
257 <listitem><para><emphasis>Creating <filename>conf/bitbake.conf</filename>:</emphasis>
258 The <filename>conf/bitbake.conf</filename> includes a number of
259 configuration variables BitBake uses for metadata and recipe
260 files.
261 For this example, you need to create the file in your project directory
262 and define some key BitBake variables.
Brad Bishop316dfdd2018-06-25 12:45:53 -0400263 For more information on the <filename>bitbake.conf</filename> file,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500264 see
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500265 <ulink url='http://git.openembedded.org/bitbake/tree/conf/bitbake.conf'></ulink>.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500266 </para>
267 <para>Use the following commands to create the <filename>conf</filename>
268 directory in the project directory:
269 <literallayout class='monospaced'>
270 $ mkdir conf
271 </literallayout>
272 From within the <filename>conf</filename> directory, use
273 some editor to create the <filename>bitbake.conf</filename>
274 so that it contains the following:
275 <literallayout class='monospaced'>
Brad Bishop316dfdd2018-06-25 12:45:53 -0400276 <link linkend='var-PN'>PN</link> = "${@bb.parse.BBHandler.vars_from_file(d.getVar('FILE', False),d)[0] or 'defaultpkgname'}"
277 </literallayout>
278 <literallayout class='monospaced'>
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500279 TMPDIR = "${<link linkend='var-TOPDIR'>TOPDIR</link>}/tmp"
280 <link linkend='var-CACHE'>CACHE</link> = "${TMPDIR}/cache"
Brad Bishop316dfdd2018-06-25 12:45:53 -0400281 <link linkend='var-STAMP'>STAMP</link> = "${TMPDIR}/${PN}/stamps"
282 <link linkend='var-T'>T</link> = "${TMPDIR}/${PN}/work"
283 <link linkend='var-B'>B</link> = "${TMPDIR}/${PN}"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500284 </literallayout>
Brad Bishop316dfdd2018-06-25 12:45:53 -0400285 <note>
286 Without a value for <filename>PN</filename>, the
287 variables <filename>STAMP</filename>,
288 <filename>T</filename>, and <filename>B</filename>,
289 prevent more than one recipe from working. You can fix
290 this by either setting <filename>PN</filename> to have
291 a value similar to what OpenEmbedded and BitBake use
292 in the default <filename>bitbake.conf</filename> file
293 (see previous example). Or, by manually updating each
294 recipe to set <filename>PN</filename>. You will also
295 need to include <filename>PN</filename> as part of the
296 <filename>STAMP</filename>, <filename>T</filename>, and
297 <filename>B</filename> variable definitions in the
298 <filename>local.conf</filename> file.
299 </note>
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500300 The <filename>TMPDIR</filename> variable establishes a directory
Brad Bishop316dfdd2018-06-25 12:45:53 -0400301 that BitBake uses for build output and intermediate files other
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500302 than the cached information used by the
303 <link linkend='setscene'>Setscene</link> process.
304 Here, the <filename>TMPDIR</filename> directory is set to
305 <filename>hello/tmp</filename>.
306 <note><title>Tip</title>
307 You can always safely delete the <filename>tmp</filename>
308 directory in order to rebuild a BitBake target.
309 The build process creates the directory for you
310 when you run BitBake.
311 </note></para>
312 <para>For information about each of the other variables defined in this
313 example, click on the links to take you to the definitions in
314 the glossary.
315 </para></listitem>
316 <listitem><para><emphasis>Run Bitbake:</emphasis>
317 After making sure that the <filename>conf/bitbake.conf</filename>
318 file exists, you can run the <filename>bitbake</filename>
319 command again:
320 <literallayout class='monospaced'>
Brad Bishop316dfdd2018-06-25 12:45:53 -0400321 $ bitbake
322 ERROR: Traceback (most recent call last):
323 File "/home/scott-lenovo/bitbake/lib/bb/cookerdata.py", line 163, in wrapped
324 return func(fn, *args)
325 File "/home/scott-lenovo/bitbake/lib/bb/cookerdata.py", line 177, in _inherit
326 bb.parse.BBHandler.inherit(bbclass, "configuration INHERITs", 0, data)
327 File "/home/scott-lenovo/bitbake/lib/bb/parse/parse_py/BBHandler.py", line 92, in inherit
328 include(fn, file, lineno, d, "inherit")
329 File "/home/scott-lenovo/bitbake/lib/bb/parse/parse_py/ConfHandler.py", line 100, in include
330 raise ParseError("Could not %(error_out)s file %(fn)s" % vars(), oldfn, lineno)
331 ParseError: ParseError in configuration INHERITs: Could not inherit file classes/base.bbclass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500332
Brad Bishop316dfdd2018-06-25 12:45:53 -0400333 ERROR: Unable to parse base: ParseError in configuration INHERITs: Could not inherit file classes/base.bbclass
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500334 </literallayout>
335 In the sample output, BitBake could not find the
336 <filename>classes/base.bbclass</filename> file.
337 You need to create that file next.
338 </para></listitem>
339 <listitem><para><emphasis>Creating <filename>classes/base.bbclass</filename>:</emphasis>
340 BitBake uses class files to provide common code and functionality.
341 The minimally required class for BitBake is the
342 <filename>classes/base.bbclass</filename> file.
343 The <filename>base</filename> class is implicitly inherited by
344 every recipe.
345 BitBake looks for the class in the <filename>classes</filename>
346 directory of the project (i.e <filename>hello/classes</filename>
347 in this example).
348 </para>
349 <para>Create the <filename>classes</filename> directory as follows:
350 <literallayout class='monospaced'>
351 $ cd $HOME/hello
352 $ mkdir classes
353 </literallayout>
354 Move to the <filename>classes</filename> directory and then
355 create the <filename>base.bbclass</filename> file by inserting
356 this single line:
357 <literallayout class='monospaced'>
358 addtask build
359 </literallayout>
360 The minimal task that BitBake runs is the
361 <filename>do_build</filename> task.
362 This is all the example needs in order to build the project.
363 Of course, the <filename>base.bbclass</filename> can have much
364 more depending on which build environments BitBake is
365 supporting.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500366 </para></listitem>
367 <listitem><para><emphasis>Run Bitbake:</emphasis>
368 After making sure that the <filename>classes/base.bbclass</filename>
369 file exists, you can run the <filename>bitbake</filename>
370 command again:
371 <literallayout class='monospaced'>
372 $ bitbake
373 Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.
374 </literallayout>
375 BitBake is finally reporting no errors.
376 However, you can see that it really does not have anything
377 to do.
378 You need to create a recipe that gives BitBake something to do.
379 </para></listitem>
380 <listitem><para><emphasis>Creating a Layer:</emphasis>
381 While it is not really necessary for such a small example,
382 it is good practice to create a layer in which to keep your
383 code separate from the general metadata used by BitBake.
384 Thus, this example creates and uses a layer called "mylayer".
385 <note>
Brad Bishop316dfdd2018-06-25 12:45:53 -0400386 You can find additional information on layers in the
387 "<link linkend='layers'>Layers</link>" section.
388 </note></para>
389
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500390 <para>Minimally, you need a recipe file and a layer configuration
391 file in your layer.
392 The configuration file needs to be in the <filename>conf</filename>
393 directory inside the layer.
394 Use these commands to set up the layer and the <filename>conf</filename>
395 directory:
396 <literallayout class='monospaced'>
397 $ cd $HOME
398 $ mkdir mylayer
399 $ cd mylayer
400 $ mkdir conf
401 </literallayout>
402 Move to the <filename>conf</filename> directory and create a
403 <filename>layer.conf</filename> file that has the following:
404 <literallayout class='monospaced'>
405 BBPATH .= ":${<link linkend='var-LAYERDIR'>LAYERDIR</link>}"
406
407 <link linkend='var-BBFILES'>BBFILES</link> += "${LAYERDIR}/*.bb"
408
409 <link linkend='var-BBFILE_COLLECTIONS'>BBFILE_COLLECTIONS</link> += "mylayer"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600410 <link linkend='var-BBFILE_PATTERN'>BBFILE_PATTERN_mylayer</link> := "^${LAYERDIR_RE}/"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500411 </literallayout>
412 For information on these variables, click the links
413 to go to the definitions in the glossary.</para>
414 <para>You need to create the recipe file next.
415 Inside your layer at the top-level, use an editor and create
416 a recipe file named <filename>printhello.bb</filename> that
417 has the following:
418 <literallayout class='monospaced'>
419 <link linkend='var-DESCRIPTION'>DESCRIPTION</link> = "Prints Hello World"
420 <link linkend='var-PN'>PN</link> = 'printhello'
421 <link linkend='var-PV'>PV</link> = '1'
422
423 python do_build() {
424 bb.plain("********************");
425 bb.plain("* *");
426 bb.plain("* Hello, World! *");
427 bb.plain("* *");
428 bb.plain("********************");
429 }
430 </literallayout>
431 The recipe file simply provides a description of the
432 recipe, the name, version, and the <filename>do_build</filename>
433 task, which prints out "Hello World" to the console.
434 For more information on these variables, follow the links
435 to the glossary.
436 </para></listitem>
437 <listitem><para><emphasis>Run Bitbake With a Target:</emphasis>
438 Now that a BitBake target exists, run the command and provide
439 that target:
440 <literallayout class='monospaced'>
441 $ cd $HOME/hello
442 $ bitbake printhello
443 ERROR: no recipe files to build, check your BBPATH and BBFILES?
444
445 Summary: There was 1 ERROR message shown, returning a non-zero exit code.
446 </literallayout>
447 We have created the layer with the recipe and the layer
448 configuration file but it still seems that BitBake cannot
449 find the recipe.
450 BitBake needs a <filename>conf/bblayers.conf</filename> that
451 lists the layers for the project.
452 Without this file, BitBake cannot find the recipe.
453 </para></listitem>
454 <listitem><para><emphasis>Creating <filename>conf/bblayers.conf</filename>:</emphasis>
455 BitBake uses the <filename>conf/bblayers.conf</filename> file
456 to locate layers needed for the project.
457 This file must reside in the <filename>conf</filename> directory
458 of the project (i.e. <filename>hello/conf</filename> for this
459 example).</para>
460 <para>Set your working directory to the <filename>hello/conf</filename>
461 directory and then create the <filename>bblayers.conf</filename>
462 file so that it contains the following:
463 <literallayout class='monospaced'>
464 BBLAYERS ?= " \
465 /home/&lt;you&gt;/mylayer \
466 "
467 </literallayout>
468 You need to provide your own information for
469 <filename>you</filename> in the file.
470 </para></listitem>
471 <listitem><para><emphasis>Run Bitbake With a Target:</emphasis>
472 Now that you have supplied the <filename>bblayers.conf</filename>
473 file, run the <filename>bitbake</filename> command and provide
474 the target:
475 <literallayout class='monospaced'>
476 $ bitbake printhello
477 Parsing recipes: 100% |##################################################################################|
478 Time: 00:00:00
479 Parsing of 1 .bb files complete (0 cached, 1 parsed). 1 targets, 0 skipped, 0 masked, 0 errors.
480 NOTE: Resolving any missing task queue dependencies
481 NOTE: Preparing RunQueue
482 NOTE: Executing RunQueue Tasks
483 ********************
484 * *
485 * Hello, World! *
486 * *
487 ********************
488 NOTE: Tasks Summary: Attempted 1 tasks of which 0 didn't need to be rerun and all succeeded.
489 </literallayout>
490 BitBake finds the <filename>printhello</filename> recipe and
491 successfully runs the task.
492 <note>
493 After the first execution, re-running
494 <filename>bitbake printhello</filename> again will not
495 result in a BitBake run that prints the same console
496 output.
497 The reason for this is that the first time the
498 <filename>printhello.bb</filename> recipe's
499 <filename>do_build</filename> task executes
500 successfully, BitBake writes a stamp file for the task.
501 Thus, the next time you attempt to run the task
502 using that same <filename>bitbake</filename> command,
503 BitBake notices the stamp and therefore determines
504 that the task does not need to be re-run.
505 If you delete the <filename>tmp</filename> directory
506 or run <filename>bitbake -c clean printhello</filename>
507 and then re-run the build, the "Hello, World!" message will
508 be printed again.
509 </note>
510 </para></listitem>
511 </orderedlist>
512 </section>
513</appendix>