<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://maxiniuc.com</id>
  <title>Alexandru Maxiniuc</title>
  <updated>2026-02-06T14:48:14.639110+00:00</updated>
  <link href="https://maxiniuc.com"/>
  <link href="https://maxiniuc.com/blogs/atom.xml" rel="self"/>
  <generator uri="https://ablog.readthedocs.io/" version="0.11.12">ABlog</generator>
  <entry>
    <id>https://maxiniuc.com/blogs/2026/smarty_p2.html</id>
    <title>Smarty - Matter on ESP32 with Zephyr - Part 2</title>
    <updated>2026-01-25T00:00:00+00:00</updated>
    <content type="html">&lt;section id="smarty-matter-on-esp32-with-zephyr-part-2"&gt;

&lt;div class="dropdown warning admonition"&gt;
&lt;p class="admonition-title"&gt;Disclaimer&lt;/p&gt;
&lt;p&gt;I am an engineer who knows his sh*t and prioritizes learning and innovation to getting certifications for tinkering with hardware. To follow along with the hardware portions of this guide, you should either:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Be an engineer who also knows their sh*t.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Accept that you are responsible for your own components and safety.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Or just sit back and enjoy the read.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reading is still learning, and you are more than welcome to just follow the code without touching a single wire. However, if you do decide to dive into the hardware and things go sideways, whether it’s a fried ESP32, a tripped breaker, or a “spicy” encounter with mains power, I am not to be held accountable. I take no responsibility for your hardware, your home, or yourself. You are the captain of your own (hopefully well-insulated) ship.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In the &lt;a class="reference internal" href="blogs/2026/smarty_p1.html"&gt;&lt;span class="std std-doc"&gt;previous part&lt;/span&gt;&lt;/a&gt; we built the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Hello&lt;/span&gt; &lt;span class="pre"&gt;World&lt;/span&gt;&lt;/code&gt; Zephyr sample project and analyzed the build environment, dependencies and generated artifacts.&lt;/p&gt;
&lt;p&gt;Now I will setup a &lt;a class="reference external" href="https://github.com/cuinixam/yanga"&gt;yanga&lt;/a&gt; project to build the same example. The goal is to configure in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;yanga&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;the two external dependencies &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;zephyr&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;hal_espressif&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the toolchain &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;riscv64-zephyr-elf&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the platform &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;esp32h2_devkitm&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;hello_world&lt;/span&gt;&lt;/code&gt; component&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One shall be able to clone the repository and build it using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;yanga&lt;/span&gt;&lt;/code&gt; on both Ubuntu and Windows by running a single command.
No extra steps to install toolchains or other dependencies.&lt;/p&gt;
&lt;section id="part-2-hello-world-on-esp32-h2-with-yanga"&gt;
&lt;h2&gt;PART 2: Hello World on ESP32-H2 with Yanga&lt;/h2&gt;
&lt;p&gt;I followed the &lt;a class="reference external" href="https://yanga.readthedocs.io/en/latest/getting_started/hello_yanga.html"&gt;yanga getting started guide&lt;/a&gt; to setup my project. This project has two variants, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;English&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;German&lt;/span&gt;&lt;/code&gt;, which greet the user in their language (e.g. “Hello World” or “Hallo Welt”).&lt;/p&gt;
&lt;p&gt;I pushed the code to github &lt;a class="reference external" href="https://github.com/cuinixam/smarty"&gt;cuinixam/smarty&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next steps are to add a new &lt;a class="reference external" href="https://yanga.readthedocs.io/en/latest/features/platform.html"&gt;platform&lt;/a&gt; for ESP32-H2 and Zephyr.&lt;/p&gt;
&lt;p&gt;The new platform &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;esp32h2-zephyr&lt;/span&gt;&lt;/code&gt; will:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;use the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;riscv64-zephyr-elf&lt;/span&gt;&lt;/code&gt; toolchain&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;define two dependencies: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;zephyr&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;hal_espressif&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To be continued…&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2026/smarty_p2.html"/>
    <summary>I am an engineer who knows his sh*t and prioritizes learning and innovation to getting certifications for tinkering with hardware. To follow along with the hardware portions of this guide, you should either:Be an engineer who also knows their sh*t.</summary>
    <category term="clanguru" label="clanguru"/>
    <category term="esp-idf" label="esp-idf"/>
    <category term="esp32" label="esp32"/>
    <category term="espressif" label="espressif"/>
    <category term="matter" label="matter"/>
    <category term="smarthome" label="smart home"/>
    <category term="thread" label="thread"/>
    <category term="zephyr" label="zephyr"/>
    <published>2026-01-25T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2026/smarty_p1.html</id>
    <title>Smarty - Matter on ESP32 with Zephyr - Part 1</title>
    <updated>2026-01-25T00:00:00+00:00</updated>
    <content type="html">&lt;section id="smarty-matter-on-esp32-with-zephyr-part-1"&gt;

&lt;div class="dropdown warning admonition"&gt;
&lt;p class="admonition-title"&gt;Disclaimer&lt;/p&gt;
&lt;p&gt;I am an engineer who knows his sh*t and prioritizes learning and innovation to getting certifications for tinkering with hardware. To follow along with the hardware portions of this guide, you should either:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Be an engineer who also knows their sh*t.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Accept that you are responsible for your own components and safety.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Or just sit back and enjoy the read.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reading is still learning, and you are more than welcome to just follow the code without touching a single wire. However, if you do decide to dive into the hardware and things go sideways, whether it’s a fried ESP32, a tripped breaker, or a “spicy” encounter with mains power, I am not to be held accountable. I take no responsibility for your hardware, your home, or yourself. You are the captain of your own (hopefully well-insulated) ship.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This is the first in a series of articles about upgrading my DIY smart home devices software stack to use Zephyr and Matter.&lt;/p&gt;
&lt;p&gt;My existing smart home devices are based on &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;esp32-h2&lt;/span&gt;&lt;/code&gt; development boards and use the ESP-IDF framework with the &lt;a class="reference external" href="https://github.com/espressif/esp-matter"&gt;esp-matter&lt;/a&gt; stack. I had &lt;a class="reference external" href="https://zephyrproject.org/"&gt;Zephyr&lt;/a&gt; on my todo list for a while now and I finally decided to give it a try.&lt;/p&gt;
&lt;p&gt;The plan is to migrate my existing smart home devices to use Zephyr and Matter.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Start with the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Hello&lt;/span&gt; &lt;span class="pre"&gt;World&lt;/span&gt;&lt;/code&gt; Zephyr example and try to understand the build system&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build the same example using &lt;a class="reference external" href="https://github.com/cuinixam/yanga"&gt;yanga&lt;/a&gt; build system generator. This will allow me to separate the Zephyr build tools from the Zephyr source code, and to use my own build system generator with the proper abstraction layer for defining software products.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make the onboard LED a “smart light” Matter device that I can integrate with Home Assistant and Apple HomeKit.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;section id="part-1-hello-world-on-esp32-h2"&gt;
&lt;h2&gt;PART 1: Hello World on ESP32-H2&lt;/h2&gt;
&lt;p&gt;I started by following the &lt;a class="reference external" href="https://docs.zephyrproject.org/latest/develop/getting_started/index.html"&gt;Zephyr Getting Started Guide&lt;/a&gt;.
I followed the Ubuntu instructions because I am using an Ubuntu VM on my Mac using Parallels.&lt;/p&gt;
&lt;p&gt;These are basically the commands I ran:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Create a virtual environment and activate it&lt;/span&gt;
python3&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;venv&lt;span class="w"&gt; &lt;/span&gt;~/zephyrproject/.venv
&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/zephyrproject/.venv/bin/activate

&lt;span class="c1"&gt;# Install west and initialize the zephyr project&lt;/span&gt;
pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;west
west&lt;span class="w"&gt; &lt;/span&gt;init&lt;span class="w"&gt; &lt;/span&gt;~/zephyrproject&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/zephyrproject&lt;span class="se"&gt;\&lt;/span&gt;
west&lt;span class="w"&gt; &lt;/span&gt;update

&lt;span class="c1"&gt;# Export the zephyr project&lt;/span&gt;
west&lt;span class="w"&gt; &lt;/span&gt;zephyr-export

&lt;span class="c1"&gt;# Install west packages&lt;/span&gt;
west&lt;span class="w"&gt; &lt;/span&gt;packages&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;--install
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/zephyrproject/zephyr

&lt;span class="c1"&gt;# Install Zephyr SDK&lt;/span&gt;
west&lt;span class="w"&gt; &lt;/span&gt;sdk&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--help
west&lt;span class="w"&gt; &lt;/span&gt;sdk&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--toolchains&lt;span class="w"&gt; &lt;/span&gt;riscv64-zephyr-elf

&lt;span class="c1"&gt;# Fetch the hal_espressif zephyr module&lt;/span&gt;
west&lt;span class="w"&gt; &lt;/span&gt;blobs&lt;span class="w"&gt; &lt;/span&gt;fetch&lt;span class="w"&gt; &lt;/span&gt;hal_espressif

&lt;span class="c1"&gt;# Check available boards&lt;/span&gt;
west&lt;span class="w"&gt; &lt;/span&gt;boards&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;esp32h2

&lt;span class="c1"&gt;# Build the hello world example for the esp32-h2&lt;/span&gt;
west&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;-b&lt;span class="w"&gt; &lt;/span&gt;esp32h2_devkitm&lt;span class="w"&gt; &lt;/span&gt;samples/hello_world
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I have an ESP32-H2 devkit board from Waveshare:&lt;/p&gt;
&lt;p&gt;&lt;img alt="esp32-h2 devkit board" src="https://maxiniuc.com/_images/esp32-h2-devkitm.jpeg" style="width: 200px;" /&gt;&lt;/p&gt;
&lt;p&gt;I connected it to my computer, answered yes to a dozen questions to confirm that I want to make the USB port available to my VM and then I wanted to flash the hello world example to it.&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;west&lt;span class="w"&gt; &lt;/span&gt;flash
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition error"&gt;
&lt;p class="admonition-title"&gt;Error&lt;/p&gt;
&lt;p&gt;This failed because it could not access the serial port.&lt;/p&gt;
&lt;div class="highlight-txt notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Serial port /dev/ttyACM0:
/dev/ttyACM0 failed to connect: Could not open /dev/ttyACM0: Permission denied
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I had to add my user to the dialout group (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;sudo&lt;/span&gt; &lt;span class="pre"&gt;usermod&lt;/span&gt; &lt;span class="pre"&gt;-aG&lt;/span&gt; &lt;span class="pre"&gt;dialout&lt;/span&gt; &lt;span class="pre"&gt;$USER&lt;/span&gt;&lt;/code&gt;) and reboot to make it work.
I skipped installing the official espressif udev tools which ensures the system
recognizes the ESP32-H2 correctly and assigns the right permissions every time you plug it in.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I now flashed the hello world example to the esp32-h2 and connected to it using the espressif monitor.&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;west&lt;span class="w"&gt; &lt;/span&gt;flash
west&lt;span class="w"&gt; &lt;/span&gt;espressif&lt;span class="w"&gt; &lt;/span&gt;monitor&lt;span class="w"&gt; &lt;/span&gt;--port&lt;span class="w"&gt; &lt;/span&gt;/dev/ttyACM0
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It worked! 🤓&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;*** Booting Zephyr OS build v4.3.0-5066-gf28522e03666 ***
Hello World! esp32h2_devkitm/esp32h2
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition tip"&gt;
&lt;p class="admonition-title"&gt;Tip&lt;/p&gt;
&lt;p&gt;If it still doesn’t work, your board might be in the &lt;em&gt;download mode&lt;/em&gt;.
You can exit it by pressing and holding the &lt;em&gt;BOOT&lt;/em&gt; button shortly.&lt;/p&gt;
&lt;/div&gt;
&lt;section id="analyze-the-build-environment-and-generated-artifacts"&gt;
&lt;h3&gt;Analyze the build environment and generated artifacts&lt;/h3&gt;
&lt;p&gt;First, let’s analyze what did we installed:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/zephyrproject&lt;/span&gt; &lt;span class="pre"&gt;-&lt;/span&gt; &lt;span class="pre"&gt;9GB&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;modules&lt;/span&gt; &lt;span class="pre"&gt;-&lt;/span&gt; &lt;span class="pre"&gt;6.7GB&lt;/span&gt;&lt;/code&gt;: it contains the hardware abstraction layers (HAL) modules, including &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;hal_espressif&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;zephyr&lt;/span&gt; &lt;span class="pre"&gt;-&lt;/span&gt; &lt;span class="pre"&gt;1.4GB&lt;/span&gt;&lt;/code&gt;: this is the main Zephyr source code (core, drivers, cmake scripts, west scripts, etc.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.venv&lt;/span&gt; &lt;span class="pre"&gt;-&lt;/span&gt; &lt;span class="pre"&gt;800MB&lt;/span&gt;&lt;/code&gt;: this is the virtual environment used to run west and other Zephyr tools&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/zephyr-sdk-0.17.4&lt;/span&gt; &lt;span class="pre"&gt;-&lt;/span&gt; &lt;span class="pre"&gt;2.4GB&lt;/span&gt;&lt;/code&gt;: this is the Zephyr SDK having the compiler suite for &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;riscv64-zephyr-elf&lt;/span&gt;&lt;/code&gt; and the host tools&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It seems we need around 12GB of disk space to build the hello world example. 🫣
The modules directory has not only the espressif HAL, but also many other HAL modules.
Of course, we downloaded more than we needed, but it is still a lot of space.&lt;/p&gt;
&lt;p&gt;Let’s us now have a look at the binary file to understand which components are included and the dependencies.&lt;/p&gt;
&lt;p&gt;I implemented a python app &lt;a class="reference external" href="https://github.com/cuinixam/clanguru"&gt;clanguru&lt;/a&gt; to help me analyze binary files and C code in general.&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Install clanguru&lt;/span&gt;
pipx&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;clanguru
clanguru&lt;span class="w"&gt; &lt;/span&gt;--version
&lt;span class="c1"&gt;# Generate the objects dependencies graph&lt;/span&gt;
clanguru&lt;span class="w"&gt; &lt;/span&gt;analyze&lt;span class="w"&gt; &lt;/span&gt;--help
clanguru&lt;span class="w"&gt; &lt;/span&gt;analyze&lt;span class="w"&gt; &lt;/span&gt;--compilation-database&lt;span class="w"&gt; &lt;/span&gt;build/compile_commands.json&lt;span class="w"&gt; &lt;/span&gt;--output-file&lt;span class="w"&gt; &lt;/span&gt;obj_deps.html
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I linked below the interactive objects dependencies graph.
This is a interactive view where you can select which directories shall be visible, search for particular files or click on nodes to highlight their direct dependencies. Check the help button for an overview of all features.&lt;/p&gt;
&lt;div class="sd-container-fluid sd-sphinx-override sd-mb-4 docutils"&gt;
&lt;div class="sd-row sd-row-cols-1 sd-row-cols-xs-1 sd-row-cols-sm-1 sd-row-cols-md-1 sd-row-cols-lg-1 sd-g-1 sd-g-xs-1 sd-g-sm-1 sd-g-md-1 sd-g-lg-1 docutils"&gt;
&lt;div class="sd-col sd-d-flex-row docutils"&gt;
&lt;div class="sd-card sd-sphinx-override sd-w-50 sd-shadow-sm sd-card-hover sd-text-justify docutils"&gt;
&lt;img alt="" class="sd-card-img-top" src="https://maxiniuc.com/_images/obj_deps_hello_world.png" /&gt;
&lt;div class="sd-card-body docutils"&gt;
&lt;div class="sd-card-title sd-font-weight-bold docutils"&gt;
Objects dependencies&lt;/div&gt;
&lt;/div&gt;
&lt;a class="sd-stretched-link sd-hide-link-text reference external" href="/objects_deps_hello_world_zephyr/index.html"&gt;&lt;span&gt;/objects_deps_hello_world_zephyr/index.html&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;We can see that there are two main parts:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;zephyr&lt;/span&gt;&lt;/code&gt;: for the Zephyr kernel, drivers and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;espressif&lt;/span&gt;&lt;/code&gt; soc support&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;hal_espressif&lt;/span&gt;&lt;/code&gt;: for the espressif hardware abstraction layer including the bootloader support&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="next-steps"&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;We identified the main parts and the toolchain required to build the hello world example. Next I will setup a &lt;a class="reference external" href="https://github.com/cuinixam/yanga"&gt;yanga&lt;/a&gt; project to build the same example. The goal is to configure in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;yanga&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;the two external dependencies &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;zephyr&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;hal_espressif&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the toolchain &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;riscv64-zephyr-elf&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the platform &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;esp32h2_devkitm&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;hello_world&lt;/span&gt;&lt;/code&gt; component&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One shall be able to clone the repository and build it using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;yanga&lt;/span&gt;&lt;/code&gt; on both Ubuntu and Windows by running a single command.
No extra steps to install toolchains or other dependencies.&lt;/p&gt;
&lt;p&gt;I think this will be fun and I hope I’ll get to it by the end of the week.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2026/smarty_p1.html"/>
    <summary>I am an engineer who knows his sh*t and prioritizes learning and innovation to getting certifications for tinkering with hardware. To follow along with the hardware portions of this guide, you should either:Be an engineer who also knows their sh*t.esp32-h2 devkit board</summary>
    <category term="clanguru" label="clanguru"/>
    <category term="esp-idf" label="esp-idf"/>
    <category term="esp32" label="esp32"/>
    <category term="espressif" label="espressif"/>
    <category term="matter" label="matter"/>
    <category term="smarthome" label="smart home"/>
    <category term="thread" label="thread"/>
    <category term="zephyr" label="zephyr"/>
    <published>2026-01-25T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2026/hello_di%C3%A1taxis.html</id>
    <title>Hello Diátaxis</title>
    <updated>2026-01-25T00:00:00+00:00</updated>
    <content type="html">&lt;section id="hello-diataxis"&gt;

&lt;p&gt;As a software engineer, I tend to treat user documentation as something to be written late in the development when I think that someone might use my software.
I could improve this klingonian approach, but not today.&lt;/p&gt;
&lt;p&gt;Anyway, this weekend I decided to spend 10 minutes and request my apprentice (aka AI Agent) to refactor the documentation I wrote for one of my python application.
I already had some documentation, but this was … well, let’s say not the best ;)&lt;/p&gt;
&lt;p&gt;So I need to tell my apprentice what I want, but writing prompts takes also time, so I wrote a prompt to write me a prompt.&lt;/p&gt;
&lt;p&gt;This was my request:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;want&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;documentation&lt;/span&gt;
&lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;publish&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;readthedocs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Please&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;ai&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="n"&gt;documentation&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;documentation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Look&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="n"&gt;practices&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;writing&lt;/span&gt; &lt;span class="n"&gt;documentation&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt; &lt;span class="n"&gt;them&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Remember&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;less&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In seconds I got this prompt:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt; &lt;span class="n"&gt;Prompt&lt;/span&gt;

&lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;expert&lt;/span&gt; &lt;span class="n"&gt;Technical&lt;/span&gt; &lt;span class="n"&gt;Writer&lt;/span&gt; &lt;span class="n"&gt;specializing&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt;
&lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Diátaxis&lt;/span&gt; &lt;span class="n"&gt;framework&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Objective&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Parse&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;provided&lt;/span&gt; &lt;span class="n"&gt;codebase&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="n"&gt;notes&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;comprehensive&lt;/span&gt; &lt;span class="n"&gt;documentation&lt;/span&gt; &lt;span class="n"&gt;suite&lt;/span&gt; &lt;span class="n"&gt;optimized&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Read&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Docs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MkDocs&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;Sphinx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Guidelines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Clarity&lt;/span&gt; &lt;span class="n"&gt;over&lt;/span&gt; &lt;span class="n"&gt;Volume&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;less is more&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;principle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Avoid&lt;/span&gt; &lt;span class="n"&gt;fluff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;prioritize&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Time to First Hello World.&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Standards&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ensure&lt;/span&gt; &lt;span class="nb"&gt;all&lt;/span&gt; &lt;span class="n"&gt;docstrings&lt;/span&gt; &lt;span class="n"&gt;follow&lt;/span&gt; &lt;span class="n"&gt;PEP&lt;/span&gt; &lt;span class="mi"&gt;257&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Google&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;NumPy&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Structure&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Diátaxis&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;Organize&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="n"&gt;into&lt;/span&gt; &lt;span class="n"&gt;four&lt;/span&gt; &lt;span class="n"&gt;distinct&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Tutorials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Learning&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;oriented&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Step&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;beginners&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;How&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;Guides&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Problem&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;oriented&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Solving&lt;/span&gt; &lt;span class="n"&gt;specific&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Information&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;oriented&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="n"&gt;technical&lt;/span&gt; &lt;span class="n"&gt;descriptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Explanation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Understanding&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;oriented&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Concepts&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;architecture&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="n"&gt;Requirements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;README&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Create&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;high&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="n"&gt;overview&lt;/span&gt; &lt;span class="n"&gt;including&lt;/span&gt; &lt;span class="n"&gt;installation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Quick Start&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;links&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;full&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="n"&gt;Reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Automatically&lt;/span&gt; &lt;span class="n"&gt;generate&lt;/span&gt; &lt;span class="n"&gt;documentation&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;all&lt;/span&gt; &lt;span class="n"&gt;public&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;functions&lt;/span&gt; &lt;span class="n"&gt;based&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;logic&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;docstrings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Requirement&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ensure&lt;/span&gt; &lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;pyproject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toml&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;correctly&lt;/span&gt; &lt;span class="n"&gt;reflected&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Generate&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;basic&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readthedocs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yaml&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;mkdocs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yml&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;ensure&lt;/span&gt; &lt;span class="n"&gt;seamless&lt;/span&gt; &lt;span class="n"&gt;deployment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="n"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Provide&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;documentation&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Markdown&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;MkDocs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;reStructuredText&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Sphinx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Like a good master I am ;), I actually checked the prompt my apprentice wrote and immediately stopped at the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Diátaxis&lt;/span&gt; &lt;span class="pre"&gt;framework&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At this point I was like, why does my apprentice invent words and frameworks again? :)&lt;/p&gt;
&lt;p&gt;In the end, what should have been a 10 minutes task to let my apprentice generate the documentation and then me to review and approve it,
took me over one hour to understand what &lt;a class="reference external" href="https://diataxis.fr/start-here/"&gt;Diátaxis&lt;/a&gt; is and why didn’t I know about it before. :)&lt;/p&gt;
&lt;p&gt;I had to adapt the prompt slightly for my project setup (Sphinx, Myst Parser, etc.).
The Sphinx theme and layout remained untouched but the chapters changed completely.
Here is the refactored documentation: &lt;a class="reference external" href="https://pypeline-runner.readthedocs.io/en/latest/"&gt;pypeline-runner.readthedocs.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I think I will use this “framework” for my future documentation projects and of course create an agentic &lt;a class="reference external" href="https://agentskills.io/specification"&gt;SKILL&lt;/a&gt; to do this for me. ;)&lt;/p&gt;
&lt;p&gt;Have fun and happy documenting!&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2026/hello_di%C3%A1taxis.html"/>
    <summary>As a software engineer, I tend to treat user documentation as something to be written late in the development when I think that someone might use my software.
I could improve this klingonian approach, but not today.Anyway, this weekend I decided to spend 10 minutes and request my apprentice (aka AI Agent) to refactor the documentation I wrote for one of my python application.
I already had some documentation, but this was … well, let’s say not the best ;)</summary>
    <category term="ai" label="ai"/>
    <category term="diátaxis" label="diátaxis"/>
    <category term="docs" label="docs"/>
    <category term="sphinx" label="sphinx"/>
    <category term="technicaldocumentation" label="technical documentation"/>
    <published>2026-01-25T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2025/spl_bootstrap.html</id>
    <title>SPL Bootstrap - Handle Python Version</title>
    <updated>2025-11-24T00:00:00+00:00</updated>
    <content type="html">&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/home/runner/work/cuinixam.github.io/cuinixam.github.io/docs/blogs/2025/spl_bootstrap.md&lt;/span&gt;, line 8)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="spl-bootstrap-handle-python-version"&gt;

&lt;section id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;The pipeline implementation for our Software Product Line (SPL) repositories uses the &lt;a class="reference external" href="https://github.com/cuinixam/pypeline"&gt;pypeline&lt;/a&gt; Python application.
In order to start the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pypeline&lt;/span&gt;&lt;/code&gt; application, we need to install Python (with a specified version) and create the Python virtual environment.
This step we call &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bootstrap&lt;/span&gt;&lt;/code&gt; and it is implemented &lt;a class="reference external" href="https://github.com/avengineers/bootstrap"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Currently bootstrap supports only Windows for installing Python.&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="workflow"&gt;
&lt;h2&gt;Workflow&lt;/h2&gt;
&lt;p&gt;The bootstrap process is managed by two main scripts: a PowerShell script (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bootstrap.ps1&lt;/span&gt;&lt;/code&gt;) for environment setup and a Python script (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bootstrap.py&lt;/span&gt;&lt;/code&gt;) for dependency management. The diagram below shows the high-level workflow.&lt;/p&gt;
&lt;pre  class="mermaid"&gt;
        flowchart TD
    A[bootstrap.ps1] --&amp;gt; B{Python installed?};
    B -- No --&amp;gt; C[Install Python w/ Scoop 1️⃣];
    B -- Yes --&amp;gt; D;
    C --&amp;gt; D[Execute bootstrap.py];

    D --&amp;gt; E{Dependencies changed?};
    E -- No --&amp;gt; F[End];
    E -- Yes --&amp;gt; G[Create/Update .venv 2️⃣];
    G --&amp;gt; H[Install Dependencies];
    H --&amp;gt; F;
    &lt;/pre&gt;&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;This step will first install the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;scoop&lt;/span&gt;&lt;/code&gt; windows package manager and then call &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;scoop&lt;/span&gt;&lt;/code&gt; to install the configured python version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The python virtual environment is updated in multiple steps:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;create an empty virtual environment with python &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;venv.create&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;install the package manager (e.g., poetry, uv, etc.) and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pip-system-certs&lt;/span&gt;&lt;/code&gt; for using the system SSL certificates&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;run the package manager to install the python dependencies&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;section id="bootstrap-ps1"&gt;
&lt;h3&gt;bootstrap.ps1&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Scoop configuration and installation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Python version detection and installation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delegates to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bootstrap.py&lt;/span&gt;&lt;/code&gt; for virtual environment setup&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="bootstrap-py"&gt;
&lt;h3&gt;bootstrap.py&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;OS-agnostic virtual environment creation (Windows/Unix)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Smart caching with hash-based dependency tracking. Only runs if dependencies have been updated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Package manager support: Poetry, UV, Pipenv&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automatic pip configuration for custom PyPI sources&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="bootstrap-json-configuration"&gt;
&lt;h3&gt;bootstrap.json Configuration&lt;/h3&gt;
&lt;div class="highlight-json notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;python_version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;3.11&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;python_package_manager&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;poetry&amp;gt;=2.1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;scoop_ignore_scoopfile&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="things-to-improve"&gt;
&lt;h2&gt;Things to Improve&lt;/h2&gt;
&lt;section id="python-version-management"&gt;
&lt;h3&gt;Python Version Management&lt;/h3&gt;
&lt;p&gt;Currently the bootstrap process installs a fixed Python version as specified in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bootstrap.json&lt;/span&gt;&lt;/code&gt; configuration file.
When checking if python is installed, it only verifies that the major and minor version match (e.g., &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;3.11&lt;/span&gt;&lt;/code&gt;),
but does not check for patch versions (e.g., &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;3.11.4&lt;/span&gt;&lt;/code&gt; vs &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;3.11.5&lt;/span&gt;&lt;/code&gt;).
This can lead to situations that on different machines, slightly different patch versions of Python are installed.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;This might not be a big issue in most cases, but it can lead to inconsistencies in our particular case,
because we use the python &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;venv&lt;/span&gt;&lt;/code&gt; module to create the initial virtual environment,
which will include the standard library of the installed Python version.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The solution could be:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;to find all installed python versions in path and check if the exact version is already installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;it might be that python versions installed with scoop are not available in path, so we need to query scoop for installed versions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;if the exact version is not installed, install it with scoop&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bootstrap.py&lt;/span&gt;&lt;/code&gt; shall fail if the python interpreter version does not match exactly the configured version.
This can happen if the user runs the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pypeline&lt;/span&gt;&lt;/code&gt; including the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CreateVEnv&lt;/span&gt;&lt;/code&gt; step which calls directly the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bootstrap.py&lt;/span&gt;&lt;/code&gt; script.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="virtual-environment-management"&gt;
&lt;h3&gt;Virtual Environment Management&lt;/h3&gt;
&lt;p&gt;As mention above, the virtual environment is created in three steps:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;create an empty virtual environment with python &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;venv.create&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;install the package manager (e.g., poetry, uv, etc.) and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pip-system-certs&lt;/span&gt;&lt;/code&gt; for using the system SSL certificates&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;run the package manager to install the python dependencies&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There is a problem with the second step: the package manager and pip-system certs are installed with pip.
These packages have their own dependencies, which are not tracked by pip because pip has no support for &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.lock&lt;/span&gt;&lt;/code&gt; files.&lt;/p&gt;
&lt;p&gt;When step three is executed, the package manager might need to upgrade/downgrade some packages which were implicitly installed in step two
and this can cause crashes due to dependency conflicts.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="solution-1-user-defined-initial-packages"&gt;
&lt;h3&gt;Solution 1: User-defined Initial Packages&lt;/h3&gt;
&lt;p&gt;One way to solve this is to let the user define the list of package to install in step two in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bootstrap.json&lt;/span&gt;&lt;/code&gt; configuration file.
For example:&lt;/p&gt;
&lt;div class="highlight-json notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;python_version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;3.11.4&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;venv_initial_packages&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;poetry==2.1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pip-system-certs==2.2.1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;wrapt==1.14.0&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In case there is a conflict between the initial packages and the packages defined in the package manager lock file,
the user can adjust the versions in the configuration file accordingly.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="solution-2-install-package-manager-with-scoop"&gt;
&lt;h3&gt;Solution 2: Install Package Manager with Scoop&lt;/h3&gt;
&lt;p&gt;Another way could be to install both python and the package manager with scoop in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bootstrap.ps1&lt;/span&gt;&lt;/code&gt; script,
so that pip is not involved at all in step two. This means that package manager will install exactly the version specified in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.lock&lt;/span&gt;&lt;/code&gt; file
and therefore always have a consistent set of dependencies.&lt;/p&gt;
&lt;p&gt;The problem with this approach is that &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pip-system-certs&lt;/span&gt;&lt;/code&gt; is not available as a scoop package
and package installation will fail when ran against an on-premise PyPI server with self-signed certificates.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="package-manager-command"&gt;
&lt;h3&gt;Package manager command&lt;/h3&gt;
&lt;p&gt;Currently the command to install dependencies with the package manager is hardcoded in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bootstrap.py&lt;/span&gt;&lt;/code&gt; script
with extra arguments being supported via the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bootstrap.json&lt;/span&gt;&lt;/code&gt; configuration file.&lt;/p&gt;
&lt;div class="highlight-json notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;python_package_manager_args&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;--clean&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;We added the feature to support extra arguments because we needed for some projects to pass the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--clean&lt;/span&gt;&lt;/code&gt; to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pipenv&lt;/span&gt;&lt;/code&gt;
to remove unused packages already existing in the virtual environment.&lt;/p&gt;
&lt;p&gt;A better approach would be to let the user define the full command to install dependencies with the package manager.
For example:&lt;/p&gt;
&lt;div class="highlight-json notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;venv_install_command&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;poetry install --no-dev --clean&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This would give the user full control over how dependencies are installed.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/home/runner/work/cuinixam.github.io/cuinixam.github.io/docs/blogs/2025/spl_bootstrap.md&lt;/span&gt;, line 157)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;If all these improvements are implemented, the bootstrap process will be more reliable and flexible, ensuring consistent Python environments.&lt;/p&gt;
&lt;p&gt;The configuration will allow users to tailor the bootstrap process to their specific needs.&lt;/p&gt;
&lt;div class="highlight-json notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;python_version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;3.11.4&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;venv_initial_packages&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;poetry==2.1.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pip-system-certs==2.2.1&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;venv_install_command&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;poetry install --no-dev --clean&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This configuration will ensure that:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Python 3.11.4 is installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The package manager and pip-system-certs are installed with the specified versions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dependencies are installed with the specified command&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Have fun bootstrapping! ✌️&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2025/spl_bootstrap.html"/>
    <summary>Document headings start at H2, not H1 [myst.header]The pipeline implementation for our Software Product Line (SPL) repositories uses the pypeline Python application.
