<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="https://www.schuam.de/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.schuam.de/" rel="alternate" type="text/html" /><updated>2025-05-27T08:47:08+02:00</updated><id>https://www.schuam.de/feed.xml</id><title type="html">schuam</title><author><name>Andreas Schuster</name></author><entry><title type="html">Zephyr Shell on NUCLEO-G0B1RE</title><link href="https://www.schuam.de/en/posts/d5acef08de.html" rel="alternate" type="text/html" title="Zephyr Shell on NUCLEO-G0B1RE" /><published>2025-05-26T00:00:00+02:00</published><updated>2025-05-27T00:00:00+02:00</updated><id>https://www.schuam.de/en/posts/zephyr-shell-on-nucleo-g0b1e</id><content type="html" xml:base="https://www.schuam.de/en/posts/d5acef08de.html"><![CDATA[<h1 id="tldr">tl;dr</h1>

<p><a href="https://www.zephyrproject.org">Zephyr</a> <a class="citation" href="#zephyr2025zephyrProject">[1]</a> includes a <a href="https://docs.zephyrproject.org/4.1.0/services/shell/index.html">shell</a> <a class="citation" href="#zephyr2025docsShell">[2]</a> subsystem, which is a pretty cool feature.
Unfortunately, the default device tree for the NUCLEO-G0B1RE board configures
the shell to use <code class="language-plaintext highlighter-rouge">usart2</code> on pins <code class="language-plaintext highlighter-rouge">PA2</code> and <code class="language-plaintext highlighter-rouge">PA3</code> of the STM32G0B1
microcontroller. The issue with this setup is that these pins are also
connected to a USART interface of the ST-LINK controller on the board. As a
result, this configuration effectively makes it impossible to use Zephyr’s
shell.</p>

<p>There are (at least) two solutions to solve the problem:</p>

<ol>
  <li>You can adjust the device tree (s. the <a href="#use-an-overlay-file">Use an Overlay
File</a> section).</li>
  <li>You can deactivate the ST-LINK controller (s the <a href="#deactivate-the-st-link-after-flashing">Deactivate the ST-LINK After Flashing</a>).</li>
</ol>

<h1 id="backgroundproblem">Background/Problem</h1>

<p>I wanted to try something out using Zephyr’s shell, and since I had a
NUCLEO-G0B1RE lying around, I decided to use that board for my tests. What was
supposed to be a quick task turned into a bit of a lengthy debugging session.</p>

<p>Here’s what happened:</p>

<p>I have a Zephyr workspace directory on my computer that I use for quick tests
like this. It looks like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>zephyr_workspace/
├── bootloader
├── build
├── .cache
├── modules
├── tools
├── .venv
├── .west
├── zephyr        <span class="c"># Zephyr source code (v4.1.0 commit 21b20de1e)</span>
├── .env
└── README.md
</code></pre></div></div>

<p>Within that workspace directory, I built Zephyr’s <code class="language-plaintext highlighter-rouge">shell_module</code> example:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>zephyr_workspace
<span class="nb">source</span> .venv/bin/activate
west build <span class="nt">-p</span> always <span class="nt">-b</span> nucleo_g0b1re/stm32g0b1xx zephyr/samples/subsys/shell/shell_module/
</code></pre></div></div>

<p>A quick look into the file <code class="language-plaintext highlighter-rouge">build/zephyr/zephyr.dts</code> revealed, that the default
device tree for the STM32G0B1 controller on the NUCLEO-G0B1RE board uses
<code class="language-plaintext highlighter-rouge">usart2</code> for the shell, and that pin <code class="language-plaintext highlighter-rouge">PA2</code> is used for TX and pin <code class="language-plaintext highlighter-rouge">PA3</code> is used
for RX.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># zephyr.dts</span>
<span class="o">(</span>...<span class="o">)</span>
	chosen <span class="o">{</span>
		zephyr,flash-controller <span class="o">=</span> &amp;flash<span class="p">;</span>
		zephyr,console <span class="o">=</span> &amp;usart2<span class="p">;</span>
		zephyr,shell-uart <span class="o">=</span> &amp;usart2<span class="p">;</span>
		zephyr,uart-mcumgr <span class="o">=</span> &amp;usart2<span class="p">;</span>
		zephyr,sram <span class="o">=</span> &amp;sram0<span class="p">;</span>
		zephyr,flash <span class="o">=</span> &amp;flash0<span class="p">;</span>
		zephyr,code-partition <span class="o">=</span> &amp;slot0_partition<span class="p">;</span>
		zephyr,canbus <span class="o">=</span> &amp;fdcan1<span class="p">;</span>
	<span class="o">}</span><span class="p">;</span>
<span class="o">(</span>...<span class="o">)</span>
		usart2: serial@40004400 <span class="o">{</span>
			compatible <span class="o">=</span> <span class="s2">"st,stm32-usart"</span>, <span class="s2">"st,stm32-uart"</span><span class="p">;</span>
			reg <span class="o">=</span> &lt; 0x40004400 0x400 <span class="o">&gt;</span><span class="p">;</span>
			clocks <span class="o">=</span> &lt; &amp;rcc 0x3c 0x20000 <span class="o">&gt;</span><span class="p">;</span>
			resets <span class="o">=</span> &lt; &amp;rctl 0x591 <span class="o">&gt;</span><span class="p">;</span>
			interrupts <span class="o">=</span> &lt; 0x1c 0x0 <span class="o">&gt;</span><span class="p">;</span>
			status <span class="o">=</span> <span class="s2">"okay"</span><span class="p">;</span>
			pinctrl-0 <span class="o">=</span> &lt; &amp;usart2_tx_pa2 &amp;usart2_rx_pa3 <span class="o">&gt;</span><span class="p">;</span>
			pinctrl-names <span class="o">=</span> <span class="s2">"default"</span><span class="p">;</span>
			current-speed <span class="o">=</span> &lt; 0x1c200 <span class="o">&gt;</span><span class="p">;</span>
		<span class="o">}</span><span class="p">;</span>
<span class="o">(</span>...<span class="o">)</span>
</code></pre></div></div>

<p>A quick look at the NUCLEO-G0B1RE schematics showed that pin <code class="language-plaintext highlighter-rouge">PA2</code> of the
microcontroller is connected to pin 6 of connector <code class="language-plaintext highlighter-rouge">CN10</code>, and pin <code class="language-plaintext highlighter-rouge">PA3</code> to pin
34 of the same connector. So, I hooked up a small UART-to-USB transceiver board
to these pins (plus GND), connected that board to my computer, and then
connected the NUCLEO-G0B1RE via its onboard USB port.</p>

<p>Once everything was wired up, I launched <code class="language-plaintext highlighter-rouge">minicom</code> (a serial communication
program on Linux) and configured it to use the USB interface of the UART-to-USB
board. Then I flashed the project onto the NUCLEO-G0B1RE using <code class="language-plaintext highlighter-rouge">west</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>west flash <span class="nt">--runner</span> openocd
</code></pre></div></div>

<p>At first the output of the board in <code class="language-plaintext highlighter-rouge">minicom</code> looked promissing:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>uart:~<span class="err">$</span>
</code></pre></div></div>

<p>I did get the shell prompt. But when I tried to interact with it, absolutely
nothing happened. I couldn’t type any commands. And that’s where the debugging
began:</p>

<ul>
  <li>I double-checked all my connections on the board.</li>
  <li>I deleted the <code class="language-plaintext highlighter-rouge">build</code> directory, rebuilt the project, and flashed it again.
Just to rule out any build issues.</li>
  <li>Since I had a second NUCLEO-G0B1RE on hand, I repeated the same steps on that
board. Still no success.</li>
  <li>I looked through the source code of the example project and inspected the
device tree of the built project, but nothing obvious stood out.</li>
</ul>

<p>The first time I tried this, I wasn’t at home and didn’t have access to an
oscilloscope to probe the communication lines, so I decided to put the testing
on hold. A few days later, when I finally had the chance to examine the
signals, I noticed something odd: when sending commands to the NUCLEO-G0B1RE,
the voltage at pin <code class="language-plaintext highlighter-rouge">PA3</code> didn’t toggle between 3.3V and 0V as expected during
UART communication. It only dropped slightly, just a few millivolts below 3.3V.</p>

<p>And that’s when it hit me: the NUCLEO-G0B1RE has an onboard ST-LINK module used
to flash the STM32G0B1 microcontroller. Maybe the to ST-LINK controller and the
STM32G0B1 are not only connected via JTAG, but also via UART. A closer look at
the schematics confirmed my suspicion. The ST-LINK can communicate with the
STM32G0B1 via the usart2 interface. As long as the ST-LINK is powered and
active, its TX line holds the RX pin of usart2 on the STM32G0B1 high. That’s
why the commands sent from my computer didn’t get to the microcontroller.</p>

<h1 id="solution">Solution</h1>

<p>There are (at least) two solutions to this problem.</p>

<h2 id="use-an-overlay-file">Use an Overlay File</h2>

<p>To work around the issue, you can use an overlay file to configure a different
USART interface on the STM32G0B1. Create a file named <code class="language-plaintext highlighter-rouge">nucleo_g0b1re.overlay</code>
in the <code class="language-plaintext highlighter-rouge">zephyr/samples/subsys/shell/shell_module/boards/</code> directory with the
following content:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ <span class="o">{</span>
	chosen <span class="o">{</span>
		zephyr,console <span class="o">=</span> &amp;usart1<span class="p">;</span>
		zephyr,shell-uart <span class="o">=</span> &amp;usart1<span class="p">;</span>
		zephyr,uart-mcumgr <span class="o">=</span> &amp;usart1<span class="p">;</span>
	<span class="o">}</span><span class="p">;</span>
<span class="o">}</span><span class="p">;</span>
</code></pre></div></div>

<p>Then, rebuild the project using the same command as before. If you check the
file <code class="language-plaintext highlighter-rouge">build/zephyr/zephyr.dts</code> again, you’ll see the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>...<span class="o">)</span>
		zephyr,console <span class="o">=</span> &amp;usart1<span class="p">;</span>
		zephyr,shell-uart <span class="o">=</span> &amp;usart1<span class="p">;</span>
		zephyr,uart-mcumgr <span class="o">=</span> &amp;usart1<span class="p">;</span>
<span class="o">(</span>...<span class="o">)</span>
			pinctrl-0 <span class="o">=</span> &lt; &amp;usart1_tx_pc4 &amp;usart1_rx_pc5 <span class="o">&gt;</span><span class="p">;</span>
<span class="o">(</span>...<span class="o">)</span>
</code></pre></div></div>

<p>So, you’ll need to change the pins your UART-to-USB board is connected to. Pin
<code class="language-plaintext highlighter-rouge">PC4</code> connects to pin 35 of connector <code class="language-plaintext highlighter-rouge">CN10</code>, and pin <code class="language-plaintext highlighter-rouge">PC5</code> connects to pin 37
of the same connector.</p>

<p>After changing the connections, flash the project again using the command from
above. You’ll see the same output in <code class="language-plaintext highlighter-rouge">minicom</code> as before, but this time, you
can actually interact with the shell:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>uart:~$ help
Please press the &lt;Tab&gt; button to see all available commands.
You can also use the &lt;Tab&gt; button to prompt or auto-complete all commands or its subcommands.
You can try to call commands with &lt;-h&gt; or &lt;--help&gt; parameter for more information.

Shell supports following meta-keys:
  Ctrl + (a key from: abcdefklnpuw)
  Alt  + (a key from: bf)
Please refer to shell documentation for more details.

Available commands:
  bypass              : Bypass shell
  clear               : Clear screen.
  date                : Date commands
  demo                : Demo commands
  device              : Device commands
  devmem              : Read/write physical memory
                        Usage:
                        Read memory at address with optional width:
                        devmem &lt;address&gt; [&lt;width&gt;]
                        Write memory at address with mandatory width and value:
                        devmem &lt;address&gt; &lt;width&gt; &lt;value&gt;
  dynamic             : Demonstrate dynamic command usage.
  help                : Prints the help message.
  history             : Command history.
  kernel              : Kernel commands
  log                 : Commands for controlling logger
  log_test            : Log test
  rem                 : Ignore lines beginning with 'rem '
  resize              : Console gets terminal screen size or assumes default in
                        case the readout fails. It must be executed after each
                        terminal width change to ensure correct text display.
  retval              : Print return value of most recent command
  section_cmd         : Demo command using section for subcommand registration
  shell               : Useful, not Unix-like shell commands.
  shell_uart_release  : Uninitialize shell instance and release uart, start
                        loopback on uart. Shell instance is reinitialized when
                        'x' is pressed
  stats               : Stats commands
  version             : Show kernel version
uart:~$
</code></pre></div></div>

<p>Alternatively, it should be possible to configure usart2 to use different pins,
but I haven’t checked whether the NUCLEO-G0B1RE board has any available pins
that won’t interfere with other connections.</p>

<h2 id="deactivate-the-st-link-after-flashing">Deactivate the ST-LINK After Flashing</h2>

<p>There’s another way to work around the problem without changing any software.
You can put the ST-LINK controller into reset after flashing the STM32G0B1.
This is what the pin header <code class="language-plaintext highlighter-rouge">JP1</code> next to the USB port is for. Simply place
a jumper across the two pins to short them.</p>

<p>However, this also disables the power supply to the STM32G0B1, which of corse
isn’t ideal. To avoid that, you need to move the jumper on pin header <code class="language-plaintext highlighter-rouge">JP2</code>. By
default (at least on my boards), the jumper on <code class="language-plaintext highlighter-rouge">JP2</code> shorts the two pins
labeled “STLK”. Move this jumper all the way to the other side, onto the pins
labeled “CHG”.</p>

<p>It’s probably best to do this while the board is powered off. So the sequence
would be:</p>

<ol>
  <li>Remove power by unplugging the USB cable.</li>
  <li>Move the jumper on pin header <code class="language-plaintext highlighter-rouge">JP2</code> to the new position.</li>
  <li>Reconnect the UART-to-USB board to pins 6 (<code class="language-plaintext highlighter-rouge">PA2</code>) and 34 (<code class="language-plaintext highlighter-rouge">PA3</code>) of
connector <code class="language-plaintext highlighter-rouge">CN10</code>.</li>
  <li>Power up the board.</li>
  <li>Build the project without using any board overlay file.</li>
  <li>Flash the STM32G0B1 controller.</li>
  <li>Place a jumper on <code class="language-plaintext highlighter-rouge">JP1</code>.</li>
  <li>Reset the STM32G0B1 by pressing the reset button on the board.</li>
</ol>

<p>Now you should see the shell prompt in <code class="language-plaintext highlighter-rouge">minicom</code> and be able to interact with
it.</p>

<h1 id="conclusion">Conclusion</h1>

<p>I’m not entirely sure why the default device tree setup for the NUCLEO-G0B1RE
board in Zephyr results in a configuration that makes using the shell
impossible. I’ll try to investigate the reason behind this setup and see if it
can be changed. But until then, you’ll need to make some adjustments to either
the software or hardware to get Zephyr’s shell_module sample running.</p>

<p>Well, I hope this was helpful!
<br />
\</p>
<h1 id="change-log">Change Log</h1>

<ul>
  <li>
    <p>2025-05-27:</p>

    <ul>
      <li>Made some corrections about the communication between the ST-LINK
controller and the STM32G0B1.</li>
    </ul>
  </li>
</ul>

<p><br />
<br />
Take care,<br />
Andreas</p>

<hr />

<h1 id="references">References</h1>
<ol class="bibliography"><li><span id="zephyr2025zephyrProject">Zephyr Project, “Zephyr.” [Online]. Available at: https://www.zephyrproject.org. [Accessed: 26-May-2025].</span></li>
<li><span id="zephyr2025docsShell">Zephyr Project members and individual contributors, “Zephyr Docs: Shell.” [Online]. Available at: https://docs.zephyrproject.org/4.1.0/services/shell/index.html. [Accessed: 26-May-2025].</span></li></ol>]]></content><author><name>Andreas Schuster</name></author><category term="hacking" /><category term="stm32," /><category term="zephyr," /><category term="shell," /><category term="nucleo" /><summary type="html"><![CDATA[tl;dr]]></summary></entry><entry><title type="html">Run a Root Filesystem for a Raspberry Pi 3 Model B+</title><link href="https://www.schuam.de/en/posts/170bdeda3b.html" rel="alternate" type="text/html" title="Run a Root Filesystem for a Raspberry Pi 3 Model B+" /><published>2025-02-01T00:00:00+01:00</published><updated>2025-02-01T00:00:00+01:00</updated><id>https://www.schuam.de/en/posts/run-a-root-filesystem-for-a-raspberry-pi-3-model-b+</id><content type="html" xml:base="https://www.schuam.de/en/posts/170bdeda3b.html"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>This is the seventh post in a series of posts (in a tutorial) about building an
embedded Linux system for a Raspberry Pi 3 Model B+. A summary post for the
tutorial can be found <a href="https://schuam.de/en/posts/13369f2b32.html">here</a>. The
summary post also talks about …</p>

<ul>
  <li>what is required to follow along,</li>
  <li>the <code class="language-plaintext highlighter-rouge">&lt;project/root/dir&gt;</code>,</li>
  <li>some terms that will be used throughout the series,</li>
  <li>a link to an article that explains how to prepare the SD card,</li>
  <li>some hints on how to use the UART interface of the Raspberry Pi.</li>
</ul>

<p>Now it’s finally time to put it all together and run a entire embedded Linux
system on a Raspberry Pi 3 Model B+. Let’s get started.</p>

<h1 id="final-preparations-of-the-root-filesystem">Final Preparations of the Root Filesystem</h1>

<p>After <a href="https://schuam.de/en/posts/e02855a6e3.html">Building a Root Filesystem for a Raspberry Pi 3 Model
B+</a> with busybox, we have to make
some minor adjustments to it.</p>

<p>First of all let’s add a root user to the system. This user won’t have a
password right now. You should not use such a setup in production, but for now
it’s ok. In order to do so, we have to create an <code class="language-plaintext highlighter-rouge">etc</code> directory to the root
filesystem and add two files to it.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> &lt;project/root/dir&gt;/rootfs
<span class="nb">mkdir </span>etc
<span class="nb">echo</span> <span class="s2">"root:x:0:0:/root:/bin/sh"</span> <span class="o">&gt;</span> etc/passwd
<span class="nb">echo</span> <span class="s2">"root::::::::"</span> <span class="o">&gt;</span> etc/shadow
<span class="nb">chmod </span>0600 etc/shadow
</code></pre></div></div>

<p class="notice--info"><strong>Noto</strong>: For more information about these files and its entries run the
commands <code class="language-plaintext highlighter-rouge">man passwd</code> and <code class="language-plaintext highlighter-rouge">man 5 shadow</code></p>

<p>Linux will try to run a program called <code class="language-plaintext highlighter-rouge">init</code> at startup. This usually starts
up and monitors other programs (like a shell for example). The <code class="language-plaintext highlighter-rouge">init</code> program
that busybox provides reads a configuration file <code class="language-plaintext highlighter-rouge">etc/inittab</code>. So let’s create
that file:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> <span class="o">&gt;</span> etc/inittab <span class="o">&lt;&lt;</span> <span class="no">EOF</span><span class="sh">
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/ash
</span><span class="no">EOF
</span></code></pre></div></div>

<p>This file will cause <code class="language-plaintext highlighter-rouge">init</code> to run the script <code class="language-plaintext highlighter-rouge">/etc/init.d/rcS</code> and afterwards
ask the user to press enter before it runs the <code class="language-plaintext highlighter-rouge">/bin/ash</code> shell. We can use
<code class="language-plaintext highlighter-rouge">/etc/init.d/rcS</code> to mound to pseudo file systems (<code class="language-plaintext highlighter-rouge">/proc</code> and <code class="language-plaintext highlighter-rouge">/sys</code>). So
let’s create the necessary directories, the script, and make the script
executable:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir </span>proc sys
<span class="nb">mkdir </span>etc/init.d
<span class="nb">cat</span> <span class="o">&gt;</span> etc/init.d/rcS <span class="o">&lt;&lt;</span> <span class="no">EOF</span><span class="sh">
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
</span><span class="no">EOF
</span><span class="nb">chmod </span>u+x etc/init.d/rcS
</code></pre></div></div>

<p>On your host system all files in the root filesystem belong to your user, but
on the Raspberry Pi they have to belong to the root user. So the ownership of
all files in the created root filesystem have to be changed:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> &lt;project/root/dir&gt;
<span class="nb">sudo chown</span> <span class="nt">-R</span> root:root rootfs
</code></pre></div></div>

<p class="notice--danger"><strong>Note</strong>: For the next step make sure where to find your root filesystem
partion of your SD card when inserted into your host system. In my case it is
under <code class="language-plaintext highlighter-rouge">/dev/sdb2</code>.</p>

<p>Now it’s time to bring the root filesystem on the SD card that has been used
earlier when <a href="https://schuam.de/en/posts/e23647e1c2.html">running U-Boot</a> and
when <a href="https://schuam.de/en/posts/cce82eee4a.html">running the kernel</a>. So
insert it into your host system, mount the root filesystem partition and copy
the root filesystem onto the SD card.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> &lt;project/root/dir&gt;
<span class="nb">sudo mkdir</span> /mnt/rootfs
<span class="nb">sudo </span>mount /dev/sdb2 /mnt/rootfs
<span class="nb">sudo </span>rsync <span class="nt">-aHAXP</span> <span class="nt">--stats</span> rootfs/ /mnt/rootfs/
</code></pre></div></div>

<p>Now you can unmount the SD card from your host system:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> <span class="nb">sudo </span>umount /mnt/boot
</code></pre></div></div>

<p>and place the card into the card holder of the Raspberry Pi. Once the SD card
is in the Raspberry Pi and you have a connection to the UART interface, you can
power up the board. You should see the same output as shown in the post <a href="https://schuam.de/en/posts/e23647e1c2.html">Run
U-Boot on a Raspberry Pi</a>. Once you
have the <code class="language-plaintext highlighter-rouge">u-boot</code> command prompt, the following commands make the kernel boot:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>U-Boot&gt; setenv bootargs 8250.nr_uarts<span class="o">=</span>1 <span class="nv">console</span><span class="o">=</span>ttyS0,115200 <span class="nv">root</span><span class="o">=</span>/dev/mmcblk0p2 <span class="nv">rootfstype</span><span class="o">=</span>ext4 rw rootwait
U-Boot&gt; load mmc 0:1 <span class="nv">$kernel_addr_r</span> Image
15034880 bytes <span class="nb">read </span><span class="k">in </span>1362 ms <span class="o">(</span>10.5 MiB/s<span class="o">)</span>
U-Boot&gt; load mmc 0:1 <span class="nv">$fdt_addr_r</span> bcm2837-rpi-3-b-plus.dtb
21027 bytes <span class="nb">read </span><span class="k">in </span>5 ms <span class="o">(</span>4 MiB/s<span class="o">)</span>
U-Boot&gt; booti <span class="nv">$kernel_addr_r</span> - <span class="nv">$fdt_addr_r</span>
<span class="c">## Flattened Device Tree blob at 02600000</span>
   Booting using the fdt blob at 0x2600000
Working FDT <span class="nb">set </span>to 2600000
   Using Device Tree <span class="k">in </span>place at 0000000002600000, end 0000000002608222
Working FDT <span class="nb">set </span>to 2600000

Starting kernel ...
</code></pre></div></div>

<p>After <code class="language-plaintext highlighter-rouge">Starting kernel ...</code> more output should appear. It looks something like
this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>    0.000000] Booting Linux on physical CPU 0x0000000000 <span class="o">[</span>0x410fd034]
<span class="o">[</span>    0.000000] Linux version 4.19.127-v8-schuam+ <span class="o">(</span>andreas@penguin<span class="o">)</span> <span class="o">(</span>gcc version 13.2.0 <span class="o">(</span>crosstool-NG 1.26.0<span class="o">))</span> <span class="c">#1 SMP PREEMPT Fri Jan 31 10:24:51 CET 2025</span>
<span class="o">[</span>    0.000000] Machine model: Raspberry Pi 3 Model B Plus Rev 1.3

... output shortened ...

<span class="o">[</span>    1.349537] mmcblk0: mmc0:0002 N/A   1.88 GiB
<span class="o">[</span>    1.349980] of_cfs_init: OK
<span class="o">[</span>    1.358888]  mmcblk0: p1 p2
<span class="o">[</span>    1.382711] EXT4-fs <span class="o">(</span>mmcblk0p2<span class="o">)</span>: mounted filesystem with ordered data mode. Opts: <span class="o">(</span>null<span class="o">)</span>
<span class="o">[</span>    1.391031] VFS: Mounted root <span class="o">(</span>ext4 filesystem<span class="o">)</span> on device 179:2.
<span class="o">[</span>    1.398479] devtmpfs: error mounting <span class="nt">-2</span>
<span class="o">[</span>    1.411324] Freeing unused kernel memory: 2880K
<span class="o">[</span>    1.416150] Run /sbin/init as init process
<span class="o">[</span>    1.552705] usb 1-1: new high-speed USB device number 2 using dwc2

Please press Enter to activate this console. <span class="o">[</span>    1.737027] usb 1-1: New USB device found, <span class="nv">idVendor</span><span class="o">=</span>0424, <span class="nv">idProduct</span><span class="o">=</span>2514, <span class="nv">bcdDevice</span><span class="o">=</span> b.b3

... output shortened ...

<span class="o">[</span>    3.640081] random: crng init <span class="k">done</span>
</code></pre></div></div>

<p>Since the kernel and the <code class="language-plaintext highlighter-rouge">init</code> program write their output to the same
terminal, it’s a little hard to see, but the <code class="language-plaintext highlighter-rouge">init</code> program actually printed
<code class="language-plaintext highlighter-rouge">Please press Enter to activate this console.</code>. So if you press enter, you will
get a console with the following command prompt:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ <span class="c">#</span>
</code></pre></div></div>

<p>And now you can use your new embedded Linux on the Raspberry Pi 3 Model B+ and
run command like <code class="language-plaintext highlighter-rouge">ps</code>, <code class="language-plaintext highlighter-rouge">ls</code> or what ever:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ <span class="c"># ls</span>
bin         linuxrc     proc        sys
etc         lost+found  sbin        usr
</code></pre></div></div>

<p>Well, that’s it for this series of post about embedded Linux on the Raspberry
Pi 3 Model B+. Have fun with it.</p>

<p><br />
<br />
Take care,<br />
Andreas</p>

<hr />

<h1 id="references">References</h1>

<ol class="bibliography"></ol>]]></content><author><name>Andreas Schuster</name></author><category term="hacking" /><category term="root_filesystem," /><category term="raspberry_pi," /><category term="embedded," /><category term="linux" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">Tutorial: How to Build and Run an Embedded Linux System for/on a Raspberry Pi Model B+</title><link href="https://www.schuam.de/en/posts/13369f2b32.html" rel="alternate" type="text/html" title="Tutorial: How to Build and Run an Embedded Linux System for/on a Raspberry Pi Model B+" /><published>2025-02-01T00:00:00+01:00</published><updated>2025-02-01T00:00:00+01:00</updated><id>https://www.schuam.de/en/posts/tutorial-build-and-run-embedded-linux-on-a-raspberry-pi-3-model-b+</id><content type="html" xml:base="https://www.schuam.de/en/posts/13369f2b32.html"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>According to the book “Mastering Embedded Linux Programming” <a class="citation" href="#vasquez2015masteringEmbeddedLinux">[1]</a> there are four things you need to build an
embedded Linux system. They are:</p>

<ol>
  <li>a (cross) toolchain</li>
  <li>a bootloader</li>
  <li>a kernel</li>
  <li>a root file system</li>
</ol>

<p>Back in 2023 I started a series of post on how to build such an embeeded Linux
system for and run it on a Raspberry Pi 3 Model B+. This was supposed to become
a little tutorial. Unfortunately, I somehow didn’t find the time to finish it
until now. But now it finally consist of seven post (excluding this one) that
explain all necessary steps:</p>

<ul>
  <li><a href="https://schuam.de/en/posts/df990fc1bc.html">Build a (Cross) Toolchain With Crosstool-NG</a></li>
  <li><a href="https://schuam.de/en/posts/4ff321aed0.html">Build U-Boot For a Raspberry Pi 3 Model B+</a></li>
  <li><a href="https://schuam.de/en/posts/e23647e1c2.html">Run U-Boot on a Raspberry Pi 3 Model B+</a></li>
  <li><a href="https://schuam.de/en/posts/3a495cf5d5.html">Build the Linux Kernel for a Raspberry Pi 3 Model B+</a></li>
  <li><a href="https://schuam.de/en/posts/cce82eee4a.html">Run the Linux Kernel on a Raspberry Pi 3 Model B+</a></li>
  <li><a href="https://schuam.de/en/posts/e02855a6e3.html">Build a Root Filesystem for a Raspberry Pi 3 Model B+</a></li>
  <li><a href="https://schuam.de/en/posts/170bdeda3b.html">Run a Root Filesystem for a Raspberry Pi 3 Model B+</a></li>
</ul>

<p>In case you are wondering why that specific Raspberry Pi model? Well, I just
happen to have one of them lying around 😊.</p>

<h1 id="what-you-need-to-follow-along">What You Need to Follow Along</h1>

<ul>
  <li>A Raspberry Pi 3 Model B+</li>
  <li>A SD card</li>
  <li>A way to communicate with the UART (serial) interface of the Raspberry Pi</li>
  <li>A development system on which everything is built.</li>
</ul>

<p>Ideally development system is a computer that runs Linux. I used an Arch Linux
system. I haven’t tried to go through the process on any other computer. I have
to admit I don’t exactly know which packages have to be installed on the
system, as my installation had pretty much all that was needed already
installed. Maybe I’ll find the time to check that in the future. One thing that
is essential though, you need to be able to run commands using <code class="language-plaintext highlighter-rouge">sudo</code>.</p>

<p>A long explanation on how to communicate with the UART of the Raspberry Pi is
out of the scope of this tutorial. In short:</p>

<p>You need to connect three of the GPIO pins (GND, UART_TXD0, and UART_RXD0) of
the Raspberry Pi to some UART to USB cable/board and connect that cable/board
to one of the USB ports of your development computer. On your computer you need
a program to display the serial communication. To find out which pins of the
Raspberry Pi to use, have a look in the article <a href="https://www.electronicwings.com/raspberry-pi/raspberry-pi-uart-communication-using-python-and-c">Raspberry Pi UART
Communication using Python and
C</a>
<a class="citation" href="#electronicwingsXXXXraspberryPiUart">[2]</a>. One such UART to USB cable is
the <a href="https://ftdichip.com/products/usb-rs232-we-5000-bt_3-3/">USB-RS232-WE-5000-BT_3.3 -
FDTI</a> <a class="citation" href="#fdtiXXXXusbRs232Cable">[3]</a> by FDTI. For possible programs to display the serial
communication, I recommand to have a look into the article <a href="https://wiki.archlinux.org/title/working_with_the_serial_console">Working with the
serial
console</a> <a class="citation" href="#archWiki2023WorkingWithTheSerialConsole">[4]</a> in the Arch Wiki.</p>

<h1 id="project-setup">Project Setup</h1>

<p>Within the tutorial, I will refere to the host system a couple of times. This
is the development computer. I also refere to the <code class="language-plaintext highlighter-rouge">&lt;project/root/dir&gt;</code>
directory multiple times. This is some directory on your host system that
should be empty at the beginning. It could be some directory in your <code class="language-plaintext highlighter-rouge">home</code>
directory (e.g. <code class="language-plaintext highlighter-rouge">/home/&lt;your-username&gt;/embedded_linux_tutorial/</code>). So whenever
you see <code class="language-plaintext highlighter-rouge">&lt;project/root/dir&gt;</code> somewhere in a command, make sure you replace it
with the correct path to the project root directory on your machine.</p>

<p>Before we actually get started, we need to prepare the SD card. An in depth
explanation on how to prepare the SD card can be found in the article <a href="https://hechao.li/posts/Boot-Raspberry-Pi-4-Using-uboot-and-Initramfs/">“Boot a
Raspberry Pi 4 using u-boot and
Initramfs”</a>
<a class="citation" href="#hechao2023bootARaspberryPi">[5]</a> by Hechao. Please follow the instruction
in that article. Afterwards you should have an SD card with two partions on it.
One with a FAT32 and one with an ext4 file system on it.</p>

<p>Well, let’s get started with the actual tutorial and <a href="https://schuam.de/en/posts/df990fc1bc.html">build a
toolchain</a>.</p>

<p><br />
<br />
Take care,<br />
Andreas</p>

<hr />

<h1 id="references">References</h1>

<ol class="bibliography"><li><span id="vasquez2015masteringEmbeddedLinux">F. Vasquez and C. Simmonds, <i>Mastering Embedded Linux Programming</i>, 3rd ed. Packt Publishing Ltd., 2021.</span></li>
<li><span id="electronicwingsXXXXraspberryPiUart">ElectronicWings, “Raspberry Pi UART Communication using Python and C.” [Online]. Available at: https://www.electronicwings.com/raspberry-pi/raspberry-pi-uart-communication-using-python-and-c. [Accessed: 02-Nov-2023].</span></li>
<li><span id="fdtiXXXXusbRs232Cable">FDTI Chip, “USB-RS232-WE-5000-BT_3.3 - FDTI.” [Online]. Available at: https://ftdichip.com/products/usb-rs232-we-5000-bt_3-3/. [Accessed: 02-Nov-2023].</span></li>
<li><span id="archWiki2023WorkingWithTheSerialConsole">ArchWiki, “Working with the serial console,” 25-Jul-2023. [Online]. Available at: https://wiki.archlinux.org/title/working_with_the_serial_console. [Accessed: 02-Nov-2023].</span></li>
<li><span id="hechao2023bootARaspberryPi">Hechao, “Boot a Raspberry Pi 4 using u-boot and Initramfs,” 20-Dec-2020. [Online]. Available at: https://hechao.li/posts/Boot-Raspberry-Pi-4-Using-uboot-and-Initramfs/. [Accessed: 31-Jan-2025].</span></li></ol>]]></content><author><name>Andreas Schuster</name></author><category term="hacking" /><category term="raspberry_pi," /><category term="embedded," /><category term="linux" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">Build a Root Filesystem for a Raspberry Pi 3 Model B+</title><link href="https://www.schuam.de/en/posts/e02855a6e3.html" rel="alternate" type="text/html" title="Build a Root Filesystem for a Raspberry Pi 3 Model B+" /><published>2025-02-01T00:00:00+01:00</published><updated>2025-02-01T00:00:00+01:00</updated><id>https://www.schuam.de/en/posts/build-a-root-filesystem-for-a-raspberry-pi-3-model-b+</id><content type="html" xml:base="https://www.schuam.de/en/posts/e02855a6e3.html"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>This is the sixth post in a series of posts (in a tutorial) about building an
embedded Linux system for a Raspberry Pi 3 Model B+. A summary post for the
tutorial can be found <a href="https://schuam.de/en/posts/13369f2b32.html">here</a>. The
summary post also talks about …</p>

<ul>
  <li>what is required to follow along,</li>
  <li>the <code class="language-plaintext highlighter-rouge">&lt;project/root/dir&gt;</code>,</li>
  <li>some terms that will be used throughout the series,</li>
  <li>a link to an article that explains how to prepare the SD card,</li>
  <li>some hints on how to use the UART interface of the Raspberry Pi.</li>
</ul>