In order to start the pypeline application, we need to install Python (with a specified version) and create the Python virtual environment.
This step we call bootstrap and it is implemented here.</summary>
    <category term="bootstrap" label="bootstrap"/>
    <category term="python" label="python"/>
    <category term="venv" label="venv"/>
    <published>2025-11-24T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2025/yanga_spl_components.html</id>
    <title>SPL components in YANGA</title>
    <updated>2025-10-15T00:00:00+00:00</updated>
    <content type="html">&lt;section id="spl-components-in-yanga"&gt;

&lt;p&gt;When building &lt;em&gt;software products&lt;/em&gt;, you often need different &lt;em&gt;variants&lt;/em&gt; for different purposes (like a basic vs. a pro version).
These variants consist of &lt;em&gt;components&lt;/em&gt; that implement the features required for that particular variant.
Each variant might be built for different &lt;em&gt;platforms&lt;/em&gt;, different hardware environments with their own constraints and capabilities.&lt;/p&gt;
&lt;p&gt;When thinking about components, there are two main categories:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Components which implement your product’s features&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Components which provide the functionality for other components to run on a certain hardware platform&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sometimes you might hear these referred to as the application layer and drivers or low-level layer.&lt;/p&gt;
&lt;p&gt;It seems natural to have two separate places to configure these components:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;One place for components that define what your product does&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Another place for components that every product needs when targeting a specific platform&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Indeed in YANGA, one can configure variant components and platform components.&lt;/p&gt;
&lt;p&gt;Example for a variant configuration:&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Spa&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;The LED brightness pulsates and cycles through different colors&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;light_controller&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Light control algorithms&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;power_button&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="c1"&gt;# Power button handling&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# ... and other core features&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Example for a platform configuration:&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;arduino_uno&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;arduino_core&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;digital_pins&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;toolchain_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;arduino.cmake&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I happen to find a third use case that doesn’t fit neatly into either of these two categories: components that only some product variants need, and only on specific platforms.&lt;/p&gt;
&lt;p&gt;I had a component that it only worked for Arduino, but I used it only for one of the four variants I was building. It didn’t make sense to put it in the platform configuration, because not every product variant needed it. But it also didn’t make sense to put it in the variant configuration, because it was platform specific.&lt;/p&gt;
&lt;section id="three-places-for-components"&gt;
&lt;h2&gt;Three Places for Components&lt;/h2&gt;
&lt;p&gt;YANGA gives you three places to define components:&lt;/p&gt;
&lt;section id="in-your-product-variant"&gt;
&lt;h3&gt;1. In Your Product Variant&lt;/h3&gt;
&lt;p&gt;For components that define what your product does, regardless of platform.&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;WeatherStation&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;sensor_reader&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;data_logger&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;weather_predictor&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="in-your-platform-configuration"&gt;
&lt;h3&gt;2. In Your Platform Configuration&lt;/h3&gt;
&lt;p&gt;For components that every product needs when targeting this platform.&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;arduino_uno&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;arduino_core&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;digital_pins&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;toolchain_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;arduino.cmake&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="in-your-variant-s-platform-specific-section"&gt;
&lt;h3&gt;3. In Your Variant’s Platform-Specific Section&lt;/h3&gt;
&lt;p&gt;For components that only some products need, and only on specific platforms.&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;WeatherStation&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;sensor_reader&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;data_logger&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;arduino_uno&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;hc_sr04_ultrasonic&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Arduino-specific sensor&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;raspberry_pi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;camera_module&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;# RPi-specific camera&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Another use case for this third place is when you want to write integration tests. One can create components that combine together multiple components and only define them in the variant-platform section to be used only in that context.&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="nt"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Disco&lt;/span&gt;
&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;The LED blinks and one can change the blinking frequency&lt;/span&gt;
&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;spled&lt;/span&gt;
&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;power_signal_processing&lt;/span&gt;
&lt;span class="linenos"&gt; 7&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;light_controller&lt;/span&gt;
&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;features_selection_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;variants/Disco/config.txt&lt;/span&gt;
&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;gtest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span class="hll"&gt;&lt;span class="linenos"&gt;11&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;test_integrations_spled&lt;/span&gt;&lt;span class="p p-Indicator"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Three places to define components might seem like overkill, but they help keep a clear structure in your project configuration.
Each place has its purpose, and using them correctly can make your project easier to manage as it grows in complexity.&lt;/p&gt;
&lt;p&gt;If you want to learn more about YANGA please check the &lt;a class="reference external" href="https://yanga.readthedocs.io/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;✌️&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2025/yanga_spl_components.html"/>
    <summary>When building software products, you often need different variants for different purposes (like a basic vs. a pro version).
These variants consist of components that implement the features required for that particular variant.
Each variant might be built for different platforms, different hardware environments with their own constraints and capabilities.When thinking about components, there are two main categories:</summary>
    <category term="component" label="component"/>
    <category term="spl" label="spl"/>
    <category term="variant" label="variant"/>
    <category term="yanga" label="yanga"/>
    <published>2025-10-15T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2025/spled_meets_arduino.html</id>
    <title>SPLED meets Arduino Uno</title>
    <updated>2025-10-03T00:00:00+00:00</updated>
    <content type="html">&lt;section id="spled-meets-arduino-uno"&gt;

&lt;p&gt;For our Software Product Line Engineering (SPLE) training we use the &lt;a class="reference external" href="https://github.com/avengineers/SPLed"&gt;SPLED&lt;/a&gt; repository,
which is a simple software product line for controlling an LED with different variants:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Disco: the LED blinks and one can change the blinking frequency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sleep: the LED has a constant color and one can change the brightness&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spa: the LED brightness pulsates and cycles through different colors&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The current implementation creates an executable which can be run in the terminal and uses ANSI escape codes to change the color of the text.&lt;/p&gt;
&lt;p&gt;What I want to do is to port this example to an Arduino Uno, so that we can control an RGB LED with it.&lt;/p&gt;
&lt;p&gt;I will not use the CMake build environment based on the &lt;a class="reference external" href="https://github.com/avengineers/spl-core"&gt;spl-core&lt;/a&gt; CMake modules,
but instead use &lt;a class="reference external" href="https://github.com/cuinixam/yanga"&gt;YANGA&lt;/a&gt; which is a software product line build environment written in Python.
YANGA uses &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.yaml&lt;/span&gt;&lt;/code&gt; configuration files and generates the CMake files for the actual build.&lt;/p&gt;
&lt;p&gt;Although YANGA is platform independent, SPLED tools dependencies are only defined as &lt;a class="reference external" href="https://scoop.sh/"&gt;scoop&lt;/a&gt; packages for Windows.
I am using a Windows 11 machine for this hacking session.&lt;/p&gt;
&lt;section id="get-the-code"&gt;
&lt;h2&gt;Get the code&lt;/h2&gt;
&lt;p&gt;I forked SPLED and created the YANGA configuration files to build the different variants as with the original spl-core environment.&lt;/p&gt;
&lt;p&gt;Clone the repository and setup the virtual environment to be able to run yanga:&lt;/p&gt;
&lt;div class="highlight-powershell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;clone&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-branch&lt;/span&gt; &lt;span class="n"&gt;arduino_platform&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-single-branch&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;://&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cuinixam&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;SPLed&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:\&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;spled&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:\&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;spled&lt;/span&gt;
&lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ps1&lt;/span&gt; &lt;span class="n"&gt;-install&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;We can check the SPL configuration with:&lt;/p&gt;
&lt;div class="highlight-powershell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;yanga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ps1&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-print&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This should print the variants, components and supported platforms:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;PS&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;\&lt;span class="n"&gt;temp&lt;/span&gt;\&lt;span class="n"&gt;spled&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;\&lt;span class="n"&gt;yanga&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ps1&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;
&lt;span class="n"&gt;START&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Starting&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;-----------------------------------------&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Project&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;\&lt;span class="n"&gt;temp&lt;/span&gt;\&lt;span class="n"&gt;spled&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Parsed&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Found&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Found&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;BlinkLed&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Dev&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Disco&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Sleep&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Spa&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Found&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;win_exe&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;gtest&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;arduino_nano&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;arduino_uno_r3&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Found&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;CreateVEnv&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;ScoopInstall&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;WestInstall&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;GenerateEnvSetupScript&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gen&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;KConfigGen&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;GenerateBuildSystemFiles&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;ExecuteBuild&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;-----------------------------------------&lt;/span&gt;
&lt;span class="n"&gt;STOP&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Finished&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;PS&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;\&lt;span class="n"&gt;temp&lt;/span&gt;\&lt;span class="n"&gt;spled&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Our focus will be to build the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Spa&lt;/span&gt;&lt;/code&gt; variant for the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;arduino_uno_r3&lt;/span&gt;&lt;/code&gt; platform.&lt;/p&gt;
&lt;section id="build-the-spa-variant-for-arduino-uno-r3"&gt;
&lt;h3&gt;Build the Spa variant for Arduino Uno R3&lt;/h3&gt;
&lt;p&gt;In order to build the Spa variant for the Arduino platform we have to make sure that the Windows executable (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;win_exe&lt;/span&gt;&lt;/code&gt; platform) specific components are not included when building for Arduino.
YANGA supports variant and platform specific components, so we can define that some components are only included for the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;win_exe&lt;/span&gt;&lt;/code&gt; platform.&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Spa&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;The LED brightness pulsates and can cycle through different colors&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;rte&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;spled&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;power_signal_processing&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;light_controller&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;power_button&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;main_control_knob&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;brightness_controller&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;features_selection_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;variants/Spa/config.txt&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;win_exe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# Windows main&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;main&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# Simulates a period task using `usleep`&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;os&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# Prints to console using ANSI escape codes&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;console_interface&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# Reads key presses to detect `power on/off` and `up/down`&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;keyboard_interface&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The variant platform specific components should be used to defined components which are relevant for this specific variant when built for the given platform.
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;main&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;os&lt;/span&gt;&lt;/code&gt; components are needed actually by all variants when built for &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;win_exe&lt;/span&gt;&lt;/code&gt;, so placing them in the variant platform specific section is not ideal.&lt;/p&gt;
&lt;p&gt;For this, YANGA supports platform specific components at the platform level, which would be a better place to define these components.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;To be continued …&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2025/spled_meets_arduino.html"/>
    <summary>For our Software Product Line Engineering (SPLE) training we use the SPLED repository,
which is a simple software product line for controlling an LED with different variants:Disco: the LED blinks and one can change the blinking frequency</summary>
    <category term="arduino" label="arduino"/>
    <category term="spl" label="spl"/>
    <category term="spled" label="spled"/>
    <category term="windows" label="windows"/>
    <category term="yanga" label="yanga"/>
    <published>2025-10-03T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2025/what_you_did_not_want_to_know_about_your_code.html</id>
    <title>What you did not want to know about your code</title>
    <updated>2025-09-26T00:00:00+00:00</updated>
    <content type="html">&lt;section id="what-you-did-not-want-to-know-about-your-code"&gt;

&lt;p&gt;As any other engineer, I also like to hack around in the weekends at six o’clock in the morning when the family is still asleep.
This time I wanted to play around with the new Matter smart home connectivity standard and add some temperature and humidity sensors to my smart home setup.&lt;/p&gt;
&lt;p&gt;I bought some &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;esp32-h2&lt;/span&gt;&lt;/code&gt; development boards (they support WiFi, Thread and BLE), some &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;SHT31-D&lt;/span&gt;&lt;/code&gt; sensors and started the old fashioned read the docs to get started.
I must say the &lt;a class="reference external" href="https://docs.espressif.com/projects/esp-matter/en/latest/esp32/developing.html"&gt;espressif docs&lt;/a&gt; are well written and the community is very active.&lt;/p&gt;
&lt;p&gt;I am using a WSL Ubuntu on Windows 11 and getting the build environment to work was not as straightforward as I hoped:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;idf.py&lt;/span&gt;&lt;/code&gt; build system comes in a separate repository and needs to be set up to work&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the esp-matter repo is huge and takes a while to clone&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getting the esp32 device visible as &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/dev/ttyUSB0&lt;/span&gt;&lt;/code&gt; was tricky&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After a while I got the first example compiled and flashed to the device.&lt;/p&gt;
&lt;p&gt;What I have noticed though was that the resulting binary was quite large (over 1.5MB) and I wondered what is actually in there.&lt;/p&gt;
&lt;p&gt;Instead of just looking at the final binary and the object files, I thought it would be more interesting to see it as a dependency graph between the object files.
For this I added a new command in a Python app I use to parse and analyze C/C++ sources, called &lt;a class="reference external" href="https://github.com/cuinixam/clanguru"&gt;clanguru&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The new &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;analyze&lt;/span&gt;&lt;/code&gt; command does the following:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;parse the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;compile_commands.json&lt;/span&gt;&lt;/code&gt; file to find all the object files&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;run &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;nm&lt;/span&gt;&lt;/code&gt; on each object file to get the global symbols and determine their type:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;extern&lt;/span&gt;&lt;/code&gt; for undefined globals - these are the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;required&lt;/span&gt;&lt;/code&gt; symbols&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;local&lt;/span&gt;&lt;/code&gt; for local globals - these are the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;provided&lt;/span&gt;&lt;/code&gt; symbols&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;create an html report using &lt;a class="reference external" href="https://cytoscape.org/"&gt;cytoscape.js&lt;/a&gt; to visualize the dependencies between the object files.
I also added an option to group the object files in named containers based on their path.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is a nice interactive graph where you can zoom in and out, drag the nodes around and enjoy the complexity of the code base.&lt;/p&gt;
&lt;p&gt;For my project it looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="dependency graph" src="https://maxiniuc.com/_images/objects_dependencies_graph.png" /&gt;&lt;/p&gt;
&lt;p&gt;You can find the actual report &lt;a class="reference external" href="https://maxiniuc.com/objects_deps/index.html"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/home/runner/work/cuinixam.github.io/cuinixam.github.io/docs/blogs/2025/what_you_did_not_want_to_know_about_your_code.md&lt;/span&gt;, line 46)&lt;/p&gt;
&lt;p&gt;Non-consecutive header level increase; H1 to H3 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="use-clanguru-to-analyze-your-c-c-code"&gt;
&lt;h2&gt;Use clanguru to analyze your C/C++ code&lt;/h2&gt;
&lt;p&gt;Install clanguru using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pipx&lt;/span&gt;&lt;/code&gt; for isolated installation:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;pipx&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;clanguru&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Check the help for the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;analyze&lt;/span&gt;&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;clanguru&lt;/span&gt; &lt;span class="n"&gt;analyze&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;help&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You need to build your project and provide the compilation database (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;compile_commands.json&lt;/span&gt;&lt;/code&gt;).
Also, ensure that the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;nm&lt;/span&gt;&lt;/code&gt; command-line tool is available in your user PATH.&lt;/p&gt;
&lt;p&gt;To generate an html dynamic dependency report, run the following command:&lt;/p&gt;
&lt;div class="highlight-shell notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;clanguru&lt;span class="w"&gt; &lt;/span&gt;analyze&lt;span class="w"&gt; &lt;/span&gt;--compilation-database&lt;span class="w"&gt; &lt;/span&gt;compile_commands.json&lt;span class="w"&gt; &lt;/span&gt;--output-file&lt;span class="w"&gt; &lt;/span&gt;dependencies.html&lt;span class="w"&gt; &lt;/span&gt;--use-parent-deps
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--use-parent-deps&lt;/span&gt;&lt;/code&gt; option will group the object files based on their parent directory.&lt;/p&gt;
&lt;p&gt;If the output file extension is &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.xlsx&lt;/span&gt;&lt;/code&gt;, an Excel report will be generated instead of an HTML one.&lt;/p&gt;
&lt;p&gt;If you have any questions or feedback, feel free to create an issue in the clanguru GitHub repository.&lt;/p&gt;
&lt;p&gt;Have fun exploring your code dependencies! 😎&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2025/what_you_did_not_want_to_know_about_your_code.html"/>
    <summary>As any other engineer, I also like to hack around in the weekends at six o’clock in the morning when the family is still asleep.