<p>The fourth and final piece of the puzzle of building an embedded Linux system
is the root filesystem. Let’s build that part now.</p>

<h1 id="build-a-root-filesystem-with-busybox">Build a Root Filesystem with Busybox</h1>

<p>There are many way to build a Root Filesystem for Linux. I decided to use
<a href="https://www.busybox.net/">Busybox</a> <a class="citation" href="#busybox1999homepage">[1]</a>. The
following steps show how to do it.</p>

<p>First of all let’s create a directory that will hold a copy of the root
filesystem on the host machine:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> &lt;project/root/dir&gt;
<span class="nb">mkdir </span>rootfs
</code></pre></div></div>

<p>Now we need the source code of busybox. In the <code class="language-plaintext highlighter-rouge">&lt;project/root/dir&gt;</code>, run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone git://busybox.net/busybox.git
<span class="nb">cd </span>busybox
git checkout 1_36_1
</code></pre></div></div>

<p>After checking out the tag <code class="language-plaintext highlighter-rouge">1_36_1</code>, I was on commit <code class="language-plaintext highlighter-rouge">1a64f6a2</code> of the busybox
repository.</p>

<p>To build the root filesystem, we need the toolchain that was built before.
Therefore we have to prepend our <strong>PATH</strong>. In case you followed my description
in <a href="https://schuam.de/en/posts/df990fc1bc.html">Building a (Cross) Toolchain</a>,
you just have to adjust <code class="language-plaintext highlighter-rouge">&lt;project/root/dir&gt;</code> in the following command.
Otherwise you might have to adjust more. To prepend the <strong>PATH</strong>, run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">PATH</span><span class="o">=</span>&lt;project/root/dir&gt;/x-tools/aarch64-rpi3-linux-gnu/bin/:<span class="nv">$PATH</span>
</code></pre></div></div>

<p>Additionally we need to set two environment variable:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">CROSS_COMPILE</span><span class="o">=</span>aarch64-rpi3-linux-gnu-
<span class="nb">export </span><span class="nv">ARCH</span><span class="o">=</span>arm64
</code></pre></div></div>

<p>Now we can start building the root filesystem.</p>

<p class="notice--danger"><strong>Note</strong>: The following command will delete a file called <strong>.config</strong> in your
current working directory. In case you have been working on a configuration for
a root filesystem before, you might want to save this <strong>.config</strong> file for
later use. For good measure, you should run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make distclean
</code></pre></div></div>

<p>Then set the default configuration for <code class="language-plaintext highlighter-rouge">Busybox</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make defconfig
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">make menuconfig</code> didn’t work on my machine, due to some package dependency
problems. At the time of writing this post, I didn’t want to bother with it,
and instead of using <code class="language-plaintext highlighter-rouge">make menuconfig</code> to adjust some parts of the
configuration, I opened the file <strong>.config</strong> in an editor and chanaged the
following lines manually:</p>

<p>In the line starting with <code class="language-plaintext highlighter-rouge">CONFIG_CROSS_COMPILER_PREFIX</code>, I added the content
of the CROSS_COMPILE environment variable:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">CONFIG_CROSS_COMPILER_PREFIX</span><span class="o">=</span><span class="s2">"aarch64-rpi3-linux-gnu-"</span>
</code></pre></div></div>

<p>In the line starting with <code class="language-plaintext highlighter-rouge">CONFIG_PREFIX</code> I changed the path to the path of the
newly created <code class="language-plaintext highlighter-rouge">rootfs</code> directory:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">CONFIG_PREFIX</span><span class="o">=</span><span class="s2">"../rootfs"</span>
</code></pre></div></div>

<p>This changes the directory into which the root filesystem will be installed.</p>

<p>To link the executable of busybox statically, I changed the line that read like
this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># CONFIG_STATIC is not set</span>
</code></pre></div></div>

<p>to</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">CONFIG_STATIC</span><span class="o">=</span>y
</code></pre></div></div>