This time I wanted to play around with the new Matter smart home connectivity standard and add some temperature and humidity sensors to my smart home setup.I bought some esp32-h2 development boards (they support WiFi, Thread and BLE), some SHT31-D sensors and started the old fashioned read the docs to get started.
I must say the espressif docs are well written and the community is very active.dependency graph</summary>
    <category term="c" label="c"/>
    <category term="clanguru" label="clanguru"/>
    <category term="cytoscape" label="cytoscape"/>
    <category term="dependency" label="dependency"/>
    <category term="nm" label="nm"/>
    <category term="objects" label="objects"/>
    <published>2025-09-26T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2025/c_portable_pointer_size.html</id>
    <title>Why unit tests crashed on windows but worked on the microcontroller</title>
    <updated>2025-09-24T00:00:00+00:00</updated>
    <content type="html">&lt;section id="why-unit-tests-crashed-on-windows-but-worked-on-the-microcontroller"&gt;

&lt;p&gt;When writing cross-platform code, it’s easy to forget that &lt;strong&gt;pointers don’t always have the same size&lt;/strong&gt;.
This caused a segmentation fault in one of our unit tests.&lt;/p&gt;
&lt;section id="what-happened"&gt;
&lt;h2&gt;What happened&lt;/h2&gt;
&lt;p&gt;On the Aurix MCU, pointers are 32-bit.
In the code a pointer (memory address) was stored in a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;uint32_t&lt;/span&gt;&lt;/code&gt; variable.
That worked fine on the target.&lt;/p&gt;
&lt;p&gt;But, when running the unit tests on the PC (Windows with GCC), pointers are &lt;strong&gt;64-bit&lt;/strong&gt;.
Storing a 64-bit pointer into a 32-bit integer truncated the address.
The compiler only issued a warning and still compiled.&lt;/p&gt;
&lt;p&gt;Later, when the truncated value was cast back to a pointer and dereferenced, it pointed to invalid memory and crashed with a &lt;strong&gt;segmentation fault&lt;/strong&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="how-to-fix-it-and-make-the-code-portable"&gt;
&lt;h2&gt;How to fix it and make the code portable&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;🚫 Never store a pointer in a fixed-width integer type (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;uint32_t&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;uint64_t&lt;/span&gt;&lt;/code&gt;, …).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ Always use &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;uintptr_t&lt;/span&gt;&lt;/code&gt; from &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;lt;stdint.h&amp;gt;&lt;/span&gt;&lt;/code&gt; because it is guaranteed to be wide enough for a pointer on the current platform.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="wrong-vs-right"&gt;
&lt;h2&gt;Wrong vs Right&lt;/h2&gt;
&lt;div class="highlight-c notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdint.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;inttypes.h&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// ❌ Wrong: pointer stored in fixed 32-bit type&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wrong&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;// Works on 32-bit MCU, truncates on 64-bit host&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;wrong&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;// may segfault&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;wrong addr = 0x%08&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PRIx32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wrong&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// ✅ Right: use uintptr_t (pointer-sized integer type)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;good&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;right addr = 0x%&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PRIxPTR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;, *good = %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;good&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Both &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;PRIx32&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;PRIxPTR&lt;/span&gt;&lt;/code&gt; come from &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;lt;inttypes.h&amp;gt;&lt;/span&gt;&lt;/code&gt;.
They expand to the correct &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;printf&lt;/span&gt;&lt;/code&gt; format string for the given integer type:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;PRIx32&lt;/span&gt;&lt;/code&gt; - prints a 32-bit integer in hex.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;PRIxPTR&lt;/span&gt;&lt;/code&gt; - prints a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;uintptr_t&lt;/span&gt;&lt;/code&gt; (pointer-sized integer) in hex.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This ensures portable and correct logging across platforms.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="final-thought"&gt;
&lt;h2&gt;Final thought&lt;/h2&gt;
&lt;p&gt;Using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;uintptr_t&lt;/span&gt;&lt;/code&gt; instead of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;uint32_t&lt;/span&gt;&lt;/code&gt; to store pointers makes the code portable when running unit tests on a 64-bit host.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2025/c_portable_pointer_size.html"/>
    <summary>When writing cross-platform code, it’s easy to forget that pointers don’t always have the same size.
This caused a segmentation fault in one of our unit tests.On the Aurix MCU, pointers are 32-bit.
In the code a pointer (memory address) was stored in a uint32_t variable.
That worked fine on the target.</summary>
    <category term="c" label="c"/>
    <category term="gtest" label="gtest"/>
    <category term="pointer" label="pointer"/>
    <category term="portability" label="portability"/>
    <published>2025-09-24T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2025/coding_dojo_detect_ci_context.html</id>
    <title>Coding Dojo - Detect CI Context</title>
    <updated>2025-03-08T00:00:00+00:00</updated>
    <content type="html">&lt;section id="coding-dojo-detect-ci-context"&gt;

&lt;section id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Coding Dojos are a great way to practice and enhance your skills in Test-Driven Development (TDD), software craftsmanship, and incremental software design.
In this blog post, we’ll explore how to implement the &lt;a class="reference external" href="https://maxiniuc.com/coding_dojo_ci_context/presentation.html"&gt;Detect CI Context&lt;/a&gt; coding example using Python.&lt;/p&gt;
&lt;p&gt;You’ll learn how to:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Clearly define APIs and data structures.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Translate the requirements to tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement the solution incrementally using TDD.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Refactor code for clarity, maintainability, and scalability.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="understand-the-problem-and-define-the-api"&gt;
&lt;h2&gt;Understand the problem and define the API&lt;/h2&gt;
&lt;p&gt;The problem is to detect whether a program runs on Jenkins or locally.
The inputs are the specific environment variables that Jenkins sets when running a build.&lt;/p&gt;
&lt;p&gt;The expected outputs are:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;The CI system name (e.g. Jenkins or Unknown).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Is the build trigger a Pull Request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Name of the target branch (branch to merge into).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Name of the current branch being built or tested.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using our module should be as straightforward as calling a &lt;strong&gt;method&lt;/strong&gt; that returns the required &lt;strong&gt;data&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In Python, our method signature could look like this:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;detect_ci_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;We can use a data class to represent the CI context:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;#: CI system where the build is running&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="c1"&gt;#: Whether the build is for a pull request&lt;/span&gt;
    &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;
    &lt;span class="c1"&gt;#: The branch to merge into&lt;/span&gt;
    &lt;span class="n"&gt;target_branch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;#: Branch being built&lt;/span&gt;
    &lt;span class="n"&gt;current_branch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="implement-the-solution-incrementally"&gt;
&lt;h2&gt;Implement the Solution Incrementally&lt;/h2&gt;
&lt;p&gt;The way to determine the CI context for Jenkins can be summarized as follows:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Presence of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;JENKINS_HOME&lt;/span&gt;&lt;/code&gt; indicates Jenkins.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If running a pull request (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CHANGE_ID&lt;/span&gt;&lt;/code&gt;), &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CHANGE_TARGET&lt;/span&gt;&lt;/code&gt; is the target branch and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CHANGE_BRANCH&lt;/span&gt;&lt;/code&gt; is the current branch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If not a pull request, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;BRANCH_NAME&lt;/span&gt;&lt;/code&gt; is the current branch.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;section id="first-requirement-detect-jenkins"&gt;
&lt;h3&gt;First requirement - Detect Jenkins&lt;/h3&gt;
&lt;p&gt;Let’s start by writing a test that checks if the CI system is Jenkins.&lt;/p&gt;
&lt;p&gt;Our test should:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Set the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;JENKINS_HOME&lt;/span&gt;&lt;/code&gt; environment variable to a non-empty value.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Call the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;detect_ci_context&lt;/span&gt;&lt;/code&gt; method.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check if the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;name&lt;/span&gt;&lt;/code&gt; is &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;JENKINS&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first issue we encounter is to be able to manipulate the environment variables.&lt;/p&gt;
&lt;p&gt;In Python, we can mock the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;os.getenv&lt;/span&gt;&lt;/code&gt; function using the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;unittest.mock&lt;/span&gt;&lt;/code&gt; module:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;mock_on_getenv&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;os.getenv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;mock_os_getenv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;mock_os_getenv&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;What this fixture does is to replace the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;os.getenv&lt;/span&gt;&lt;/code&gt; function with a mock object that we can control.&lt;/p&gt;
&lt;p&gt;Now we can write the test:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_jenkins_branch_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_on_getenv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MagicMock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Setup&lt;/span&gt;
    &lt;span class="n"&gt;mock_on_getenv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;side_effect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;JENKINS_HOME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/jenkins/home&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Run&lt;/span&gt;
    &lt;span class="n"&gt;ci_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detect_ci_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# Check&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;ci_context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;JENKINS&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;We override the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;os.getenv&lt;/span&gt;&lt;/code&gt; function with a &lt;a class="reference external" href="https://realpython.com/ref/keywords/lambda"&gt;lambda&lt;/a&gt; that returns the value of the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;JENKINS_HOME&lt;/span&gt;&lt;/code&gt; environment variable. It has the same signature as the original function, taking two arguments: the variable name and a default value.&lt;/p&gt;
&lt;p&gt;To fix the test, we need to implement the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;detect_ci_context&lt;/span&gt;&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;detect_ci_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;JENKINS&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;target_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;current_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;You might be surprised that we hardcoded the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;name&lt;/span&gt;&lt;/code&gt; to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;JENKINS&lt;/span&gt;&lt;/code&gt;.
This is one idea behind &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Test-driven_development"&gt;TDD&lt;/a&gt;: write the simplest code that makes the test pass.&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="second-requirement-detect-unknown"&gt;
&lt;h3&gt;Second requirement - Detect Unknown&lt;/h3&gt;
&lt;p&gt;Let’s write a test that checks if the CI system is unknown:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_unknown_ci_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_on_getenv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MagicMock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Setup&lt;/span&gt;
    &lt;span class="n"&gt;mock_on_getenv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;side_effect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="c1"&gt;# Run&lt;/span&gt;
    &lt;span class="n"&gt;ci_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detect_ci_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# Check&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;ci_context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Unknown&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To fix the test and do not break the previous one, we can’t just return a constant value but need to &lt;em&gt;check&lt;/em&gt; the environment variables:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;detect_ci_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;JENKINS&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;JENKINS_HOME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Unknown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;target_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;current_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="implement-the-rest-of-the-requirements"&gt;
&lt;h3&gt;Implement the rest of the requirements&lt;/h3&gt;
&lt;p&gt;We can continue implementing the rest of the requirements in the same way, writing tests and fixing them by implementing the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;detect_ci_context&lt;/span&gt;&lt;/code&gt; method.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;detect_ci_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ci_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;UNKNOWN&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;is_pull_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;target_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;current_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;JENKINS_HOME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ci_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;JENKINS&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;is_pull_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CHANGE_ID&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;target_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CHANGE_TARGET&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;current_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CHANGE_BRANCH&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;target_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;BRANCH_NAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;current_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target_branch&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ci_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;target_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;target_branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;current_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;current_branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="refactor"&gt;
&lt;h2&gt;Refactor&lt;/h2&gt;
&lt;p&gt;First thing that looks odd is the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CIContext.name&lt;/span&gt;&lt;/code&gt; variable type. Strings are too generic and can lead to errors. Imagine a typo in the string or different capitalization, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;JENKINS&lt;/span&gt;&lt;/code&gt; vs &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Jenkins&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can use an enumeration to represent the CI system:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;CISystem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;JENKINS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;UNKNOWN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;and update the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CIContext&lt;/span&gt;&lt;/code&gt; class:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;#: CI system where the build is running&lt;/span&gt;
    &lt;span class="n"&gt;ci_system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CISystem&lt;/span&gt;
    &lt;span class="c1"&gt;#: Whether the build is for a pull request&lt;/span&gt;
    &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;
    &lt;span class="c1"&gt;#: The branch to merge into&lt;/span&gt;
    &lt;span class="n"&gt;target_branch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;#: Branch being built&lt;/span&gt;
    &lt;span class="n"&gt;current_branch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ci_system&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;This refactoring caused no tests to fail nor did it change the API.&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="new-feature-request"&gt;
&lt;h2&gt;New Feature Request&lt;/h2&gt;
&lt;p&gt;There is a new requirement to detect &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;GitHub&lt;/span&gt; &lt;span class="pre"&gt;Actions&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The way to determine the CI context for &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;GitHub&lt;/span&gt; &lt;span class="pre"&gt;Actions&lt;/span&gt;&lt;/code&gt; can be summarized as follows:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;GITHUB_ACTIONS&lt;/span&gt;&lt;/code&gt; indicates GitHub Actions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If running a pull request (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;GITHUB_EVENT_NAME&lt;/span&gt;&lt;/code&gt; is &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pull_request&lt;/span&gt;&lt;/code&gt;), &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;GITHUB_BASE_REF&lt;/span&gt;&lt;/code&gt; is the target branch and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;GITHUB_HEAD_REF&lt;/span&gt;&lt;/code&gt; is the current branch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If not a pull request, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;GITHUB_REF_NAME&lt;/span&gt;&lt;/code&gt; is the current branch.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;section id="first-requirement-detect-github-actions"&gt;
&lt;h3&gt;First requirement - Detect Github Actions&lt;/h3&gt;
&lt;p&gt;We can write a test for this new requirement:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_github_actions_pull_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_on_getenv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MagicMock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Setup&lt;/span&gt;
    &lt;span class="n"&gt;mock_on_getenv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;side_effect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;GITHUB_ACTIONS&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Run&lt;/span&gt;
    &lt;span class="n"&gt;ci_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detect_ci_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# Check&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;ci_context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;GITHUB_ACTIONS&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;We can implement the new feature by extending the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;detect_ci_context&lt;/span&gt;&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;detect_ci_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ci_system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CISystem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;
    &lt;span class="n"&gt;is_pull_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;target_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;current_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;JENKINS_HOME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ci_system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CISystem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JENKINS&lt;/span&gt;
        &lt;span class="n"&gt;is_pull_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CHANGE_ID&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;target_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CHANGE_TARGET&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;current_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CHANGE_BRANCH&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;target_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;BRANCH_NAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;current_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target_branch&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GITHUB_ACTIONS&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ci_system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CISystem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GITHUB_ACTIONS&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ci_system&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cis_system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;target_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;target_branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;current_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;current_branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="id1"&gt;
&lt;h3&gt;Implement the rest of the requirements&lt;/h3&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: INFO/1 (&lt;span class="docutils literal"&gt;/home/runner/work/cuinixam.github.io/cuinixam.github.io/docs/blogs/2025/coding_dojo_detect_ci_context.md&lt;/span&gt;, line 263); &lt;em&gt;&lt;a href="#id1"&gt;backlink&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Duplicate implicit target name: “implement the rest of the requirements”.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;We can continue implementing the rest of the requirements in the same way, writing tests and fixing them by implementing the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;detect_ci_context&lt;/span&gt;&lt;/code&gt; method.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;detect_ci_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ci_system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CISystem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;
    &lt;span class="n"&gt;is_pull_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;target_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;current_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;JENKINS_HOME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ci_system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CISystem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JENKINS&lt;/span&gt;
        &lt;span class="n"&gt;is_pull_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CHANGE_ID&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;target_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CHANGE_TARGET&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;current_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CHANGE_BRANCH&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;target_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;BRANCH_NAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;current_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target_branch&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GITHUB_ACTIONS&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ci_system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CISystem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GITHUB_ACTIONS&lt;/span&gt;
        &lt;span class="n"&gt;is_pull_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GITHUB_EVENT_NAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;pull_request&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;target_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GITHUB_BASE_REF&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;current_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GITHUB_HEAD_REF&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;current_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GITHUB_REF_NAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;target_branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_branch&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ci_system&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ci_system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;target_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;target_branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;current_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;current_branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="id2"&gt;
&lt;h2&gt;Refactor&lt;/h2&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: INFO/1 (&lt;span class="docutils literal"&gt;/home/runner/work/cuinixam.github.io/cuinixam.github.io/docs/blogs/2025/coding_dojo_detect_ci_context.md&lt;/span&gt;, line 299); &lt;em&gt;&lt;a href="#id2"&gt;backlink&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Duplicate implicit target name: “refactor”.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;Is obvious that the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;detect_ci_context&lt;/span&gt;&lt;/code&gt; code smells. It does too many things and is not extensible.
Every time we add a new CI system, we need to modify this method. This is a violation of the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle"&gt;Open/Closed Principle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This translates to a new non-functional requirement: &lt;em&gt;Support more CI systems without modifying the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;detect_ci_context&lt;/span&gt;&lt;/code&gt; method.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To do this, “abstraction is the key”. We can create a new class that will be responsible for detecting the CI system.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;CIDetector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;We can now create a detector for every CI system and the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;detect_ci_context&lt;/span&gt;&lt;/code&gt; method will only iterate over the detectors and return the first result.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;detect_ci_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;detectors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;JenkinsDetector&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;GitHubActionsDetector&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;detector&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;detectors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ci_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detector&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ci_context&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ci_context&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ci_system&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CISystem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;target_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;current_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Hmm, we still have to modify the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;detect_ci_context&lt;/span&gt;&lt;/code&gt; method every time we add a new CI system detector 😧 and the new value in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CISystem&lt;/span&gt;&lt;/code&gt; enumeration.&lt;/p&gt;
&lt;p&gt;One solution could be to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;link&lt;/span&gt;&lt;/code&gt; the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CISystem&lt;/span&gt;&lt;/code&gt; enumeration with the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CIDetector&lt;/span&gt;&lt;/code&gt; class.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;CISystem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;UNKNOWN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Special case for unknown&lt;/span&gt;
    &lt;span class="n"&gt;JENKINS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;JenkinsDetector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Add new CI systems here:  MY_CI = (auto(), MyCIDetector)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detector_class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CIDetector&lt;/span&gt;&lt;span class="p"&gt;]]):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_value_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;  &lt;span class="c1"&gt;# Use auto() value, but ignore it in __init__&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;detector_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detector_class&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_detector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CIDetector&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;detector_class&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;detector_class&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Some of you might be surprised to see that one can add extra arguments to an enumeration member.&lt;/p&gt;
&lt;p&gt;What we’ve done is to add a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;detector_class&lt;/span&gt;&lt;/code&gt; attribute to each enumeration member to link it with the detector class. This way, we can create a detector for each CI system and the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;detect_ci_context&lt;/span&gt;&lt;/code&gt; method will only iterate over the detectors and return the first result.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;detect_ci_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ci_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ci_system&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;CISystem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;detector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ci_system&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_detector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;detector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;ci_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detector&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ci_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;  &lt;span class="c1"&gt;# Stop at the first detected CI&lt;/span&gt;
    &lt;span class="c1"&gt;# If no CI system was detected, return unknown CIContext&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ci_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CIContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;ci_system&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CISystem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;is_pull_request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;target_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;current_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ci_context&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Now, to add a new CI system, we only need to create the new detector class and add the enumeration member. 😎&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this blog post, we explored how to implement the &lt;a class="reference external" href="https://maxiniuc.com/coding_dojo_ci_context/presentation.html"&gt;Detect CI Context&lt;/a&gt; coding example using Python.&lt;/p&gt;
&lt;p&gt;We’ve learned how to:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;incrementally implement the solution using TDD, refactoring, and design patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;learned how to use the Unittest &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;patch&lt;/span&gt;&lt;/code&gt; method to manipulate the environment variables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;learned how to add extra arguments to an enumeration.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope you enjoyed this coding dojo and learned something new.&lt;/p&gt;
&lt;p&gt;Happy coding! ⌨️&lt;/p&gt;
&lt;/section&gt;
&lt;section id="references"&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://maxiniuc.com/coding_dojo_ci_context/presentation.html"&gt;Detect CI Context Presentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/cuinixam/HelloPython/blob/main/src/hello_python/ci_context.py"&gt;Source Code&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2025/coding_dojo_detect_ci_context.html"/>
    <summary>Coding Dojos are a great way to practice and enhance your skills in Test-Driven Development (TDD), software craftsmanship, and incremental software design.
In this blog post, we’ll explore how to implement the Detect CI Context coding example using Python.You’ll learn how to:</summary>
    <category term="coding-dojo" label="coding-dojo"/>
    <category term="python" label="python"/>
    <category term="software-craftsmanship" label="software-craftsmanship"/>
    <category term="tdd" label="tdd"/>
    <published>2025-03-08T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2025/vs_code_ctest.html</id>
    <title>Making Unit Tests Visible in Visual Studio Code with CTest and GoogleTest</title>
    <updated>2025-02-05T00:00:00+00:00</updated>
    <content type="html">&lt;section id="making-unit-tests-visible-in-visual-studio-code-with-ctest-and-googletest"&gt;

&lt;section id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Many C/C++ projects rely on CMake, GoogleTest (GTest), and GoogleMock (GMock) for unit testing.
Sometimes developers want to see and run/debug these tests directly in their IDE.&lt;/p&gt;
&lt;p&gt;In this blog post, I will show how to make the unit tests visible in Visual Studio Code (VS Code) using CTest and GoogleTest.
I will start by explaining the standard way to build and run the tests with CMake.
Then we will see how CTest can help us discover the tests and how to display them in the VS Code interface.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="building-and-running-tests-with-cmake-and-googletest"&gt;
&lt;h2&gt;Building and Running Tests with CMake and GoogleTest&lt;/h2&gt;
&lt;p&gt;In a typical setup one defines executables for each “component” with its own sources and unit tests files.
There are CMake custom targets for every component to link the component sources and the test sources and execute the tests.&lt;/p&gt;
&lt;p&gt;An instructive example of a CMakeLists.txt file is shown below:&lt;/p&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="nb"&gt;cmake_minimum_required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;VERSION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;3.20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="nb"&gt;project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;MyProject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="linenos"&gt; 3&lt;/span&gt;
&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;CMAKE_CXX_STANDARD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;CMAKE_CXX_STANDARD_REQUIRED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;gtest_force_shared_crt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;CACHE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;BOOL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;FORCE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="linenos"&gt; 7&lt;/span&gt;
&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="c"&gt;# Add GoogleTest components&lt;/span&gt;
&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="nb"&gt;add_subdirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/build/external/gtest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_BUILD_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/.gtest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="linenos"&gt;10&lt;/span&gt;
&lt;span class="linenos"&gt;11&lt;/span&gt;&lt;span class="c"&gt;# Configure directories to search for headers&lt;/span&gt;
&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="nb"&gt;include_directories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/build/external/gtest/googletest/include&lt;/span&gt;
&lt;span class="linenos"&gt;15&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/build/external/gtest/googlemock/include&lt;/span&gt;
&lt;span class="linenos"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="linenos"&gt;17&lt;/span&gt;
&lt;span class="linenos"&gt;18&lt;/span&gt;&lt;span class="c"&gt;# Create the test executable&lt;/span&gt;
&lt;span class="linenos"&gt;19&lt;/span&gt;&lt;span class="nb"&gt;add_executable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;greeter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;greeter.c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;greeter_test.cc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="linenos"&gt;20&lt;/span&gt;
&lt;span class="linenos"&gt;21&lt;/span&gt;&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;greeter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;GTest::gtest_main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;GTest::gmock_main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;pthread&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="linenos"&gt;22&lt;/span&gt;&lt;span class="nb"&gt;target_compile_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;greeter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;PRIVATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;-ggdb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--coverage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="linenos"&gt;23&lt;/span&gt;&lt;span class="nb"&gt;target_link_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;greeter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;PRIVATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--coverage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="linenos"&gt;24&lt;/span&gt;
&lt;span class="linenos"&gt;25&lt;/span&gt;&lt;span class="c"&gt;# Run the test executable, generate JUnit report and return success independent of the test result&lt;/span&gt;
&lt;span class="linenos"&gt;26&lt;/span&gt;&lt;span class="nb"&gt;add_custom_target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;greeter_test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ALL&lt;/span&gt;
&lt;span class="linenos"&gt;27&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;BYPRODUCTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_BUILD_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/greeter_junit.xml&lt;/span&gt;
&lt;span class="linenos"&gt;28&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;DEPENDS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;greeter&lt;/span&gt;
&lt;span class="linenos"&gt;29&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;COMMAND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;greeter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--gtest_output=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;xml:greeter_junit.xml&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_COMMAND&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;-E&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="linenos"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;What this basically does is:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;create a test executable &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;greeter&lt;/span&gt;&lt;/code&gt; that links the sources &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;greeter.c&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;greeter_test.cc&lt;/span&gt;&lt;/code&gt; with the GoogleTest libraries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;create a custom target &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;greeter_test&lt;/span&gt;&lt;/code&gt; that runs the test executable and generates a JUnit report&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To build and run the tests, one would typically execute the following commands:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;cmake&lt;span class="w"&gt; &lt;/span&gt;-B&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;.
cmake&lt;span class="w"&gt; &lt;/span&gt;--build&lt;span class="w"&gt; &lt;/span&gt;build
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This will build all targets and generate the test executable &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;greeter&lt;/span&gt;&lt;/code&gt; and the JUnit report &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;greeter_junit.xml&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id1"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;greeter_junit.xml&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-xml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="linenos"&gt;1&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;/span&gt;
&lt;span class="linenos"&gt;2&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;testsuites&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;tests=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;failures=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;disabled=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;errors=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;time=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;AllTests&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="linenos"&gt;3&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;testsuite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;GreeterTest&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;tests=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;failures=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;disabled=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;skipped=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;errors=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;time=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="linenos"&gt;4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;testcase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Greeting1&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;file=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;greeter_test.cc&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;line=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;14&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;status=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;run&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;result=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;completed&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;time=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;classname=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;GreeterTest&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="linenos"&gt;5&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/testsuite&amp;gt;&lt;/span&gt;
&lt;span class="linenos"&gt;6&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/testsuites&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="discovering-tests"&gt;
&lt;h2&gt;Discovering Tests&lt;/h2&gt;
&lt;p&gt;Let us first shortly discuss on how one would discover the tests in the project.&lt;/p&gt;
&lt;p&gt;Having the tests source files, one could try to parse them and extract the test cases.
However, this is not a trivial task and it is error-prone.
Imagine that a test is only enabled if a certain preprocessor definition is set.
One should properly parse the (preprocessed) test source files to determine the list of relevant tests.
I am not even touching on how creative some developers might be on creating their on macros to define tests.&lt;/p&gt;
&lt;p&gt;Another approach is to “ask” the test executable to list the tests.
This has the disadvantage that the test executable must be built and run.
On the good side, the test executable knows exactly which tests are available and can provide additional information about them.&lt;/p&gt;
&lt;p&gt;Indeed, the GTTest framework provides a way to list the tests.
Running the test executable with the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--gtest_list_tests&lt;/span&gt;&lt;/code&gt; flag will list all the tests and their test cases.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exe&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;gtest_list_tests&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Calling the test executable with the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--help&lt;/span&gt;&lt;/code&gt; option will print all the available options.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;We will go with the second approach and determine the tests by running the test executable.
CMake has a built-in tool called CTest that can help us with this.&lt;/p&gt;
&lt;section id="defining-and-running-tests-with-ctest"&gt;
&lt;h3&gt;Defining and running tests with CTest&lt;/h3&gt;
&lt;p&gt;CTest is a testing tool that is part of the CMake suite.
CMake facilitates testing through special commands and the CTest executable.&lt;/p&gt;
&lt;p&gt;The integration is quite simple. One needs to “enable testing” and define tests using the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;add_test&lt;/span&gt;&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;CTest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# automatically calls enable_testing()&lt;/span&gt;
&lt;span class="nb"&gt;add_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;NAME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;greeter_test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;COMMAND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;add_test&lt;/span&gt;&lt;/code&gt; command has a simple syntax:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;NAME&lt;/span&gt;&lt;/code&gt; - the name of the test, can be any string&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;COMMAND&lt;/span&gt;&lt;/code&gt; - the command to run the test, in our case the test executable &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;greeter&lt;/span&gt;&lt;/code&gt;. This can be any command that is run in the shell and returns a status code (non-zero for failure).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To run the tests, one would execute the following command:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;
&lt;span class="n"&gt;ctest&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;CTest has many options to control the test execution, like running only a specific test, running tests in parallel, or generating a JUnit report.
For more information check the help or the documentation.&lt;/p&gt;
&lt;section id="details-on-how-cmake-ctest-work"&gt;
&lt;h4&gt;Details on how CMake/CTest work&lt;/h4&gt;
&lt;p&gt;When testing is enabled, CMake generates a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CTestTestfile.cmake&lt;/span&gt;&lt;/code&gt; file in the build directory.
This file will include all the tests defined with the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;add_test&lt;/span&gt;&lt;/code&gt; command.
When running &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ctest&lt;/span&gt;&lt;/code&gt;, CTest will read this file and execute the tests.&lt;/p&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c"&gt;# CMake generated Testfile&lt;/span&gt;
&lt;span class="nb"&gt;add_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Multiline"&gt;[=[greeter_test]=]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C:/project/build/greeter.exe&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set_tests_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Multiline"&gt;[=[greeter_test]=]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;PROPERTIES&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;_BACKTRACE_TRIPLES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C:/project/CMakeLists.txt;34;add_test;C:/project/CMakeLists.txt;0;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;subdirs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/.gtest&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition important"&gt;
&lt;p class="admonition-title"&gt;Important&lt;/p&gt;
&lt;p&gt;This file is generated during CMake configure and does not require the test executable to be built.
It is only used by CTest to run the tests, like having a target to run the test executable.&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="discovering-tests-with-googletest-ctest-integration"&gt;
&lt;h3&gt;Discovering tests with GoogleTest CTest integration&lt;/h3&gt;
&lt;p&gt;CMake provides a helpful module &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;GoogleTest&lt;/span&gt;&lt;/code&gt; which simplifies test discovery and registration when using GTest:&lt;/p&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;CTest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;GoogleTest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Automatically discover all GoogleTest test cases in the greeter binary&lt;/span&gt;
&lt;span class="nb"&gt;gtest_discover_tests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;greeter&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c"&gt;# Optional: specify a WORKING_DIRECTORY or other properties&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This approach removes the need to manually call add_test(NAME &amp;lt;some_name&amp;gt; COMMAND &amp;lt;test_executable&amp;gt;) for each test.
Instead, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;gtest_discover_tests&lt;/span&gt;&lt;/code&gt; takes care of enumerating them by parsing the output of the actual test binary.&lt;/p&gt;
&lt;section id="details-on-how-googletest-ctest-integration-works"&gt;
&lt;h4&gt;Details on how GoogleTest CTest integration works&lt;/h4&gt;
&lt;p&gt;When using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;gtest_discover_tests&lt;/span&gt;&lt;/code&gt;, CMake generates a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CTestTestfile.cmake&lt;/span&gt;&lt;/code&gt; file that includes the tests discovered by GoogleTest.&lt;/p&gt;
&lt;p&gt;After CMake configure (no tests executable are yet built nor executed) there is a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CTestTestfile.cmake&lt;/span&gt;&lt;/code&gt; file that includes the tests discovered by GoogleTest.
There is a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;lt;component&amp;gt;_include.cmake&lt;/span&gt;&lt;/code&gt; file generated for every component which is just defines a dummy test.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id2"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;greeeter[1]_include.cmake&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C:/build/greeter[1]_tests.cmake&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C:/build/greeter[1]_tests.cmake&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;else&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;add_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;greeter_NOT_BUILT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;greeter_NOT_BUILT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Executing &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ctest&lt;/span&gt;&lt;/code&gt; will fail because the command &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;greeter_NOT_BUILT&lt;/span&gt;&lt;/code&gt; does not exist.&lt;/p&gt;
&lt;div class="admonition important"&gt;
&lt;p class="admonition-title"&gt;Important&lt;/p&gt;
&lt;p&gt;CMake will add a custom command (see &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;build.ninja&lt;/span&gt;&lt;/code&gt; build file) to generate the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;greeter_tests.cmake&lt;/span&gt;&lt;/code&gt; file.
This command will run the test executable and parse the output to generate the test cases.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;After building the test executable, the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;greeeter[1]_tests.cmake&lt;/span&gt;&lt;/code&gt; file is generated and this contains all tests discovered by GoogleTest.&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id3"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;greeeter[1]_tests.cmake&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;add_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Multiline"&gt;[=[GreeterTest.Greeting1]=]&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;C:/project/build/greeter.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s s-Multiline"&gt;[==[--gtest_filter=GreeterTest.Greeting1]==]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--gtest_also_run_disabled_tests&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set_tests_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Multiline"&gt;[=[GreeterTest.Greeting1]=]&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;PROPERTIES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;WORKING_DIRECTORY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;C:/project/build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;SKIP_REGULAR_EXPRESSION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s s-Multiline"&gt;[==[\[  SKIPPED \]]==]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;add_test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Multiline"&gt;[=[GreeterTest.Greeting2]=]&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;C:/project/build/greeter.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s s-Multiline"&gt;[==[--gtest_filter=GreeterTest.Greeting2]==]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--gtest_also_run_disabled_tests&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set_tests_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Multiline"&gt;[=[GreeterTest.Greeting2]=]&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;PROPERTIES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;WORKING_DIRECTORY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;C:/project/build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;SKIP_REGULAR_EXPRESSION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s s-Multiline"&gt;[==[\[  SKIPPED \]]==]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;greeter_TESTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;GreeterTest.Greeting1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;GreeterTest.Greeting2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Executing &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ctest&lt;/span&gt;&lt;/code&gt; will now run the tests and generate the JUnit report.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="vs-code-integration"&gt;
&lt;h3&gt;VS Code integration&lt;/h3&gt;
&lt;p&gt;The CMake Tools extension for VS Code supports since version 1.14 a Test Explorer for CTest.
The extension will automatically detect the CTest tests from the generated CTest files and display them in the Test Explorer.&lt;/p&gt;
&lt;p&gt;This means that one can run, debug, and see the test results directly in the VS Code interface.&lt;/p&gt;
&lt;p&gt;Of course, this approach inherits all the “limitations” of discovering the tests with CTest.
After CMake configure, one will see only “_NOT_BUILT” tests in the Test Explorer.&lt;/p&gt;
&lt;p&gt;&lt;img alt="NOT_BUILT tests displayed" src="https://maxiniuc.com/_images/not_build_tests.png" /&gt;&lt;/p&gt;
&lt;p&gt;Only after building the test executable, the tests will be visible.&lt;/p&gt;
&lt;p&gt;&lt;img alt="All tests displayed" src="https://maxiniuc.com/_images/all_tests.png" /&gt;&lt;/p&gt;
&lt;div class="admonition important"&gt;
&lt;p class="admonition-title"&gt;Important&lt;/p&gt;
&lt;p&gt;When executing the tests from the Test Explorer, before running the tests, the CMake Tools extension will start the current select CMake build target.
This might be confusing if one switches between build targets and suddenly when executing a test, a target is build which has nothing to do with the test.&lt;/p&gt;
&lt;p&gt;I think their idea is to ensure that the test executable is built before running the tests, to update the test cases.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;When executing single tests, CTest is called with the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--tests-regex&lt;/span&gt;&lt;/code&gt; option to run only the selected tests.
The problem is that when a test fails there is no way to go from the Test Explorer to the failing test.
This is a general issue with CTest that there is no automatic traceability between the test name and the test source file and line.
There is another VS Code extension &lt;a class="reference external" href="https://marketplace.visualstudio.com/items?itemName=fredericbonnet.cmake-test-adapter"&gt;CMake Test Explorer&lt;/a&gt;
which attempts to address this issue by using the CTest properties feature. I did not test this extension. Maybe in a future blog post.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Integrating the unit tests in the VS Code interface is a nice feature to have. CTest and GoogleTest provide a good way to discover and run the tests.
While not perfect, the integration with the CMake Tools extension for VS Code is a good start.
One can run, debug, and see the test results directly in the VS Code interface.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="references"&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://cmake.org/cmake/help/book/mastering-cmake/chapter/Testing%20With%20CMake%20and%20CTest.html"&gt;Testing With CMake and CTest&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/microsoft/vscode-cmake-tools"&gt;VS Code CMake Tools Extension&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2025/vs_code_ctest.html"/>
    <summary>Many C/C++ projects rely on CMake, GoogleTest (GTest), and GoogleMock (GMock) for unit testing.