<p>To actually build and install the root filesystem into the ´rootfs` directory,
run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make
make <span class="nb">install</span>
</code></pre></div></div>

<p>If you followed along this tutorial (including the previous posts) and run
<code class="language-plaintext highlighter-rouge">tree -L 2</code> in your <code class="language-plaintext highlighter-rouge">&lt;project/root/dir&gt;</code>, you should see the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> <span class="nb">cd</span> &lt;project/root/dir&gt;
<span class="nv">$&gt;</span> tree <span class="nt">-L</span> 2
<span class="nb">.</span>
├── busybox
│   ├── ... <span class="o">(</span>output shorened<span class="o">)</span>
├── crosstool-ng
│   ├── bin
│   ├── ... <span class="o">(</span>output shortened<span class="o">)</span>
├── linux
│   ├── ... <span class="o">(</span>output shorened<span class="o">)</span>
├── rootfs
│   ├── bin
│   ├── sbin
│   ├── usr
│   └── linuxrc -&gt; bin/busybox
├── rpi
│   └── firmware-master
├── u-boot
│   ├── ... <span class="o">(</span>output shortened<span class="o">)</span>
│   ├── u-boot.bin
│   ├── ... <span class="o">(</span>output shortened<span class="o">)</span>
└── x-tools
    └── aarch64-rpi3-linux-gnu
</code></pre></div></div>

<p>For how to use the root filesystem, see the post <a href="https://schuam.de/en/posts/170bdeda3b.html">Run a Root Filesystem for a
Raspberry Pi 3 Model B+</a>.</p>

<p><br />
<br />
Take care,<br />
Andreas</p>

<hr />

<h1 id="references">References</h1>

<ol class="bibliography"><li><span id="busybox1999homepage">Erik Andersen, “Busybox.” [Online]. Available at: https://www.busybox.net/. [Accessed: 31-Jan-2025].</span></li></ol>]]></content><author><name>Andreas Schuster</name></author><category term="hacking" /><category term="root_filesystem," /><category term="raspberry_pi," /><category term="embedded," /><category term="linux" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">Yocto: Unravel the Way Files Take When Packaging Software With BitBake</title><link href="https://www.schuam.de/en/posts/f967b7239f.html" rel="alternate" type="text/html" title="Yocto: Unravel the Way Files Take When Packaging Software With BitBake" /><published>2024-03-05T00:00:00+01:00</published><updated>2024-03-05T00:00:00+01:00</updated><id>https://www.schuam.de/en/posts/yocto-where-does-stuff-go-during-a-packaging-process</id><content type="html" xml:base="https://www.schuam.de/en/posts/f967b7239f.html"><![CDATA[<h1 id="introduction">Introduction</h1>

<p><strong>Ever wondered where files go when you package software with <code class="language-plaintext highlighter-rouge">bitbake</code>?</strong></p>

<p>In my last <a href="https://schuam.de/en/posts/01a3d44462.html">post</a> I discussed where
files go when you build software with <code class="language-plaintext highlighter-rouge">bitbake</code>. I did that in an example
project and used <code class="language-plaintext highlighter-rouge">openssl</code> as an example software package. If you haven’t read
that post, I encourage you to do so before reading this one. I know that the
last post is kind of long. If you don’t want to read all of it, go at least
through the
<a href="https://schuam.de/en/posts/01a3d44462.html#introduction">Introduction</a> to see
how the project is set up in order to follow along with the current post. I use
the exact same set up here.</p>

<p>In the last post, it would have been possible to run <code class="language-plaintext highlighter-rouge">bitbake openssl</code> to run
through the whole process from downloading the source code all the way to a
finished <code class="language-plaintext highlighter-rouge">openssl</code> package. I didn’t do that. Instead, I split the process up
and ran individual tasks manually. This allowed me to explain everything in
more detail. It also allowed me to stop after the <code class="language-plaintext highlighter-rouge">compile</code> task. Missing was
what happens afterwards. And that is what this post is all about. But instead
of keep using <code class="language-plaintext highlighter-rouge">openssl</code> as an example, I decided to use <code class="language-plaintext highlighter-rouge">tzdata</code> for this post.
Why? Because with <code class="language-plaintext highlighter-rouge">tzdata</code> it is a little easier to explain <code class="language-plaintext highlighter-rouge">bitbake</code>’s <code class="language-plaintext highlighter-rouge">FILES</code>
variable.</p>

<p>I will start by building <code class="language-plaintext highlighter-rouge">tzdata</code> the same way I build <code class="language-plaintext highlighter-rouge">openssl</code> in the last
post (without going into details about that process) and then I will explain
the steps (tasks) necessary to wrap everything up into packages.</p>

<h1 id="build-tzdata-with-bitbake">Build <code class="language-plaintext highlighter-rouge">tzdata</code> with Bitbake</h1>

<p>OK, let’s build the <code class="language-plaintext highlighter-rouge">tzdata</code> software the same way I built <code class="language-plaintext highlighter-rouge">openssl</code> in my last
<a href="https://schuam.de/en/posts/01a3d44462.html">post</a>. The very first step is to
<code class="language-plaintext highlighter-rouge">cd</code> into the project directory and source the <code class="language-plaintext highlighter-rouge">oe-init-build-env</code> script:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> &lt;path/to/example/project&gt;
<span class="nb">source </span>poky/oe-init-build-env build/
</code></pre></div></div>

<p>After that, the current working directory is <code class="language-plaintext highlighter-rouge">&lt;path/to/example/project&gt;/build</code>.
All the following command have to be run in this <code class="language-plaintext highlighter-rouge">build</code> directory. Also all
given paths are relative to the <code class="language-plaintext highlighter-rouge">build</code> directory.</p>

<p>By the way, the recipe for <code class="language-plaintext highlighter-rouge">tzdata</code> can be found under
<code class="language-plaintext highlighter-rouge">../poky/meta/recipes-extended/timezone/</code> and is called <code class="language-plaintext highlighter-rouge">tzdata.bb</code>. To build
<code class="language-plaintext highlighter-rouge">tzdata</code>, we need to run the following commands:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bitbake <span class="nt">-c</span> fetch tzdata
bitbake <span class="nt">-c</span> unpack tzdata
bitbake <span class="nt">-c</span> patch tzdata
bitbake <span class="nt">-c</span> configure tzdata
bitbake <span class="nt">-c</span> compile tzdata
</code></pre></div></div>

<p class="notice--info"><strong>Note</strong>: We could have just invoked <code class="language-plaintext highlighter-rouge">bitbake -c compile tzdata</code> and <code class="language-plaintext highlighter-rouge">bitbake</code>
would have taken care of running the tasks that the <code class="language-plaintext highlighter-rouge">compile</code> task depends on.
But I wanted to show the five steps I ran in the last
<a href="https://schuam.de/en/posts/01a3d44462.html">post</a> once more.</p>

<p class="notice--info"><strong>Note</strong>: Just like in the last
<a href="https://schuam.de/en/posts/01a3d44462.html#workdir">post</a>, it’s a good idea to
create an environment file for <code class="language-plaintext highlighter-rouge">tzdata</code> to analyze some <code class="language-plaintext highlighter-rouge">bitbake</code> variables.
This can be done by running: <code class="language-plaintext highlighter-rouge">bitbake -e tzdata &gt; environment.txt</code>. I will
refer to that file as the “environment file”.</p>

<p><code class="language-plaintext highlighter-rouge">bitbake</code>’s <code class="language-plaintext highlighter-rouge">WORKDIR</code> variable is a different for <code class="language-plaintext highlighter-rouge">tzdata</code> compared to
<code class="language-plaintext highlighter-rouge">openssl</code>. I won’t analyze how it is set, as I have done that in detail in the
last <a href="https://schuam.de/en/posts/01a3d44462.html#workdir">post</a>. Just this
much, <code class="language-plaintext highlighter-rouge">tzdata</code> sets the <code class="language-plaintext highlighter-rouge">PACKAGE_ARCH</code> variable explicitly to <code class="language-plaintext highlighter-rouge">all</code>. To do so,
<code class="language-plaintext highlighter-rouge">tzdata.bb</code> inherits <code class="language-plaintext highlighter-rouge">allarch</code>. <code class="language-plaintext highlighter-rouge">allarch.bbclass</code> can be found under
<code class="language-plaintext highlighter-rouge">../poky/meta/classes/</code> and sets the <code class="language-plaintext highlighter-rouge">PACKAGE_ARCH</code> variable. The whole
<code class="language-plaintext highlighter-rouge">WORKDIR</code> variable for <code class="language-plaintext highlighter-rouge">tzdata</code> is set to the
<code class="language-plaintext highlighter-rouge">tmp/work/all-poky-linux/tzdata/2023c-r0</code> directory within the <code class="language-plaintext highlighter-rouge">build</code>
directory.</p>

<p>After the <code class="language-plaintext highlighter-rouge">compile</code> task you end up with a <code class="language-plaintext highlighter-rouge">WORKDIR</code> that looks like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>tree <span class="nt">-L</span> 1 tmp/work/all-poky-linux/tzdata/2023c-r0/
tmp/work/all-poky-linux/tzdata/2023c-r0/
├── build
├── deploy-source-date-epoch
├── recipe-sysroot
├── recipe-sysroot-native
├── source-date-epoch
├── sstate-install-deploy_source_date_epoch
├── temp
├── tz
└── configure.sstate
</code></pre></div></div>

<p>Except for <code class="language-plaintext highlighter-rouge">configure.sstate</code> all other entries are directories with more or
less sub-directories and files in them.</p>

<p>The compiled files ended up in the <code class="language-plaintext highlighter-rouge">build</code> directory inside the <code class="language-plaintext highlighter-rouge">WORKDIR</code>. That
is because the recipe for <code class="language-plaintext highlighter-rouge">tzdata</code> sets the <code class="language-plaintext highlighter-rouge">B</code> variable to <code class="language-plaintext highlighter-rouge">${WORKDIR}/build</code>
and the recipe defines the whole <code class="language-plaintext highlighter-rouge">compile</code> task and used the <code class="language-plaintext highlighter-rouge">B</code> variable
within it.</p>

<p class="notice--info"><strong>Note</strong>: <code class="language-plaintext highlighter-rouge">tzdata</code> is not a “normal” software where the output of the
compilation task is one or multiple executables. Instead the compilation yields
files in the timezone information format (TZif). This does not matter for the
purpose of this article.</p>

<h1 id="package-tzdata-with-bitbake">Package <code class="language-plaintext highlighter-rouge">tzdata</code> with Bitbake</h1>

<p>Now let’s look into the steps necessary to package <code class="language-plaintext highlighter-rouge">tzdata</code> and where files end
up during that process.</p>

<h2 id="do_install"><code class="language-plaintext highlighter-rouge">do_install</code></h2>

<p>The first step is to run the <code class="language-plaintext highlighter-rouge">install</code> task:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bitbake <span class="nt">-c</span> <span class="nb">install </span>tzdata
</code></pre></div></div>

<p>This task places the compiled files into a “holding” area that <code class="language-plaintext highlighter-rouge">bitbake</code>’s <code class="language-plaintext highlighter-rouge">D</code>
variable points to. <code class="language-plaintext highlighter-rouge">D</code> is set to <code class="language-plaintext highlighter-rouge">${WORKDIR}/image</code>. The <code class="language-plaintext highlighter-rouge">install</code> task is
entirely defined in <code class="language-plaintext highlighter-rouge">tzdata.bb</code> and it actually uses the <code class="language-plaintext highlighter-rouge">D</code> variable 😀.</p>

<p>That means that after the <code class="language-plaintext highlighter-rouge">install</code> task finishes you can find a new directory
called <code class="language-plaintext highlighter-rouge">image</code> in the <code class="language-plaintext highlighter-rouge">WORKDIR</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>tree <span class="nt">-L</span> 4 tmp/work/all-poky-linux/tzdata/2023c-r0/image/
tmp/work/all-poky-linux/tzdata/2023c-r0/image/
├── etc
│   ├── localtime -&gt; /usr/share/zoneinfo/Universal
│   └── timezone
└── usr
    └── share
        └── zoneinfo
            ├── Africa
            ├── <span class="o">(</span>...<span class="o">)</span>    // output truncated
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">image</code> directory contains two sub-directories (<code class="language-plaintext highlighter-rouge">etc</code> and <code class="language-plaintext highlighter-rouge">usr</code>). These are
the sub-directories that would contain files from the <code class="language-plaintext highlighter-rouge">tzdata</code> package on a
Linux machine.</p>

<h2 id="do_package"><code class="language-plaintext highlighter-rouge">do_package</code></h2>

<p>Once the holding area is filled. It’s time for the next step which is the
<code class="language-plaintext highlighter-rouge">package</code> task:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bitbake <span class="nt">-c</span> package tzdata
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">package</code> task creates three new directories in the <code class="language-plaintext highlighter-rouge">WORKDIR</code> that are of
interest here:</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">package</code> (pointed to by <code class="language-plaintext highlighter-rouge">bitbake</code>’s <code class="language-plaintext highlighter-rouge">PKGD</code> variable)</p>

    <p>It contains the whole <code class="language-plaintext highlighter-rouge">tzdata</code> package before it is split up into individual
packages. In this case it’s basically a copy of the <code class="language-plaintext highlighter-rouge">image</code> directory.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">packages-split</code> (pointed to by <code class="language-plaintext highlighter-rouge">bitbake</code>’s <code class="language-plaintext highlighter-rouge">PKGDEST</code> variable)</p>

    <p>We will look into this directory in just a bit.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">pkgdata</code> (pointed to by <code class="language-plaintext highlighter-rouge">bitbake</code>’s <code class="language-plaintext highlighter-rouge">PKGDESTWORKDIR</code> variable)</p>

    <p>This is a temporary work directory used by the <code class="language-plaintext highlighter-rouge">package</code> task to keep some
metadata about the individual packages.</p>
  </li>
</ul>

<p>If you look into the <code class="language-plaintext highlighter-rouge">packages-split</code> directory, you will find the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>tree <span class="nt">-L</span> 1 tmp/work/all-poky-linux/tzdata/2023c-r0/packages-split/
tmp/work/all-poky-linux/tzdata/2023c-r0/packages-split/
├── tzdata
├── tzdata-africa
├── tzdata-americas
├── tzdata-antarctica
├── tzdata-arctic
├── tzdata-asia
├── tzdata-atlantic
├── tzdata-australia
├── tzdata-core
├── tzdata-europe
├── tzdata-misc
├── tzdata-pacific
├── tzdata-posix
├── tzdata-right
└── tzdata-src
</code></pre></div></div>

<p><strong>Where do these directories come from?</strong></p>

<p>If you look into the environment file and search for the <code class="language-plaintext highlighter-rouge">PACKAGES</code> variable,
you will find that this variable is a list of packages with the same names as
the directories. These <code class="language-plaintext highlighter-rouge">PACKAGES</code> are defined in the recipe file <code class="language-plaintext highlighter-rouge">tzdata.bb</code>.
Also in <code class="language-plaintext highlighter-rouge">tzdata.bb</code>, you can find multiple lines that look like these examples:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FILES:tzdata-antarctica +<span class="o">=</span> <span class="s2">"</span><span class="k">${</span><span class="nv">datadir</span><span class="k">}</span><span class="s2">/zoneinfo/Antarctica"</span>

FILES:tzdata-arctic +<span class="o">=</span> <span class="s2">"</span><span class="k">${</span><span class="nv">datadir</span><span class="k">}</span><span class="s2">/zoneinfo/Arctic"</span>
</code></pre></div></div>

<p>These definitions define which files from <code class="language-plaintext highlighter-rouge">tzdata</code> go into which of the
individual packages and therefore different directories in
<code class="language-plaintext highlighter-rouge">${WORKDIR}/packages-split</code>.</p>

<h2 id="do_packagedata"><code class="language-plaintext highlighter-rouge">do_packagedata</code></h2>

<p>This step creates metadata for the subsequent step of generating the actual
packages. The data can be found in <code class="language-plaintext highlighter-rouge">${WORKDIR}/pkgdata-pdata-input</code>.</p>

<h2 id="do_package_write_rpm"><code class="language-plaintext highlighter-rouge">do_package_write_rpm</code></h2>

<p>Now it is finally time to generate the packages that can then be installed into
a distribution image. In this case we need to run the <code class="language-plaintext highlighter-rouge">package_write_rpm</code> task.</p>

<p><strong>Why to we create <code class="language-plaintext highlighter-rouge">rpm</code> packages?</strong></p>

<p>If you have a look into the set up of the example project that we work in all
the time, you find the <code class="language-plaintext highlighter-rouge">PACKAGE_CLASSES</code> variable set to <code class="language-plaintext highlighter-rouge">package_rpm</code> in the
<code class="language-plaintext highlighter-rouge">conf/local.conf</code> file (see the
<a href="https://schuam.de/en/posts/01a3d44462.html#introduction">Introduction</a> of my
last post). This variable determines which type of packages will be build. An
alternative to <code class="language-plaintext highlighter-rouge">rpm</code> packages would, for example, be <code class="language-plaintext highlighter-rouge">deb</code> packages.</p>

<p>All right let’s run the <code class="language-plaintext highlighter-rouge">package_write_rpm</code> task:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bitbake <span class="nt">-c</span> package_write_rpm tzdata
</code></pre></div></div>

<p>This will create all the individual packages in a sub-directory of the
<code class="language-plaintext highlighter-rouge">${DEPLOY_DIR_RPM}</code> directory. The name of this sub-directory depends on the
<code class="language-plaintext highlighter-rouge">PACKAGE_ARCH</code> variable. Usually it is just exactly the value of that variable,
but if <code class="language-plaintext highlighter-rouge">PACKAGE_ARCH</code> is set to <code class="language-plaintext highlighter-rouge">all</code> (see above), the sub-directory is called
<code class="language-plaintext highlighter-rouge">noarch</code>. The reason for why that is the case can be found in the documentation
file <code class="language-plaintext highlighter-rouge">../poky/documentation/migration-guides/migration-2.3.rst</code>.</p>

<p>All right, looking into <code class="language-plaintext highlighter-rouge">tmp/deploy/rpm/noarch</code> (<code class="language-plaintext highlighter-rouge">${DEPLOY_DIR_RPM}/noarch</code>)
reveals the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>tree <span class="nt">-L</span> 1 tmp/deploy/rpm/noarch/
tmp/deploy/rpm/noarch/
├── tzdata-2023c-r0.noarch.rpm
├── tzdata-africa-2023c-r0.noarch.rpm
├── tzdata-americas-2023c-r0.noarch.rpm
├── tzdata-antarctica-2023c-r0.noarch.rpm
├── tzdata-arctic-2023c-r0.noarch.rpm
├── tzdata-asia-2023c-r0.noarch.rpm
├── tzdata-atlantic-2023c-r0.noarch.rpm
├── tzdata-australia-2023c-r0.noarch.rpm
├── tzdata-core-2023c-r0.noarch.rpm
├── tzdata-europe-2023c-r0.noarch.rpm
├── tzdata-misc-2023c-r0.noarch.rpm
├── tzdata-pacific-2023c-r0.noarch.rpm
├── tzdata-posix-2023c-r0.noarch.rpm
└── tzdata-right-2023c-r0.noarch.rpm
</code></pre></div></div>

<p>There you have it, readily build <code class="language-plaintext highlighter-rouge">tzdata</code> packages that can be installed into a
distribution image, but this would be a topic for another post.</p>

<p><br />
<br />
Take care,<br />
Andreas</p>

<hr />

<h1 id="references">References</h1>

<ol class="bibliography"></ol>]]></content><author><name>Andreas Schuster</name></author><category term="hacking" /><category term="yocto," /><category term="linux," /><category term="embedded_linux," /><category term="bitbake" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">Yocto: Unravel the Way Files Take When Building Software With BitBake</title><link href="https://www.schuam.de/en/posts/01a3d44462.html" rel="alternate" type="text/html" title="Yocto: Unravel the Way Files Take When Building Software With BitBake" /><published>2024-02-12T00:00:00+01:00</published><updated>2024-02-12T00:00:00+01:00</updated><id>https://www.schuam.de/en/posts/yocto-where-does-stuff-go-during-a-build-process</id><content type="html" xml:base="https://www.schuam.de/en/posts/01a3d44462.html"><![CDATA[<h1 id="introduction">Introduction</h1>

<p><strong>Ever wondered where files go when you build software with <code class="language-plaintext highlighter-rouge">bitbake</code>?</strong></p>

<p>Well, I have. The <code class="language-plaintext highlighter-rouge">build</code> directory seemed to be a mess at the beginning. While
the <a href="https://
docs.yoctoproject.org/overview-manual/concepts.html#
openembedded-build-system-concepts">OpenEmbedded Build System Concepts</a> <a class="citation" href="#linuxFoundation2024overviewManualOpenEmbeddedBuildSystemConcpts">[1]</a> section of
the <a href="https://docs.yoctoproject.org/
overview-manual/index.html">Yocto Project Overview and Concepts Manual</a> <a class="citation" href="#linuxFoundation2024overviewManual">[2]</a> does a
really good job of explaining the general concept, I still found it difficult
to really understand all the paths in the build directory. That’s why I
thought, I would do the following:</p>

<ul>
  <li>Look at a concrete example.</li>
  <li>Run through the different tasks needed to build the software.</li>
  <li>Explain where to find the relevant <code class="language-plaintext highlighter-rouge">bitbake</code> variables.</li>
  <li>Show where different files end up in the build directory.</li>
</ul>

<p>I hope this will shine a light on the apparent mess in the build directory.</p>

<p>To do so, I started out by setting up an example project directory. This was
done in accordance to the directory structure I suggested in a previous
<a href="https://www.schuam.de/en/posts/769d48ed26.html">post</a>. I wanted to keep the
example relatively simple, yet it should include a little more then just the
“default” layers that <code class="language-plaintext highlighter-rouge">poky</code> supplies. Since I’m working on a project that is
supposed to run on a BeagleBone Black board, I include the <code class="language-plaintext highlighter-rouge">meta-ti</code> and the
<code class="language-plaintext highlighter-rouge">meta-arm</code> layers from the <a href="http://layers.openembedded.org/layerindex/ branch/master/layers/">OpenEmbedded Layer
Index</a> <a class="citation" href="#openembeddedXXXXlayerIndex">[3]</a>. The latter one was include, because
<code class="language-plaintext highlighter-rouge">meta-ti</code> depends on it.</p>

<p>So I started out with an example project directory that looked like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd</span> &lt;path/to/example/project&gt;
<span class="nv">$ </span>tree <span class="nt">-L</span> 2
<span class="nb">.</span>
├── bitbake
│   ├── downloads
│   └── sstate-cache
├── build
│   └── conf
├── layers
│   ├── meta-arm
│   └── meta-ti
├── poky
│   ├── <span class="o">(</span>...<span class="o">)</span>           // output truncated
│   └── SECURITY.md
└── README.md
</code></pre></div></div>

<p class="notice--info"><strong>Note</strong>: For those interested in the exact setup: <code class="language-plaintext highlighter-rouge">meta-arm</code>, <code class="language-plaintext highlighter-rouge">meta-ti</code>, and
<code class="language-plaintext highlighter-rouge">poky</code> were added as git submodules to the project. I used the <code class="language-plaintext highlighter-rouge">kirkstone</code>
branch for all these submodules. The specific commits are:<br />
<code class="language-plaintext highlighter-rouge">meta-arm</code>: 98cf1495dbca946b8c6a82efdfce0afb63ff41e2<br />
<code class="language-plaintext highlighter-rouge">meta-ti</code>: 155218f03ee8222eeb02f11ea9bc41135cf28e38<br />
<code class="language-plaintext highlighter-rouge">poky</code>: d8d6d921fad14b82167d9f031d4fca06b5e01883</p>

<p>Besides some other stuff that is not relevant for this post, the file
<code class="language-plaintext highlighter-rouge">./build/conf/local.conf</code> contains the following variables:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MACHINE ?<span class="o">=</span> <span class="s2">"beaglebone"</span>
DL_DIR ?<span class="o">=</span> <span class="s2">"</span><span class="k">${</span><span class="nv">TOPDIR</span><span class="k">}</span><span class="s2">/../bitbake/downloads"</span>
SSTATE_DIR ?<span class="o">=</span> <span class="s2">"</span><span class="k">${</span><span class="nv">TOPDIR</span><span class="k">}</span><span class="s2">/../bitbake/sstate-cache"</span>
DISTRO ?<span class="o">=</span> <span class="s2">"poky"</span>
PACKAGE_CLASSES ?<span class="o">=</span> <span class="s2">"package_rpm"</span>
</code></pre></div></div>

<p>The file <code class="language-plaintext highlighter-rouge">./build/conf/bblayers.conf</code> looks like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># POKY_BBLAYERS_CONF_VERSION is increased each time build/conf/bblayers.conf</span>
<span class="c"># changes incompatibly</span>
POKY_BBLAYERS_CONF_VERSION <span class="o">=</span> <span class="s2">"2"</span>

BBPATH <span class="o">=</span> <span class="s2">"</span><span class="k">${</span><span class="nv">TOPDIR</span><span class="k">}</span><span class="s2">"</span>
BBFILES ?<span class="o">=</span> <span class="s2">""</span>

BBLAYERS ?<span class="o">=</span> <span class="s2">" </span><span class="se">\</span><span class="s2">
  /home/andreas/projects/active/beagle/yocto/poky/meta </span><span class="se">\</span><span class="s2">
  /home/andreas/projects/active/beagle/yocto/poky/meta-poky </span><span class="se">\</span><span class="s2">
  /home/andreas/projects/active/beagle/yocto/poky/meta-yocto-bsp </span><span class="se">\</span><span class="s2">
  /home/andreas/projects/active/beagle/yocto/layers/meta-arm/meta-arm-toolchain </span><span class="se">\</span><span class="s2">
  /home/andreas/projects/active/beagle/yocto/layers/meta-arm/meta-arm </span><span class="se">\</span><span class="s2">
  /home/andreas/projects/active/beagle/yocto/layers/meta-ti/meta-ti-bsp </span><span class="se">\</span><span class="s2">
  "</span>
</code></pre></div></div>

<p>All right, now let’s look at an example software package and build it with
<code class="language-plaintext highlighter-rouge">bitbake</code>. For whatever reason I decided to look at the package <code class="language-plaintext highlighter-rouge">openssl</code> for
this post.</p>

<h1 id="build-openssl-with-bitbake">Build <code class="language-plaintext highlighter-rouge">openssl</code> with Bitbake</h1>

<p>After setting up the project, you could just do the following to build and
package <code class="language-plaintext highlighter-rouge">openssl</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> &lt;path/to/example/project&gt;
<span class="nb">source </span>poky/oe-init-build-env build/
bitbake openssl
</code></pre></div></div>

<p>But to demonstrate what’s going on during the build process, I skipped the last
command. In the following, I will go through the build process in small steps
by running the individual tasks that are needed to build <code class="language-plaintext highlighter-rouge">openssl</code>. You can
run <code class="language-plaintext highlighter-rouge">bitbake -c listtasks openssl</code> to see all available tasks related to the
<code class="language-plaintext highlighter-rouge">openssl</code> recipe. Not all of them are needed to build <code class="language-plaintext highlighter-rouge">openssl</code>. I will just go
through the ones relevant to the build process.</p>

<p class="notice--info"><strong>Note</strong>: You can find the recipe that build <code class="language-plaintext highlighter-rouge">openssl</code> in
<code class="language-plaintext highlighter-rouge">&lt;path/to/example/project&gt;/poky/meta/recipes-connectivity/openssl/</code>. The recipe
file is called <code class="language-plaintext highlighter-rouge">openssl_3.0.12.bb</code>.</p>

<p>OK, let’s finally get started.</p>

<p class="notice--info"><strong>Note</strong>: All the following commands are run from within the <code class="language-plaintext highlighter-rouge">build</code> directory
inside the example project directory. If you want to follow along make sure
that you have run the first two commands from the previous command listing. The
current working directory should be <code class="language-plaintext highlighter-rouge">&lt;path/to/example/project/build</code>.</p>

<h2 id="do_fetch"><code class="language-plaintext highlighter-rouge">do_fetch</code></h2>

<p>The first command you need to run is:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bitbake <span class="nt">-c</span> fetch openssl
</code></pre></div></div>

<p>This command fetches all the source files necessary to build the software. If
you actually start from scratch, it will actually do a little more than just
fetching the source code for the particular software (<code class="language-plaintext highlighter-rouge">openssl</code> in this case).
I will ignore that “little more” for now. The really interesting result of this
command is a new file called: <code class="language-plaintext highlighter-rouge">openssl-3.0.12.tar.gz</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>tree ../bitbake/downloads
../bitbake/downloads/
├── <span class="o">(</span>...<span class="o">)</span>    // output truncated
├── openssl-3.0.12.tar.gz
└── openssl-3.0.12.tar.gz.done
</code></pre></div></div>

<p class="notice--info"><strong>Note</strong>: Remember we are in the <code class="language-plaintext highlighter-rouge">build</code> directory. That’s the reason why we
had to use <code class="language-plaintext highlighter-rouge">../bitbake/downloads</code>.</p>

<p><strong>So, how did <code class="language-plaintext highlighter-rouge">bitbake</code> know what to download?</strong></p>

<p>Well, if you take a look into the recipe file (<code class="language-plaintext highlighter-rouge">openssl_3.0.12.bb</code>), you will
find the following lines:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SRC_URI <span class="o">=</span> <span class="s2">"http://www.openssl.org/source/openssl-</span><span class="k">${</span><span class="nv">PV</span><span class="k">}</span><span class="s2">.tar.gz </span><span class="se">\</span><span class="s2">
           file://run-ptest </span><span class="se">\</span><span class="s2">
           file://0001-buildinfo-strip-sysroot-and-debug-prefix-map-from-co.patch </span><span class="se">\</span><span class="s2">
           file://afalg.patch </span><span class="se">\</span><span class="s2">
           file://0001-Configure-do-not-tweak-mips-cflags.patch </span><span class="se">\</span><span class="s2">
           "</span>
</code></pre></div></div>

<p>The first line tells <code class="language-plaintext highlighter-rouge">bitbake</code> what to download. The <code class="language-plaintext highlighter-rouge">${PV}</code> variable in the
path is not explicitly set in the recipe, but taken from the recipe file name
(“3.0.12”).</p>

<p><strong>And why does the downloaded file end up in <code class="language-plaintext highlighter-rouge">../bitbake/downloads/</code>?</strong></p>

<p>This is determined by the <code class="language-plaintext highlighter-rouge">$DL_DIR</code> variable set in <code class="language-plaintext highlighter-rouge">conf/local.conf</code>
(see <a href="#introduction">Introduction</a>).</p>

<h2 id="do_unpack"><code class="language-plaintext highlighter-rouge">do_unpack</code></h2>

<p>Next, the source files have to be unpacked. This can be done with:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bitbake <span class="nt">-c</span> unpack openssl
</code></pre></div></div>

<p><strong>Where do the unpacked files end up?</strong></p>

<p>If you look into the <a href="https://
docs.yoctoproject.org/overview-manual/index.html">Yocto Project Overview and Concepts Manual</a> <a class="citation" href="#linuxFoundation2024overviewManual">[2]</a> (specifically section “4.3.5.1 Source
Fetching”), you will see that two directories become important here. They are
set in <code class="language-plaintext highlighter-rouge">bitbake</code>’s variables <code class="language-plaintext highlighter-rouge">WORKDIR</code> and <code class="language-plaintext highlighter-rouge">S</code>.</p>

<p>Before explaining were these two variables are set, let’s look at the result of
the previous command. For the <code class="language-plaintext highlighter-rouge">openssl</code> recipe, the <code class="language-plaintext highlighter-rouge">WORKDIR</code> variable is set
to <code class="language-plaintext highlighter-rouge">tmp/work/armv7at2hf-neon-poky-linux-gnueabi/openssl/2.0.12-r0</code> within the
<code class="language-plaintext highlighter-rouge">build</code> directory. It is actually set as an absolute path, I just skipped the
beginning to safe some space. If you look into that directory, you will find
the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">ls </span>tmp/work/armv7at2hf-neon-poky-linux-gnueabi/openssl/2.0.12-r0
openssl-3.0.12         0001-buildinfo-strip-sysroot-and-debug-prefix-map-from-co.patch
recipe-sysroot         0001-Configure-do-not-tweak-mips-cflags.patch
recipe-sysroot-native  afalg.patch
source-date-epoch      run-ptest
temp
</code></pre></div></div>

<p>On the left hand side you can see five directories which I will not discuss for
now. On the right hand side you can see the four files. These are the four
files that were additionally set in the <code class="language-plaintext highlighter-rouge">SRC_URI</code> variable in the recipe file
(see <a href="#do_fetch">do_fetch</a>). The <code class="language-plaintext highlighter-rouge">do_unpack</code> task didn’t download them. It
found them under <code class="language-plaintext highlighter-rouge">../poky/meta/recipes-connectivity/openssl/openssl/</code> and
copied them into the <code class="language-plaintext highlighter-rouge">WORKDIR</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">S</code> variable is set to the <code class="language-plaintext highlighter-rouge">openssl-3.0.12</code> directory in the <code class="language-plaintext highlighter-rouge">WORKDIR</code>.
Again, this is actually done as an absolute path, but I shortened it to safe
some space. If you look into the <code class="language-plaintext highlighter-rouge">S</code> directory, you will find the decompressed
source code that was compressed into the
<code class="language-plaintext highlighter-rouge">../bitbake/downloads/openssl-3.0.12.tar.gz</code> archive.</p>

<p>All right now let’s look into how <code class="language-plaintext highlighter-rouge">WORKDIR</code> and <code class="language-plaintext highlighter-rouge">S</code> are set. It’s a little
complicated (at least for <code class="language-plaintext highlighter-rouge">WORKDIR</code>).</p>

<h3 id="workdir"><code class="language-plaintext highlighter-rouge">WORKDIR</code></h3>

<p>Looking into the <a href="https://docs.yoctoproject.org/
ref-manual/variables.html">Variable Glossary</a> <a class="citation" href="#linuxFoundation2024variableGlossary">[4]</a>
reveals that the <code class="language-plaintext highlighter-rouge">WORKDIR</code> for a recipe is defined as:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">${</span><span class="nv">TMPDIR</span><span class="k">}</span>/work/<span class="k">${</span><span class="nv">MULTIMACH_TARGET_SYS</span><span class="k">}</span>/<span class="k">${</span><span class="nv">PN</span><span class="k">}</span>/<span class="k">${</span><span class="nv">EXTENDPE</span><span class="k">}${</span><span class="nv">PV</span><span class="k">}</span>-<span class="k">${</span><span class="nv">PR</span><span class="k">}</span>
</code></pre></div></div>

<p>In our case, this unfolds to be the
<code class="language-plaintext highlighter-rouge">tmp/work/armv7at2hf-neon-poky-linux-gnueabi/openssl/3.0.12-r0</code> within the
build directory.</p>

<p><strong>But how do we get there?</strong></p>

<p>Well, that’s a lot to unpack. Let’s do it! If not otherwise noted, the
following information comes from the <a href="https://docs.yoctoproject.org/ref-manual/variables.html">Variable
Glossary</a> <a class="citation" href="#linuxFoundation2024variableGlossary">[4]</a>. Additionally, when trying to analyze
something like such <code class="language-plaintext highlighter-rouge">bitbake</code> variables, it is helpful to get <code class="language-plaintext highlighter-rouge">bitbake</code>’s
environment for a recipe. I temporarily saved it into a file called
<code class="language-plaintext highlighter-rouge">environment.txt</code> by running:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bitbake <span class="nt">-e</span> openssl <span class="o">&gt;</span> environment.txt
</code></pre></div></div>

<p class="notice--info"><strong>Note</strong>: In the following, I will refer to the <code class="language-plaintext highlighter-rouge">environment.txt</code> file as the
“environment file”.</p>

<p>Let’s dissect the <code class="language-plaintext highlighter-rouge">WORKDIR</code> variable piece by piece:</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">TMPDIR</code></p>

    <p>If not specifically set in <code class="language-plaintext highlighter-rouge">conf/local.conf</code>, the default is
 <code class="language-plaintext highlighter-rouge">${TOPDIR}/tmp</code>. <code class="language-plaintext highlighter-rouge">TOPDIR</code> is the build directory. Since I didn’t explicitly
 set <code class="language-plaintext highlighter-rouge">TMPDIR</code> it is the <code class="language-plaintext highlighter-rouge">tmp</code> directory inside the <code class="language-plaintext highlighter-rouge">build</code> directory.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">MULTIMACH_TARGET_SYS</code></p>

    <p>This variable is set to <code class="language-plaintext highlighter-rouge">${PACKAGE_ARCH}${TARGET_VENDOR}-${TARGET_OS}</code>. Well,
another variable to take apart:</p>

    <ul>
      <li>
        <p><code class="language-plaintext highlighter-rouge">PACKAGE_ARCH</code></p>

        <p>Some recipes set this variable explicitly (e.g. to <code class="language-plaintext highlighter-rouge">MACHINE_ARCH</code>). This
is not the case here. If it is not set explicitly in the recipe,
<code class="language-plaintext highlighter-rouge">PACKAGE_ARCH</code> is set to <code class="language-plaintext highlighter-rouge">TUNE_PKGARCH</code> in
<code class="language-plaintext highlighter-rouge">../poky/meta/conf/bitbake.conf</code>. All right, let’s find out where
<code class="language-plaintext highlighter-rouge">TUNE_PKGARCH</code> is defined. Bear with me 💪:</p>

        <ul>
          <li>
            <p><code class="language-plaintext highlighter-rouge">TUNE_PKGARCH</code></p>

            <p>This is were it gets a little complicated. If you look into the
environment file, you will find the following line:</p>

            <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">TUNE_PKGARCH</span><span class="o">=</span><span class="s2">"armv7at2hf-neon"</span>
</code></pre></div>            </div>

            <p>Immediately above that line you can find a “pre-expansion value” that
looks like this:</p>

            <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">${</span><span class="nv">ARMPKGARCH</span><span class="k">}${</span><span class="nv">ARMPKGSFX_THUMB</span><span class="k">}${</span><span class="nv">ARMPKGSFX_DSP</span><span class="k">}${</span><span class="nv">ARMPKGSFX_EABI</span><span class="k">}${</span><span class="nv">ARMPKGSFX_ENDIAN</span><span class="k">}${</span><span class="nv">ARMPKGSFX_FPU</span><span class="k">}</span>
</code></pre></div>            </div>

            <p>And above that, some options where that value might come from.
Analyzing this part of the environment file reveals it was set in
line 12 of <code class="language-plaintext highlighter-rouge">../poky/meta/conf/machine/include/arm/arch-arm.inc</code>. I
trust that you can verify that for yourself.</p>

            <p>I won’t go into further detail on where to find these new variables.
I hope, after reading this post, you will have a good idea how to
analyze that yourself. But I will explain why <code class="language-plaintext highlighter-rouge">arch-arm.inc</code> plays a
role in setting <code class="language-plaintext highlighter-rouge">TUNE_PKGARCH</code>.</p>

            <p><strong>So, why is <code class="language-plaintext highlighter-rouge">arch-arm.inc</code> relevant?</strong></p>

            <p>If you remember, I set the <code class="language-plaintext highlighter-rouge">MACHINE</code> variable to <code class="language-plaintext highlighter-rouge">beaglebone</code> in
<code class="language-plaintext highlighter-rouge">conf/local.conf</code> (see <a href="#introduction">Introduction</a>). This causes
<code class="language-plaintext highlighter-rouge">bitbake</code> to look for a machine configuration file called
<code class="language-plaintext highlighter-rouge">beaglebone.conf</code> and it finds it in
<code class="language-plaintext highlighter-rouge">../layers/meta-ti/meta-ti-bsp/conf/machine/</code>. This file has the
following line in it:</p>

            <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>require conf/machine/include/ti33x.inc
</code></pre></div>            </div>

            <p>The file <code class="language-plaintext highlighter-rouge">ti33x.inc</code> can be found in
<code class="language-plaintext highlighter-rouge">../layers/meta-ti/meta-ti-bsp/conf/machine/include/</code> and in turn
requires <code class="language-plaintext highlighter-rouge">conf/machine/include/arm/armv7a/tune-cortexa8.inc</code>. Since
<code class="language-plaintext highlighter-rouge">bitbake</code> searches in all layer directories that are set in the
<code class="language-plaintext highlighter-rouge">BBLAYERS</code> variable in <code class="language-plaintext highlighter-rouge">conf/bblayers.conf</code> (see
<a href="#introduction">Introduction</a>), it finds the required file under
<code class="language-plaintext highlighter-rouge">../poky/meta/conf/machine/include/arm/armv7a/</code>.</p>

            <p>What follows now is a trace of required files. Starting point is
<code class="language-plaintext highlighter-rouge">tune-cortexa8.inc</code>. It requires the next file in the list and so
forth until the last file will finally be <code class="language-plaintext highlighter-rouge">arch-arm.inc´. I will just
list the names of the required files. All these file can be found
under </code>../poky/meta/conf/machine/include/arm/`.</p>

            <ul>
              <li>tune-cortexa8.inc</li>
              <li>arch-armv7a.inc</li>
              <li>arch-armv6.inc</li>
              <li>arch-armv5-dsp.inc</li>
              <li>arch-amrv5.inc</li>
              <li>arch-armv4.inc</li>
              <li>arch-arm.inc</li>
            </ul>

            <p>Please verify this trace for yourself if you want. Have fun 😉!</p>
          </li>
        </ul>
      </li>
      <li>
        <p><code class="language-plaintext highlighter-rouge">TARGET_VENDOR</code></p>

        <p>Since I set the <code class="language-plaintext highlighter-rouge">DISTRO</code> variable to <code class="language-plaintext highlighter-rouge">poky</code> in <code class="language-plaintext highlighter-rouge">conf/local.conf</code> (see
<a href="#introduction">Introduction</a>), the <code class="language-plaintext highlighter-rouge">TARGET_VENDOR</code> variable is set to
<code class="language-plaintext highlighter-rouge">-poky</code> in <code class="language-plaintext highlighter-rouge">../poky/meta-poky/conf/distro/poky.conf</code>. Looking into the
environment file gave a first clue on where to look 😀.</p>
      </li>
      <li>
        <p><code class="language-plaintext highlighter-rouge">TARGET_OS</code></p>

        <p>This variable is set in <code class="language-plaintext highlighter-rouge">../poky/meta/conf/bibake.conf</code> to the value:
<code class="language-plaintext highlighter-rouge">linux${LIBCEXTENSION}${ABIEXTENSION}</code>. Again, I started by looking into
the environment file. As with <code class="language-plaintext highlighter-rouge">TUNE_PKGARCH</code>, I won’t go any deeper in
explaining where the contained variables come from. By now, you should
have a good idea on how to find them yourself.</p>
      </li>
    </ul>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">PN</code></p>

    <p>This variable is usually extracted from the recipe file name. In our case it
is set to <code class="language-plaintext highlighter-rouge">openssl</code>. More details can be found in the <a href="https://docs.yoctoproject.org/ref-manual/variables.html">Variable
Glossary</a> <a class="citation" href="#linuxFoundation2024variableGlossary">[4]</a>.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">EXTENDPE</code></p>

    <p>This variable has to do with the epoch of a recipe. In our case it is just
set to an empty string. More details can be found in the <a href="https://docs.yoctoproject.org/ref-manual/variables.html">Variable
Glossary</a> <a class="citation" href="#linuxFoundation2024variableGlossary">[4]</a>.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">PV</code></p>

    <p>This is the package/recipe version and is normally extracted from the file
name of the recipe. In our case it is set to <code class="language-plaintext highlighter-rouge">3.0.12</code>. More details can be
found in the <a href="https://docs.yoctoproject.org/ref-manual/variables.html">Variable
Glossary</a> <a class="citation" href="#linuxFoundation2024variableGlossary">[4]</a>.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">PR</code></p>

    <p>This is the revision of the recipe. The default value is <code class="language-plaintext highlighter-rouge">r0</code> and that is the
value that is used here. The <a href="https://docs.yoctoproject.org/ref-manual/variables.html">Variable
Glossary</a> <a class="citation" href="#linuxFoundation2024variableGlossary">[4]</a> explains how it is set.</p>
  </li>
</ul>

<h3 id="s"><code class="language-plaintext highlighter-rouge">S</code></h3>

<p>Compared to the <code class="language-plaintext highlighter-rouge">WORKDIR</code> variable, finding out what <code class="language-plaintext highlighter-rouge">S</code> is set to is quite
easy. Some package recipes set this variable explicitly. In such a case you
would find it in the recipe file. That is not the case with <code class="language-plaintext highlighter-rouge">openssl</code>. As it is
not set in the recipe itself, the definition from
<code class="language-plaintext highlighter-rouge">../poky/meta/conf/bitbake.conf</code> is used. There it is set to
<code class="language-plaintext highlighter-rouge">${WORKDIR}/${BP}</code>. <code class="language-plaintext highlighter-rouge">BP</code> on the other hand is set to <code class="language-plaintext highlighter-rouge">${BPN}-${PV}</code>. And these
two variables are also set in <code class="language-plaintext highlighter-rouge">bitbake.conf</code> by means of so called “executable
meta data”. <code class="language-plaintext highlighter-rouge">BPN</code> is basically the package name and <code class="language-plaintext highlighter-rouge">PV</code> the package version
(see the <a href="https://docs.yoctoproject.org/ref-manual/variables.html">Variable
Glossary</a> <a class="citation" href="#linuxFoundation2024variableGlossary">[4]</a>).</p>

<h2 id="do_patch"><code class="language-plaintext highlighter-rouge">do_patch</code></h2>

<p>After unpacking the source code, the next step is to patch the sources (if
necessary). The recipe for <code class="language-plaintext highlighter-rouge">openssl</code> does define some patches as seen earlier.
Running <code class="language-plaintext highlighter-rouge">bitbake -c patch openssl</code> may take longer than you might think. This
is because <code class="language-plaintext highlighter-rouge">bitbake</code> first needs to build some tools that are needed to do the
actual patching. But once the command finishes, the patches we saw earlier have
been applied. More details about the <code class="language-plaintext highlighter-rouge">do_patch</code> task can be found in the <a href="https://docs.yoctoproject.org/overview-manual/index.html">Yocto
Project Overview and Concepts
Manual</a> <a class="citation" href="#linuxFoundation2024overviewManual">[2]</a> (specifically in the section “4.3.5.2
Patching”).</p>

<h2 id="do_configure"><code class="language-plaintext highlighter-rouge">do_configure</code></h2>

<p>The <code class="language-plaintext highlighter-rouge">do_configure</code> task configures the software. This is pretty specific to the
software being build. In the case of <code class="language-plaintext highlighter-rouge">openssl</code>, the recipe defines the whole
task. One of the main steps it takes is running a perl script that can be found
here: <code class="language-plaintext highlighter-rouge">${S}/Configure</code>. The <code class="language-plaintext highlighter-rouge">do_configure</code> task is the first time the <code class="language-plaintext highlighter-rouge">B</code>
variable becomes relevant. It contains the path to where the software is build.
If the recipe does not set it, it is set to <code class="language-plaintext highlighter-rouge">${S}</code>, but the <code class="language-plaintext highlighter-rouge">openssl</code> recipe
sets is explicitely to <code class="language-plaintext highlighter-rouge">${WORKDIR}/build</code>.</p>

<p>After running <code class="language-plaintext highlighter-rouge">bitbake -c configure openssl</code>, you can check that the <code class="language-plaintext highlighter-rouge">B</code>
directory exists and mainly contains a bunch of empty directories (and a few
files). Again, this task takes a little longer than you might expect. Also
because <code class="language-plaintext highlighter-rouge">bitbake</code> has to build some auxiliary software, before it can configure
<code class="language-plaintext highlighter-rouge">openssl</code>.</p>

<p>More details about the <code class="language-plaintext highlighter-rouge">do_patch</code> task can be found in the <a href="https://docs.yoctoproject.org/overview-manual/index.html">Yocto Project
Overview and Concepts
Manual</a> <a class="citation" href="#linuxFoundation2024overviewManual">[2]</a> (specifically in the section “4.3.5.3
Configuration, Compilation, and Staging”).</p>

<h2 id="do_compile"><code class="language-plaintext highlighter-rouge">do_compile</code></h2>

<p>Finally lets actually build/compile <code class="language-plaintext highlighter-rouge">openssl</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bitbake <span class="nt">-c</span> compile openssl
</code></pre></div></div>

<p>Once this task has finished, you can find all compiled object files and the
linked software in the <code class="language-plaintext highlighter-rouge">B</code> directory. I won’t show the whole content of that
directory, because this would be way to much. But two files that can be found
in the <code class="language-plaintext highlighter-rouge">B</code> directory are:</p>

<ul>
  <li>libcrypto.so.3</li>
  <li>libssl.so.3</li>
</ul>

<p>And corresponding links called <code class="language-plaintext highlighter-rouge">libcrypto.so</code> and <code class="language-plaintext highlighter-rouge">libssl.so</code>.</p>

<p>More details about the <code class="language-plaintext highlighter-rouge">do_patch</code> task can be found in the <a href="https://docs.yoctoproject.org/overview-manual/index.html">Yocto Project
Overview and Concepts
Manual</a> <a class="citation" href="#linuxFoundation2024overviewManual">[2]</a> (specifically in the section “4.3.5.3
Configuration, Compilation, and Staging”).</p>