Sometimes developers want to see and run/debug these tests directly in their IDE.In this blog post, I will show how to make the unit tests visible in Visual Studio Code (VS Code) using CTest and GoogleTest.
I will start by explaining the standard way to build and run the tests with CMake.
Then we will see how CTest can help us discover the tests and how to display them in the VS Code interface.NOT_BUILT tests displayed</summary>
    <category term="cmake" label="cmake"/>
    <category term="ctest" label="ctest"/>
    <category term="gtest" label="gtest"/>
    <category term="testing" label="testing"/>
    <category term="vscode" label="vs code"/>
    <published>2025-02-05T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2025/allocate_memory_sections.html</id>
    <title>Allocate Code and Data to Specific Memory Sections</title>
    <updated>2025-01-07T00:00:00+00:00</updated>
    <content type="html">&lt;section id="allocate-code-and-data-to-specific-memory-sections"&gt;

&lt;section id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;There was a question in our support channel about the memory sections from an external library.
The colleague was trying to understand how the different sections were defined for the library and how they were placed in memory.
When checking the linker configuration file (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;*.lsl&lt;/span&gt;&lt;/code&gt;) the observation was that the memory sections for:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;all the objects for our code, the section pattern was &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.&amp;lt;type&amp;gt;.&amp;lt;filename&amp;gt;.&amp;lt;symbol&amp;gt;&lt;/span&gt;&lt;/code&gt;. For example, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;select&lt;/span&gt; &lt;span class="pre"&gt;.text.component.main&lt;/span&gt;&lt;/code&gt; or &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;select&lt;/span&gt; &lt;span class="pre"&gt;.data.component.my_var&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the library, the section pattern was &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.&amp;lt;type&amp;gt;.&amp;lt;some_name&amp;gt;.&amp;lt;symbol&lt;/span&gt;&lt;/code&gt;. For example, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.rodata.adjustable_parameters.my_param&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The question was: how can we define the sections for the library in the linker script file and if they can use the same pattern as our code?&lt;/p&gt;
&lt;/section&gt;
&lt;section id="understanding-the-sections"&gt;
&lt;h2&gt;Understanding the sections&lt;/h2&gt;
&lt;section id="why-do-we-need-to-define-different-memory-sections"&gt;
&lt;h3&gt;Why do we need to define different memory sections?&lt;/h3&gt;
&lt;p&gt;When integrating code with different safety levels or from external libraries, it is important to separate the memory sections to restrict the access to certain areas of the memory only to specific code.
This safety measure is important to avoid that a bug in one part of the code can corrupt the data of another part of the code.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="how-are-the-sections-defined"&gt;
&lt;h3&gt;How are the sections defined?&lt;/h3&gt;
&lt;p&gt;The SW architecture describes the logical allocation of the components based on different abstraction levels and safety requirements.
Based on this and the memory map of the micro-controller, the sections are defined in the linker script file.&lt;/p&gt;
&lt;p&gt;Here are some section types that are commonly used:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.text&lt;/span&gt;&lt;/code&gt; - the code section&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.data&lt;/span&gt;&lt;/code&gt; - the initialized data section&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.bss&lt;/span&gt;&lt;/code&gt; - the uninitialized data section&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.rodata&lt;/span&gt;&lt;/code&gt; - the read-only data section&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="how-does-my-c-code-ends-up-in-the-sections"&gt;
&lt;h2&gt;How does my C code ends up in the sections?&lt;/h2&gt;
&lt;p&gt;By default, the compiler generates the sections based on the file name and the symbol name.
Let us consider the following code:&lt;/p&gt;
&lt;div class="highlight-c notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;my_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;my_var&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The compiler will generate the following sections:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.text.my_file.my_func&lt;/span&gt;&lt;/code&gt; - the code of the function &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;my_func&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.data.my_file.my_var&lt;/span&gt;&lt;/code&gt; - the initialized data of the variable &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;my_var&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the linker script file, we can &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;select&lt;/span&gt;&lt;/code&gt; the sections to be placed in the memory map.&lt;/p&gt;
&lt;p&gt;In the memory group where this component &lt;em&gt;code&lt;/em&gt; shall be placed, we can refer to it as:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;my_file&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This means that all the sections that start with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.text.my_file.&lt;/span&gt;&lt;/code&gt; will be placed in this memory group.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="renaming-the-sections"&gt;
&lt;h2&gt;Renaming the sections&lt;/h2&gt;
&lt;p&gt;Let’s say that one has some adjustable parameters which shall be allocated to a specific memory area.&lt;/p&gt;
&lt;div class="highlight-c notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parameters_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_parameters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;By default the compiler will generate a section named &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.rodata.my_file.my_parameters&lt;/span&gt;&lt;/code&gt; for the read-only data of the variable &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;my_parameters&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If one wants to give a specific name to this section, one can instruct the compiler to use a specific section name.
Depending on the compiler, there are different ways to do this.&lt;/p&gt;
&lt;p&gt;When using the TASKING compiler, there is a special ´#pragma´ to define the section name.&lt;/p&gt;
&lt;div class="highlight-c notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#pragma section all &amp;quot;sw_constants&amp;quot; &lt;/span&gt;&lt;span class="c1"&gt;// define the section name&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parameters_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_parameters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="cp"&gt;#pragma section restore all &lt;/span&gt;&lt;span class="c1"&gt;// restore the default section name&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For more information see the TASKING User Guide, section 1.12.1 Rename Sections in the &lt;a class="reference internal" href="blogs/2025/allocate_memory_sections.html#references"&gt;&lt;span class="std std-ref"&gt;References&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When using the GCC compiler, there is a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;__attribute__&lt;/span&gt;&lt;/code&gt; that can be used to define the section name.&lt;/p&gt;
&lt;div class="highlight-c notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parameters_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_parameters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;__attribute__&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sw_constants&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;See more about the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;__attribute__&lt;/span&gt;&lt;/code&gt; in the &lt;a class="reference external" href="https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-section-variable-attribute"&gt;GCC documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="answering-the-question-s"&gt;
&lt;h2&gt;Answering the question(s)&lt;/h2&gt;
&lt;p&gt;The linker configuration file (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;*.lsl&lt;/span&gt;&lt;/code&gt;) is used to define the memory layout, collect and group memory sections from objects and libraries.
The compiler generates the sections based on the file name and the symbol name and type. The user can rename the sections using compiler specific directives.&lt;/p&gt;
&lt;p&gt;The memory sections defined for the objects inside the library have a different pattern because there were renamed using the compiler specific directives (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pragma&lt;/span&gt;&lt;/code&gt; or &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;__attribute__&lt;/span&gt; &lt;span class="pre"&gt;section&lt;/span&gt;&lt;/code&gt;).
One can not change the section names after the compilation and therefore must rely on naming conventions to know which sections are present in the library.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="references"&gt;
&lt;span id="id1"&gt;&lt;/span&gt;&lt;h2&gt;References&lt;/h2&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: INFO/1 (&lt;span class="docutils literal"&gt;/home/runner/work/cuinixam.github.io/cuinixam.github.io/docs/blogs/2025/allocate_memory_sections.md&lt;/span&gt;, line 117); &lt;em&gt;&lt;a href="#id1"&gt;backlink&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Duplicate implicit target name: “references”.&lt;/p&gt;
&lt;/aside&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://resources.tasking.com/sites/default/files/uberflip_docs/file_254.pdf"&gt;TASKING User Guide&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://resources.tasking.com/sites/default/files/uberflip_docs/file_847.pdf"&gt;GCC to TASKING Migration&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-section-variable-attribute"&gt;GCC documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.autosar.org/fileadmin/standards/R24-11/CP/AUTOSAR_CP_SWS_MemoryMapping.pdf"&gt;AUTOSAR Memory Mapping&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2025/allocate_memory_sections.html"/>
    <summary>There was a question in our support channel about the memory sections from an external library.
The colleague was trying to understand how the different sections were defined for the library and how they were placed in memory.
When checking the linker configuration file (*.lsl) the observation was that the memory sections for:all the objects for our code, the section pattern was .&lt;type&gt;.&lt;filename&gt;.&lt;symbol&gt;. For example, select .text.component.main or select .data.component.my_var.</summary>
    <category term="gcc" label="gcc"/>
    <category term="linker" label="linker"/>
    <category term="linkerscriptinglanguage" label="linker scripting language"/>
    <category term="lsl" label="lsl"/>
    <category term="memory" label="memory"/>
    <category term="section" label="section"/>
    <category term="tasking" label="tasking"/>
    <published>2025-01-07T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2024/homelab_setup_main_machine.html</id>
    <title>Homelab - setup main machine</title>
    <updated>2024-11-17T00:00:00+00:00</updated>
    <content type="html">&lt;section id="homelab-setup-main-machine"&gt;

&lt;section id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;When starting my homelab journey, I needed a main machine to manage all devices and services in my network. Many homelab enthusiasts typically choose Linux for this purpose. However, my primary computer runs Windows 11, so I decided to use &lt;a class="reference external" href="https://learn.microsoft.com/en-us/windows/wsl/install"&gt;WSL2&lt;/a&gt; with an Ubuntu distribution to fulfill this role.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="installing-wsl2"&gt;
&lt;h2&gt;Installing WSL2&lt;/h2&gt;
&lt;p&gt;I followed the instructions provided in the &lt;a class="reference external" href="https://learn.microsoft.com/en-us/windows/wsl/install"&gt;official documentation&lt;/a&gt; to install WSL2 and had Ubuntu up and running quickly.&lt;/p&gt;
&lt;section id="network-configuration"&gt;
&lt;h3&gt;Network configuration&lt;/h3&gt;
&lt;p&gt;By default, WSL2 uses &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;NAT&lt;/span&gt;&lt;/code&gt; networking mode, assigning dynamic IP addresses, which prevents access to other devices in the local network, like routers and Raspberry Pis. To solve this, I switched WSL2 to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;mirrored&lt;/span&gt;&lt;/code&gt; network mode via the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;WSL&lt;/span&gt; &lt;span class="pre"&gt;Settings&lt;/span&gt;&lt;/code&gt;. After shutting down (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;wsl&lt;/span&gt; &lt;span class="pre"&gt;--shutdown&lt;/span&gt;&lt;/code&gt;) and restarting WSL2 (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;wsl&lt;/span&gt;&lt;/code&gt;), I could successfully ping other devices in my network:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ping&lt;span class="w"&gt; &lt;/span&gt;router.local
ping&lt;span class="w"&gt; &lt;/span&gt;rpi3.local
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="using-vs-code-remote-wsl"&gt;
&lt;h3&gt;Using VS Code Remote - WSL&lt;/h3&gt;
&lt;p&gt;I found the easiest way to interact with files inside WSL2 was to use the &lt;a class="reference external" href="https://code.visualstudio.com/docs/remote/wsl"&gt;VS Code Remote - WSL&lt;/a&gt; extension. Installing this extension allowed me to seamlessly edit files inside the Ubuntu distribution directly through VS Code’s interface.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="installing-ansible"&gt;
&lt;h2&gt;Installing Ansible&lt;/h2&gt;
&lt;p&gt;To automate the configuration of remote devices, I chose Ansible. Following the instructions from Ansible’s &lt;a class="reference external" href="https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-and-upgrading-ansible-with-pipx"&gt;installation page&lt;/a&gt;, I created a script called &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;bootstrap_main_machine.sh&lt;/span&gt;&lt;/code&gt; to install both &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pipx&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ansible&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Install pipx - see https://pipx.pypa.io/stable/&lt;/span&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;update
sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pipx
pipx&lt;span class="w"&gt; &lt;/span&gt;ensurepath