<p><br />
<br />
Originally I thought I would also write about the packaging of software as
well. But this post has become quite long already. I will probably do that in a
following post. Stay tuned.</p>

<p><br />
<br />
Take care,<br />
Andreas</p>

<hr />

<h1 id="references">References</h1>

<ol class="bibliography"><li><span id="linuxFoundation2024overviewManualOpenEmbeddedBuildSystemConcpts">The Linux Foundation, “OpenEmbedded Build System Concepts,” 07-Feb-2024. [Online]. Available at: https://docs.yoctoproject.org/overview-manual/concepts.html#openembedded-build-system-concepts. [Accessed: 12-Feb-2024].</span></li>
<li><span id="linuxFoundation2024overviewManual">The Linux Foundation, “Yocto Project Overview and Concepts Manual,” 07-Feb-2024. [Online]. Available at: https://docs.yoctoproject.org/overview-manual/index.html. [Accessed: 12-Feb-2024].</span></li>
<li><span id="openembeddedXXXXlayerIndex">Openembedded.org, “OpenEmbedded Layer Index.” [Online]. Available at: http://layers.openembedded.org/layerindex/branch/master/layers/. [Accessed: 15-Jan-2024].</span></li>
<li><span id="linuxFoundation2024variableGlossary">The Linux Foundation, “Variable Glossary – The Yocto Project,” 22-Jan-2024. [Online]. Available at: https://docs.yoctoproject.org/ref-manual/variables.html. [Accessed: 22-Jan-2024].</span></li></ol>]]></content><author><name>Andreas Schuster</name></author><category term="hacking" /><category term="yocto," /><category term="linux," /><category term="embedded_linux," /><category term="bitbake" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">Convert pandas MultiIndex DataFrame to Nested Dictonary</title><link href="https://www.schuam.de/en/posts/20e0cf55c1.html" rel="alternate" type="text/html" title="Convert pandas MultiIndex DataFrame to Nested Dictonary" /><published>2024-01-26T00:00:00+01:00</published><updated>2024-10-23T00:00:00+02:00</updated><id>https://www.schuam.de/en/posts/pandas-multiindex-dataframe-to-nested-dict</id><content type="html" xml:base="https://www.schuam.de/en/posts/20e0cf55c1.html"><![CDATA[<h1 id="backgroundproblem">Background/Problem</h1>

<p>Just as yesterday’s <a href="https://schuam.de/en/posts/dafc36bb6e.html">post</a>, I
started writing this post about a year ago, but never finished it. Let’s change
that now.</p>

<p>So, I had the following problem: I wanted to turn the data in a
<a href="https://docs.u-boot.org/en/latest/"><code class="language-plaintext highlighter-rouge">pandas.DataFrame</code></a> <a class="citation" href="#numFocusXXXXapiRefPandasDataFrame">[1]</a> with a <code class="language-plaintext highlighter-rouge">MultiIndex</code> into a list of nested
dictionaries. Wait what? Let me clarify with an example:</p>

<p>The following short script, sets up an example <code class="language-plaintext highlighter-rouge">DataFrame</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="n">numpy</span> <span class="k">as</span> <span class="n">np</span>

<span class="n">one</span> <span class="o">=</span> <span class="mi">6</span> <span class="o">*</span> <span class="p">[</span><span class="sh">"</span><span class="s">Alice</span><span class="sh">"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">6</span> <span class="o">*</span> <span class="p">[</span><span class="sh">"</span><span class="s">Bob</span><span class="sh">"</span><span class="p">]</span>
<span class="n">two</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="p">[</span><span class="sh">"</span><span class="s">foo</span><span class="sh">"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="p">[</span><span class="sh">"</span><span class="s">bar</span><span class="sh">"</span><span class="p">]</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="p">[</span><span class="sh">"</span><span class="s">baz</span><span class="sh">"</span><span class="p">])</span>
<span class="n">three</span> <span class="o">=</span> <span class="mi">6</span> <span class="o">*</span> <span class="p">[</span><span class="sh">"</span><span class="s">one</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">two</span><span class="sh">"</span><span class="p">]</span>

<span class="n">tuples</span> <span class="o">=</span> <span class="nf">list</span><span class="p">(</span><span class="nf">zip</span><span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="n">one</span><span class="p">,</span> <span class="n">two</span><span class="p">,</span> <span class="n">three</span><span class="p">]))</span>

<span class="n">index</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">MultiIndex</span><span class="p">.</span><span class="nf">from_tuples</span><span class="p">(</span><span class="n">tuples</span><span class="p">,</span> <span class="n">names</span><span class="o">=</span><span class="p">[</span><span class="sh">"</span><span class="s">first</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">second</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">third</span><span class="sh">"</span><span class="p">])</span>

<span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="nf">seed</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span>
<span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">DataFrame</span><span class="p">(</span>
    <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="nf">randint</span><span class="p">(</span><span class="n">low</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">high</span><span class="o">=</span><span class="mi">11</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">12</span><span class="p">)),</span>
    <span class="n">index</span><span class="o">=</span><span class="p">[</span><span class="sh">"</span><span class="s">A</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">B</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">c</span><span class="sh">"</span><span class="p">],</span>
    <span class="n">columns</span><span class="o">=</span><span class="n">index</span>
<span class="p">)</span>
</code></pre></div></div>

<p>If you print it (<code class="language-plaintext highlighter-rouge">print(df)</code>), you get the following output:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">first</span>  <span class="n">Alice</span>                     <span class="n">Bob</span>
<span class="n">second</span>   <span class="n">foo</span>     <span class="n">bar</span>     <span class="n">baz</span>     <span class="n">foo</span>     <span class="n">bar</span>     <span class="n">baz</span>
<span class="n">third</span>    <span class="n">one</span> <span class="n">two</span> <span class="n">one</span> <span class="n">two</span> <span class="n">one</span> <span class="n">two</span> <span class="n">one</span> <span class="n">two</span> <span class="n">one</span> <span class="n">two</span> <span class="n">one</span> <span class="n">two</span>
<span class="n">A</span>          <span class="mi">7</span>   <span class="mi">4</span>   <span class="mi">8</span>   <span class="mi">5</span>   <span class="mi">7</span>  <span class="mi">10</span>   <span class="mi">3</span>   <span class="mi">7</span>   <span class="mi">8</span>   <span class="mi">5</span>   <span class="mi">4</span>   <span class="mi">8</span>
<span class="n">B</span>          <span class="mi">8</span>   <span class="mi">3</span>   <span class="mi">6</span>   <span class="mi">5</span>   <span class="mi">2</span>   <span class="mi">8</span>   <span class="mi">6</span>   <span class="mi">2</span>   <span class="mi">5</span>   <span class="mi">1</span>  <span class="mi">10</span>   <span class="mi">6</span>
<span class="n">c</span>          <span class="mi">9</span>   <span class="mi">1</span>  <span class="mi">10</span>   <span class="mi">3</span>   <span class="mi">7</span>   <span class="mi">4</span>   <span class="mi">9</span>   <span class="mi">3</span>   <span class="mi">5</span>   <span class="mi">3</span>   <span class="mi">7</span>   <span class="mi">5</span>
</code></pre></div></div>