&lt;span class="c1"&gt;# Install ansible - see https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html&lt;/span&gt;
pipx&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--include-deps&lt;span class="w"&gt; &lt;/span&gt;ansible
ansible&lt;span class="w"&gt; &lt;/span&gt;--version
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;section id="creating-the-ansible-inventory"&gt;
&lt;h3&gt;Creating the Ansible inventory&lt;/h3&gt;
&lt;p&gt;I created an Ansible inventory file named &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;inventory.yml&lt;/span&gt;&lt;/code&gt; within a new directory &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;~/ansible&lt;/span&gt;&lt;/code&gt;. It lists the devices I want to manage:&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;all&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;hosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;rpi3test.local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;ansible_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;pi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I initially tested my Ansible setup using the ping module:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ansible&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;~/ansible/inventory.yml&lt;span class="w"&gt; &lt;/span&gt;rpi3test.local&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;ping
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This attempt failed, showing the following SSH error:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;rpi3test.local | UNREACHABLE! =&amp;gt; {
    &amp;quot;changed&amp;quot;: false,
    &amp;quot;msg&amp;quot;: &amp;quot;Failed to connect to the host via ssh: pi@rpi3test.local: Permission denied (publickey,password).&amp;quot;,
    &amp;quot;unreachable&amp;quot;: true
}
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Since I wasn’t using SSH keys yet, I tried providing the password explicitly with the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--ask-pass&lt;/span&gt;&lt;/code&gt; option:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ansible&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;~/ansible/inventory.yml&lt;span class="w"&gt; &lt;/span&gt;rpi3test.local&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;ping&lt;span class="w"&gt; &lt;/span&gt;--ask-pass
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;However, this still didn’t work due to a missing package:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;rpi3test.local | FAILED! =&amp;gt; {
    &amp;quot;msg&amp;quot;: &amp;quot;to use the &amp;#39;ssh&amp;#39; connection type with passwords or pkcs11_provider, you must install the sshpass program&amp;quot;
}
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To resolve this, I installed the required package:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;sshpass
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;After installing &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;sshpass&lt;/span&gt;&lt;/code&gt;, running the ping command with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--ask-pass&lt;/span&gt;&lt;/code&gt; succeeded:&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;rpi3test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;SUCCESS&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;ansible_facts&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;discovered_interpreter_python&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/usr/bin/python3.11&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;changed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;ping&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;pong&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition important"&gt;
&lt;p class="admonition-title"&gt;Important&lt;/p&gt;
&lt;p&gt;It is not recommended to authenticate using passwords because this opens the possibility for brute force attacks. The best practice is to use SSH keys for authentication. We will cover this in the next section.&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="creating-ssh-keys"&gt;
&lt;h3&gt;Creating SSH keys&lt;/h3&gt;
&lt;p&gt;To enhance security, I generated SSH keys specifically for Ansible connections using the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ED25519&lt;/span&gt;&lt;/code&gt; algorithm:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ssh-keygen&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;ed25519&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;~/.ssh/ansible
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Next, I wrote an Ansible playbook to automate the process of copying SSH keys to the devices and disabling password authentication:&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Setup authentication&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;hosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;all&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;become&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;yes&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Create .ssh directory if it doesn&amp;#39;t exist&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/{{ ansible_user }}/.ssh&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;directory&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0700&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Copy public key from local .ssh directory&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;~/.ssh/ansible.pub&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/home/{{ ansible_user }}/.ssh/authorized_keys&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0600&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Ensure password authentication is disabled in SSHD&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;lineinfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/etc/ssh/sshd_config&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;regexp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;^PasswordAuthentication&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;PasswordAuthentication&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;no&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;present&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Restart SSHD&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Lock password for {{ ansible_user }}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;password_lock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;yes&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Restart SSHD&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;sshd&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;restarted&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2024/homelab_setup_main_machine.html"/>
    <summary>When starting my homelab journey, I needed a main machine to manage all devices and services in my network. Many homelab enthusiasts typically choose Linux for this purpose. However, my primary computer runs Windows 11, so I decided to use WSL2 with an Ubuntu distribution to fulfill this role.I followed the instructions provided in the official documentation to install WSL2 and had Ubuntu up and running quickly.</summary>
    <category term="ansible" label="ansible"/>
    <category term="docker" label="docker"/>
    <category term="gettingstarted" label="getting started"/>
    <category term="homelab" label="homelab"/>
    <category term="portainer" label="portainer"/>
    <category term="setup" label="setup"/>
    <published>2024-11-17T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2024/optimizing_tool_installation_in_spl_build_environments.html</id>
    <title>Optimizing Tool Installation in SPL Build Environments</title>
    <updated>2024-10-26T00:00:00+00:00</updated>
    <content type="html">&lt;section id="optimizing-tool-installation-in-spl-build-environments"&gt;

&lt;p&gt;I assume that the reader is familiar with the concept of software product lines (SPLs) and build environments. If not, as this is a blog post about optimizing tool installation in SPL build environments, I recommend you read up on these topics first.&lt;/p&gt;
&lt;section id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;In software product line (SPL) engineering, efficiently managing tool dependencies is essential for optimizing build environments and enhancing developer productivity. This blog post explores the thought process in addressing the challenge of optimizing tool installations in an SPL. We’ll walk through the problem statement, initial ideas, the evolution of those ideas, and the final approach.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-problem-inefficient-tool-installation"&gt;
&lt;h2&gt;The Problem: Inefficient Tool Installation&lt;/h2&gt;
&lt;p&gt;In our SPL build environments, all tools are being installed regardless of the specific needs of a developer or build target. This was not noticeable initially, but as the number of variants and use cases (e.g. different packaging formats, on target debugging, etc.) increased, the inefficiency became apparent.&lt;/p&gt;
&lt;p&gt;Basically, there is an &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;install&lt;/span&gt;&lt;/code&gt; step at the beginning of the build pipeline that installs all the tools required by all variants and all possible build targets.&lt;/p&gt;
&lt;p&gt;Some use cases where this inefficiency is particularly noticeable include:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;A developer working on a specific variant needs only a subset of tools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A CI pipeline for running static analysis (e.g. Mathworks Polyspace) does not need to build or package the software.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The problem is more noticeable for developers spinning up a virtual machine (VM) to quickly test a change or for a developer setting up their environment for the first time.&lt;/p&gt;
&lt;p&gt;On the other hand, when releasing a product, it is essential to ensure that all tools are available because we have to make sure that all variants are passing the defined quality gates.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="brainstorming"&gt;
&lt;h2&gt;Brainstorming&lt;/h2&gt;
&lt;section id="categorizing-tools-into-generic-variant-and-build-target-sets"&gt;
&lt;h3&gt;💡 Categorizing Tools into Generic, Variant, and Build Target Sets&lt;/h3&gt;
&lt;p&gt;Let us categorize tools into three distinct sets:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generic Tools&lt;/strong&gt;: Tools required by all variants and build targets (e.g., CMake, Python).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Variant-Specific Tools&lt;/strong&gt;: Tools required only for certain variants (e.g., specific compilers for microcontrollers).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build Target-Specific Tools&lt;/strong&gt;: Tools required for specific build targets (e.g., static analysis tools like Polyspace).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Implementation Concept&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Separate Tool Dependency Files&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generic Tool Dependencies File&lt;/strong&gt;: Contains tools common to all variants.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Variant-Specific Tool Dependencies Files&lt;/strong&gt;: Contains tools specific to a variant or overrides for generic tools.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Merging Process&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When building a variant, merge the generic and variant-specific tool files, with the variant-specific tools overriding any conflicts.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handling Build Target-Specific Tools&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Dynamically add tools based on the selected build target.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This means that the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;install&lt;/span&gt;&lt;/code&gt; step in the pipeline will install only the tools required for the specific variant and build target.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Considerations&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;While this idea provides a structured approach to managing tool dependencies, there are some challenges/limitations to consider:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity in Linking Tools to Build Targets&lt;/strong&gt;: Tightly coupling tools to build targets might limit flexibility for other activities that require specific tools, such as generating reports or on-target debugging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Aggregation of Tools for CI&lt;/strong&gt;: Collecting all tools from generic, variant, and build target files for CI builds might introduce complexity in managing tool versions and resolving conflicts.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="introducing-scopes-as-an-abstraction-layer"&gt;
&lt;h3&gt;💡 Introducing “Scopes” as an Abstraction Layer&lt;/h3&gt;
&lt;p&gt;One issue with the previous idea is the tight coupling of tools to build targets. The problem is that, in contrast to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;variant&lt;/span&gt;&lt;/code&gt;, a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;build&lt;/span&gt; &lt;span class="pre"&gt;target&lt;/span&gt;&lt;/code&gt; is not a domain term in SPL engineering. It is a technical term from the build system. This tight coupling might lead to confusion and make the system less flexible.&lt;/p&gt;
&lt;p&gt;To address this limitation, we proposed introducing &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;scopes&lt;/span&gt;&lt;/code&gt; as a higher-level abstraction.&lt;/p&gt;
&lt;div class="admonition-scopes admonition"&gt;
&lt;p class="admonition-title"&gt;Scopes&lt;/p&gt;
&lt;p&gt;Scopes encompass a specific activity or domain within the development process, representing a set of tools, configurations and steps required for that activity.&lt;/p&gt;
&lt;p&gt;Examples of scopes may include: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Build&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;StaticAnalysis&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Debugging&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Reporting&lt;/span&gt;&lt;/code&gt;, and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Deployment&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Implementation Concept&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scope-Specific Configuration Files&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Each scope has its own configuration that lists the tools required for that scope.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It might also need to specify also what has to be done, which steps have to be executed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Merging Tool Dependencies&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hierarchy&lt;/strong&gt;: Generic tools &amp;lt; Variant Build Tools &amp;lt; Scope-specific tools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When building, the script merges tools from these files, with later ones overriding earlier ones in case of conflicts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There shall no be no variant specific tools anymore other than the toolchain for building the software.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Considerations&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity in Managing Scopes&lt;/strong&gt;: Managing a growing number of scopes might become complex.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Awareness&lt;/strong&gt;: Developers need to understand which scopes are relevant for their tasks.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How are scopes related to build targets?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="keep-current-solution-and-speed-up-installation-of-all-tools"&gt;
&lt;h3&gt;💡 Keep current solution and speed up installation of all tools&lt;/h3&gt;
&lt;p&gt;Another idea was to keep the current solution but speed up the installation process. This could be achieved by:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;CI jobs to install all tools on all agents&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Disable automatic installation of tools on developer machines. The developer can then install the required tools manually.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This idea is the least intrusive and requires the least amount of changes. However, it does not address the root cause of the problem. It is more of a workaround. 🙈&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="final-approach-introducing-scopes"&gt;
&lt;h2&gt;Final Approach: Introducing Scopes&lt;/h2&gt;
&lt;p&gt;Introducing “scopes” as an abstraction layer offers a more flexible and domain-aligned solution for optimizing tool installation in an SPL build environment.&lt;/p&gt;
&lt;p&gt;Scopes align with the development process, makes it easier for developers and platform engineers to understand and manage not only tool dependencies but also the steps required for a specific activity.&lt;/p&gt;
&lt;p&gt;The final approach involves:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Defining Scopes&lt;/strong&gt;: Identify and define scopes that represent activities or domains within the development process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scope-Specific Configuration Files&lt;/strong&gt;: Create configuration files for each scope that list the tools required for that scope.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Merging Tool Dependencies&lt;/strong&gt;: Merge tools from generic, variant build, and scope-specific files, with later ones overriding earlier ones in case of conflicts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Implementing Scope-Specific Steps&lt;/strong&gt;: Define and implement steps for each scope that specify what has to be done, which tools have to be installed, and which steps have to be executed.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach ensures that only the tools required for a specific activity are installed, optimizing the build environment and enhancing developer productivity.&lt;/p&gt;
&lt;p&gt;There is one important aspect to consider: in the end one must be able insure a certain quality level for the software product as a whole. This means one needs to:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;define quality gates for the whole product and breaking them down to the individual variants&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;map scopes to quality gates for each variant. Different variants might have different quality gates based on their maturity (e.g., prototype, beta, street ready).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The topics of quality gates and maturity levels, although related, deserve a separate blog post and I will not go into detail here.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope this blog post has provided you with insights into the challenges of optimizing tool installation in SPL build environments and the thought process in addressing this challenge.&lt;/p&gt;
&lt;p&gt;There will for sure be more questions to answer when actually implementing the solution but until then, keep fingers crossed and stay tuned for more updates on this topic.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2024/optimizing_tool_installation_in_spl_build_environments.html"/>
    <summary>I assume that the reader is familiar with the concept of software product lines (SPLs) and build environments. If not, as this is a blog post about optimizing tool installation in SPL build environments, I recommend you read up on these topics first.In software product line (SPL) engineering, efficiently managing tool dependencies is essential for optimizing build environments and enhancing developer productivity. This blog post explores the thought process in addressing the challenge of optimizing tool installations in an SPL. We’ll walk through the problem statement, initial ideas, the evolution of those ideas, and the final approach.</summary>
    <category term="build" label="build"/>
    <category term="dependency" label="dependency"/>
    <category term="optimization" label="optimization"/>
    <category term="spl" label="spl"/>
    <category term="tool" label="tool"/>
    <category term="yanga" label="yanga"/>
    <published>2024-10-26T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://maxiniuc.com/blogs/2024/efficiently_embedding_git_information_in_c_projects.html</id>
    <title>Efficiently Embedding Git Information in C Projects</title>
    <updated>2024-10-01T00:00:00+00:00</updated>
    <content type="html">&lt;section id="efficiently-embedding-git-information-in-c-projects"&gt;

&lt;section id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;In software development, embedding Git metadata (like commit IDs, branches, and tags) into your binaries is useful for debugging and traceability. However, how you integrate this information can significantly impact your build times and workflow efficiency. This post explores the challenges of embedding Git information in C projects using CMake and proposes an optimized solution.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="current-approaches"&gt;
&lt;h2&gt;Current Approaches&lt;/h2&gt;
&lt;section id="project-one-command-line-defines"&gt;
&lt;h3&gt;Project One: Command-Line Defines&lt;/h3&gt;
&lt;p&gt;In the first project, Git information is passed directly as compiler command-line arguments using &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;add_compile_options&lt;/span&gt;&lt;/code&gt; in CMake:&lt;/p&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;add_compile_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;-DGIT_CUR_COMMIT=\&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${GIT_CUR_COMMIT}\&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;-DGIT_CUR_USER=\&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${GIT_CUR_USER}\&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;These defines are then used in the codebase to embed Git metadata:&lt;/p&gt;
&lt;div class="highlight-c notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uint8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;git_commit&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GIT_CUR_COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Advantages&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Simplicity: Easy to implement and understand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Direct Integration: Git information is directly available in the code via macros.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Disadvantages&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Inefficient Builds: Any change in the command-line arguments (e.g., a new commit ID) invalidates the build cache, forcing a full recompilation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Poor Incremental Build Support: Full builds slow down development, especially in large product with multiple variants.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inconsistent Updates: The file generation relies on the CMake configure step, which may not run if no CMake files have changed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="project-two-generated-source-file"&gt;
&lt;h3&gt;Project Two: Generated Source File&lt;/h3&gt;
&lt;p&gt;The second project generates a C source file during the CMake configuration step:&lt;/p&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;GIT_INFO_TEMPLATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/src/git_info.c.in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;GIT_INFO_FILE_OUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_BINARY_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/Src/git_info.c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;add_custom_target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;version_info&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;COMMAND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_COMMAND&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;-DUPDATE_VERSION_INFORMATION_REQUESTED=1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;-DVERSION_INFORMATION_FILE=&lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO_TEMPLATE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;-DVERSION_INFORMATION_FILE_OUT=&lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO_FILE_OUT&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;-DGIT_VARIANT=&lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VARIANT&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;-P&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/src/gitinfo.cmake&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;BYPRODUCTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO_FILE_OUT&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;spl_add_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO_FILE_OUT&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;spl_create_component&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Advantages&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Selective Compilation: Only the generated file re-compiles when Git information changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improved Build Times: Reduces the need for full recompilations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Disadvantages&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Inefficient Builds: The generated file is recompiled every time, even if the Git information hasn’t changed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Complexity: Adds extra steps and dependencies in the build process.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="proposed-solution-embedding-git-information-in-the-binary"&gt;
&lt;h2&gt;Proposed Solution: Embedding Git Information in the Binary&lt;/h2&gt;
&lt;p&gt;To overcome the problems of current methods, we propose embedding Git information directly into the binary (using Intel-HEX format) after the build process. This approach eliminates unnecessary recompilation and simplifies the build process by embedding Git metadata in a dedicated memory section.&lt;/p&gt;
&lt;section id="create-the-git-information-source-files"&gt;
&lt;h3&gt;Create the git information source files&lt;/h3&gt;
&lt;p&gt;We need to have:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;global constants for the Git information&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;compiler directives to place the constants in a specific memory section&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;git_info.c&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight-c notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;quot;git_info.h&amp;quot;&lt;/span&gt;

&lt;span class="cp"&gt;#define GITINFO_START_SEC_CONST&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;quot;git_info_mem_map.h&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;git_commit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;GIT_COMMIT_LENGTH&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0123456701234567&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cp"&gt;#define GITINFO_STOP_SEC_CONST&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;quot;git_info_mem_map.h&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition important"&gt;
&lt;p class="admonition-title"&gt;Important&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;git_info.c&lt;/span&gt;&lt;/code&gt; file contains dummy Git information and is used only for a placeholder. The actual Git information will be updated after the build process.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;git_info_mem_map.h&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight-c notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#if defined( GITINFO_START_SEC_CONST )&lt;/span&gt;
&lt;span class="cp"&gt;#pragma protect&lt;/span&gt;
&lt;span class="cp"&gt;#pragma section nearrom &amp;quot;GitInfoSection&amp;quot;&lt;/span&gt;
&lt;span class="cp"&gt;#pragma section farrom &amp;quot;GitInfoSection&amp;quot;&lt;/span&gt;
&lt;span class="cp"&gt;# undef GITINFO_START_SEC_CONST&lt;/span&gt;
&lt;span class="cp"&gt;# define START_SEC_CODE&lt;/span&gt;
&lt;span class="cp"&gt;#endif&lt;/span&gt;

&lt;span class="cp"&gt;#if defined( GITINFO_STOP_SEC_CONST )&lt;/span&gt;
&lt;span class="cp"&gt;#pragma endprotect&lt;/span&gt;
&lt;span class="cp"&gt;#pragma section nearrom restore&lt;/span&gt;
&lt;span class="cp"&gt;#pragma section farrom restore&lt;/span&gt;
&lt;span class="cp"&gt;# undef GITINFO_STOP_SEC_CONST&lt;/span&gt;
&lt;span class="cp"&gt;# define STOP_SEC_CODE&lt;/span&gt;
&lt;span class="cp"&gt;#endif&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;GitInfoSection&lt;/span&gt;&lt;/code&gt; memory section shall be defined in the linker script.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;For other modules to access the Git information, we need to define the Git information in a header file:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;git_info.h&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight-c notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cp"&gt;#ifndef GIT_INFO_H&lt;/span&gt;
&lt;span class="cp"&gt;#define GIT_INFO_H&lt;/span&gt;

&lt;span class="cp"&gt;#define GIT_COMMIT_LENGTH 16&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;git_commit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;GIT_COMMIT_LENGTH&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="cp"&gt;#endif&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="linker-script-to-define-the-git-information-memory-section"&gt;
&lt;h3&gt;Linker Script to Define the Git Information Memory Section&lt;/h3&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;/* Start address to store the git information */
#define GITINFO_ADDRESS                         (0xABCD)

section_layout :vtc:linear
{
	group PFLASH0(fill = 0x00)
	{
		group GitInfoSectionGroup (ordered, run_addr=GITINFO_ADDRESS)
		{
			section &amp;quot;GitInfoSectionGroup_SEC&amp;quot; (fill, blocksize = 2, attributes = rx)
			{
					select &amp;quot;[.]rodata.GitInfoSection&amp;quot;;
			}
		}
		&amp;quot;_GitInfoSectionGroup_START&amp;quot; = &amp;quot;_lc_gb_GitInfoSectionGroup&amp;quot;;
		&amp;quot;_GitInfoSectionGroup_END&amp;quot; = (&amp;quot;_lc_ge_GitInfoSectionGroup&amp;quot; == 0) ? 0 : &amp;quot;_lc_ge_GitInfoSectionGroup&amp;quot; - 1;
		&amp;quot;_GitInfoSectionGroup_LIMIT&amp;quot; = &amp;quot;_lc_ge_GitInfoSectionGroup&amp;quot;;
	}
}
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This linker script defines a memory section &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;GitInfoSection&lt;/span&gt;&lt;/code&gt; at the specified address &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;GITINFO_ADDRESS&lt;/span&gt;&lt;/code&gt; to store the Git information.
The constants defined in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;git_info.c&lt;/span&gt;&lt;/code&gt; (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;rodata&lt;/span&gt;&lt;/code&gt;) will be placed in this memory section.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="cmake-script-to-update-the-git-information"&gt;
&lt;h3&gt;CMake script to update the Git information&lt;/h3&gt;
&lt;p&gt;We need to define custom commands to create a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;git_info.hex&lt;/span&gt;&lt;/code&gt; file containing the Git information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Requirements&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;git_info.hex&lt;/span&gt;&lt;/code&gt; file shall only be generated if the git commit has changed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the command for checking the git commit shall always run, to make sure the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;git_info.hex&lt;/span&gt;&lt;/code&gt; file is up-to-date&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="admonition important"&gt;
&lt;p class="admonition-title"&gt;Important&lt;/p&gt;
&lt;p&gt;As you might have noticed, we need to &lt;strong&gt;always&lt;/strong&gt; run the command for checking the git commit but &lt;strong&gt;only&lt;/strong&gt; generate the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;git_info.hex&lt;/span&gt;&lt;/code&gt; file if the git commit has changed. This is a bit tricky to achieve with CMake, but it is possible.&lt;/p&gt;
&lt;/div&gt;
&lt;section id="always-generate-git-commit-temporary-file"&gt;
&lt;h4&gt;Always Generate Git Commit Temporary File&lt;/h4&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Purpose: Ensures the Git commit ID is updated every build.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mechanism: Uses a fictive output &lt;strong&gt;git_commit_force_update&lt;/strong&gt; to force the command to run every time.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;add_custom_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;OUTPUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;__git_commit_force_update__&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;BYPRODUCTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_COMMIT_TMP_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;COMMAND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;describe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--always&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--dirty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--exclude&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--abbrev=8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_COMMIT_TMP_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;COMMENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Generate the git commit tmp file&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;VERBATIM&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="update-git-commit-file-if-changed"&gt;
&lt;h4&gt;Update Git Commit File if Changed&lt;/h4&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Purpose: Copies the temporary commit ID file to the final file only if it has changed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mechanism: Uses copy_if_different to avoid unnecessary updates.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;add_custom_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;OUTPUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_COMMIT_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;COMMAND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_COMMAND&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;-E&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;copy_if_different&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_COMMIT_TMP_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_COMMIT_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;COMMENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Checking and updating git commit ID&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;DEPENDS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;__git_commit_force_update__&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_COMMIT_TMP_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;VERBATIM&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="create-git-info-hex-file"&gt;
&lt;h4&gt;Create Git Info Hex File&lt;/h4&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Purpose: Converts the Git commit ID into a hex file at the specified address.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mechanism: Uses hextool to generate the hex file.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;add_custom_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;OUTPUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO_HEX_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;COMMAND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;hextool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--input-binary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_COMMIT_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--offset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GITINFO_ADDRESS&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO_HEX_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;DEPENDS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_COMMIT_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;COMMENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Creating git info hex file&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;VERBATIM&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Please notice the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--input-binary&lt;/span&gt;&lt;/code&gt; hextool option to read the git information as binary data directly from the file.&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="merge-git-info-hex-file-with-the-binary"&gt;
&lt;h3&gt;Merge Git Info Hex File with the Binary&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Purpose: Combines the main output hex file with the Git info hex file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Output: Produces &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;link_out_with_git_info.hex&lt;/span&gt;&lt;/code&gt; containing the embedded Git commit ID.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;add_custom_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;OUTPUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_BINARY_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/link_out_with_git_info.hex&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;COMMAND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;hextool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;merge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_BINARY_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/link_out.hex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO_HEX_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_BINARY_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/link_out_with_git_info.hex&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;COMMENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Merging git info to the output hex file&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;DEPENDS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO_HEX_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_BINARY_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/link_out.hex&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;VERBATIM&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="process-flow-diagram"&gt;
&lt;h3&gt;Process Flow Diagram&lt;/h3&gt;
&lt;pre  class="mermaid"&gt;
        flowchart TD
    A[Start Build] --&amp;gt; B[Generate git_commit_tmp.txt]
    B --&amp;gt; C{Has Commit ID Changed?}
    C -- Yes --&amp;gt; D[Update git_commit.txt]
    C -- No --&amp;gt; E[Skip Update]
    D &amp;amp; E --&amp;gt; F[Create git_info.hex]
    F --&amp;gt; G[Generate link_out.hex]
    G --&amp;gt; H[Merge git_info.hex with link_out.hex]
    H --&amp;gt; I[Produce link_out_with_git_info.hex]
    &lt;/pre&gt;&lt;/section&gt;
&lt;section id="alternative-approach-as-post-build-command"&gt;
&lt;h3&gt;Alternative approach as POST_BUILD command&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Purpose: Merges &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;git_info.hex&lt;/span&gt;&lt;/code&gt; directly into &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;link_out.hex&lt;/span&gt;&lt;/code&gt; as a &lt;a class="reference external" href="https://cmake.org/cmake/help/latest/command/add_custom_command.html#examples-build-events"&gt;POST_BUILD&lt;/a&gt; step, overwriting the original file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mechanism:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Post-Build Command: Executes after the link target finishes building.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Force Relinking: Adds git_info.hex as a dependency to ensure that the linker runs again if the Git info changes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Output: The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;link_out.hex&lt;/span&gt;&lt;/code&gt; file now contains the embedded Git commit ID without creating a new file.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-cmake notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="linenos"&gt; 1&lt;/span&gt;&lt;span class="c"&gt;# Add post-build command to merge the git info to the link_out.hex.&lt;/span&gt;
&lt;span class="linenos"&gt; 2&lt;/span&gt;&lt;span class="nb"&gt;add_custom_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="linenos"&gt; 3&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;TARGET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;link&lt;/span&gt;
&lt;span class="linenos"&gt; 4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;POST_BUILD&lt;/span&gt;
&lt;span class="linenos"&gt; 5&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;COMMAND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;hextool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;merge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_BINARY_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/link_out.hex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO_HEX_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_BINARY_DIR&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/link_out.hex&lt;/span&gt;
&lt;span class="linenos"&gt; 6&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;COMMENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Merging git info to the output hex file&amp;quot;&lt;/span&gt;
&lt;span class="hll"&gt;&lt;span class="linenos"&gt; 7&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c"&gt;# (!) Adding DEPENDS has no effect on POST_BUILD commands. So next line is useless.&lt;/span&gt;
&lt;/span&gt;&lt;span class="linenos"&gt; 8&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;DEPENDS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO_HEX_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="linenos"&gt; 9&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;VERBATIM&lt;/span&gt;
&lt;span class="linenos"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="linenos"&gt;11&lt;/span&gt;
&lt;span class="linenos"&gt;12&lt;/span&gt;&lt;span class="c"&gt;# (!) Force linking again if the git info hex has changed.&lt;/span&gt;
&lt;span class="linenos"&gt;13&lt;/span&gt;&lt;span class="c"&gt;# This hack adds the git info hex file as a dependency to the src_git_info target. When the git info hex file changes, the src_git_info target will be considered out of date and will be rebuilt.&lt;/span&gt;
&lt;span class="linenos"&gt;14&lt;/span&gt;&lt;span class="nb"&gt;set_property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;TARGET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;src_git_info&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;PROPERTY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;INTERFACE_LINK_DEPENDS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO_HEX_FILE&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;pre  class="mermaid"&gt;
        flowchart TD
    A[Start Build] --&amp;gt; B[Generate git_commit_tmp.txt]
    B --&amp;gt; C{Has Commit ID Changed?}
    C -- Yes --&amp;gt; D[Update git_commit.txt]
    C -- No --&amp;gt; Z[End Build]
    D --&amp;gt; F[Create git_info.hex]
    F --&amp;gt; G{Has git_info.hex Changed?}
    G -- Yes --&amp;gt; H[Link link_out.hex]
    G -- No --&amp;gt; Z
    H --&amp;gt; K[Merging git_info.hex into link_out.hex - Post-Build]
    K --&amp;gt; L[Updated link_out.hex with Git info]
    L --&amp;gt; Z
    &lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Advantages&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Single Output File: Avoids creating an extra hex file; simplifies deployment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Always Updated: Ensures &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;link_out.hex&lt;/span&gt;&lt;/code&gt; always contains the latest Git info.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Disadvantages&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Forced Relinking: Changes in git_info.hex cause the linker to run again, potentially increasing build times. Note: Only the linking step is rerun; source files are not recompiled.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Overwriting Output: Original &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;link_out.hex&lt;/span&gt;&lt;/code&gt; is modified, which may not be desirable in all workflows.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We have explored the challenges of embedding Git information in C projects and proposed an optimized solution using CMake and Intel-HEX format.
This approach ensures that Git metadata is efficiently embedded in the firmware binary without unnecessary recompilations by:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Extracting the current Git commit ID during each build.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Updating the commit ID file only when changes occur to avoid unnecessary rebuilds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Embedding the commit ID into the hex file at a specific memory address.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Merging the Git info hex file with the main output using one of the two approaches.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope this post is helpful in optimizing your build process.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://maxiniuc.com/blogs/2024/efficiently_embedding_git_information_in_c_projects.html"/>
    <summary>In software development, embedding Git metadata (like commit IDs, branches, and tags) into your binaries is useful for debugging and traceability. However, how you integrate this information can significantly impact your build times and workflow efficiency. This post explores the challenges of embedding Git information in C projects using CMake and proposes an optimized solution.In the first project, Git information is passed directly as compiler command-line arguments using add_compile_options in CMake:</summary>
    <category term="splgitpost-buildcmake" label="spl git post-build cmake"/>
    <published>2024-10-01T00:00:00+00:00</published>
  </entry>
</feed>