<p>At this point, I needed a way to convert every row (<code class="language-plaintext highlighter-rouge">A</code>, <code class="language-plaintext highlighter-rouge">B</code>, and <code class="language-plaintext highlighter-rouge">C</code>) into a
nested dictionary. Let’s say the dictionary for row <code class="language-plaintext highlighter-rouge">A</code> is stored in <code class="language-plaintext highlighter-rouge">dict_A</code>.
Printing it (<code class="language-plaintext highlighter-rouge">print(dict_A)</code>) should lead to the following output (I
reformatted it a little bit for better readability):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
    <span class="sh">'</span><span class="s">Alice</span><span class="sh">'</span><span class="p">:</span> <span class="p">{</span>
        <span class="sh">'</span><span class="s">bar</span><span class="sh">'</span><span class="p">:</span> <span class="p">{</span><span class="sh">'</span><span class="s">one</span><span class="sh">'</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span> <span class="sh">'</span><span class="s">two</span><span class="sh">'</span><span class="p">:</span> <span class="mi">5</span><span class="p">},</span>
        <span class="sh">'</span><span class="s">baz</span><span class="sh">'</span><span class="p">:</span> <span class="p">{</span><span class="sh">'</span><span class="s">one</span><span class="sh">'</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span> <span class="sh">'</span><span class="s">two</span><span class="sh">'</span><span class="p">:</span> <span class="mi">10</span><span class="p">},</span>
        <span class="sh">'</span><span class="s">foo</span><span class="sh">'</span><span class="p">:</span> <span class="p">{</span><span class="sh">'</span><span class="s">one</span><span class="sh">'</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span> <span class="sh">'</span><span class="s">two</span><span class="sh">'</span><span class="p">:</span> <span class="mi">4</span><span class="p">}</span>
    <span class="p">},</span>
    <span class="sh">'</span><span class="s">Bob</span><span class="sh">'</span><span class="p">:</span> <span class="p">{</span>
        <span class="sh">'</span><span class="s">bar</span><span class="sh">'</span><span class="p">:</span> <span class="p">{</span><span class="sh">'</span><span class="s">one</span><span class="sh">'</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span> <span class="sh">'</span><span class="s">two</span><span class="sh">'</span><span class="p">:</span> <span class="mi">5</span><span class="p">},</span>
        <span class="sh">'</span><span class="s">baz</span><span class="sh">'</span><span class="p">:</span> <span class="p">{</span><span class="sh">'</span><span class="s">one</span><span class="sh">'</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="sh">'</span><span class="s">two</span><span class="sh">'</span><span class="p">:</span> <span class="mi">8</span><span class="p">},</span>
        <span class="sh">'</span><span class="s">foo</span><span class="sh">'</span><span class="p">:</span> <span class="p">{</span><span class="sh">'</span><span class="s">one</span><span class="sh">'</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="sh">'</span><span class="s">two</span><span class="sh">'</span><span class="p">:</span> <span class="mi">7</span><span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If you think to yourself: Why would you want to do that? Well, I had a
<a href="https://jinja.palletsprojects.com/en/3.1.x/">Jinja</a> <a class="citation" href="#pallets2007jinjaHomepage">[2]</a> template and the data in the <code class="language-plaintext highlighter-rouge">DataFrame</code> was
supposed to be used in that template. A nested dictionary seemed to be the best
data format for passing the data into the Jinja’s render function.</p>

<p>Ok, let’s look at how I accomplished that.</p>

<h1 id="solution">Solution</h1>

<p>First I thought that this should be pretty easy to do. Every row in a
<code class="language-plaintext highlighter-rouge">DataFrame</code> is of type <code class="language-plaintext highlighter-rouge">&lt;class 'pandas.core.series.Series'&gt;</code>. You can verify
that with:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">print</span><span class="p">(</span><span class="nf">type</span><span class="p">(</span><span class="n">df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Series</code> objects have a method called
<a href="https://pandas.pydata.org/docs/reference/api/pandas.Series.to_dict.html"><code class="language-plaintext highlighter-rouge">to_dict</code></a>
<a class="citation" href="#numFocusXXXXapiRefSeriesToDict">[3]</a> that I was hoping would to the job:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">print</span><span class="p">(</span><span class="n">df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">to_dict</span><span class="p">())</span>
</code></pre></div></div>

<p>But the output of that command looks like this (again a little reformatted):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
    <span class="p">(</span><span class="sh">'</span><span class="s">Alice</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">foo</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">one</span><span class="sh">'</span><span class="p">):</span> <span class="mi">7</span><span class="p">,</span>
    <span class="p">(</span><span class="sh">'</span><span class="s">Alice</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">foo</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">two</span><span class="sh">'</span><span class="p">):</span> <span class="mi">4</span><span class="p">,</span>
    <span class="p">(</span><span class="sh">'</span><span class="s">Alice</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">bar</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">one</span><span class="sh">'</span><span class="p">):</span> <span class="mi">8</span><span class="p">,</span>
    <span class="p">(</span><span class="sh">'</span><span class="s">Alice</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">bar</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">two</span><span class="sh">'</span><span class="p">):</span> <span class="mi">5</span><span class="p">,</span>
    <span class="p">(</span><span class="sh">'</span><span class="s">Alice</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">baz</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">one</span><span class="sh">'</span><span class="p">):</span> <span class="mi">7</span><span class="p">,</span>
    <span class="p">(</span><span class="sh">'</span><span class="s">Alice</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">baz</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">two</span><span class="sh">'</span><span class="p">):</span> <span class="mi">10</span><span class="p">,</span>
    <span class="p">(</span><span class="sh">'</span><span class="s">Bob</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">foo</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">one</span><span class="sh">'</span><span class="p">):</span> <span class="mi">3</span><span class="p">,</span>
    <span class="p">(</span><span class="sh">'</span><span class="s">Bob</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">foo</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">two</span><span class="sh">'</span><span class="p">):</span> <span class="mi">7</span><span class="p">,</span>
    <span class="p">(</span><span class="sh">'</span><span class="s">Bob</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">bar</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">one</span><span class="sh">'</span><span class="p">):</span> <span class="mi">8</span><span class="p">,</span>
    <span class="p">(</span><span class="sh">'</span><span class="s">Bob</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">bar</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">two</span><span class="sh">'</span><span class="p">):</span> <span class="mi">5</span><span class="p">,</span>
    <span class="p">(</span><span class="sh">'</span><span class="s">Bob</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">baz</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">one</span><span class="sh">'</span><span class="p">):</span> <span class="mi">4</span><span class="p">,</span>
    <span class="p">(</span><span class="sh">'</span><span class="s">Bob</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">baz</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">two</span><span class="sh">'</span><span class="p">):</span> <span class="mi">8</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Well, not exactly what I was hoping for.</p>

<p>After some research and some tinkering around, I wrote the following function:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">convert_series_to_dict</span><span class="p">(</span><span class="n">series</span><span class="p">):</span>
    <span class="nf">if </span><span class="p">(</span><span class="n">series</span><span class="p">.</span><span class="n">index</span><span class="p">.</span><span class="n">nlevels</span> <span class="o">==</span> <span class="mi">1</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">series</span><span class="p">.</span><span class="nf">to_dict</span><span class="p">()</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">output_dict</span> <span class="o">=</span> <span class="p">{}</span>
        <span class="k">for</span> <span class="n">header</span> <span class="ow">in</span> <span class="n">series</span><span class="p">.</span><span class="n">index</span><span class="p">.</span><span class="n">levels</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span>
            <span class="nf">if </span><span class="p">(</span><span class="n">header</span> <span class="ow">in</span> <span class="n">series</span><span class="p">.</span><span class="n">index</span><span class="p">):</span>
                <span class="n">output_dict</span><span class="p">[</span><span class="n">header</span><span class="p">]</span> <span class="o">=</span> <span class="nf">convert_series_to_dict</span><span class="p">(</span><span class="n">series</span><span class="p">.</span><span class="nf">xs</span><span class="p">(</span><span class="n">header</span><span class="p">))</span>
        <span class="k">return</span> <span class="n">output_dict</span>
</code></pre></div></div>

<p>It’s a recursive function that checks the number of levels in the index of the
passed in <code class="language-plaintext highlighter-rouge">Series</code>. It that number is one, it just calls the <code class="language-plaintext highlighter-rouge">to_dict</code> method
on it. Otherwise it traverses through the levels and builds up a nested
dictionary. If you call this function on the first row of the <code class="language-plaintext highlighter-rouge">DataFrame</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">print</span><span class="p">(</span><span class="nf">convert_series_to_dict</span><span class="p">(</span><span class="n">df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span>
</code></pre></div></div>

<p>you get exactly, what I was trying to get (see above in the
<a href="#backgroundproblem">Background/Problem</a> section).</p>

<p>As a final touch, to create the list of nested dictionaries that I wanted, the
following was added to the script:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">list_of_dicts</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">index</span><span class="p">,</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">df</span><span class="p">.</span><span class="nf">iterrows</span><span class="p">():</span>
    <span class="n">list_of_dicts</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="nf">convert_series_to_dict</span><span class="p">(</span><span class="n">row</span><span class="p">))</span>
</code></pre></div></div>

<p>I won’t add the output you would get by printing <code class="language-plaintext highlighter-rouge">list_of_dicts</code>. I guess you
can imagine what is looks like.</p>

<h1 id="changelog">Changelog</h1>

<p>2024-10-23:</p>

<ul>
  <li>Added missing line in first code listing</li>
</ul>

<p><br />
<br />
Take care,<br />
Andreas</p>

<hr />

<h1 id="references">References</h1>

<ol class="bibliography"><li><span id="numFocusXXXXapiRefPandasDataFrame">NumFOCUS Inc., “pandas.DataFrame.” [Online]. Available at: https://docs.u-boot.org/en/latest/. [Accessed: 07-Feb-2024].</span></li>
<li><span id="pallets2007jinjaHomepage">Pallets, “Jina,” 2007. [Online]. Available at: https://jinja.palletsprojects.com/en/3.1.x/. [Accessed: 07-Feb-2024].</span></li>
<li><span id="numFocusXXXXapiRefSeriesToDict">NumFOCUS Inc., “pandas.DataFrame.” [Online]. Available at: https://pandas.pydata.org/docs/reference/api/pandas.Series.to_dict.html. [Accessed: 26-Jan-2024].</span></li></ol>]]></content><author><name>Andreas Schuster</name></author><category term="hacking" /><category term="python," /><category term="pandas," /><category term="dataframe," /><category term="dictonary," /><category term="multiindex" /><summary type="html"><![CDATA[Background/Problem]]></summary></entry><entry><title type="html">How to Use `converters` in `pandas.read_excel` When Using `MultiIndex` at the Same Time</title><link href="https://www.schuam.de/en/posts/dafc36bb6e.html" rel="alternate" type="text/html" title="How to Use `converters` in `pandas.read_excel` When Using `MultiIndex` at the Same Time" /><published>2024-01-25T00:00:00+01:00</published><updated>2024-01-25T00:00:00+01:00</updated><id>https://www.schuam.de/en/posts/how-to-use-converters-in-pandas-read-excel-when-using-multiindex-at-the-same-time</id><content type="html" xml:base="https://www.schuam.de/en/posts/dafc36bb6e.html"><![CDATA[<h1 id="backgroundproblem">Background/Problem</h1>

<p>Ok, I think I had this problem about two years ago (not quite sure). Then a
little over a year ago, I saw that someone had pretty much the exact same
problem on
<a href="https://stackoverflow.com/questions/68140300/how-to-use-converter-in-pandas-when-using-a-multiindex">stackoverflow</a>
and I answered it there. Well, I was kind of late and someone else had answered
before, but I felt that my solution got to the point of the question just a
little better. Then I started to write a post for my website about it, but
somehow never quite finished and published it. Today I finally got to do so 😃.</p>

<p>Enough bla bla bla, let’s get to the problem.</p>

<p>Imagen you have an excel file with measurement data. The first header row tells
you what was measured, the second one what the measurement unit is. Every row
beneath that represents a set of measurements taken at a specific time. Now you
need to read in the data from the excel file into a <code class="language-plaintext highlighter-rouge">pandas.DataFrame</code>. While
doing so, you also want to convert the measurement data into SI units.
<code class="language-plaintext highlighter-rouge">pandas.read_excel</code> function has an optional argument <code class="language-plaintext highlighter-rouge">converters</code>. To use it,
you have to pass in a dictionary. Each key in that dictionary is a column name
and the corresponding value a lambda function that will be applied to that
column. The problem is, the excel file at hand has two header rows represented
as a <code class="language-plaintext highlighter-rouge">MultiIndex</code> in <code class="language-plaintext highlighter-rouge">pandas</code>. So what do you use for the keys in the
<code class="language-plaintext highlighter-rouge">converters</code> dictionary?</p>

<h1 id="solution">Solution</h1>

<p>To better demonstrate the problem and to have an example to work with, imagine
you had an excel file with the following table:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">width</th>
      <th style="text-align: right">height</th>
    </tr>
    <tr>
      <th style="text-align: right">nano_meter</th>
      <th style="text-align: right">milli_meter</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right">1</td>
      <td style="text-align: right">4</td>
    </tr>
    <tr>
      <td style="text-align: right">2</td>
      <td style="text-align: right">5</td>
    </tr>
    <tr>
      <td style="text-align: right">3</td>
      <td style="text-align: right">6</td>
    </tr>
  </tbody>
</table>

<p>Back when I had the problem, it took me quite some time to figure out how to
set up the <code class="language-plaintext highlighter-rouge">converters</code> dictionary. When I look at it now, it’s actually pretty
easy. But I guess that’s just how it always is. Once you know how to do
something, it seems obvious and easy 🤓. Anyways, all you have to do is, use
tuples as keys in the dictionary. Here we go:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">pandas</span> <span class="k">as</span> <span class="n">pd</span>

<span class="n">converters</span> <span class="o">=</span> <span class="p">{</span>
    <span class="p">(</span><span class="sh">"</span><span class="s">width</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">nano_meter</span><span class="sh">"</span><span class="p">):</span> <span class="k">lambda</span> <span class="n">nm</span><span class="p">:</span> <span class="n">nm</span> <span class="o">/</span> <span class="mi">1_000_000_000</span><span class="p">,</span>
    <span class="p">(</span><span class="sh">"</span><span class="s">height</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">milli_meter</span><span class="sh">"</span><span class="p">):</span> <span class="k">lambda</span> <span class="n">mm</span><span class="p">:</span> <span class="n">mm</span> <span class="o">/</span> <span class="mi">1_000</span><span class="p">,</span>
<span class="p">}</span>

<span class="n">data</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">read_excel</span><span class="p">(</span><span class="sh">"</span><span class="s">&lt;PATH/TO/EXCEL/FILE&gt;</span><span class="sh">"</span><span class="p">,</span> <span class="n">header</span><span class="o">=</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span> <span class="n">converters</span><span class="o">=</span><span class="n">converters</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</code></pre></div></div>

<p>This short script produces the following output:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>          <span class="n">width</span>      <span class="n">height</span>
     <span class="n">nano_meter</span> <span class="n">milli_meter</span>
<span class="mi">0</span>  <span class="mf">1.000000e-09</span>       <span class="mf">0.004</span>
<span class="mi">1</span>  <span class="mf">2.000000e-09</span>       <span class="mf">0.005</span>
<span class="mi">2</span>  <span class="mf">3.000000e-09</span>       <span class="mf">0.006</span>
</code></pre></div></div>

<p>Of course after the conversion the units in the <code class="language-plaintext highlighter-rouge">DataFrame</code> are not correct
anymore. So you should rename them or remove them. Even though this was not
part of the exercise, here is how you can remove the units header:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">.</span><span class="n">columns</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">columns</span><span class="p">.</span><span class="nf">droplevel</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<p>Printing the data now yields the following:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>          <span class="n">width</span>  <span class="n">height</span>
<span class="mi">0</span>  <span class="mf">1.000000e-09</span>   <span class="mf">0.004</span>
<span class="mi">1</span>  <span class="mf">2.000000e-09</span>   <span class="mf">0.005</span>
<span class="mi">2</span>  <span class="mf">3.000000e-09</span>   <span class="mf">0.006</span>
</code></pre></div></div>

<p><br />
<br />
Take care,<br />
Andreas</p>]]></content><author><name>Andreas Schuster</name></author><category term="hacking" /><category term="python," /><category term="pandas" /><summary type="html"><![CDATA[Background/Problem]]></summary></entry><entry><title type="html">Yocto: Project Directory Structure</title><link href="https://www.schuam.de/en/posts/769d48ed26.html" rel="alternate" type="text/html" title="Yocto: Project Directory Structure" /><published>2024-01-15T00:00:00+01:00</published><updated>2024-01-22T00:00:00+01:00</updated><id>https://www.schuam.de/en/posts/yocto-project-directory-structure</id><content type="html" xml:base="https://www.schuam.de/en/posts/769d48ed26.html"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>When I started out using Yocto, I was never sure about the best way to set up a
directory structure for a project that uses Yocto. By now I do have a way to
set up project directories that works for me. I thought, I’d share it. On one
hand, as inspiration for others, and on the other hand, to possibly receive
feedback from experts who can tell me if this makes sense or suggest
improvements.</p>

<p>There are certain things that I like the project directory structure to
support. They are:</p>

<ul>
  <li>Everything that is not built/generated in one way or the other, should be
under version control (in a Git repository). I don’t like losing work.</li>
  <li>Everything that is built, generated, or downloaded should be in a very
limited number of places. I want to be able to get rid of it and get back to
a clean build environment quickly. Additionally this requirement ensures that
ignoring such data by means of entries in <code class="language-plaintext highlighter-rouge">.gitignore</code> is fairly easy.
Ideally there are only a few directories that have to be added to
<code class="language-plaintext highlighter-rouge">.gitignore</code> and not a long list of direcoties and files that are scattered
all over the place.</li>
  <li>It should be possible to generate a new clean build environment and trigger a
build process quickly and more or less automatically.</li>
</ul>

<p>One thing I did to figure out how to set up a project directory structure was
reading the <a href="https://docs.yoctoproject.org/ref-manual/index.html">Yocto Project Reference
Manual</a> <a class="citation" href="#linuxFoundation2024yoctoRefManual">[1]</a>. Well, at least parts of it. In that
manual you can find a chapter about the <a href="https://docs.yoctoproject.org/ref-manual/structure.html">Source Directory
Structure</a> <a class="citation" href="#linuxFoundation2024yoctoRefManualSrcDirStructure">[2]</a>. This chapter describes the
internal directory structure of the <strong>poky directory</strong> that you can obtain by
cloning the poky Git repository:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone git://git.yoctoproject.org/poky
</code></pre></div></div>

<p>But this directory structure is not what this article is about. The <strong>poky
directory</strong> that the previous command generates is just one part of the project
directory. I just mention the <a href="https://docs.yoctoproject.org/ref-manual/structure.html">Source Directory
Structure</a> <a class="citation" href="#linuxFoundation2024yoctoRefManualSrcDirStructure">[2]</a> chapter of the <a href="https://docs.yoctoproject.org/ref-manual/index.html">Yocto
Project Reference Manual</a>
<a class="citation" href="#linuxFoundation2024yoctoRefManual">[1]</a>, because it talks about the
<code class="language-plaintext highlighter-rouge">build</code> directory within the <code class="language-plaintext highlighter-rouge">poky</code> directory. Although I’m a big fan of
Yocto’s documentation (it is really good), I don’t like the idea of building
anything inside source directories. This is a general thing for me and not only
related to Yocto. Luckily the reference manual also mentions that you can run
“out of tree” builds which I much prefer.</p>

<p>All right, enough of the introduction. Here comes my current take on how to set
up a project using Yocto. This might change over time and I would love to hear
your thoughts about it. So don’t hesitate to contact me about it. Whether you
do or don’t like my approach, any feedback is welcome.</p>

<h1 id="project-directory-structure">Project Directory Structure</h1>

<p>Assume your current working directory contains a directory for your (embedded)
Linux project that uses Yocto as a build system and is called <code class="language-plaintext highlighter-rouge">&lt;project_dir&gt;</code>.
I like the project directory to be set up in such a way that running <code class="language-plaintext highlighter-rouge">tree</code> on
it, reveals the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>tree <span class="nt">-a</span> <span class="nt">-L</span> 2 &lt;project_dir&gt;/
&lt;project_dir&gt;/
├── bitbake               // ignored by Git
│   ├── downloads
│   └── sstate-cache
├── build                 // ignored by Git
│   └── conf
├── .git
│   └── <span class="o">(</span>...<span class="o">)</span>             // output truncated
├── layers
│   ├── meta-bootstrap
│   ├── &lt;meta-layer-1&gt;    // possibly added as git submodule
│   ├── &lt;meta-layer-2&gt;    // possibly added as git submodule
│   ├── <span class="o">(</span>...<span class="o">)</span>
│   └── &lt;meta-layer-n&gt;    // possibly added as git submodule
├── poky                  // added as git submodule
│   └── <span class="o">(</span>...<span class="o">)</span>             // output truncated
├── .gitignore
├── .gitmodules
└── README.md

<span class="o">(</span>...<span class="o">)</span>
</code></pre></div></div>

<p>The following sections will describe each of the directories within the
<code class="language-plaintext highlighter-rouge">&lt;project_dir&gt;</code> in more detail.</p>

<h2 id="project_dir">&lt;project_dir&gt;/</h2>

<p>The <code class="language-plaintext highlighter-rouge">&lt;project_dir&gt;</code> is the top level directory of the project and should be
named meaningfully. It is also the top-level directory for version control (the
<code class="language-plaintext highlighter-rouge">.git</code> directory resides in it). Besides some sub-directories which will be
discussed next, the <code class="language-plaintext highlighter-rouge">project_dir</code> contains three files:</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">.gitignore</code></p>

    <p>The <code class="language-plaintext highlighter-rouge">.gitignore</code> file is used to ignore the <code class="language-plaintext highlighter-rouge">bitbake</code> and the <code class="language-plaintext highlighter-rouge">build</code>
directory. It might look like this:</p>

    <pre><code class="language-gitignore"># .gitignore
bitbake
build
</code></pre>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">.gitmodules</code></p>

    <p>This file is generated by Git, when adding submodules.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">README.md</code></p>

    <p>Well, any decent project should have a <code class="language-plaintext highlighter-rouge">README.md</code> file giving an
introduction and possible further useful information about the project.</p>
  </li>
</ul>

<h2 id="project_dirbitbake">project_dir/bitbake/</h2>

<p>The <code class="language-plaintext highlighter-rouge">bitbake</code> directory is used for files downloaded or generated by <code class="language-plaintext highlighter-rouge">bitbake</code>.
Sources that <code class="language-plaintext highlighter-rouge">bitbake</code> downloads during a build process will go into the
<code class="language-plaintext highlighter-rouge">downloads</code> sub-directory. In order for that to work, <code class="language-plaintext highlighter-rouge">bitbakes</code>’s <code class="language-plaintext highlighter-rouge">DL_DIR</code>
variable has to be set appropriately in the <code class="language-plaintext highlighter-rouge">build/conf/local.conf</code> file. The
shared state cache files that <code class="language-plaintext highlighter-rouge">bitbakes</code> generates during a build process will
go into the <code class="language-plaintext highlighter-rouge">sstate-cache</code> sub-directory. In order for that to work,
<code class="language-plaintext highlighter-rouge">bitbakes</code>’s <code class="language-plaintext highlighter-rouge">SSTATE_DIR</code> variable has to be set appropriately in the
<code class="language-plaintext highlighter-rouge">build/conf/local.conf</code> file.</p>

<p>The <code class="language-plaintext highlighter-rouge">bitbake</code> directory is just an additional hierarchy level to keep the
<code class="language-plaintext highlighter-rouge">&lt;project_dir&gt;</code> clean and I feel that the two containing sub-directories kind
of belong into the same context.</p>

<p>Since everything in this directory is either downloaded or generated, the
<code class="language-plaintext highlighter-rouge">bitbake</code> directory is not under version control (ignored by git, see above:
<code class="language-plaintext highlighter-rouge">.gitignore</code>).</p>

<p>There may be cases when I want the <code class="language-plaintext highlighter-rouge">downloads</code> or <code class="language-plaintext highlighter-rouge">sstate-cache</code> direcetory to
be physically stored somewhere else, but not within the project itself. In such
case, I still like to have them in the <code class="language-plaintext highlighter-rouge">bitbake</code> directory, but I just use
symbolic links.</p>

<h2 id="project_dirbuild">project_dir/build/</h2>

<p>The <code class="language-plaintext highlighter-rouge">build</code> directory is were bitbakes builds software, packages that software,
and generates images. Everything in this directory is either generated by the
<code class="language-plaintext highlighter-rouge">poke/os-init-build-env</code> script (see section about the
<a href="#project_dirlayersmeta-bootstrap"><code class="language-plaintext highlighter-rouge">meta-bootstrap</code></a> directory) or during
<code class="language-plaintext highlighter-rouge">bitbake</code> build runs. Therefore this directory is not under version control
(ignored by git, see above: <code class="language-plaintext highlighter-rouge">.gitignore</code>).</p>

<p class="notice--warning"><strong>Note</strong>: Even though everything in the <code class="language-plaintext highlighter-rouge">build</code> directory is generated, be
careful when deleting it. It should be possible to delete this directory and
recreate it fairly easily, but the <code class="language-plaintext highlighter-rouge">conf</code> sub-directory is a little bit
special. It is perfectly fine to edit the contents of the <code class="language-plaintext highlighter-rouge">conf</code> sub-directory,
mainly the <code class="language-plaintext highlighter-rouge">local.conf</code> and the <code class="language-plaintext highlighter-rouge">bblayers.conf</code> files during development. But
once you reach a state that you feel builds your project nicely, you should
make sure the configuration of your build environment is saved in your project
repository. This is what I like to use the <code class="language-plaintext highlighter-rouge">meta-bootstrap</code> layer for (see
corresponding section below). Before wiping out the <code class="language-plaintext highlighter-rouge">build</code> directory, make
sure you can automatically recreate the configuration files and you don’t lose
any local configuration that you might still need later on.</p>

<h2 id="project_dirgit">project_dir/.git/</h2>

<p>The directory that Git does its thing in. No need to worry about it too much.
You just should not mess with it.</p>

<h2 id="project_dirlayers">project_dir/layers/</h2>

<p>I’m not sure if this is just me, but to me it seems as if most people place
additional layers that are not part of the <code class="language-plaintext highlighter-rouge">poky</code> directory directly into the
<code class="language-plaintext highlighter-rouge">&lt;project_dir&gt;</code>. I like to introduce an additional level (<code class="language-plaintext highlighter-rouge">layers</code>) into the
directory hierarchy. To me this feels better: it keeps the top-level
<code class="language-plaintext highlighter-rouge">&lt;project_dir&gt;</code> cleaner and all layers (except the ones that belong to <code class="language-plaintext highlighter-rouge">poke</code>)
are within one directory.</p>

<p>The names <code class="language-plaintext highlighter-rouge">&lt;meta-layer-1&gt;</code> through <code class="language-plaintext highlighter-rouge">&lt;meta-layer-n&gt;</code> are just placeholders for
the real layer names. These layers may come from different sources:</p>

<ul>
  <li>External sources like the <a href="http://layers.openembedded.org/layerindex/branch/master/layers/">OpenEmbedded Layer
Index</a> <a class="citation" href="#openembeddedXXXXlayerIndex">[3]</a> for example. In this case, they should be
added as git submodules.</li>
  <li>They may be layers developed by you or your team. If such a layer is specific
to the very project and it is not planned that it will be shared publicly or
with other internal project, it might just live as a normal sub-directory
within the project’s Git repository. In case you do want to share it, it
makes sense to develop the layer within an additional external Git repository
and to add it as a submodule.</li>
</ul>

<p>The directory structure and the name of any layer within the <code class="language-plaintext highlighter-rouge">layers</code> directory
should follow the recommendations from the Yocto project. You can read about
how to create layers in the <a href="https://docs.yoctoproject.org/dev-manual/layers.html">Yocto Project Development Tasks
Manual</a> <a class="citation" href="#linuxFoundation2024devTaskManual">[4]</a>, specifically in the chapter about
<a href="https://docs.yoctoproject.org/dev-manual/layers.html">Understanding and Creating
Layers</a> <a class="citation" href="#linuxFoundation2024devTaskManualUnderstandingAndCreatingLayers">[5]</a>.</p>

<p>The <code class="language-plaintext highlighter-rouge">meta-bootstrap</code> directory within <code class="language-plaintext highlighter-rouge">layers</code> is kind of special and will be
explained <a href="#project_dirlayersmeta-bootstrap">separately</a>.</p>

<h3 id="project_dirlayersmeta-bootstrap">project_dir/layers/meta-bootstrap/</h3>

<p>To set up a build environment or to start working with an existing build
environment (within the <code class="language-plaintext highlighter-rouge">build</code> directory), you usually go into the
<code class="language-plaintext highlighter-rouge">&lt;project_dir&gt;</code> and run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source </span>poky/oe-init-build-env build
</code></pre></div></div>

<p>If you don’t have a build environment yet and you have not set the
<code class="language-plaintext highlighter-rouge">TEMPLATECONF</code> environment variable, this sets up a more or less empty <code class="language-plaintext highlighter-rouge">build</code>
directory based on templates in the <code class="language-plaintext highlighter-rouge">poky</code> directory. All it contains are some
configuration files, namely:</p>

<ul>
  <li>bblayers.conf</li>
  <li>local.conf</li>
</ul>

<p>These files are used by <code class="language-plaintext highlighter-rouge">bitbake</code> to build your project.</p>

<p>This is all fine, if you just start out with a project. But once you have set
up a working build environment, you what to have some means for new developers
or yourself to recreate the build environment automatically. This is where the
<code class="language-plaintext highlighter-rouge">meta-bootstrap</code> directory comes into play. It allows you to integrate a
template configuration into your project. There is a <a href="https://docs.yoctoproject.org/dev-manual/layers.html">Yocto Project Development
Tasks Manual</a> <a class="citation" href="#linuxFoundation2024devTaskManual">[4]</a> that explains how this is done. It is
called <a href="https://docs.yoctoproject.org/dev-manual/custom-template-configuration-directory.html">Creating a Custom Template Configuration
Directory</a>
<a class="citation" href="#linuxFoundation2024devTaskManualCreatinCustomTemplate">[6]</a>.</p>

<h2 id="project_dirpoky">project_dir/poky/</h2>

<p>I don’t want to go into what’s inside the <code class="language-plaintext highlighter-rouge">poky</code> directory, as it is very well
described <a href="https://docs.yoctoproject.org/ref-manual/structure.html">here</a> <a class="citation" href="#linuxFoundation2024yoctoRefManualSrcDirStructure">[2]</a>. One way to get the
<code class="language-plaintext highlighter-rouge">poky</code> directory is to download a release tar ball from the <a href="https://www.yoctoproject.org/">Yocto Project
Website</a> <a class="citation" href="#linuxFoundationXXXXyoctoProjectWebsite">[7]</a>, but the <a href="https://docs.yoctoproject.org/what-i-wish-id-known.html">What I wish I’d known about
Yocto Project</a> <a class="citation" href="#linuxFoundation2024whatIwish">[8]</a> article recommends using Git instead. Compared
to the Introduction, I actually prefer adding it as a submodule to my projects.</p>

<h1 id="conclusion">Conclusion</h1>

<p>The outlined project directory structure with its four “main” directories
(<code class="language-plaintext highlighter-rouge">bitbake</code>, <code class="language-plaintext highlighter-rouge">build</code>, <code class="language-plaintext highlighter-rouge">layers</code>, <code class="language-plaintext highlighter-rouge">poky</code>) fulfills all my requirements:</p>

<ul>
  <li>Everything that is not generated by a build process is version controlled in
Git. The only exception might be some configuration files in
<a href="#project_dirbuild"><code class="language-plaintext highlighter-rouge">build/conf</code></a> during the development. But everything that
is needed to generate a new clean build environment should be merged into the
<a href="#project_dirlayersmeta-bootstrap"><code class="language-plaintext highlighter-rouge">meta-bootstrap</code></a> layer anyways. Any
configuration in <a href="#project_dirbuild"><code class="language-plaintext highlighter-rouge">build/conf/local.conf</code></a> that is
actually “local” to the current build host should not be part of the project
repository. An example for such a “local” configuration might be the setting
of Bitbake’s
<a href="https://docs.yoctoproject.org/ref-manual/variables.html#term-BB_NUMBER_THREADS"><code class="language-plaintext highlighter-rouge">BB_NUMBER_THREADS</code></a>
<a class="citation" href="#linuxFoundation2024variableGlossary">[9]</a> variable.</li>
  <li>Everything that is generated during a build process is located in the
directories <a href="#project_dirbitbake"><code class="language-plaintext highlighter-rouge">bitbake</code></a> or <a href="#project_dirbuild"><code class="language-plaintext highlighter-rouge">build</code></a>.
This allows me to ignore these directories easily in
<a href="#project_dir"><code class="language-plaintext highlighter-rouge">.gitignore</code></a> and I can get rid of these directories without
the fear of loosing work (with the mentioned exception of some configuration
files in <a href="#project_dirbuild"><code class="language-plaintext highlighter-rouge">build/conf/</code></a>.</li>
  <li>By setting up the <a href="#project_dirlayersmeta-bootstrap"><code class="language-plaintext highlighter-rouge">layers/meta-bootstrap</code></a>
layer, I am able to quickly set up a new build environment and trigger a
build process within it.</li>
</ul>

<p><br />
<br />
Well, this is how I like to setup a project that uses Yocto. As mentioned
before, I would love to hear your feedback about it (either by email or as a
comment at the very bottom of this post).</p>

<h1 id="change-log">Change Log</h1>

<ul>
  <li>
    <p>2024-01-18:</p>

    <ul>
      <li>Add a paragraph about storing the <code class="language-plaintext highlighter-rouge">downloads</code> and the <code class="language-plaintext highlighter-rouge">sstate-cache</code>
directory outside the project directory.</li>
      <li>Improve the very first paragraph a little bit.</li>
    </ul>
  </li>
  <li>
    <p>2024-01-19:</p>

    <ul>
      <li>Change the order of some paragraphs (requirements moved up).</li>
      <li>Improve wording in some places.</li>
      <li>Add a few internal links to sections.</li>
    </ul>
  </li>
  <li>
    <p>2024-01-22:</p>

    <ul>
      <li>Add the <a href="#conclusion">Conclusion</a> section.</li>
      <li>Improve the very first paragraph a little bit.</li>
      <li>Improve the requirements a little bit.</li>
    </ul>
  </li>
</ul>

<p><br />
<br />
Take care,<br />
Andreas</p>

<hr />

<h1 id="references">References</h1>

<ol class="bibliography"><li><span id="linuxFoundation2024yoctoRefManual">The Linux Foundation, “Yocto Project Reference Manual,” 11-Jan-2024. [Online]. Available at: https://docs.yoctoproject.org/ref-manual/index.html. [Accessed: 15-Jan-2024].</span></li>
<li><span id="linuxFoundation2024yoctoRefManualSrcDirStructure">The Linux Foundation, “Source Directory Structure,” 11-Jan-2024. [Online]. Available at: https://docs.yoctoproject.org/ref-manual/structure.html. [Accessed: 15-Jan-2024].</span></li>
<li><span id="openembeddedXXXXlayerIndex">Openembedded.org, “OpenEmbedded Layer Index.” [Online]. Available at: http://layers.openembedded.org/layerindex/branch/master/layers/. [Accessed: 15-Jan-2024].</span></li>
<li><span id="linuxFoundation2024devTaskManual">The Linux Foundation, “Yocto Project Development Tasks Manual,” 11-Jan-2024. [Online]. Available at: https://docs.yoctoproject.org/dev-manual/layers.html. [Accessed: 15-Jan-2024].</span></li>
<li><span id="linuxFoundation2024devTaskManualUnderstandingAndCreatingLayers">The Linux Foundation, “Understanding and Creating Layers,” 11-Jan-2024. [Online]. Available at: https://docs.yoctoproject.org/dev-manual/layers.html. [Accessed: 15-Jan-2024].</span></li>
<li><span id="linuxFoundation2024devTaskManualCreatinCustomTemplate">The Linux Foundation, “Creating a Custom Template Configuration Directory,” 11-Jan-2024. [Online]. Available at: https://docs.yoctoproject.org/dev-manual/custom-template-configuration-directory.html. [Accessed: 15-Jan-2024].</span></li>
<li><span id="linuxFoundationXXXXyoctoProjectWebsite">The Linux Foundation, “The Yocto Project,” 2023. [Online]. Available at: https://www.yoctoproject.org/. [Accessed: 15-Jan-2024].</span></li>
<li><span id="linuxFoundation2024whatIwish">The Linux Foundation, “What I wish I’d known about Yocto Project,” 11-Jan-2024. [Online]. Available at: https://docs.yoctoproject.org/what-i-wish-id-known.html. [Accessed: 15-Jan-2024].</span></li>
<li><span id="linuxFoundation2024variableGlossary">The Linux Foundation, “Variable Glossary – The Yocto Project,” 22-Jan-2024. [Online]. Available at: https://docs.yoctoproject.org/ref-manual/variables.html. [Accessed: 22-Jan-2024].</span></li></ol>]]></content><author><name>Andreas Schuster</name></author><category term="hacking" /><category term="yocto," /><category term="linux," /><category term="embedded_linux" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">Run the Linux Kernel on a Raspberry Pi 3 Model B+</title><link href="https://www.schuam.de/en/posts/cce82eee4a.html" rel="alternate" type="text/html" title="Run the Linux Kernel on a Raspberry Pi 3 Model B+" /><published>2023-11-08T00:00:00+01:00</published><updated>2025-02-01T00:00:00+01:00</updated><id>https://www.schuam.de/en/posts/run-the-linux-kernel-on-a-raspberry-pi-3-model-b+</id><content type="html" xml:base="https://www.schuam.de/en/posts/cce82eee4a.html"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>This is the fifth post in a series of posts (in a tutorial) about building an
embedded Linux system for a Raspberry Pi 3 Model B+. A summary post for the
tutorial can be found <a href="https://schuam.de/en/posts/13369f2b32.html">here</a>. The
summary post also talks about …</p>

<ul>
  <li>what is required to follow along,</li>
  <li>the <code class="language-plaintext highlighter-rouge">&lt;project/root/dir&gt;</code>,</li>
  <li>some terms that will be used throughout the series,</li>
  <li>a link to an article that explains how to prepare the SD card,</li>
  <li>some hints on how to use the UART interface of the Raspberry Pi.</li>
</ul>

<p>After <a href="https://schuam.de/en/posts/3a495cf5d5.html">Building the Linux Kernel for a Raspberry
Pi</a>, it can be run on a Raspberry
Pi. I used the same SD card that I had used to <a href="https://schuam.de/en/posts/e23647e1c2.html">Run U-Boot on a
Raspberry Pi</a>, install the kernel
on it, and than used U-Boot to boot the kernel.</p>

<p>It took me quite some time to make that work. Somehow most explanations about
how to make that work, didn’t work for me (for some reason or another). Anyway,
at the end and after some research I made it happen. The rest of the post
explains how.</p>

<h1 id="install-the-kernel">Install the Kernel</h1>

<p>If you followed my description on <a href="https://schuam.de/en/posts/e23647e1c2.html">Running U-Boot on a Raspberry
Pi</a>, you should be able to insert
the SD card into your host system, run the following commands, and see four
files on the card:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> <span class="nb">sudo mkdir</span> <span class="nt">-p</span> /mnt/boot
<span class="nv">$&gt;</span> <span class="nb">sudo </span>mount /dev/sdb1 /mnt/boot
<span class="nv">$&gt;</span> <span class="nb">ls</span> /mnt/boot
bootcode.bin  config.txt  start.elf  u-boot.bin
</code></pre></div></div>

<p>Now, we will copy two more files onto the SD card. One is the kernel itself,
the other one is a device tree that the kernel needs to configure the hardware
of the Raspberry Pi.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> <span class="nb">sudo cp</span> &lt;project/root/dir&gt;/linux/arch/arm64/boot/Image /mnt/boot/
<span class="nv">$&gt;</span> <span class="nb">sudo cp</span> &lt;project/root/dir&gt;/linux/arch/arm64/boot/dts/broadcom/bcm2837-rpi-3-b-plus.dtb /mnt/boot/
</code></pre></div></div>

<h1 id="boot-the-linux-kernel">Boot the Linux Kernel</h1>

<p>Now you can unmount the SD card from your host system:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$&gt;</span> <span class="nb">sudo </span>umount /mnt/boot
</code></pre></div></div>

<p>and place the card into the card holder of the Raspberry Pi. Once the SD card
is in the Raspberry Pi and you have a connection to the UART interface, you can
power up the board. You should see the same output as shown in the post <a href="https://schuam.de/en/posts/e23647e1c2.html">Run
U-Boot on a Raspberry Pi</a>. Once you
have the <code class="language-plaintext highlighter-rouge">u-boot</code> command prompt, the following commands make the kernel boot:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>U-Boot&gt; setenv bootargs 8250.nr_uarts<span class="o">=</span>1 <span class="nv">console</span><span class="o">=</span>ttyS0,115200 <span class="nv">root</span><span class="o">=</span>/dev/mmcblk0p2 <span class="nv">rootfstype</span><span class="o">=</span>ext4 rw rootwait
U-Boot&gt; load mmc 0:1 <span class="nv">$kernel_addr_r</span> Image
15034880 bytes <span class="nb">read </span><span class="k">in </span>1186 ms <span class="o">(</span>12.1 MiB/s<span class="o">)</span>
U-Boot&gt; load mmc 0:1 <span class="nv">$fdt_addr_r</span> bcm2837-rpi-3-b-plus.dtb
21027 bytes <span class="nb">read </span><span class="k">in </span>3 ms <span class="o">(</span>6.7 MiB/s<span class="o">)</span>
U-Boot&gt; booti <span class="nv">$kernel_addr_r</span> - <span class="nv">$fdt_addr_r</span>
<span class="c">## Flattened Device Tree blob at 02600000</span>
   Booting using the fdt blob at 0x2600000
Working FDT <span class="nb">set </span>to 2600000
   Using Device Tree <span class="k">in </span>place at 0000000002600000, end 0000000002608222
Working FDT <span class="nb">set </span>to 2600000

Starting kernel ...
</code></pre></div></div>

<p>After <code class="language-plaintext highlighter-rouge">Starting kernel ...</code> more output should appear. It looks something like
this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>    0.000000] Booting Linux on physical CPU 0x0000000000 <span class="o">[</span>0x410fd034]
<span class="o">[</span>    0.000000] Linux version 4.19.127-v8-schuam+ <span class="o">(</span>andreas@penguin<span class="o">)</span> <span class="o">(</span>gcc version 13.2.0 <span class="o">(</span>crosstool-NG 1.26.0<span class="o">))</span> <span class="c">#1 5</span>
<span class="o">[</span>    0.000000] Machine model: Raspberry Pi 3 Model B Plus Rev 1.3

... output shortened ...

<span class="o">[</span>    0.000000] Kernel <span class="nb">command </span>line: 8250.nr_uarts<span class="o">=</span>1 <span class="nv">console</span><span class="o">=</span>ttyS0,115200 <span class="nv">root</span><span class="o">=</span>/dev/mmcblk0p2 <span class="nv">rootfstype</span><span class="o">=</span>ext4 rw rt

... output shortened ...

<span class="o">[</span>    1.376872] EXT4-fs <span class="o">(</span>mmcblk0p2<span class="o">)</span>: mounted filesystem with ordered data mode. Opts: <span class="o">(</span>null<span class="o">)</span>
<span class="o">[</span>    1.385220] VFS: Mounted root <span class="o">(</span>ext4 filesystem<span class="o">)</span> on device 179:2.
<span class="o">[</span>    1.392808] devtmpfs: error mounting <span class="nt">-2</span>
<span class="o">[</span>    1.405762] Freeing unused kernel memory: 2880K
<span class="o">[</span>    1.410599] Run /sbin/init as init process
<span class="o">[</span>    1.414876] Run /etc/init as init process
<span class="o">[</span>    1.419093] Run /bin/init as init process
<span class="o">[</span>    1.423271] Run /bin/sh as init process
<span class="o">[</span>    1.427268] Kernel panic - not syncing: No working init found.  Try passing <span class="nv">init</span><span class="o">=</span> option to kernel. See Linux <span class="nb">.</span>

... output shortened ...

<span class="o">[</span>    1.495295] <span class="nt">---</span><span class="o">[</span> end Kernel panic - not syncing: No working init found.  Try passing <span class="nv">init</span><span class="o">=</span> option to kernel. S-
</code></pre></div></div>

<p>The output stops with a <code class="language-plaintext highlighter-rouge">Kernel panic</code> message. Don’t panic yourself! This was
expected, as there is not Root filesystem on the SD card yet. Let’s <a href="https://schuam.de/en/posts/e02855a6e3.html">Build a
Root Filesystem for a Raspberry Pi 3 Model
B+</a> next.</p>

<h1 id="change-log">Change Log</h1>

<p>2025-02-01:</p>

<ul>
  <li>Made the post fit into the series of posts about embedded Linus on a
Raspberry Pi 3 Model B+.</li>
</ul>

<p>2025-01-20:</p>

<ul>
  <li>Fixed some typos.</li>
  <li>Fixed a command.</li>
</ul>

<p>2023-11-13:</p>

<ul>
  <li>Added part about using a “wrong” device tree binary.</li>
</ul>

<p><br />
<br />
Take care,<br />
Andreas</p>

<hr />

<h1 id="references">References</h1>

<ol class="bibliography"></ol>]]></content><author><name>Andreas Schuster</name></author><category term="hacking" /><category term="kernel," /><category term="raspberry_pi," /><category term="embedded," /><category term="linux" /><summary type="html"><![CDATA[Introduction]]></summary></entry></feed>