<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on</title><link>https://steelph0enix.github.io/posts/</link><description>Recent content in Posts on</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>SteelPh0enix's Blog © 2021-2024 by SteelPh0enix is licensed under CC BY-SA 4.0</copyright><lastBuildDate>Mon, 28 Oct 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://steelph0enix.github.io/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>llama.cpp guide - Running LLMs locally, on any hardware, from scratch</title><link>https://steelph0enix.github.io/posts/llama-cpp-guide/</link><pubDate>Mon, 28 Oct 2024 00:00:00 +0000</pubDate><guid>https://steelph0enix.github.io/posts/llama-cpp-guide/</guid><description>&lt;p>&lt;em>No LLMs were harmed during creation of this post.&lt;/em>&lt;/p>
&lt;h2 id="so-i-started-playing-with-llms">so, i started playing with LLMs&amp;hellip;&lt;/h2>
&lt;p>&amp;hellip;and it&amp;rsquo;s pretty fun.
I was very skeptical about the AI/LLM &amp;ldquo;boom&amp;rdquo; back when it started.
I thought, like many other people, that they are just mostly making stuff up, and generating uncanny-valley-tier nonsense.
Boy, was i wrong.
I&amp;rsquo;ve used ChatGPT once or twice, to test the waters - it made a pretty good first impression, despite hallucinating a bit.
That was back when GPT3.5 was the top model. We came a pretty long way since then.&lt;/p></description><content>&lt;p>&lt;em>No LLMs were harmed during creation of this post.&lt;/em>&lt;/p>
&lt;h2 id="so-i-started-playing-with-llms">so, i started playing with LLMs&amp;hellip;&lt;/h2>
&lt;p>&amp;hellip;and it&amp;rsquo;s pretty fun.
I was very skeptical about the AI/LLM &amp;ldquo;boom&amp;rdquo; back when it started.
I thought, like many other people, that they are just mostly making stuff up, and generating uncanny-valley-tier nonsense.
Boy, was i wrong.
I&amp;rsquo;ve used ChatGPT once or twice, to test the waters - it made a pretty good first impression, despite hallucinating a bit.
That was back when GPT3.5 was the top model. We came a pretty long way since then.&lt;/p>
&lt;p>However, despite ChatGPT not disappointing me, i was still skeptical.
Everything i&amp;rsquo;ve wrote, and every piece of response was fully available to OpenAI, or whatever other provider i&amp;rsquo;d want to use.
This is not a big deal, but it tickles me in a wrong way, and also means i can&amp;rsquo;t use LLMs for any work-related non-open-source stuff.
Also, ChatGPT is free only to a some degree - if i&amp;rsquo;d want to go full-in on AI, i&amp;rsquo;d probably have to start paying.
Which, obviously, i&amp;rsquo;d rather avoid.&lt;/p>
&lt;p>At some point i started looking at open-source models.
I had no idea how to use them, but the moment i saw the sizes of &amp;ldquo;small&amp;rdquo; models, like Llama 2 7B, i&amp;rsquo;ve realized that my RTX 2070 Super with mere 8GB of VRAM would probably have issues running them (i was wrong on that too!), and running them on CPU would probably yield very bad performance.
And then, i&amp;rsquo;ve bought a new GPU - RX 7900 XT, with 20GB of VRAM, which is definitely more than enough to run small-to-medium LLMs.
Yay!&lt;/p>
&lt;p>Now my issue was finding some software that could run an LLM on that GPU.
CUDA was the most popular back-end - but that&amp;rsquo;s for NVidia GPUs, not AMD.
After doing a bit of research, i&amp;rsquo;ve found out about ROCm and found &lt;a href="https://lmstudio.ai/">LM Studio&lt;/a>.
And this was exactly what i was looking for - at least for the time being.
Great UI, easy access to many models, and the quantization - that was the thing that absolutely sold me into self-hosting LLMs.
Existence of quantization made me realize that you don&amp;rsquo;t need powerful hardware for running LLMs!
You can even run &lt;a href="https://www.reddit.com/r/raspberry_pi/comments/1ati2ki/how_to_run_a_large_language_model_llm_on_a/">LLMs on RaspberryPi&amp;rsquo;s&lt;/a> at this point (with &lt;code>llama.cpp&lt;/code> too!)
Of course, the performance will be &lt;em>abysmal&lt;/em> if you don&amp;rsquo;t run the LLM with a proper backend on a decent hardware, but the bar is currently not very high.&lt;/p>
&lt;p>If you came here with intention of finding some piece of software that will allow you to &lt;strong>easily run popular models on most modern hardware for non-commercial purposes&lt;/strong> - grab &lt;a href="https://lmstudio.ai/">LM Studio&lt;/a>, read the &lt;a href="https://steelph0enix.github.io/posts/llama-cpp-guide/#but-first---some-disclaimers-for-expectation-management">next section&lt;/a> of this post, and go play with it.
It fits this description very well, just make sure to use appropriate back-end for your GPU/CPU for optimal performance.&lt;/p>
&lt;p>However, if you:&lt;/p>
&lt;ul>
&lt;li>Want to learn more about &lt;code>llama.cpp&lt;/code> (which LM Studio uses as a back-end), and LLMs in general&lt;/li>
&lt;li>Want to use LLMs for commercial purposes (&lt;a href="https://lmstudio.ai/terms">LM Studio&amp;rsquo;s terms&lt;/a> forbid that)&lt;/li>
&lt;li>Want to run LLMs on exotic hardware (LM Studio provides only the most popular backends)&lt;/li>
&lt;li>Don&amp;rsquo;t like closed-source software (which LM Studio, unfortunately, is) and/or don&amp;rsquo;t trust anything you don&amp;rsquo;t build yourself&lt;/li>
&lt;li>Want to have access to latest features and models as soon as possible&lt;/li>
&lt;/ul>
&lt;p>you should find the rest of this post pretty useful!&lt;/p>
&lt;h2 id="but-first---some-disclaimers-for-expectation-management">but first - some disclaimers for expectation management&lt;/h2>
&lt;p>Before i proceed, i want to make some stuff clear.
This &amp;ldquo;FAQ&amp;rdquo; answers some questions i&amp;rsquo;d like to know answers to before getting into self-hosted LLMs.&lt;/p>
&lt;h3 id="do-i-need-rtx-2070-superrx-7900-xt-ot-similar-midhigh-end-gpu-to-do-what-you-did-here">Do I need RTX 2070 Super/RX 7900 XT ot similar mid/high-end GPU to do what you did here?&lt;/h3>
&lt;p>No, you don&amp;rsquo;t.
I&amp;rsquo;ll elaborate later, but you can run LLMs with no GPU at all.
As long as you have reasonably modern hardware (by that i mean &lt;em>at least&lt;/em> a decent CPU with AVX support) - you&amp;rsquo;re &lt;em>compatible&lt;/em>.
&lt;strong>But remember - your performance may vary.&lt;/strong>&lt;/p>
&lt;h3 id="what-performance-can-i-expect">What performance can I expect?&lt;/h3>
&lt;p>This is a very hard question to answer directly.
The speed of text generation depends on multiple factors, but primarily&lt;/p>
&lt;ul>
&lt;li>matrix operations performance on your hardware&lt;/li>
&lt;li>memory bandwidth&lt;/li>
&lt;li>model size&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;ll explain this with more details later, but you can generally get reasonable performance from the LLM by picking model small enough for your hardware.
If you intend to use GPU, and it has enough memory for a model with it&amp;rsquo;s context - expect real-time text generation.
In case you want to use both GPU and CPU, or only CPU - you should expect much lower performance, but real-time text generation is possible with small models.&lt;/p>
&lt;h3 id="what-quality-of-responses-can-i-expect">What quality of responses can I expect?&lt;/h3>
&lt;p>That heavily depends on your usage and chosen model.
I can&amp;rsquo;t answer that question directly, you&amp;rsquo;ll have to play around and find out yourself.
A rule of thumb is &amp;ldquo;larger the model, better the response&amp;rdquo; - consider the fact that size of SOTA (state-of-the-art) models, like GPT-4 or Claude, is usually measured in hundreds of billions of parameters.
Unless you have multiple GPUs or unreasonable amount of RAM and patience - you&amp;rsquo;ll most likely be restricted to models with less than 20 billion parameters.
From my experience, 7-8B models are pretty good for generic purposes and programming - and they are not &lt;em>very&lt;/em> far from SOTA models like GPT-4o or Claude in terms of raw quality of generated responses, but the difference is definitely noticeable.
Keep in mind that the choice of a model is only a part of the problem - providing proper context and system prompt, or fine-tuning LLMs can do wonders.&lt;/p>
&lt;h3 id="can-i-replace-chatgptclaudeinsert-online-llm-provider-with-that">Can i replace ChatGPT/Claude/[insert online LLM provider] with that?&lt;/h3>
&lt;p>Maybe. In theory - yes, but in practice - it depends on your tools.
&lt;code>llama.cpp&lt;/code> provides OpenAI-compatible server.
As long as your tools communicate with LLMs via OpenAI API, and you are able to set custom endpoint, you will be able to use self-hosted LLM with them.&lt;/p>
&lt;h2 id="prerequisites">prerequisites&lt;/h2>
&lt;ul>
&lt;li>Reasonably modern CPU.
If you&amp;rsquo;re rocking any Ryzen, or Intel&amp;rsquo;s 8th gen or newer, you&amp;rsquo;re good to go, but all of this should work on older hardware too.&lt;/li>
&lt;li>Optimally, a GPU.
More VRAM, the better.
If you have at least 8GB of VRAM, you should be able to run 7-8B models, i&amp;rsquo;d say that it&amp;rsquo;s reasonable minimum.
Vendor doesn&amp;rsquo;t matter, llama.cpp supports NVidia, AMD and Apple GPUs (not sure about Intel, but i think i saw a backend for that - if not, Vulkan should work).&lt;/li>
&lt;li>If you&amp;rsquo;re not using GPU or it doesn&amp;rsquo;t have enough VRAM, you need RAM for the model.
As above, at least 8GB of free RAM is recommended, but more is better.
Keep in mind that when only GPU is used by llama.cpp, RAM usage is very low.&lt;/li>
&lt;/ul>
&lt;p>In the guide, i&amp;rsquo;ll assume you&amp;rsquo;re using either Windows or Linux.
I can&amp;rsquo;t provide any support for Mac users, so they should follow Linux steps and consult the llama.cpp docs wherever possible.&lt;/p>
&lt;p>Some context-specific formatting is used in this post:&lt;/p>
&lt;blockquote>
&lt;p class="windows-bg">Parts of this post where i&amp;rsquo;ll write about Windows-specific stuff will have this background.
You&amp;rsquo;ll notice they&amp;rsquo;re much longer than Linux ones - Windows is a PITA.
Linux is preferred. I will still explain everything step-by-step for Windows, but in case of issues - try Linux.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p class="linux-bg">And parts where i&amp;rsquo;ll write about Linux-specific stuff will have this background.&lt;/p>
&lt;/blockquote>
&lt;h2 id="building-the-llama">building the llama&lt;/h2>
&lt;p>In &lt;a href="https://github.com/ggerganov/llama.cpp/blob/master/docs/build.md">&lt;code>docs/build.md&lt;/code>&lt;/a>, you&amp;rsquo;ll find detailed build instructions for all the supported platforms.
&lt;strong>By default, &lt;code>llama.cpp&lt;/code> builds with auto-detected CPU support.&lt;/strong>
We&amp;rsquo;ll talk about enabling GPU and advanced CPU support later, first - let&amp;rsquo;s try building it as-is, because it&amp;rsquo;s a good baseline to start with, and it doesn&amp;rsquo;t require any external dependencies.
To do that, we only need a C++ toolchain, &lt;a href="https://cmake.org/">CMake&lt;/a> and &lt;a href="https://ninja-build.org/">Ninja&lt;/a>.&lt;/p>
&lt;blockquote>
&lt;p>If you are &lt;strong>very&lt;/strong> lazy, you can download a release from Github and skip building steps.
Make sure to download correct version for your hardware/backend.
If you have troubles picking, i recommend following the build guide anyway - it&amp;rsquo;s simple enough and should explain what you should be looking for.
Keep in mind that release won&amp;rsquo;t contain Python scripts that we&amp;rsquo;re going to use, so if you&amp;rsquo;ll want to quantize models manually, you&amp;rsquo;ll need to get them from repository.&lt;/p>
&lt;/blockquote>
&lt;p class="windows-bg">On Windows, i recommend using &lt;a href="https://www.msys2.org/">MSYS&lt;/a> to setup the environment for building and using &lt;code>llama.cpp&lt;/code>.
&lt;a href="https://visualstudio.microsoft.com/downloads/">Microsoft Visual C++&lt;/a> is supported too, but trust me on that - you&amp;rsquo;ll want to use MSYS instead (it&amp;rsquo;s still a bit of pain in the ass, Linux setup is much simpler).
Follow the guide on the main page to install MinGW for x64 UCRT environment, which you probably should be using.
CMake, Ninja and Git can be installed in UCRT MSYS environment like that:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>pacman -S git mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="windows-bg">However, if you&amp;rsquo;re using any other toolchain (MSVC, or non-MSYS one), you should install CMake, Git and Ninja via &lt;code>winget&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-powershell" data-lang="powershell">&lt;span style="display:flex;">&lt;span>winget install cmake git.git ninja-build.ninja
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="windows-bg">You&amp;rsquo;ll also need Python, which you can get via winget.
Get the 3.12 version, because 3.13 does not have working PyTorch package yet.&lt;/p>
&lt;p class="windows-bg">&lt;strong>DO NOT USE PYTHON FROM MSYS, IT WILL NOT WORK PROPERLY DUE TO ISSUES WITH BUILDING &lt;code>llama.cpp&lt;/code> DEPENDENCY PACKAGES!&lt;/strong>
&lt;strong>We&amp;rsquo;re going to be using MSYS only for &lt;em>building&lt;/em> &lt;code>llama.cpp&lt;/code>, nothing more.&lt;/strong>&lt;/p>
&lt;p class="windows-bg">&lt;strong>If you&amp;rsquo;re using MSYS, remember to add it&amp;rsquo;s &lt;code>/bin&lt;/code> (&lt;code>C:\msys64\ucrt64\bin&lt;/code> by default) directory to PATH, so Python can use MinGW for building packages.&lt;/strong>
&lt;strong>Check if GCC is available by opening PowerShell/Command line and trying to run &lt;code>gcc --version&lt;/code>.&lt;/strong>
Also; check if it&amp;rsquo;s &lt;em>correct&lt;/em> GCC by running &lt;code>where.exe gcc.exe&lt;/code> and seeing where the first entry points to.
Reorder your PATH if you&amp;rsquo;ll notice that you&amp;rsquo;re using wrong GCC.&lt;/p>
&lt;p class="windows-bg">&lt;strong>If you&amp;rsquo;re using MSVC - ignore this disclaimer, it should be &amp;ldquo;detectable&amp;rdquo; by default.&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-powershell" data-lang="powershell">&lt;span style="display:flex;">&lt;span>winget install python.python.3.12
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="windows-bg">I recommend installing/upgrading &lt;code>pip&lt;/code>, &lt;code>setuptools&lt;/code> and &lt;code>wheel&lt;/code> packages before continuing.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-powershell" data-lang="powershell">&lt;span style="display:flex;">&lt;span>python -m pip install --upgrade pip wheel setuptools
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="linux-bg">On Linux, GCC is recommended, but you should be able to use Clang if you&amp;rsquo;d prefer by setting &lt;code>CMAKE_C_COMPILER=clang&lt;/code> and &lt;code>CMAKE_CXX_COMPILER=clang++&lt;/code> variables.
You should have GCC preinstalled (check &lt;code>gcc --version&lt;/code> in terminal), if not - get latest version for your distribution using your package manager.
Same applies to CMake, Ninja, Python 3 (with &lt;code>setuptools&lt;/code>, &lt;code>wheel&lt;/code> and &lt;code>pip&lt;/code>) and Git.&lt;/p>
&lt;p>Let&amp;rsquo;s start by grabbing a copy of &lt;a href="https://github.com/ggerganov/llama.cpp">&lt;code>llama.cpp&lt;/code> source code&lt;/a>, and moving into it.&lt;/p>
&lt;blockquote>
&lt;p>Disclaimer: this guide assumes all commands are ran from user&amp;rsquo;s home directory (&lt;code>/home/[yourusername]&lt;/code> on Linux, &lt;code>C:/Users/[yourusername]&lt;/code> on Windows).
You can use any directory you&amp;rsquo;d like, just keep in mind that if &amp;ldquo;starting directory&amp;rdquo; is not explicitly mentioned, start from home dir/your chosen one.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p class="windows-bg">&lt;strong>If you&amp;rsquo;re using MSYS&lt;/strong>, remember that MSYS home directory is different from Windows home directory. Make sure to use &lt;code>cd&lt;/code> (without arguments) to move into it after starting MSYS.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;em>(If you have previously configured SSH auth w/ GitHub, use &lt;code>git@github.com:ggerganov/llama.cpp.git&lt;/code> instead of the URL below)&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>git clone https://github.com/ggerganov/llama.cpp.git
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd llama.cpp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git submodule update --init --recursive
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now we&amp;rsquo;ll use CMake to generate build files, build the project, and install it.
Run the following command to generate build files in &lt;code>build/&lt;/code> subdirectory:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE&lt;span style="color:#f92672">=&lt;/span>Release -DCMAKE_INSTALL_PREFIX&lt;span style="color:#f92672">=&lt;/span>/your/install/dir -DLLAMA_BUILD_TESTS&lt;span style="color:#f92672">=&lt;/span>OFF -DLLAMA_BUILD_EXAMPLES&lt;span style="color:#f92672">=&lt;/span>ON -DLLAMA_BUILD_SERVER&lt;span style="color:#f92672">=&lt;/span>ON
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>There&amp;rsquo;s a lot of CMake variables being defined, which we could ignore and let llama.cpp use it&amp;rsquo;s defaults, but we won&amp;rsquo;t:&lt;/p>
&lt;ul>
&lt;li>&lt;code>CMAKE_BUILD_TYPE&lt;/code> is set to release for obvious reasons - we want maximum performance.&lt;/li>
&lt;li>&lt;a href="https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html">&lt;code>CMAKE_INSTALL_PREFIX&lt;/code>&lt;/a> is where the &lt;code>llama.cpp&lt;/code> binaries and python scripts will go. Replace the value of this variable, or remove it&amp;rsquo;s definition to keep default value.
&lt;ul>
&lt;li>On Windows, default directory is &lt;code>c:/Program Files/llama.cpp&lt;/code>.
As above, you&amp;rsquo;ll need admin privileges to install it, and you&amp;rsquo;ll have to add the &lt;code>bin/&lt;/code> subdirectory to your &lt;code>PATH&lt;/code> to make llama.cpp binaries accessible system-wide.
I prefer installing llama.cpp in &lt;code>$env:LOCALAPPDATA/llama.cpp&lt;/code> (&lt;code>C:/Users/[yourusername]/AppData/Local/llama.cpp&lt;/code>), as it doesn&amp;rsquo;t require admin privileges.&lt;/li>
&lt;li>On Linux, default directory is &lt;code>/usr/local&lt;/code>.
You can ignore this variable if that&amp;rsquo;s fine with you, but you&amp;rsquo;ll need superuser permissions to install the binaries there.
If you don&amp;rsquo;t have them, change it to point somewhere in your user directory and add it&amp;rsquo;s &lt;code>bin/&lt;/code> subdirectory to &lt;code>PATH&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;code>LLAMA_BUILD_TESTS&lt;/code> is set to &lt;code>OFF&lt;/code> because we don&amp;rsquo;t need tests, it&amp;rsquo;ll make the build a bit quicker.&lt;/li>
&lt;li>&lt;code>LLAMA_BUILD_EXAMPLES&lt;/code> is &lt;code>ON&lt;/code> because we&amp;rsquo;re gonna be using them.&lt;/li>
&lt;li>&lt;code>LLAMA_BUILD_SERVER&lt;/code> - see above. Note: Disabling &lt;code>LLAMA_BUILD_EXAMPLES&lt;/code> unconditionally disables building the server, both must be &lt;code>ON&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>If you&amp;rsquo;d ignore all of those variables, the default would be used - in case of &lt;code>LLAMA_BUILD_*&lt;/code> variables, all of those is &lt;code>ON&lt;/code> (if you&amp;rsquo;re building llama.cpp as standalone app, which you most likely are).
&lt;code>CMAKE_BUILD_TYPE&lt;/code> is &lt;em>usually&lt;/em> set to &lt;code>Release&lt;/code> by default, but not &lt;em>always&lt;/em>, so i&amp;rsquo;d rather set it manually.
&lt;code>CMAKE_INSTALL_PREFIX&lt;/code> is not necessary at all if you don&amp;rsquo;t intend to install the executables and you&amp;rsquo;re fine with adding the build dir&amp;rsquo;s &lt;code>/bin&lt;/code> subdirectory to PATH.&lt;/p>
&lt;p>Now, let&amp;rsquo;s build the project.
Replace &lt;code>X&lt;/code> with amount of cores your CPU has for faster compilation.
In theory, Ninja should automatically use all available cores, but i still prefer passing this argument manually.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>cmake --build build --config Release -j X
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Building should take only a few minutes.
After that, we can install the binaries for easier usage.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>cmake --install build --config Release
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, after going to &lt;code>CMAKE_INSTALL_PREFIX/bin&lt;/code> directory, we should see a list of executables and Python scripts:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>/c/Users/phoen/llama-build/bin
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>❯ l
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Mode Size Date Modified Name
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 203k 7 Nov 16:14 convert_hf_to_gguf.py
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-batched-bench.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-batched.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.4M 7 Nov 16:18 llama-bench.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-cli.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.2M 7 Nov 16:18 llama-convert-llama2c-to-ggml.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-cvector-generator.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-embedding.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-eval-callback.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-export-lora.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.0M 7 Nov 16:18 llama-gbnf-validator.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 1.2M 7 Nov 16:18 llama-gguf-hash.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.0M 7 Nov 16:18 llama-gguf-split.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 1.1M 7 Nov 16:18 llama-gguf.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-gritlm.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-imatrix.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-infill.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 4.2M 7 Nov 16:18 llama-llava-cli.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-lookahead.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-lookup-create.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 1.2M 7 Nov 16:18 llama-lookup-merge.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-lookup-stats.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-lookup.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 4.1M 7 Nov 16:18 llama-minicpmv-cli.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-parallel.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-passkey.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 4.0M 7 Nov 16:18 llama-perplexity.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.0M 7 Nov 16:18 llama-quantize-stats.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.2M 7 Nov 16:18 llama-quantize.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-retrieval.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-save-load-state.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 5.0M 7 Nov 16:19 llama-server.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.0M 7 Nov 16:18 llama-simple-chat.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.0M 7 Nov 16:18 llama-simple.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9M 7 Nov 16:18 llama-speculative.exe
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.1M 7 Nov 16:18 llama-tokenize.exe
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Don&amp;rsquo;t feel overwhelmed by the amount, we&amp;rsquo;re only going to be using few of them.
You should try running one of them to check if the executables have built correctly, for example - try &lt;code>llama-cli --help&lt;/code>.
We can&amp;rsquo;t do anything meaningful yet, because we lack a single critical component - a model to run.&lt;/p>
&lt;h2 id="getting-a-model">getting a model&lt;/h2>
&lt;p>The main place to look for models is &lt;a href="https://huggingface.co/">HuggingFace&lt;/a>.
You can also find datasets and other AI-related stuff there, great site.&lt;/p>
&lt;p>We&amp;rsquo;re going to use &lt;a href="https://huggingface.co/collections/HuggingFaceTB/smollm2-6723884218bcda64b34d7db9">&lt;code>SmolLM2&lt;/code>&lt;/a> here, a model series created by HuggingFace and published fairly recently (1st November 2024).
The reason i&amp;rsquo;ve chosen this model is the size - as the name implies, it&amp;rsquo;s &lt;em>small&lt;/em>.
Largest model from this series has 1.7 billion parameters, which means that it requires approx. 4GB of system memory to run in &lt;em>raw, unquantized&lt;/em> form (excluding context)!
There are also 360M and 135M variants, which are even smaller and should be easily runnable on RaspberryPi or a smartphone.&lt;/p>
&lt;p>There&amp;rsquo;s but one issue - &lt;code>llama.cpp&lt;/code> cannot run &amp;ldquo;raw&amp;rdquo; models directly.
What is usually provided by most LLM creators are original weights in &lt;code>.safetensors&lt;/code> or similar format.
&lt;code>llama.cpp&lt;/code> expects models in &lt;code>.gguf&lt;/code> format.
Fortunately, there is a very simple way of converting original model weights into &lt;code>.gguf&lt;/code> - &lt;code>llama.cpp&lt;/code> provides &lt;code>convert_hf_to_gguf.py&lt;/code> script exactly for this purpose!
Sometimes the creator provides &lt;code>.gguf&lt;/code> files - for example, two variants of &lt;code>SmolLM2&lt;/code> are provided by HuggingFace in this format.
This is not a very common practice, but you can also find models in &lt;code>.gguf&lt;/code> format uploaded there by community.
However, i&amp;rsquo;ll ignore the existence of pre-quantized &lt;code>.gguf&lt;/code> files here, and focus on quantizing our models by ourselves here, as it&amp;rsquo;ll allow us to experiment and adjust the quantization parameters of our model without having to download it multiple times.&lt;/p>
&lt;p>Grab the content of &lt;a href="https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct/tree/main">SmolLM2 1.7B Instruct&lt;/a> repository (you can use &lt;a href="https://huggingface.co/HuggingFaceTB/SmolLM2-360M-Instruct/tree/main">360M Instruct&lt;/a> or &lt;a href="https://huggingface.co/HuggingFaceTB/SmolLM2-135M-Instruct/tree/main">135M Instruct&lt;/a> version instead, if you have less than 4GB of free (V)RAM - or use any other model you&amp;rsquo;re already familiar with, if it supports &lt;code>transformers&lt;/code>), but omit the LFS files - we only need a single one, and we&amp;rsquo;ll download it manually.&lt;/p>
&lt;blockquote>
&lt;p>Why &lt;em>Instruct&lt;/em>, specifically?
You might have noticed that there are two variants of all those models - &lt;em>Instruct&lt;/em> and &lt;em>the other one without a suffix&lt;/em>.
&lt;em>Instruct&lt;/em> is trained for chat conversations, base model is only trained for text completion and is usually used as a base for further training.
This rule applies to most LLMs, but not all, so make sure to read the model&amp;rsquo;s description before using it!&lt;/p>
&lt;/blockquote>
&lt;p class="linux-bg-padded">If you&amp;rsquo;re using Bash/ZSH or compatible shell:&lt;/p>
&lt;p class="windows-bg-padded">&lt;em>MSYS uses Bash by default, so it applies to it too.&lt;/em>
&lt;em>From now on, assume that Linux commands work on MSYS too, unless i explicitly say otherwise.&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>GIT_LFS_SKIP_SMUDGE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span> git clone https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="windows-bg-padded">If you&amp;rsquo;re using PowerShell:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>$env:GIT_LFS_SKIP_SMUDGE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git clone https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="windows-bg-padded">If you&amp;rsquo;re using cmd.exe (VS Development Prompt, for example):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>set GIT_LFS_SKIP_SMUDGE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git clone https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>HuggingFace also supports Git over SSH. You can look up the &lt;code>git clone&lt;/code> command for every repo here:
&lt;img src="https://steelph0enix.github.io/img/llama-cpp/clone-hf-button.png" alt="huggingface - where to find git clone button">&lt;/p>
&lt;p>After cloning the repo, &lt;strong>download the &lt;code>model.safetensors&lt;/code> file from HuggingFace manually.&lt;/strong>
The reason why we used &lt;code>GIT_LFS_SKIP_SMUDGE&lt;/code> is because there&amp;rsquo;s many other large model files hidden in repo, and we don&amp;rsquo;t need them.
Also, downloading very large files manually is faster, because Git LFS sucks in that regard.&lt;/p>
&lt;p>After downloading everything, our local copy of the SmolLM repo should look like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>PS D:\LLMs\repos\SmolLM2-1.7B-Instruct&amp;gt; l
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Mode Size Date Modified Name
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 806 2 Nov 15:16 all_results.json
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 888 2 Nov 15:16 config.json
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 602 2 Nov 15:16 eval_results.json
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 139 2 Nov 15:16 generation_config.json
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 515k 2 Nov 15:16 merges.txt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.4G 2 Nov 15:34 model.safetensors
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>d---- - 2 Nov 15:16 onnx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 11k 2 Nov 15:16 README.md
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>d---- - 2 Nov 15:16 runs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 689 2 Nov 15:16 special_tokens_map.json
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 2.2M 2 Nov 15:16 tokenizer.json
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 3.9k 2 Nov 15:16 tokenizer_config.json
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 240 2 Nov 15:16 train_results.json
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 89k 2 Nov 15:16 trainer_state.json
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 129 2 Nov 15:16 training_args.bin
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 801k 2 Nov 15:16 vocab.json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We&amp;rsquo;re gonna (indirectly) use only four of those files:&lt;/p>
&lt;ul>
&lt;li>&lt;code>config.json&lt;/code> contains configuration/metadata of our model&lt;/li>
&lt;li>&lt;code>model.safetensors&lt;/code> contains model weights&lt;/li>
&lt;li>&lt;code>tokenizer.json&lt;/code> contains tokenizer data (mapping of text tokens to their ID&amp;rsquo;s, and other stuff).
Sometimes this data is stored in &lt;code>tokenizer.model&lt;/code> file instead.&lt;/li>
&lt;li>&lt;code>tokenizer_config.json&lt;/code> contains tokenizer configuration (for example, special tokens and chat template)&lt;/li>
&lt;/ul>
&lt;p class="anti-plag">i&amp;rsquo;m leaving this sentence here as anti-plagiarism token.
If you&amp;rsquo;re not currently reading this on my blog, which is @ steelph0enix.github.io, someone probably stolen that article without permission&lt;/p>
&lt;h3 class="post-anti-plag" id="converting-huggingface-model-to-gguf">converting huggingface model to GGUF&lt;/h3>
&lt;p>In order to convert this raw model to something that &lt;code>llama.cpp&lt;/code> will understand, we&amp;rsquo;ll use aforementioned &lt;code>convert_hf_to_gguf.py&lt;/code> script that comes with &lt;code>llama.cpp&lt;/code>.
For all our Python needs, we&amp;rsquo;re gonna need a virtual environment.
I recommend making it outside of &lt;code>llama.cpp&lt;/code> repo, for example - in your home directory.&lt;/p>
&lt;p class="linux-bg-padded">To make one on Linux, run this command (tweak the path if you&amp;rsquo;d like):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>python -m venv ~/llama-cpp-venv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="windows-bg-padded">If you&amp;rsquo;re using PowerShell, this is the equivalent:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-powershell" data-lang="powershell">&lt;span style="display:flex;">&lt;span>python -m venv $env:USERPROFILE/llama-cpp-venv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="windows-bg-padded">If you&amp;rsquo;re using cmd.exe, this is the equivalent:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-batch" data-lang="batch">&lt;span style="display:flex;">&lt;span>python -m venv %USERPROFILE%/llama-cpp-venv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then, we need to activate it.&lt;/p>
&lt;p class="linux-bg-padded">On Linux:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>source ~/llama-cpp-venv/bin/activate
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="windows-bg-padded">With PowerShell:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-powershell" data-lang="powershell">&lt;span style="display:flex;">&lt;span>. $env:USERPROFILE/llama-cpp-venv/Scripts/Activate.ps1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="windows-bg-padded">With cmd.exe:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cmd" data-lang="cmd">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">call&lt;/span> %USERPROFILE%/llama-cpp-venv/Scripts/activate.bat
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After that, let&amp;rsquo;s make sure that our virtualenv has all the core packages up-to-date.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>python -m pip install --upgrade pip wheel setuptools
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next, we need to install prerequisites for the llama.cpp scripts.
Let&amp;rsquo;s look into &lt;code>requirements/&lt;/code> directory of our &lt;code>llama.cpp&lt;/code> repository.
We should see something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>❯ l llama.cpp/requirements
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Mode Size Date Modified Name
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 428 11 Nov 13:57 requirements-all.txt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 34 11 Nov 13:57 requirements-compare-llama-bench.txt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 111 11 Nov 13:57 requirements-convert_hf_to_gguf.txt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 111 11 Nov 13:57 requirements-convert_hf_to_gguf_update.txt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 99 11 Nov 13:57 requirements-convert_legacy_llama.txt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 43 11 Nov 13:57 requirements-convert_llama_ggml_to_gguf.txt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 96 11 Nov 13:57 requirements-convert_lora_to_gguf.txt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 48 11 Nov 13:57 requirements-pydantic.txt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>-a--- 13 11 Nov 13:57 requirements-test-tokenizer-random.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As we can see, there&amp;rsquo;s a file with deps for our script!
To install dependencies from it, run this command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>python -m pip install --upgrade -r llama.cpp/requirements/requirements-convert_hf_to_gguf.txt
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If &lt;code>pip&lt;/code> failed during build, make sure you have working C/C++ toolchain in your &lt;code>PATH&lt;/code>.&lt;/p>
&lt;blockquote>
&lt;p class="windows-bg">If you&amp;rsquo;re using MSYS for that, don&amp;rsquo;t. Go back to PowerShell/cmd, install Python via winget and repeat the setup.
As far as i&amp;rsquo;ve tested it, Python deps don&amp;rsquo;t detect the platform correctly on MSYS and try to use wrong build config.
This is what i warned you about earlier.&lt;/p>
&lt;/blockquote>
&lt;p>Now we can use the script to create our GGUF model file.
Start with printing the help and reading the options.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>python llama.cpp/convert_hf_to_gguf.py --help
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If this command printed help, you can continue.
Otherwise make sure that python&amp;rsquo;s virtualenv is active and dependencies are correctly installed, and try again.
To convert our model we can simply pass the path to directory with model&amp;rsquo;s repository and, optionally, path to output file.
We don&amp;rsquo;t need to tweak the quantization here, for maximum flexibility we&amp;rsquo;re going to create a floating-point GGUF file which we&amp;rsquo;ll then quantize down.
That&amp;rsquo;s because &lt;code>llama-quantize&lt;/code> offers much more quantization options, and this script picks optimal floating-point format by default.&lt;/p>
&lt;p>To create GGUF file from our downloaded HuggingFace repository with SmolLM2 (Replace &lt;code>SmolLM2-1.7B-Instruct&lt;/code> with your path, if it&amp;rsquo;s different) run this command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>python llama.cpp/convert_hf_to_gguf.py SmolLM2-1.7B-Instruct --outfile ./SmolLM2.gguf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If everything went correctly, you should see similar output:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:Loading model: SmolLM2-1.7B-Instruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:gguf.gguf_writer:gguf: This GGUF file is for Little Endian only
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:Exporting model...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:gguf: loading model part &amp;#39;model.safetensors&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:token_embd.weight, torch.bfloat16 --&amp;gt; F16, shape = {2048, 49152}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:blk.0.attn_norm.weight, torch.bfloat16 --&amp;gt; F32, shape = {2048}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:blk.9.attn_q.weight, torch.bfloat16 --&amp;gt; F16, shape = {2048, 2048}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:blk.9.attn_v.weight, torch.bfloat16 --&amp;gt; F16, shape = {2048, 2048}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:output_norm.weight, torch.bfloat16 --&amp;gt; F32, shape = {2048}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:Set meta model
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:Set model parameters
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:gguf: context length = 8192
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:gguf: embedding length = 2048
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:gguf: feed forward length = 8192
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:gguf: head count = 32
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:gguf: key-value head count = 32
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:gguf: rope theta = 130000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:gguf: rms norm epsilon = 1e-05
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:gguf: file type = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:Set model tokenizer
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:gguf.vocab:Adding 48900 merge(s).
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:gguf.vocab:Setting special token type bos to 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:gguf.vocab:Setting special token type eos to 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:gguf.vocab:Setting special token type unk to 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:gguf.vocab:Setting special token type pad to 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:gguf.vocab:Setting chat_template to {% for message in messages %}{% if loop.first and messages[0][&amp;#39;role&amp;#39;] != &amp;#39;system&amp;#39; %}{{ &amp;#39;&amp;lt;|im_start|&amp;gt;system
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>You are a helpful AI assistant named SmolLM, trained by Hugging Face&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;#39; }}{% endif %}{{&amp;#39;&amp;lt;|im_start|&amp;gt;&amp;#39; + message[&amp;#39;role&amp;#39;] + &amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;#39; + message[&amp;#39;content&amp;#39;] + &amp;#39;&amp;lt;|im_end|&amp;gt;&amp;#39; + &amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;#39;}}{% endfor %}{% if add_generation_prompt %}{{ &amp;#39;&amp;lt;|im_start|&amp;gt;assistant
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;#39; }}{% endif %}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:Set model quantization version
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:gguf.gguf_writer:Writing the following files:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:gguf.gguf_writer:SmolLM2.gguf: n_tensors = 218, total_size = 3.4G
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Writing: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3.42G/3.42G [00:15&amp;lt;00:00, 215Mbyte/s]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>INFO:hf-to-gguf:Model successfully exported to SmolLM2.gguf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="quantizing-the-model">quantizing the model&lt;/h3>
&lt;p>Now we can finally quantize our model!
To do that, we&amp;rsquo;ll use &lt;code>llama-quantize&lt;/code> executable that we previously compiled with other &lt;code>llama.cpp&lt;/code> executables.
First, let&amp;rsquo;s check what quantizations we have available.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>llama-quantize --help
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As of now, &lt;code>llama-quantize --help&lt;/code> shows following types:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>usage: llama-quantize [--help] [--allow-requantize] [--leave-output-tensor] [--pure] [--imatrix] [--include-weights] [--exclude-weights] [--output-tensor-type] [--token-embedding-type] [--override-kv] model-f32.gguf [model-quant.gguf] type [nthreads]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Allowed quantization types:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 2 or Q4_0 : 4.34G, +0.4685 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 3 or Q4_1 : 4.78G, +0.4511 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 8 or Q5_0 : 5.21G, +0.1316 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 9 or Q5_1 : 5.65G, +0.1062 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 19 or IQ2_XXS : 2.06 bpw quantization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 20 or IQ2_XS : 2.31 bpw quantization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 28 or IQ2_S : 2.5 bpw quantization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 29 or IQ2_M : 2.7 bpw quantization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 24 or IQ1_S : 1.56 bpw quantization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 31 or IQ1_M : 1.75 bpw quantization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 36 or TQ1_0 : 1.69 bpw ternarization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 37 or TQ2_0 : 2.06 bpw ternarization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 10 or Q2_K : 2.96G, +3.5199 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 21 or Q2_K_S : 2.96G, +3.1836 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 23 or IQ3_XXS : 3.06 bpw quantization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 26 or IQ3_S : 3.44 bpw quantization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 27 or IQ3_M : 3.66 bpw quantization mix
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 12 or Q3_K : alias for Q3_K_M
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 22 or IQ3_XS : 3.3 bpw quantization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 11 or Q3_K_S : 3.41G, +1.6321 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 12 or Q3_K_M : 3.74G, +0.6569 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 13 or Q3_K_L : 4.03G, +0.5562 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 25 or IQ4_NL : 4.50 bpw non-linear quantization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 30 or IQ4_XS : 4.25 bpw non-linear quantization
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 15 or Q4_K : alias for Q4_K_M
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 14 or Q4_K_S : 4.37G, +0.2689 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 15 or Q4_K_M : 4.58G, +0.1754 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 17 or Q5_K : alias for Q5_K_M
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 16 or Q5_K_S : 5.21G, +0.1049 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 17 or Q5_K_M : 5.33G, +0.0569 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 18 or Q6_K : 6.14G, +0.0217 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 7 or Q8_0 : 7.96G, +0.0026 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 33 or Q4_0_4_4 : 4.34G, +0.4685 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 34 or Q4_0_4_8 : 4.34G, +0.4685 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 35 or Q4_0_8_8 : 4.34G, +0.4685 ppl @ Llama-3-8B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 1 or F16 : 14.00G, +0.0020 ppl @ Mistral-7B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 32 or BF16 : 14.00G, -0.0050 ppl @ Mistral-7B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> 0 or F32 : 26.00G @ 7B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> COPY : only copy tensors, no quantizing
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Let&amp;rsquo;s decode this table.
From the left, we have IDs and names of quantization types - you can use either when calling &lt;code>llama-quantize&lt;/code>.
After the &lt;code>:&lt;/code>, there&amp;rsquo;s a short description that in most cases shows either the example model&amp;rsquo;s size and perplexity, or the amount of bits per tensor weight (bpw) for that specific quantization.
Perplexity is a metric that describes how certain the model is about it&amp;rsquo;s predictions.
We can think about it like that: lower perplexity -&amp;gt; model is more certain about it&amp;rsquo;s predictions -&amp;gt; model is more accurate.
This is a daily reminder that LLMs are nothing more than overcomplicated autocompletion algorithms.
The &amp;ldquo;bits per weight&amp;rdquo; metric tells us the average size of quantized tensor&amp;rsquo;s weight.
You may think it&amp;rsquo;s strange that those are floating-point values, but we&amp;rsquo;ll see the reason for that soon.&lt;/p>
&lt;p>Now, the main question that needs to be answered is &amp;ldquo;which quantization do we pick?&amp;rdquo;.
And the answer is &amp;ldquo;it depends&amp;rdquo;.
My rule of thumb for picking quantization type is &amp;ldquo;the largest i can fit in my VRAM, unless it&amp;rsquo;s too slow for my taste&amp;rdquo; and i recommend this approach if you don&amp;rsquo;t know where to start!
Obviously, if you don&amp;rsquo;t have/want to use GPU, replace &amp;ldquo;VRAM&amp;rdquo; with &amp;ldquo;RAM&amp;rdquo; and &amp;ldquo;largest i can fit&amp;rdquo; with &amp;ldquo;largest i can fit without forcing the OS to move everything to swap&amp;rdquo;.
That creates another question - &amp;ldquo;what is the largest quant i can fit in my (V)RAM&amp;rdquo;?
And this depends on the original model&amp;rsquo;s size and encoding, and - obviously - the amount of (V)RAM you have.
Since we&amp;rsquo;re using SmolLM2, our model is relatively small.
GGUF file of 1.7B-Instruct variant in BF16 format weights 3.4GB.
Most models you&amp;rsquo;ll encounter will be encoded in either BF16 or FP16 format, rarely we can find FP32-encoded LLMs.
That means most of models have 16 bits per weight by default.
We can easily approximate the size of quantized model by multiplying the original size with approximate ratio of bits per weight.
For example, let&amp;rsquo;s assume we want to know how large will SmolLM2 1.7B-Instruct be after Q8_0 quantization.
Let&amp;rsquo;s assume Q8_0 quant uses 8 bits per word, which means the ratio is simply 1/2, so our model should weight ~1.7GB.
Let&amp;rsquo;s check that!&lt;/p>
&lt;p>The first argument is source model, second - target file.
Third is the quantization type, and last is the amount of cores for parallel processing.
Replace &lt;code>N&lt;/code> with amount of cores in your system and run this command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>llama-quantize SmolLM2.gguf SmolLM2.q8.gguf Q8_0 N
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You should see similar output:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>main: build = 4200 (46c69e0e)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: built with gcc (GCC) 14.2.1 20240910 for x86_64-pc-linux-gnu
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: quantizing &amp;#39;SmolLM2.gguf&amp;#39; to &amp;#39;SmolLM2.q8.gguf&amp;#39; as Q8_0 using 24 threads
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: loaded meta data with 37 key-value pairs and 218 tensors from SmolLM2.gguf (version GGUF V3 (latest))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: Dumping metadata keys/values. Note: KV overrides do not apply in this output.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 0: general.architecture str = llama
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 1: general.type str = model
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 2: general.name str = SmolLM2 1.7B Instruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 3: general.finetune str = Instruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 4: general.basename str = SmolLM2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 5: general.size_label str = 1.7B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 6: general.license str = apache-2.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 7: general.base_model.count u32 = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 8: general.base_model.0.name str = SmolLM2 1.7B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 9: general.base_model.0.organization str = HuggingFaceTB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 10: general.base_model.0.repo_url str = https://huggingface.co/HuggingFaceTB/...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 11: general.tags arr[str,4] = [&amp;#34;safetensors&amp;#34;, &amp;#34;onnx&amp;#34;, &amp;#34;transformers...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 12: general.languages arr[str,1] = [&amp;#34;en&amp;#34;]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 13: llama.block_count u32 = 24
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 14: llama.context_length u32 = 8192
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 15: llama.embedding_length u32 = 2048
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 16: llama.feed_forward_length u32 = 8192
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 17: llama.attention.head_count u32 = 32
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 18: llama.attention.head_count_kv u32 = 32
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 19: llama.rope.freq_base f32 = 130000.000000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 20: llama.attention.layer_norm_rms_epsilon f32 = 0.000010
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 21: general.file_type u32 = 32
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 22: llama.vocab_size u32 = 49152
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 23: llama.rope.dimension_count u32 = 64
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 24: tokenizer.ggml.model str = gpt2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 25: tokenizer.ggml.pre str = smollm
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 26: tokenizer.ggml.tokens arr[str,49152] = [&amp;#34;&amp;lt;|endoftext|&amp;gt;&amp;#34;, &amp;#34;&amp;lt;|im_start|&amp;gt;&amp;#34;, &amp;#34;&amp;lt;|...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 27: tokenizer.ggml.token_type arr[i32,49152] = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 28: tokenizer.ggml.merges arr[str,48900] = [&amp;#34;Ġ t&amp;#34;, &amp;#34;Ġ a&amp;#34;, &amp;#34;i n&amp;#34;, &amp;#34;h e&amp;#34;, &amp;#34;Ġ Ġ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 29: tokenizer.ggml.bos_token_id u32 = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 30: tokenizer.ggml.eos_token_id u32 = 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 31: tokenizer.ggml.unknown_token_id u32 = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 32: tokenizer.ggml.padding_token_id u32 = 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 33: tokenizer.chat_template str = {% for message in messages %}{% if lo...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 34: tokenizer.ggml.add_space_prefix bool = false
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 35: tokenizer.ggml.add_bos_token bool = false
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 36: general.quantization_version u32 = 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - type f32: 49 tensors
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - type bf16: 169 tensors
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ggml_vulkan: Found 1 Vulkan devices:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ggml_vulkan: 0 = AMD Radeon RX 7900 XT (AMD open-source driver) | uma: 0 | fp16: 1 | warp size: 64
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 1/ 218] output_norm.weight - [ 2048, 1, 1, 1], type = f32, size = 0.008 MB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 2/ 218] token_embd.weight - [ 2048, 49152, 1, 1], type = bf16, converting to q8_0 .. size = 192.00 MiB -&amp;gt; 102.00 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 3/ 218] blk.0.attn_k.weight - [ 2048, 2048, 1, 1], type = bf16, converting to q8_0 .. size = 8.00 MiB -&amp;gt; 4.25 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 4/ 218] blk.0.attn_norm.weight - [ 2048, 1, 1, 1], type = f32, size = 0.008 MB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 5/ 218] blk.0.attn_output.weight - [ 2048, 2048, 1, 1], type = bf16, converting to q8_0 .. size = 8.00 MiB -&amp;gt; 4.25 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 6/ 218] blk.0.attn_q.weight - [ 2048, 2048, 1, 1], type = bf16, converting to q8_0 .. size = 8.00 MiB -&amp;gt; 4.25 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 7/ 218] blk.0.attn_v.weight - [ 2048, 2048, 1, 1], type = bf16, converting to q8_0 .. size = 8.00 MiB -&amp;gt; 4.25 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 212/ 218] blk.23.attn_output.weight - [ 2048, 2048, 1, 1], type = bf16, converting to q8_0 .. size = 8.00 MiB -&amp;gt; 4.25 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 213/ 218] blk.23.attn_q.weight - [ 2048, 2048, 1, 1], type = bf16, converting to q8_0 .. size = 8.00 MiB -&amp;gt; 4.25 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 214/ 218] blk.23.attn_v.weight - [ 2048, 2048, 1, 1], type = bf16, converting to q8_0 .. size = 8.00 MiB -&amp;gt; 4.25 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 215/ 218] blk.23.ffn_down.weight - [ 8192, 2048, 1, 1], type = bf16, converting to q8_0 .. size = 32.00 MiB -&amp;gt; 17.00 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 216/ 218] blk.23.ffn_gate.weight - [ 2048, 8192, 1, 1], type = bf16, converting to q8_0 .. size = 32.00 MiB -&amp;gt; 17.00 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 217/ 218] blk.23.ffn_norm.weight - [ 2048, 1, 1, 1], type = f32, size = 0.008 MB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[ 218/ 218] blk.23.ffn_up.weight - [ 2048, 8192, 1, 1], type = bf16, converting to q8_0 .. size = 32.00 MiB -&amp;gt; 17.00 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_quantize_internal: model size = 3264.38 MB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_quantize_internal: quant size = 1734.38 MB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: quantize time = 2289.97 ms
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: total time = 2289.97 ms
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And yeah, we were more-or-less correct, it&amp;rsquo;s 1.7GB!
&lt;strong>BUT&lt;/strong> this is only the model&amp;rsquo;s size, we also have to consider the memory requirements for the context.
Fortunately, context doesn&amp;rsquo;t require massive amounts of memory - i&amp;rsquo;m not really sure how much exactly it eats up, but it&amp;rsquo;s safe to assume that we should have at least 1GB memory for it.
So, taking all that in account, we can approximate that to load this model to memory with the context, we need at least 3GB of free (V)RAM.
This is not so bad, and most modern consumer GPUs have at least this amount (even the old GTX 1060 3GB should be able to run this model in Q8_0 quant).
However, if that&amp;rsquo;s still too much, we can easily go lower!
Reducing the amount of bits per weight via quantization not only reduces the model&amp;rsquo;s size, but also increases the speed of data generation.
Unfortunately, it also makes the model more stupid.
The change is gradual, you may not notice it when going from Q8_0 to Q6_K, but going below Q4 quant can be noticeable.
I strongly recommend experimenting on your own with different models and quantization types, because your experience may be different from mine!&lt;/p>
&lt;blockquote>
&lt;p>Oh, by the way - remember that right now our &lt;code>llama.cpp&lt;/code> build will use CPU for calculations, so the model will reside in RAM.
Make sure you have at least 3GB of free RAM before trying to use the model, if you don&amp;rsquo;t - quantize it with smaller quant, or get a smaller version.&lt;/p>
&lt;/blockquote>
&lt;p>Anyway, we got our quantized model now, we can &lt;strong>finally&lt;/strong> use it!&lt;/p>
&lt;h2 id="running-llamacpp-server">running llama.cpp server&lt;/h2>
&lt;p>If going through the first part of this post felt like pain and suffering, don&amp;rsquo;t worry - i felt the same writing it.
That&amp;rsquo;s why it took a month to write.
But, at long last we can do something fun.&lt;/p>
&lt;p>Let&amp;rsquo;s start, as usual, with printing the help to make sure our binary is working fine:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>llama-server --help
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You should see a lot of options.
Some of them will be explained here in a bit, some of them you&amp;rsquo;ll have to research yourself.
For now, the only options that are interesting to us are:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>-m, --model FNAME model path (default: `models/$filename` with filename from `--hf-file`
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> or `--model-url` if set, otherwise models/7B/ggml-model-f16.gguf)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (env: LLAMA_ARG_MODEL)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>--host HOST ip address to listen (default: 127.0.0.1)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (env: LLAMA_ARG_HOST)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>--port PORT port to listen (default: 8080)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (env: LLAMA_ARG_PORT)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Run &lt;code>llama-server&lt;/code> with model&amp;rsquo;s path set to quantized SmolLM2 GGUF file.
If you don&amp;rsquo;t have anything running on &lt;code>127.0.0.1:8080&lt;/code>, you can leave the host and port on defaults.
Notice that you can also use environmental variables instead of arguments, so you can setup your env and just call &lt;code>llama-server&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>llama-server -m SmolLM2.q8.gguf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You should see similar output after running this command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>build: 4182 (ab96610b) with cc (GCC) 14.2.1 20240910 for x86_64-pc-linux-gnu
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>system info: n_threads = 12, n_threads_batch = 12, total_threads = 24
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>system_info: n_threads = 12 (n_threads_batch = 12) / 24 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | LLAMAFILE = 1 | AARCH64_REPACK = 1 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: HTTP server is listening, hostname: 127.0.0.1, port: 8080, http threads: 23
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: loading model
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>srv load_model: loading model &amp;#39;SmolLM2.q8.gguf&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: loaded meta data with 37 key-value pairs and 218 tensors from SmolLM2.q8.gguf (version GGUF V3 (latest))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: Dumping metadata keys/values. Note: KV overrides do not apply in this output.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 0: general.architecture str = llama
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 1: general.type str = model
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 2: general.name str = SmolLM2 1.7B Instruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 3: general.finetune str = Instruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 4: general.basename str = SmolLM2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 5: general.size_label str = 1.7B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 6: general.license str = apache-2.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 7: general.base_model.count u32 = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 8: general.base_model.0.name str = SmolLM2 1.7B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 9: general.base_model.0.organization str = HuggingFaceTB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 10: general.base_model.0.repo_url str = https://huggingface.co/HuggingFaceTB/...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 11: general.tags arr[str,4] = [&amp;#34;safetensors&amp;#34;, &amp;#34;onnx&amp;#34;, &amp;#34;transformers...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 12: general.languages arr[str,1] = [&amp;#34;en&amp;#34;]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 13: llama.block_count u32 = 24
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 14: llama.context_length u32 = 8192
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 15: llama.embedding_length u32 = 2048
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 16: llama.feed_forward_length u32 = 8192
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 17: llama.attention.head_count u32 = 32
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 18: llama.attention.head_count_kv u32 = 32
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 19: llama.rope.freq_base f32 = 130000.000000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 20: llama.attention.layer_norm_rms_epsilon f32 = 0.000010
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 21: general.file_type u32 = 7
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 22: llama.vocab_size u32 = 49152
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 23: llama.rope.dimension_count u32 = 64
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 24: tokenizer.ggml.model str = gpt2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 25: tokenizer.ggml.pre str = smollm
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 26: tokenizer.ggml.tokens arr[str,49152] = [&amp;#34;&amp;lt;|endoftext|&amp;gt;&amp;#34;, &amp;#34;&amp;lt;|im_start|&amp;gt;&amp;#34;, &amp;#34;&amp;lt;|...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 27: tokenizer.ggml.token_type arr[i32,49152] = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 28: tokenizer.ggml.merges arr[str,48900] = [&amp;#34;Ġ t&amp;#34;, &amp;#34;Ġ a&amp;#34;, &amp;#34;i n&amp;#34;, &amp;#34;h e&amp;#34;, &amp;#34;Ġ Ġ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 29: tokenizer.ggml.bos_token_id u32 = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 30: tokenizer.ggml.eos_token_id u32 = 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 31: tokenizer.ggml.unknown_token_id u32 = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 32: tokenizer.ggml.padding_token_id u32 = 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 33: tokenizer.chat_template str = {% for message in messages %}{% if lo...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 34: tokenizer.ggml.add_space_prefix bool = false
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 35: tokenizer.ggml.add_bos_token bool = false
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - kv 36: general.quantization_version u32 = 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - type f32: 49 tensors
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: - type q8_0: 169 tensors
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_vocab: special tokens cache size = 17
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_vocab: token to piece cache size = 0.3170 MB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: format = GGUF V3 (latest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: arch = llama
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: vocab type = BPE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_vocab = 49152
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_merges = 48900
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: vocab_only = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_ctx_train = 8192
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_embd = 2048
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_layer = 24
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_head = 32
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_head_kv = 32
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_rot = 64
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_swa = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_embd_head_k = 64
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_embd_head_v = 64
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_gqa = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_embd_k_gqa = 2048
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_embd_v_gqa = 2048
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: f_norm_eps = 0.0e+00
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: f_norm_rms_eps = 1.0e-05
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: f_clamp_kqv = 0.0e+00
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: f_max_alibi_bias = 0.0e+00
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: f_logit_scale = 0.0e+00
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_ff = 8192
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_expert = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_expert_used = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: causal attn = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: pooling type = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: rope type = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: rope scaling = linear
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: freq_base_train = 130000.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: freq_scale_train = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: n_ctx_orig_yarn = 8192
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: rope_finetuned = unknown
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: ssm_d_conv = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: ssm_d_inner = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: ssm_d_state = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: ssm_dt_rank = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: ssm_dt_b_c_rms = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: model type = ?B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: model ftype = Q8_0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: model params = 1.71 B
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: model size = 1.69 GiB (8.50 BPW)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: general.name = SmolLM2 1.7B Instruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: BOS token = 1 &amp;#39;&amp;lt;|im_start|&amp;gt;&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: EOS token = 2 &amp;#39;&amp;lt;|im_end|&amp;gt;&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: EOT token = 0 &amp;#39;&amp;lt;|endoftext|&amp;gt;&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: UNK token = 0 &amp;#39;&amp;lt;|endoftext|&amp;gt;&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: PAD token = 2 &amp;#39;&amp;lt;|im_end|&amp;gt;&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: LF token = 143 &amp;#39;Ä&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: EOG token = 0 &amp;#39;&amp;lt;|endoftext|&amp;gt;&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: EOG token = 2 &amp;#39;&amp;lt;|im_end|&amp;gt;&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_print_meta: max token length = 162
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_tensors: CPU_Mapped model buffer size = 1734.38 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>................................................................................................
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_seq_max = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_ctx = 4096
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_ctx_per_seq = 4096
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_batch = 2048
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_ubatch = 512
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: flash_attn = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: freq_base = 130000.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: freq_scale = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_ctx_per_seq (4096) &amp;lt; n_ctx_train (8192) -- the full capacity of the model will not be utilized
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_kv_cache_init: CPU KV buffer size = 768.00 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: KV self size = 768.00 MiB, K (f16): 384.00 MiB, V (f16): 384.00 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: CPU output buffer size = 0.19 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: CPU compute buffer size = 280.01 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: graph nodes = 774
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: graph splits = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>common_init_from_params: warming up the model with an empty run - please wait ... (--no-warmup to disable)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>srv init: initializing slots, n_slots = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>slot init: id 0 | task -1 | new slot n_ctx_slot = 4096
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: model loaded
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: chat template, built_in: 1, chat_example: &amp;#39;&amp;lt;|im_start|&amp;gt;system
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>You are a helpful assistant&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;user
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Hello&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;assistant
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Hi there&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;user
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>How are you?&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;assistant
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;#39;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: server is listening on http://127.0.0.1:8080 - starting the main loop
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>srv update_slots: all slots are idle
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And now we can access the web UI on &lt;code>http://127.0.0.1:8080&lt;/code> or whatever host/port combo you&amp;rsquo;ve set.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/llama-cpp/llama-cpp-webui.png" alt="llama.cpp webui">&lt;/p>
&lt;p>From this point, we can freely chat with the LLM using the web UI, or you can use the OpenAI-compatible API that &lt;code>llama-server&lt;/code> provides.
I won&amp;rsquo;t dig into the API itself here, i&amp;rsquo;ve written &lt;a href="https://github.com/SteelPh0enix/unreasonable-llama">a Python library&lt;/a> for it if you&amp;rsquo;re interested in using it (i&amp;rsquo;m trying to keep it up-to-date with &lt;code>llama.cpp&lt;/code> master, but it might not be all the time).
I recommend looking into the &lt;a href="https://github.com/ggerganov/llama.cpp/tree/master/examples/server">&lt;code>llama-server&lt;/code> source code and README&lt;/a> for more details about endpoints.&lt;/p>
&lt;p>Let&amp;rsquo;s see what we can do with web UI.
On the left, we have list of conversations.
Those are stored in browser&amp;rsquo;s localStorage (as the disclaimer on the bottom-left graciously explains), which means they are persistent even if you restart the browser.
Keep in mind that changing the host/port of the server will &amp;ldquo;clear&amp;rdquo; those.
Current conversation is passed to the LLM as context, and the context size is limited by server settings (we will learn how to tweak it in a second).
I recommend making new conversations often, and keeping their context focused on the subject for optimal performance.&lt;/p>
&lt;p>On the top-right, we have (from left to right) &amp;ldquo;remove conversation&amp;rdquo;, &amp;ldquo;download conversation&amp;rdquo; (in JSON format), &amp;ldquo;configuration&amp;rdquo; and &amp;ldquo;Theme&amp;rdquo; buttons.
In the configuration window, we can tweak generation settings for our LLM.
&lt;strong>Those are currently global, not per-conversation.&lt;/strong>
All of those settings are briefly described &lt;a href="https://steelph0enix.github.io/posts/llama-cpp-guide/#llm-configuration-options-explained">below&lt;/a>.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/llama-cpp/llama-cpp-webui-config.png" alt="llama.cpp webui config">&lt;/p>
&lt;h3 id="llamacpp-server-settings">llama.cpp server settings&lt;/h3>
&lt;p>Web UI provides only a small subset of configuration options we have available, and only those related to LLM samplers.
For the full set, we need to call &lt;code>llama-server --help&lt;/code>.
With those options we can drastically improve (or worsen) the behavior of our model and performance of text generation, so it&amp;rsquo;s worth knowing them.
I won&amp;rsquo;t explain &lt;em>all&lt;/em> of the options listed there, because it would be mostly redundant, but i&amp;rsquo;ll probably explain &lt;em>most&lt;/em> of them in one way of another here.
I&amp;rsquo;ll try to explain all &lt;em>interesting&lt;/em> options though.&lt;/p>
&lt;p>One thing that&amp;rsquo;s also worth mentioning is that most of those parameters are read from the environment.
This is also the case for most other &lt;code>llama.cpp&lt;/code> executables, and the parameter names (and environment variables) are the same for them.
Names of those variables are provided in &lt;code>llama-server --help&lt;/code> output, i&amp;rsquo;ll add them to each described option here.&lt;/p>
&lt;p>Let&amp;rsquo;s start with &lt;code>common params&lt;/code> section:&lt;/p>
&lt;ul>
&lt;li>&lt;code>--threads&lt;/code>/&lt;code>--threads-batch&lt;/code> (&lt;code>LLAMA_ARG_THREADS&lt;/code>) - amount of CPU threads used by LLM.
Default value is -1, which tells &lt;code>llama.cpp&lt;/code> to detect the amount of cores in the system.
This behavior is probably good enough for most of people, so unless you have &lt;em>exotic&lt;/em> hardware setup and you know what you&amp;rsquo;re doing - leave it on default.
If you &lt;em>do&lt;/em> have an exotic setup, you may also want to look at other NUMA and offloading-related flags.&lt;/li>
&lt;li>&lt;code>--ctx-size&lt;/code> (&lt;code>LLAMA_ARG_CTX_SIZE&lt;/code>) - size of the prompt context.
In other words, the amount of tokens that the LLM can remember at once.
Increasing the context size also increases the memory requirements for the LLM.
Every model has a context size limit, when this argument is set to &lt;code>0&lt;/code>, &lt;code>llama.cpp&lt;/code> tries to use it.&lt;/li>
&lt;li>&lt;code>--predict&lt;/code> (&lt;code>LLAMA_ARG_N_PREDICT&lt;/code>) - number of tokens to predict.
When LLM generates text, it stops either after generating end-of-message token (when it decides that the generated sentence is over), or after hitting this limit.
Default is &lt;code>-1&lt;/code>, which makes the LLM generate text ad infinitum.
If we want to limit it to context size, we can set it to &lt;code>-2&lt;/code>.&lt;/li>
&lt;li>&lt;code>--batch-size&lt;/code>/&lt;code>--ubatch-size&lt;/code> (&lt;code>LLAMA_ARG_BATCH&lt;/code>/&lt;code>LLAMA_ARG_UBATCH&lt;/code>) - amount of tokens fed to the LLM in single processing step.
Optimal value of those arguments depends on your hardware, model, and context size - i encourage experimentation, but defaults are probably good enough for start.&lt;/li>
&lt;li>&lt;code>--flash-attn&lt;/code> (&lt;code>LLAMA_ARG_FLASH_ATTN&lt;/code>) - &lt;a href="https://www.hopsworks.ai/dictionary/flash-attention">Flash attention&lt;/a> is an optimization that&amp;rsquo;s supported by most recent models.
Read the linked article for details, in short - enabling it should improve the generation performance for some models.
&lt;code>llama.cpp&lt;/code> will simply throw a warning when a model that doesn&amp;rsquo;t support flash attention is loaded, so i keep it on at all times without any issues.&lt;/li>
&lt;li>&lt;code>--mlock&lt;/code> (&lt;code>LLAMA_ARG_MLOCK&lt;/code>) - this option is called exactly like &lt;a href="https://man7.org/linux/man-pages/man2/mlock.2.html">Linux function&lt;/a> that it uses underneath.
On Windows, it uses &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtuallock">VirtualLock&lt;/a>.
If you have enough virtual memory (RAM or VRAM) to load the whole model into, you can use this parameter to prevent OS from swapping it to the hard drive.
Enabling it can increase the performance of text generation, but may slow everything else down in return if you hit the virtual memory limit of your machine.&lt;/li>
&lt;li>&lt;code>--no-mmap&lt;/code> (&lt;code>LLAMA_ARG_NO_MMAP&lt;/code>) - by default, &lt;code>llama.cpp&lt;/code> will map the model to memory (using &lt;a href="https://man7.org/linux/man-pages/man2/mmap.2.html">&lt;code>mmap&lt;/code>&lt;/a> on Linux and &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createfilemappinga">&lt;code>CreateFileMappingA&lt;/code>&lt;/a> on Windows).
Using this switch will disable this behavior.&lt;/li>
&lt;li>&lt;code>--gpu-layers&lt;/code> (&lt;code>LLAMA_ARG_N_GPU_LAYERS&lt;/code>) - if GPU offloading is available, this parameter will set the maximum amount of LLM layers to offload to GPU.
Number and size of layers is dependent on the used model.
Usually, if we want to load the whole model to GPU, we can set this parameter to some unreasonably large number like 999.
For partial offloading, you must experiment yourself.
&lt;code>llama.cpp&lt;/code> must be built with GPU support, otherwise this option will have no effect.
If you have multiple GPUs, you may also want to look at &lt;code>--split-mode&lt;/code> and &lt;code>--main-gpu&lt;/code> arguments.&lt;/li>
&lt;li>&lt;code>--model&lt;/code> (&lt;code>LLAMA_ARG_MODEL&lt;/code>) - path to the GGUF model file.&lt;/li>
&lt;/ul>
&lt;p>Most of the options from &lt;code>sampling params&lt;/code> section are described in detail &lt;a href="https://steelph0enix.github.io/posts/llama-cpp-guide/#list-of-llm-configuration-options-and-samplers-available-in-llamacpp">below&lt;/a>.
Server-specific arguments are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>--no-context-shift&lt;/code> (&lt;code>LLAMA_ARG_NO_CONTEXT_SHIFT&lt;/code>) - by default, when context is filled up, it will be shifted (&amp;ldquo;oldest&amp;rdquo; tokens are discarded in favour of freshly generated ones).
This parameter disables that behavior, and it will make the generation stop instead.&lt;/li>
&lt;li>&lt;code>--cont-batching&lt;/code> (&lt;code>LLAMA_ARG_CONT_BATCHING&lt;/code>) - continuous batching allows processing prompts in parallel with text generation.
This usually improves performance and is enabled by default.
You can disable it with &lt;code>--no-cont-batching&lt;/code> (&lt;code>LLAMA_ARG_NO_CONT_BATCHING&lt;/code>) parameter.&lt;/li>
&lt;li>&lt;code>--alias&lt;/code> (&lt;code>LLAMA_ARG_ALIAS&lt;/code>) - Alias for the model name, used by the REST API.
Set to model name by default.&lt;/li>
&lt;li>&lt;code>--host&lt;/code> (&lt;code>LLAMA_ARG_HOST&lt;/code>) and &lt;code>--port&lt;/code> (&lt;code>LLAMA_ARG_PORT&lt;/code>) - host and port for &lt;code>llama.cpp&lt;/code> server.&lt;/li>
&lt;li>&lt;code>--slots&lt;/code> (&lt;code>LLAMA_ARG_ENDPOINT_SLOTS&lt;/code>) - enables &lt;code>/slots&lt;/code> endpoint of &lt;code>llama.cpp&lt;/code> server.&lt;/li>
&lt;li>&lt;code>--props&lt;/code> (&lt;code>LLAMA_ARG_ENDPOINT_PROPS&lt;/code>) - enables &lt;code>/props&lt;/code> endpoint of &lt;code>llama.cpp&lt;/code> server.&lt;/li>
&lt;/ul>
&lt;h2 id="other-llamacpp-tools">other llama.cpp tools&lt;/h2>
&lt;p>Webserver is not the only thing &lt;code>llama.cpp&lt;/code> provides.
There&amp;rsquo;s few other useful tools hidden in built binaries.&lt;/p>
&lt;h3 id="llama-bench">&lt;code>llama-bench&lt;/code>&lt;/h3>
&lt;p>&lt;code>llama-bench&lt;/code> allows us to benchmark the prompt processing and text generation speed of our &lt;code>llama.cpp&lt;/code> build for a selected model.
To run an example benchmark, we can simply run the executable with path to selected model.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>llama-bench --model selected_model.gguf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>llama-bench&lt;/code> will try to use optimal &lt;code>llama.cpp&lt;/code> configuration for your hardware.
Default settings will try to use full GPU offloading (99 layers) and mmap.
I recommend enabling flash attention manually (with &lt;code>--flash-attn&lt;/code> flag, unfortunately &lt;code>llama-bench&lt;/code> does not read the environmental variables)
Tweaking prompt length (&lt;code>--n-prompt&lt;/code>) and batch sizes (&lt;code>--batch-size&lt;/code>/&lt;code>--ubatch-size&lt;/code>) may affect the result of prompt processing benchmark.
Tweaking number of tokens to generate (&lt;code>--n-gen&lt;/code>) may affect the result of text generation benchmark.
You can also set the number of repetitions with &lt;code>--repetitions&lt;/code> argument.&lt;/p>
&lt;p>Results for SmolLM2 1.7B Instruct quantized to Q8 w/ flash attention on my setup (CPU only, Ryzen 5900X, DDR4 RAM @3200MHz):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>&amp;gt; llama-bench --flash-attn 1 --model ./SmolLM2.q8.gguf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| model | size | params | backend | threads | fa | test | t/s |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| ------------------------------ | ---------: | ---------: | ---------- | ------: | -: | ------------: | -------------------: |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| llama ?B Q8_0 | 1.69 GiB | 1.71 B | CPU | 12 | 1 | pp512 | 162.54 ± 1.70 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| llama ?B Q8_0 | 1.69 GiB | 1.71 B | CPU | 12 | 1 | tg128 | 22.50 ± 0.05 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>build: dc223440 (4215)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The table is mostly self-explanatory, except two last columns.
&lt;code>test&lt;/code> contains the benchmark identifier, made from two parts.
First two letters define the bench type (&lt;code>pp&lt;/code> for prompt processing, &lt;code>tg&lt;/code> for text generation).
The number defines the prompt size (for prompt processing benchmark) or amount of generated tokens (for text generation benchmark).
&lt;code>t/s&lt;/code> column is the result in tokens processed/generated per second.&lt;/p>
&lt;p>There&amp;rsquo;s also a &lt;em>mystery&lt;/em> &lt;code>-pg&lt;/code> argument, which can be used to perform mixed prompt processing+text generation test.
For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>&amp;gt; llama-bench --flash-attn 1 --model ./SmolLM2.q8.gguf -pg 1024,256
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| model | size | params | backend | threads | fa | test | t/s |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| ------------------------------ | ---------: | ---------: | ---------- | ------: | -: | ------------: | -------------------: |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| llama ?B Q8_0 | 1.69 GiB | 1.71 B | CPU | 12 | 1 | pp512 | 165.50 ± 1.95 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| llama ?B Q8_0 | 1.69 GiB | 1.71 B | CPU | 12 | 1 | tg128 | 22.44 ± 0.01 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| llama ?B Q8_0 | 1.69 GiB | 1.71 B | CPU | 12 | 1 | pp1024+tg256 | 63.51 ± 4.24 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>build: dc223440 (4215)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is probably the most realistic benchmark, because as long as you have continuous batching enabled you&amp;rsquo;ll use the model like that.&lt;/p>
&lt;h3 id="llama-cli">&lt;code>llama-cli&lt;/code>&lt;/h3>
&lt;p>This is a simple CLI interface for the LLM.
It allows you to generate a completion for specified prompt, or chat with the LLM.&lt;/p>
&lt;p>It shares most arguments with &lt;code>llama-server&lt;/code>, except some specific ones:&lt;/p>
&lt;ul>
&lt;li>&lt;code>--prompt&lt;/code> - can also be used with &lt;code>llama-server&lt;/code>, but here it&amp;rsquo;s bit more useful.
Sets the starting/system prompt for the LLM.
Prompt can also be loaded from file by specifying it&amp;rsquo;s path using &lt;code>--file&lt;/code> or &lt;code>--binary-file&lt;/code> argument.&lt;/li>
&lt;li>&lt;code>--color&lt;/code> - enables colored output, it&amp;rsquo;s disabled by default.&lt;/li>
&lt;li>&lt;code>--no-context-shift&lt;/code> (&lt;code>LLAMA_ARG_NO_CONTEXT_SHIFT&lt;/code>) - does the same thing as in &lt;code>llama-server&lt;/code>.&lt;/li>
&lt;li>&lt;code>--reverse-prompt&lt;/code> - when LLM generates a reverse prompt, it stops generation and returns the control over conversation to the user, allowing him to respond.
Basically, this is list of stopping words/sentences.&lt;/li>
&lt;li>&lt;code>--conversation&lt;/code> - enables conversation mode by enabling interactive mode and not printing special tokens (like those appearing in chat template)
This is probably how you want to use this program.&lt;/li>
&lt;li>&lt;code>--interactive&lt;/code> - enables interactive mode, allowing you to chat with the LLM. In this mode, the generation starts right away and you should set the &lt;code>--prompt&lt;/code> to get any reasonable output.
Alternatively, we can use &lt;code>--interactive-first&lt;/code> to start chatting with control over chat right away.&lt;/li>
&lt;/ul>
&lt;p>Here are specific usage examples:&lt;/p>
&lt;h4 id="text-completion">text completion&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>&amp;gt; llama-cli --flash-attn --model ./SmolLM2.q8.gguf --prompt &amp;#34;The highest mountain on earth&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>build: 4215 (dc223440) with cc (GCC) 14.2.1 20240910 for x86_64-pc-linux-gnu
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: llama backend init
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: load the model and apply lora adapter, if any
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: loaded meta data with 37 key-value pairs and 218 tensors from ./SmolLM2.q8.gguf (version GGUF V3 (latest))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_tensors: CPU_Mapped model buffer size = 1734,38 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>................................................................................................
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_seq_max = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_ctx = 4096
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_ctx_per_seq = 4096
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_batch = 2048
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_ubatch = 512
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: flash_attn = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: freq_base = 130000,0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: freq_scale = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_ctx_per_seq (4096) &amp;lt; n_ctx_train (8192) -- the full capacity of the model will not be utilized
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_kv_cache_init: CPU KV buffer size = 768,00 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: KV self size = 768,00 MiB, K (f16): 384,00 MiB, V (f16): 384,00 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: CPU output buffer size = 0,19 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: CPU compute buffer size = 104,00 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: graph nodes = 679
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: graph splits = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>common_init_from_params: warming up the model with an empty run - please wait ... (--no-warmup to disable)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: llama threadpool init, n_threads = 12
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>system_info: n_threads = 12 (n_threads_batch = 12) / 24 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | LLAMAFILE = 1 | AARCH64_REPACK = 1 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sampler seed: 2734556630
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sampler params:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> repeat_last_n = 64, repeat_penalty = 1,000, frequency_penalty = 0,000, presence_penalty = 0,000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dry_multiplier = 0,000, dry_base = 1,750, dry_allowed_length = 2, dry_penalty_last_n = -1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> top_k = 40, top_p = 0,950, min_p = 0,050, xtc_probability = 0,000, xtc_threshold = 0,100, typical_p = 1,000, temp = 0,800
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mirostat = 0, mirostat_lr = 0,100, mirostat_ent = 5,000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sampler chain: logits -&amp;gt; logit-bias -&amp;gt; penalties -&amp;gt; dry -&amp;gt; top-k -&amp;gt; typical -&amp;gt; top-p -&amp;gt; min-p -&amp;gt; xtc -&amp;gt; temp-ext -&amp;gt; dist
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>generate: n_ctx = 4096, n_batch = 2048, n_predict = -1, n_keep = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>The highest mountain on earth is Mount Everest, which stands at an astonishing 8,848.86 meters (29,031.7 feet) above sea level. Located in the Mahalangur Sharhungtrigangla Range in the Himalayas, it&amp;#39;s a marvel of nature that draws adventurers and thrill-seekers from around the globe.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Standing at the base camp, the mountain appears as a majestic giant, its rugged slopes and snow-capped peaks a testament to its formidable presence. The climb to the summit is a grueling challenge that requires immense physical and mental fortitude, as climbers must navigate steep inclines, unpredictable weather, and crevasses.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>The ascent begins at Base Camp, a bustling hub of activity, where climbers gather to share stories, exchange tips, and prepare for the climb ahead. From Base Camp, climbers make their way to the South Col, a precarious route that offers breathtaking views of the surrounding landscape. The final push to the summit involves a grueling ascent up the steep and treacherous Lhotse Face, followed by a scramble up the near-vertical wall of the Western Cwm.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Upon reaching the summit, climbers are rewarded with an unforgettable sight: the majestic Himalayan range unfolding before them, with the sun casting a golden glow on the snow. The sense of accomplishment and awe is indescribable, and the experience is etched in the memories of those who have conquered this mighty mountain.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>The climb to Everest is not just about reaching the summit; it&amp;#39;s an adventure that requires patience, perseverance, and a deep respect for the mountain. Climbers must be prepared to face extreme weather conditions, altitude sickness, and the ever-present risk of accidents or crevasses. Despite these challenges, the allure of Everest remains a powerful draw, inspiring countless individuals to push their limits and push beyond them. [end of text]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_perf_sampler_print: sampling time = 12,58 ms / 385 runs ( 0,03 ms per token, 30604,13 tokens per second)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_perf_context_print: load time = 318,81 ms
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_perf_context_print: prompt eval time = 59,26 ms / 5 tokens ( 11,85 ms per token, 84,38 tokens per second)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_perf_context_print: eval time = 17797,98 ms / 379 runs ( 46,96 ms per token, 21,29 tokens per second)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_perf_context_print: total time = 17891,23 ms / 384 tokens
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="chat-mode">chat mode&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>&amp;gt; llama-cli --flash-attn --model ./SmolLM2.q8.gguf --prompt &amp;#34;You are a helpful assistant&amp;#34; --conversation
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>build: 4215 (dc223440) with cc (GCC) 14.2.1 20240910 for x86_64-pc-linux-gnu
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: llama backend init
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: load the model and apply lora adapter, if any
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_model_loader: loaded meta data with 37 key-value pairs and 218 tensors from ./SmolLM2.q8.gguf (version GGUF V3 (latest))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llm_load_tensors: CPU_Mapped model buffer size = 1734,38 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>................................................................................................
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_seq_max = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_ctx = 4096
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_ctx_per_seq = 4096
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_batch = 2048
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_ubatch = 512
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: flash_attn = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: freq_base = 130000,0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: freq_scale = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: n_ctx_per_seq (4096) &amp;lt; n_ctx_train (8192) -- the full capacity of the model will not be utilized
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_kv_cache_init: CPU KV buffer size = 768,00 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: KV self size = 768,00 MiB, K (f16): 384,00 MiB, V (f16): 384,00 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: CPU output buffer size = 0,19 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: CPU compute buffer size = 104,00 MiB
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: graph nodes = 679
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_new_context_with_model: graph splits = 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>common_init_from_params: warming up the model with an empty run - please wait ... (--no-warmup to disable)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: llama threadpool init, n_threads = 12
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: chat template example:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;system
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>You are a helpful assistant&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;user
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Hello&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;assistant
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Hi there&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;user
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>How are you?&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;assistant
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>system_info: n_threads = 12 (n_threads_batch = 12) / 24 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | LLAMAFILE = 1 | AARCH64_REPACK = 1 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main: interactive mode on.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sampler seed: 968968654
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sampler params:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> repeat_last_n = 64, repeat_penalty = 1,000, frequency_penalty = 0,000, presence_penalty = 0,000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dry_multiplier = 0,000, dry_base = 1,750, dry_allowed_length = 2, dry_penalty_last_n = -1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> top_k = 40, top_p = 0,950, min_p = 0,050, xtc_probability = 0,000, xtc_threshold = 0,100, typical_p = 1,000, temp = 0,800
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mirostat = 0, mirostat_lr = 0,100, mirostat_ent = 5,000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sampler chain: logits -&amp;gt; logit-bias -&amp;gt; penalties -&amp;gt; dry -&amp;gt; top-k -&amp;gt; typical -&amp;gt; top-p -&amp;gt; min-p -&amp;gt; xtc -&amp;gt; temp-ext -&amp;gt; dist
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>generate: n_ctx = 4096, n_batch = 2048, n_predict = -1, n_keep = 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>== Running in interactive mode. ==
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - Press Ctrl+C to interject at any time.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - Press Return to return control to the AI.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - To return control without starting a new line, end your input with &amp;#39;/&amp;#39;.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - If you want to submit another line, end your input with &amp;#39;\&amp;#39;.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>system
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>You are a helpful assistant
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;gt; hi
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Hello! How can I help you today?
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_perf_sampler_print: sampling time = 0,27 ms / 22 runs ( 0,01 ms per token, 80291,97 tokens per second)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_perf_context_print: load time = 317,46 ms
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_perf_context_print: prompt eval time = 2043,02 ms / 22 tokens ( 92,86 ms per token, 10,77 tokens per second)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_perf_context_print: eval time = 407,66 ms / 9 runs ( 45,30 ms per token, 22,08 tokens per second)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>llama_perf_context_print: total time = 5302,60 ms / 31 tokens
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Interrupted by user
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="building-the-llama-but-better">building the llama, but better&lt;/h2>
&lt;p>All right, now that we know how to use &lt;code>llama.cpp&lt;/code> and tweak runtime parameters, let&amp;rsquo;s learn how to tweak build configuration.
We already set some generic settings in &lt;a href="https://steelph0enix.github.io/posts/llama-cpp-guide/#building-the-llama">chapter about building the &lt;code>llama.cpp&lt;/code>&lt;/a> but we haven&amp;rsquo;t touched any backend-related ones yet.&lt;/p>
&lt;p>Back-end for &lt;code>llama.cpp&lt;/code> is provided via &lt;code>ggml&lt;/code> library (created by the same author!).
This library contains the code for math operations used to run LLMs, and it supports many hardware accelerations that we can enable to get the maximum LLM performance on our hardware.
Let&amp;rsquo;s start with clearing up the &lt;code>llama.cpp&lt;/code> repository (and, optionally, making sure that we have the latest commit):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>cd llama.cpp
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git clean -xdf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git pull
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git submodule update --recursive
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, we need to generate the build files for a custom backend.
As of writing this, the list of backends supported by &lt;code>ggml&lt;/code> library is following:&lt;/p>
&lt;ul>
&lt;li>&lt;code>Metal&lt;/code> - acceleration for Apple Silicon&lt;/li>
&lt;li>&lt;code>Accelerate&lt;/code> - BLAS (Basic Linear Algebra Subprograms) acceleration for Mac PCs, enabled by default.&lt;/li>
&lt;li>&lt;code>OpenBLAS&lt;/code> - BLAS acceleration for CPUs&lt;/li>
&lt;li>&lt;code>BLIS&lt;/code> - relatively recently released high-performance BLAS framework&lt;/li>
&lt;li>&lt;code>SYCL&lt;/code> - acceleration for Intel GPUs (Data Center Max series, Flex series, Arc series, Built-in GPUs and iGPUs)&lt;/li>
&lt;li>&lt;code>Intel oneMKL&lt;/code> - acceleration for Intel CPUs&lt;/li>
&lt;li>&lt;code>CUDA&lt;/code> - acceleration for Nvidia GPUs&lt;/li>
&lt;li>&lt;code>MUSA&lt;/code> - acceleration for Moore Threads GPUs&lt;/li>
&lt;li>&lt;code>hipBLAS&lt;/code> - BLAS acceleration for AMD GPUs&lt;/li>
&lt;li>&lt;code>Vulkan&lt;/code> - generic acceleration for GPUs&lt;/li>
&lt;li>&lt;code>CANN&lt;/code> - acceleration for Ascend NPU&lt;/li>
&lt;/ul>
&lt;p>As we can see, there&amp;rsquo;s something for everyone.
&lt;em>It&amp;rsquo;s good to note here that &lt;code>llama.cpp&lt;/code> also supports Android!&lt;/em>
My backend selection recommendation is following:&lt;/p>
&lt;ul>
&lt;li>Users without GPUs should try &lt;code>Intel oneMKL&lt;/code> in case of Intel CPUs, or &lt;code>BLIS&lt;/code>/&lt;code>OpenBLAS&lt;/code>.&lt;/li>
&lt;li>Users with Nvidia GPUs should use &lt;code>CUDA&lt;/code> or &lt;code>Vulkan&lt;/code>&lt;/li>
&lt;li>Users with AMD GPUs should use &lt;code>Vulkan&lt;/code> or &lt;code>ROCm&lt;/code> (order important here, ROCm was bugged last time i&amp;rsquo;ve used it)&lt;/li>
&lt;li>Users with Intel GPUs should use &lt;code>SYCL&lt;/code> or &lt;code>Vulkan&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>As we can see, Vulkan is the most generic option for GPU acceleration and i believe it&amp;rsquo;s the simplest to build for, so i&amp;rsquo;ll explain in detail how to do that.
Doesn&amp;rsquo;t matter whether you&amp;rsquo;re Nvidia, AMD or Intel graphics user - Vulkan implementation should work on &lt;strong>most&lt;/strong> (if not all) of their GPUs, as long as they&amp;rsquo;re reasonably modern.
The build process for every backend is very similar - install the necessary dependencies, generate the &lt;code>llama.cpp&lt;/code> build files with proper flag to enable the specific backend, and build it.&lt;/p>
&lt;p>Oh, and don&amp;rsquo;t worry about Python and it&amp;rsquo;s dependencies.
The performance of model conversion scripts is not limited by &lt;code>pytorch&lt;/code>, so there&amp;rsquo;s no point in installing CUDA/ROCm versions.&lt;/p>
&lt;p>Before generating the build file, we need to install &lt;a href="https://www.lunarg.com/vulkan-sdk/">Vulkan SDK&lt;/a>.&lt;/p>
&lt;p class="windows-bg-padded">On Windows, it&amp;rsquo;s easiest to do via MSYS.
That&amp;rsquo;s why i&amp;rsquo;ve recommended using it at the beginning.
I have tried installing it directly on Windows, but encountered issues that i haven&amp;rsquo;t seen when using MSYS - so, obviously, MSYS is a better option.
Run this command in MSYS (make sure to use UCRT runtime) to install the required dependencies:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>pacman -S git &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> mingw-w64-ucrt-x86_64-gcc &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> mingw-w64-ucrt-x86_64-cmake &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> mingw-w64-ucrt-x86_64-vulkan-devel &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> mingw-w64-ucrt-x86_64-shaderc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p class="windows-bg-padded">If you &lt;em>really&lt;/em> don&amp;rsquo;t want to use MSYS, i recommend &lt;a href="https://github.com/ggerganov/llama.cpp/blob/master/docs/build.md#vulkan">following the docs&lt;/a>&lt;/p>
&lt;p class="linux-bg-padded">On Linux, i recommend installing Vulkan SDK using the package manager.
If it&amp;rsquo;s not in package manager of your distro, i assume you know what you&amp;rsquo;re doing and how to install it manually.&lt;/p>
&lt;p>Afterwards, we can generate the build files.
To enable Vulkan back-end, we need to set &lt;code>GGML_VULKAN&lt;/code> variable to &lt;code>ON&lt;/code>.
Replace &lt;code>/your/install/dir&lt;/code> with custom installation directory, if you want one - otherwise remove this variable:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>cmake -S . -B build -G Ninja -DGGML_VULKAN&lt;span style="color:#f92672">=&lt;/span>ON -DCMAKE_BUILD_TYPE&lt;span style="color:#f92672">=&lt;/span>Release -DCMAKE_INSTALL_PREFIX&lt;span style="color:#f92672">=&lt;/span>/your/install/dir -DLLAMA_BUILD_TESTS&lt;span style="color:#f92672">=&lt;/span>OFF -DLLAMA_BUILD_EXAMPLES&lt;span style="color:#f92672">=&lt;/span>ON -DLLAMA_BUILD_SERVER&lt;span style="color:#f92672">=&lt;/span>ON
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>and build/install the binaries (replace &lt;code>X&lt;/code> with amount of cores in your system):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>cmake --build build --config Release -j X
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cmake --install build --config Release
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Don&amp;rsquo;t mind the warnings you&amp;rsquo;ll see, i get them too.
Now our &lt;code>llama.cpp&lt;/code> binaries should be able to use our GPU.
We can test it by running &lt;code>llama-server&lt;/code> or &lt;code>llama-cli&lt;/code> with &lt;code>--list-devices&lt;/code> argument:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>&amp;gt; llama-cli --list-devices
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ggml_vulkan: Found 1 Vulkan devices:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ggml_vulkan: 0 = AMD Radeon RX 7900 XT (AMD open-source driver) | uma: 0 | fp16: 1 | warp size: 64
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Available devices:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Vulkan0: AMD Radeon RX 7900 XT (20464 MiB, 20464 MiB free)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Running that command previously would print an empty list:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>&amp;gt; llama-cli --list-devices
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Available devices:
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Remember the &lt;code>llama-bench&lt;/code> results i&amp;rsquo;ve got previously on CPU build?
This is them now:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>&amp;gt; llama-bench --flash-attn 1 --model ./SmolLM2.q8.gguf -pg 1024,256
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ggml_vulkan: Found 1 Vulkan devices:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ggml_vulkan: 0 = AMD Radeon RX 7900 XT (AMD open-source driver) | uma: 0 | fp16: 1 | warp size: 64
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| model | size | params | backend | ngl | fa | test | t/s |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| ------------------------------ | ---------: | ---------: | ---------- | --: | -: | ------------: | -------------------: |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ggml_vulkan: Compiling shaders..............................Done!
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| llama ?B Q8_0 | 1.69 GiB | 1.71 B | Vulkan | 99 | 1 | pp512 | 880.55 ± 5.30 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| llama ?B Q8_0 | 1.69 GiB | 1.71 B | Vulkan | 99 | 1 | tg128 | 89.78 ± 1.66 |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| llama ?B Q8_0 | 1.69 GiB | 1.71 B | Vulkan | 99 | 1 | pp1024+tg256 | 115.25 ± 0.83 |
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, that&amp;rsquo;s some &lt;em>good shit&lt;/em> right here.
Prompt processing speed has increased from ~165 to ~880 tokens per second (5.3x faster).
Text generation - from ~22 to ~90 tokens per second (4x faster).
Mixed text processing went up from ~63 to ~115 tokens per second (1.8x faster).
All of this due to the fact that i&amp;rsquo;ve switched to &lt;em>correct&lt;/em> backend.&lt;/p>
&lt;h3 id="need-better-cpu-support">need better CPU support?&lt;/h3>
&lt;p>Vulkan back-end is a good generic way of running the LLMs on GPU.
But what if you don&amp;rsquo;t have one, and still want to get the maximum performance out of your hardware?
Fortunately, &lt;code>ggml&lt;/code> library has support for optional CPU instruction sets that provide faster math operations for LLMs, like AVX.&lt;/p>
&lt;p>All the configuration options for &lt;code>ggml&lt;/code> library are defined in &lt;code>ggml/CMakeLists.txt&lt;/code> file of &lt;code>llama.cpp&lt;/code> repo.
You set them just like you did when building Vulkan, &lt;code>-DVARIABLE_NAME=VALUE&lt;/code> when calling &lt;code>cmake&lt;/code> for project generation.
I&amp;rsquo;m gonna list CPU-related ones here, and explain what they do and when you should use them (if i know that).
This list may become outdated, ping me somewhere when &lt;code>ggml&lt;/code> gets updated and i don&amp;rsquo;t notice the change!
You should still double-check and validate this list with latest &lt;code>CMakeLists.txt&lt;/code> of &lt;code>ggml&lt;/code>.&lt;/p>
&lt;blockquote>
&lt;p>To quickly check which instruction sets your CPU supports, i recommend &lt;a href="https://www.hwinfo.com/">HWInfo&lt;/a>
&lt;img src="https://steelph0enix.github.io/img/llama-cpp/cpu-features.png" alt="cpu features">&lt;/p>
&lt;/blockquote>
&lt;ul>
&lt;li>&lt;code>GGML_CPU&lt;/code> - enables CPU back-end. This is enabled by default and usually should not be changed, even when GPU back-end is enabled.&lt;/li>
&lt;li>&lt;code>GGML_CPU_HBM&lt;/code> - enables &lt;a href="https://pmem.io/memkind/">&lt;code>memkind&lt;/code>&lt;/a> allocator, mostly for systems with CPU HBM memory.&lt;/li>
&lt;li>&lt;code>GGML_CPU_AARCH64&lt;/code> - enables support for AARCH64 (Armv8-A or newer) architecture.&lt;/li>
&lt;li>&lt;code>GGML_AVX&lt;/code> - enables AVX support. If you&amp;rsquo;re not cross-compiling, should be enabled by default - most modern CPUs support it.&lt;/li>
&lt;li>&lt;code>GGML_AVX_VNNI&lt;/code> - enables &lt;a href="https://en.wikipedia.org/wiki/Advanced_Vector_Extensions#CPUs_with_AVX-VNNI">AVX-VNNI support&lt;/a>. This is a sub-set of AVX instructions introduced in Intel&amp;rsquo;s Alder Lake (Q4 2021) and AMD Zen 5 (Q3 2024) CPUs or newer. Enable it if your CPU supports it.&lt;/li>
&lt;li>&lt;code>GGML_AVX2&lt;/code> - similar to &lt;code>GGML_AVX&lt;/code>, also enabled by default if not cross-compiling.&lt;/li>
&lt;li>&lt;code>GGML_AVX512&lt;/code> - enables &lt;a href="https://en.wikipedia.org/wiki/AVX-512#CPUs_with_AVX-512">AVX512F support&lt;/a>. Check the linked article for the list of supported CPUs. Enable if supported by your CPU.&lt;/li>
&lt;li>&lt;code>GGML_AVX512_VBMI&lt;/code>/&lt;code>VNNI&lt;/code>/&lt;code>BF16&lt;/code> - those three options control the optimizations for specific AVX512 subsets. Article linked above also provides info about support of those subsets, so enable it accordingly to your hardware.&lt;/li>
&lt;li>&lt;code>GGML_LASX&lt;/code>/&lt;code>LSX&lt;/code> - enables support for LoongArch (&lt;a href="https://en.wikipedia.org/wiki/Loongson">Loongson&lt;/a> CPUs)&lt;/li>
&lt;li>&lt;code>GGML_RVV&lt;/code> - enables support for RISC-V Vector Extension, affects only supported RISC-V CPUs.&lt;/li>
&lt;li>&lt;code>GGML_SVE&lt;/code> - enables support for ARM &lt;a href="https://developer.arm.com/Architectures/Scalable%20Vector%20Extensions">Scalable Vector Extensions&lt;/a>. Available on some ARM CPUs, like Armv9 Cortex-A510.&lt;/li>
&lt;/ul>
&lt;p>Following options are not supported under MSVC (another reason to &lt;em>not&lt;/em> use it):&lt;/p>
&lt;ul>
&lt;li>&lt;code>GGML_FMA&lt;/code> - enables optimizations for &lt;a href="https://en.wikipedia.org/wiki/FMA_instruction_set">FMA instruction set&lt;/a>, every modern CPU should support them and it&amp;rsquo;s enabled by default if not cross-compiling.&lt;/li>
&lt;li>&lt;code>GGML_F16C&lt;/code> - enables optimizations for &lt;a href="https://en.wikipedia.org/wiki/F16C">F16C instruction set&lt;/a>, similarly to FMA - every modern CPU supports it, enabled by default when not cross-compiling.&lt;/li>
&lt;li>&lt;code>GGML_AMX_TILE&lt;/code>/&lt;code>INT8&lt;/code>/&lt;code>BF16&lt;/code> - enables support for Intel&amp;rsquo;s &lt;a href="https://en.wikipedia.org/wiki/Advanced_Matrix_Extensions">Advanced Matrix Extensions&lt;/a>. This is available only on recent Xeon CPUs.&lt;/li>
&lt;/ul>
&lt;h3 id="what-about-cudarocmblas">what about CUDA/ROCm/BLAS?&lt;/h3>
&lt;p>I&amp;rsquo;ll write about ROCm in another blog post i&amp;rsquo;m preparing (with some benchmarks and comparisons).
For CUDA and BLAS vendors, i&amp;rsquo;ll have to refer you to &lt;a href="https://github.com/ggerganov/llama.cpp/blob/master/docs/build.md">the docs&lt;/a>.
They are clear enough, and if you were able to build &lt;code>llama.cpp&lt;/code> for CPU and you have your build environment set up correctly (with all requirements installed) - it should be equally easy to build it with custom backend support.&lt;/p>
&lt;h2 id="llm-configuration-options-explained">LLM configuration options explained&lt;/h2>
&lt;p>This will be a relatively long and very informational part full of boring explanations.
But - it&amp;rsquo;s a good knowledge to have when playing with LLMs.&lt;/p>
&lt;h3 id="how-does-llm-generate-text">how does LLM generate text?&lt;/h3>
&lt;ol>
&lt;li>
&lt;p>Prompt&lt;/p>
&lt;p>Everything starts with a prompt.
Prompt can be a simple raw string that we want the LLM to complete for us, or it can be an elaborate construction that allows the LLM to chat or use external tools.
Whatever we put in it, it&amp;rsquo;s usually in human-readable format with special &amp;ldquo;tags&amp;rdquo; (usually similar to XML tags) used for separating the parts of the prompt.&lt;/p>
&lt;p>We&amp;rsquo;ve already seen an example of a prompt used for chat completion, provided by &lt;code>llama-server&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;system
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>You are a helpful assistant&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;user
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Hello&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;assistant
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Hi there&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;user
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>How are you?&amp;lt;|im_end|&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;|im_start|&amp;gt;assistant
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>(if you&amp;rsquo;re wondering &lt;em>what are those funny &amp;lt;| and |&amp;gt; symbols&lt;/em> - those are ligatures from Fira Code font made out of &lt;code>|&lt;/code>, &lt;code>&amp;gt;&lt;/code> and &lt;code>&amp;lt;&lt;/code> characters)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Tokenization&lt;/p>
&lt;p>The LLM does not understand the human language like we do.
We use words and punctuation marks to form sentences - LLMs use tokens that can be understood as an equivalent to those.
First step in text generation is breaking the language barrier by performing prompt tokenization.
Tokenization is a process of translating input text (in human-readable format) into an array of tokens that can be processed by an LLM.
Tokens are simple numeric values, and with a vocabulary they can be easily mapped to their string representations (at least in case of BPE models, don&amp;rsquo;t know about others).
In fact, that vocabulary is available in SmolLM2 repository, in &lt;code>tokenizer.json&lt;/code> file!
That file also contains some metadata for &lt;em>special&lt;/em> tokens that have &lt;em>special&lt;/em> meaning for the LLM.
Some of those tokens represent &lt;em>meta&lt;/em> things, like start and end of a message.
Other can allow the LLM to chat with the user by providing tags for separating parts of conversation (system prompt, user messages, LLM responses).
I&amp;rsquo;ve also seen tool calling capabilities in LLM templates, which in theory should allow the LLM to use external tools, but i haven&amp;rsquo;t tested them yet (check out Qwen2.5 and CodeQwen2.5 for example models with those functions).&lt;/p>
&lt;p>We can use &lt;code>llama-server&lt;/code> API to tokenize some text and see how it looks after being translated. Hope you&amp;rsquo;ve got &lt;code>curl&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>curl -X POST -H &lt;span style="color:#e6db74">&amp;#34;Content-Type: application/json&amp;#34;&lt;/span> -d &lt;span style="color:#e6db74">&amp;#39;{&amp;#34;content&amp;#34;: &amp;#34;hello world! this is an example message!&amp;#34;}&amp;#39;&lt;/span> http://127.0.0.1:8080/tokenize
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For SmolLM2, the response should be following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{&lt;span style="color:#f92672">&amp;#34;tokens&amp;#34;&lt;/span>:[&lt;span style="color:#ae81ff">28120&lt;/span>,&lt;span style="color:#ae81ff">905&lt;/span>,&lt;span style="color:#ae81ff">17&lt;/span>,&lt;span style="color:#ae81ff">451&lt;/span>,&lt;span style="color:#ae81ff">314&lt;/span>,&lt;span style="color:#ae81ff">1183&lt;/span>,&lt;span style="color:#ae81ff">3714&lt;/span>,&lt;span style="color:#ae81ff">17&lt;/span>]}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Which we can very roughly translate to:&lt;/p>
&lt;ul>
&lt;li>28120 - hello&lt;/li>
&lt;li>905 - world&lt;/li>
&lt;li>17 - !&lt;/li>
&lt;li>451 - this&lt;/li>
&lt;li>314 - is&lt;/li>
&lt;li>354 - an&lt;/li>
&lt;li>1183 - example&lt;/li>
&lt;li>3714 - message&lt;/li>
&lt;li>17 - !&lt;/li>
&lt;/ul>
&lt;p>We can pass this JSON back to &lt;code>/detokenize&lt;/code> endpoint to get our original text back:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>curl -X POST -H &lt;span style="color:#e6db74">&amp;#34;Content-Type: application/json&amp;#34;&lt;/span> -d &lt;span style="color:#e6db74">&amp;#39;{&amp;#34;tokens&amp;#34;: [28120,905,17,451,314,354,1183,3714,17]}&amp;#39;&lt;/span> http://127.0.0.1:8080/detokenize
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{&lt;span style="color:#f92672">&amp;#34;content&amp;#34;&lt;/span>:&lt;span style="color:#e6db74">&amp;#34;hello world! this is an example message!&amp;#34;&lt;/span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Dank Magick (feeding the beast)&lt;/p>
&lt;p>I honestly don&amp;rsquo;t know what exactly happens in this step, but i&amp;rsquo;ll try my best to explain it in simple and very approximate terms.
The input is the tokenized prompt.
This prompt is fed to the LLM, and the digestion process takes a lot of processing time due to the insane amount of matrix operations that must be performed to satisfy the digital beast.
After the prompt is digested, the LLM starts &amp;ldquo;talking&amp;rdquo; to us.
LLM &amp;ldquo;talks&amp;rdquo; by outputting the probability distribution for all the tokens in it&amp;rsquo;s dictionary.
This distribution is a list of pairs containing the token value and it&amp;rsquo;s probability to be used as the next one.
Now, in theory we could use a single token with the highest probability and be done with generation, but that&amp;rsquo;s not the &lt;em>best&lt;/em> way of doing things.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Token sampling&lt;/p>
&lt;p>This is probably the most interesting step for us, because we can control it&amp;rsquo;s every single parameter.
As usual, i advise caution when working with raw output from demons - digital or not, it may result in unexpected stuff happening when handled incorrectly.
Samplers are used to choose the &amp;ldquo;best&amp;rdquo; token from the raw LLM output.
They work by trimming down all the available tokens to a single one.
Note that not every sampler will do that directly (for example, Top-K picks K tokens), but in the end of tokenization, the result is a single token.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Detokenization&lt;/p>
&lt;p>Generated tokens must be converted back to human-readable form, so a detokenization must take place.
This is the last step.
Hooray, we tamed the digital beast and forced it to talk.
I have previously feared the consequences this could bring upon the humanity, but here we are, 373 1457 260 970 1041 3935.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="list-of-llm-configuration-options-and-samplers-available-in-llamacpp">list of LLM configuration options and samplers available in llama.cpp&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>System Message&lt;/strong> - Usually, conversations with LLMs start with a &amp;ldquo;system&amp;rdquo; message that tells the LLM how to behave.
This is probably the easiest-to-use tool that can drastically change the behavior of a model.
My recommendation is to put as much useful informations and precise behavior descriptions for your application as possible, to maximize the quality of LLM output.
You may think that giving the digital demon maximum amount of knowledge may lead to bad things happening, but our reality haven&amp;rsquo;t collapsed yet so i think we&amp;rsquo;re good for now.&lt;/li>
&lt;li>&lt;strong>Temperature&lt;/strong> - per &lt;code>llama.cpp&lt;/code> docs &amp;ldquo;Controls the randomness of the generated text by affecting the probability distribution of the output tokens. Higher = more random, lower = more focused&amp;rdquo;.
I don&amp;rsquo;t have anything to add here, it controls the &amp;ldquo;creativity&amp;rdquo; of an LLM.
High values result in more random and &amp;ldquo;creative&amp;rdquo; output, but overcooking the beast may result in hallucinations, and - in certain scenarios - screams that will destroy your sanity.
Keep it in 0.2-2.0 range for a start, and keep it positive and non-zero.&lt;/li>
&lt;li>&lt;a href="https://rentry.org/dynamic_temperature">&lt;strong>Dynamic temperature&lt;/strong>&lt;/a> - Dynamic temperature sampling is an addition to temperature sampler.
The linked article describes it in detail, and it&amp;rsquo;s pretty short so i strongly recommend reading it - i can&amp;rsquo;t really do a better job explaining it.
There&amp;rsquo;s also the &lt;a href="https://www.reddit.com/r/Oobabooga/comments/191klr8/some_information_about_dynamic_temperature_added/">reddit post&lt;/a> with more explanations from the algorithm&amp;rsquo;s author.
However, in case the article goes down - the short explanation of this algorithm is: it tweaks the temperature of generated tokens based on their entropy.
Entropy here can be understood as inverse of LLMs confidence in generated tokens.
Lower entropy means that the LLM is more confident in it&amp;rsquo;s predictions, and therefore the temperature of tokens with low entropy should also be low.
High entropy works the other way around.
Effectively, this sampling can encourage creativity while preventing hallucinations at higher temperatures.
I strongly recommend testing it out, as it&amp;rsquo;s usually disabled by default.
It may require some additional tweaks to other samplers when enabled, to produce optimal results.
The parameters of dynamic temperature sampler are:
&lt;ul>
&lt;li>&lt;strong>Dynatemp range&lt;/strong> - the range of dynamic temperature to be added/subtracted&lt;/li>
&lt;li>&lt;strong>Dynatemp exponent&lt;/strong> - changing the exponent changes the dynamic temperature in following way (figure shamelessly stolen from &lt;a href="https://rentry.org/dynamic_temperature">previously linked reentry article&lt;/a>): &lt;img src="https://steelph0enix.github.io/img/llama-cpp/dynatemp-exponent.png" alt="dynatemp exponent effects">&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Top-K&lt;/strong> - Top-K sampling is a fancy name for &amp;ldquo;keep only &lt;code>K&lt;/code> most probable tokens&amp;rdquo; algorithm.
Higher values can result in more diverse text, because there&amp;rsquo;s more tokens to choose from when generating responses.&lt;/li>
&lt;li>&lt;strong>Top-P&lt;/strong> - Top-P sampling, also called &lt;em>nucleus sampling&lt;/em>, per &lt;code>llama.cpp&lt;/code> docs &amp;ldquo;Limits the tokens to those that together have a cumulative probability of at least &lt;code>p&lt;/code>&amp;rdquo;.
In human language, it means that the Top-P sampler takes a list of tokens and their probabilities as an input (note that the sum of their cumulative probabilities is by definition equal to 1), and returns tokens with highest probabilities from that list until the sum of their cumulative probabilities is greater or equal to &lt;code>p&lt;/code>.
Or, in other words, &lt;code>p&lt;/code> value changes the % of tokens returned by the Top-P sampler.
For example, when &lt;code>p&lt;/code> is equal to 0.7, the sampler will return 70% of input tokens with highest probabilities.
There&amp;rsquo;s a &lt;a href="https://rumn.medium.com/setting-top-k-top-p-and-temperature-in-llms-3da3a8f74832">pretty good article&lt;/a> about temperature, Top-K and Top-P sampling that i&amp;rsquo;ve found and can recommend if you wanna know more.&lt;/li>
&lt;li>&lt;strong>Min-P&lt;/strong> - Min-P sampling, per &lt;code>llama.cpp&lt;/code> docs &amp;ldquo;Limits tokens based on the minimum probability for a token to be considered, relative to the probability of the most likely token&amp;rdquo;.
There&amp;rsquo;s a &lt;a href="https://arxiv.org/pdf/2407.01082">paper&lt;/a> explaining this algorithm (it contains loads of citations for other LLM-related stuff too, good read).
Figure 1 from this paper nicely shows what each of the sampling algorithms does to probability distribution of tokens:
&lt;img src="https://steelph0enix.github.io/img/llama-cpp/min-p-probs.png" alt="min-p-probabilities">&lt;/li>
&lt;li>&lt;a href="https://www.reddit.com/r/LocalLLaMA/comments/1ev8n2s/exclude_top_choices_xtc_a_sampler_that_boosts/">&lt;strong>Exclude Top Choices (XTC)&lt;/strong>&lt;/a> - This is a funky one, because it works a bit differently from most other samplers.
Quoting the author, &lt;em>Instead of pruning the least likely tokens, under certain circumstances, it removes the most likely tokens from consideration&lt;/em>.
Detailed description can be found in &lt;a href="https://github.com/oobabooga/text-generation-webui/pull/6335">the PR with implementation&lt;/a>.
I recommend reading it, because i really can&amp;rsquo;t come up with anything better in few sentences, it&amp;rsquo;s a really good explanation.
I can, however, steal this image from the linked PR to show you more-or-less what XTC does: &lt;img src="https://steelph0enix.github.io/img/llama-cpp/xtc.png" alt="xtc">
The parameters for XTC sampler are:
&lt;ul>
&lt;li>&lt;strong>XTC threshold&lt;/strong> - probability cutoff threshold for top tokens, in (0, 1) range.&lt;/li>
&lt;li>&lt;strong>XTC probability&lt;/strong> - probability of XTC sampling being applied in [0, 1] range, where 0 = XTC disabled, 1 = XTC always enabled.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="https://arxiv.org/pdf/2202.00666">&lt;strong>Locally typical sampling (typical-P)&lt;/strong>&lt;/a> - per &lt;code>llama.cpp&lt;/code> docs &amp;ldquo;Sorts and limits tokens based on the difference between log-probability and entropy&amp;rdquo;.
I&amp;hellip; honestly don&amp;rsquo;t know how exactly it works.
I tried reading the linked paper, but i lack the mental capacity to understand it enough to describe it back.
&lt;a href="https://www.reddit.com/r/LocalLLaMA/comments/153bnly/what_does_typical_p_actually_do/">Some people on Reddit&lt;/a> also have the same issue, so i recommend going there and reading the comments.
I haven&amp;rsquo;t used that sampling much, so i can&amp;rsquo;t really say anything about it from experience either, so - moving on.&lt;/li>
&lt;li>&lt;a href="https://github.com/oobabooga/text-generation-webui/pull/5677">&lt;strong>DRY&lt;/strong>&lt;/a> - This sampler is used to prevent unwanted token repetition.
Simplifying, it tries to detect repeating token sequences in generated text and reduces the probabilities of tokens that will create repetitions.
As usual, i recommend reading the linked PR for detailed explanation, and as usual i&amp;rsquo;ve stolen a figure from it that shows what DRY does: &lt;img src="https://steelph0enix.github.io/img/llama-cpp/dry.png" alt="dry">
I&amp;rsquo;ll also quote a short explanation of this sampler:
&lt;em>The penalty for a token is calculated as &lt;code>multiplier * base ^ (n - allowed_length)&lt;/code>, where &lt;code>n&lt;/code> is the length of the sequence before that token that matches the end of the input, and &lt;code>multiplier&lt;/code>, &lt;code>base&lt;/code>, and &lt;code>allowed_length&lt;/code> are configurable parameters.&lt;/em>
&lt;em>If the length of the matching sequence is less than &lt;code>allowed_length&lt;/code>, no penalty is applied.&lt;/em>
The parameters for DRY sampler are:
&lt;ul>
&lt;li>&lt;strong>DRY multiplier&lt;/strong> - see explanation above&lt;/li>
&lt;li>&lt;strong>DRY base&lt;/strong> - see explanation above&lt;/li>
&lt;li>&lt;strong>DRY allowed length&lt;/strong> - see explanation above. Quoting &lt;code>llama.cpp&lt;/code> docs: &lt;em>Tokens that extend repetition beyond this receive exponentially increasing penalty&lt;/em>.&lt;/li>
&lt;li>&lt;strong>DRY penalty last N&lt;/strong> - how many tokens should be scanned for repetition. -1 = whole context, 0 = disabled.&lt;/li>
&lt;li>&lt;strong>DRY sequence breakers&lt;/strong> - characters that are used as separators for parts of sentences considered for DRY. Defaults for &lt;code>llama.cpp&lt;/code> are &lt;code>('\n', ':', '&amp;quot;', '*')&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="https://openreview.net/pdf?id=W1G1JZEIy5_">&lt;strong>Mirostat&lt;/strong>&lt;/a> - is a funky sampling algorithm that &lt;strong>overrides Top-K, Top-P and Typical-P samplers&lt;/strong>.
It&amp;rsquo;s an alternative sampler that produces text with controlled &lt;em>perplexity&lt;/em> (entropy), which means that we can control how certain the model should be in it&amp;rsquo;s predictions.
This comes without side-effects of generating repeated text (as it happens in low perplexity scenarios) or incoherent output (as it happens in high perplexity scenarios).
The configuration parameters for Mirostat are:
&lt;ul>
&lt;li>&lt;strong>Mirostat version&lt;/strong> - 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0.&lt;/li>
&lt;li>&lt;strong>Mirostat learning rate (η, eta)&lt;/strong> - specifies how fast the model converges to desired perplexity.&lt;/li>
&lt;li>&lt;strong>Mirostat target entropy (τ, tau)&lt;/strong> - the desired perplexity.
Depending on the model, it should not be too high, otherwise you may degrade it&amp;rsquo;s performance.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Max tokens&lt;/strong> - i think that&amp;rsquo;s pretty self-explanatory. -1 makes the LLM generate until it decides it&amp;rsquo;s end of the sentence (by returning end-of-sentence token), or the context is full.&lt;/li>
&lt;li>&lt;strong>Repetition penalty&lt;/strong> - Repetition penalty algorithm (not to be mistaken with DRY) simply reduces the chance that tokens that are already in the generated text will be used again.
Usually the repetition penalty algorithm is restricted to &lt;code>N&lt;/code> last tokens of the context.
In case of &lt;code>llama.cpp&lt;/code> (i&amp;rsquo;ll simplify a bit), it works like that: first, it creates a frequency map occurrences for last &lt;code>N&lt;/code> tokens.
Then, the current logit bias for each token is divided by &lt;code>repeat_penalty&lt;/code> value.
By default it&amp;rsquo;s usually set to 1.0, so to enable repetition penalty it should be set to &amp;gt;1.
Finally, frequency and presence penalties are applied based on the frequency map.
The penalty for each token is equal to &lt;code>(token_count * frequency_penalty) + (presence_penalty if token_count &amp;gt; 0)&lt;/code>.
The penalty is represented as logit bias, which can be in [-100, 100] range.
Negative values reduce the probability of token appearing in output, while positive increase it.
The configuration parameters for repetition penalty are:
&lt;ul>
&lt;li>&lt;strong>Repeat last N&lt;/strong> - Amount of tokens from the end of the context to consider for repetition penalty.&lt;/li>
&lt;li>&lt;strong>Repeat penalty&lt;/strong> - &lt;code>repeat_penalty&lt;/code> argument described above, if equal to &lt;code>1.0&lt;/code> then the repetition penalty is disabled.&lt;/li>
&lt;li>&lt;strong>Presence penalty&lt;/strong> - &lt;code>presence_penalty&lt;/code> argument from the equation above.&lt;/li>
&lt;li>&lt;strong>Frequency penalty&lt;/strong> - &lt;code>frequency_penalty&lt;/code> argument from the equation above.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>Additional literature:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.reddit.com/r/LocalLLaMA/comments/17vonjo/your_settings_are_probably_hurting_your_model_why/">Your settings are (probably) hurting your model&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>In &lt;em>Other sampler settings&lt;/em> we can find sampling queue configuration.
As i&amp;rsquo;ve mentioned earlier, the samplers are applied in a chain.
Here, we can configure the order of their application, and select which are used.
The setting uses short names for samplers, the mapping is following:&lt;/p>
&lt;ul>
&lt;li>&lt;code>d&lt;/code> - DRY&lt;/li>
&lt;li>&lt;code>k&lt;/code> - Top-K&lt;/li>
&lt;li>&lt;code>y&lt;/code> - Typical-P&lt;/li>
&lt;li>&lt;code>p&lt;/code> - Top-P&lt;/li>
&lt;li>&lt;code>m&lt;/code> - Min-P&lt;/li>
&lt;li>&lt;code>x&lt;/code> - Exclude Top Choices (XTC)&lt;/li>
&lt;li>&lt;code>t&lt;/code> - Temperature&lt;/li>
&lt;/ul>
&lt;p>Some samplers and settings i&amp;rsquo;ve listed above may be missing from web UI configuration (like Mirostat), but they all can be configured via environmental variables, CLI arguments for &lt;code>llama.cpp&lt;/code> binaries, or llama.cpp server API.&lt;/p>
&lt;h2 id="final-thoughts">final thoughts&lt;/h2>
&lt;p>That is a &lt;strong>long&lt;/strong> post, damn.
I have started writing this post at the end of October.
It&amp;rsquo;s almost December now.
During that time, multiple new models have been released - including SmolLM2, fun fact - i have originally planned to use Llama 3.2 3B.
The speed at which the LLM community moves and releases new stuff is absolutely incredible, but thankfully &lt;code>llama.cpp&lt;/code> is &lt;em>relatively&lt;/em> stable now.
I hope the knowledge i&amp;rsquo;ve gathered in this post will be useful and inspiring to the readers, and will allow them to play with LLMs freely in their homes.
That&amp;rsquo;s it, i&amp;rsquo;m tired.
I&amp;rsquo;m releasing that shit into the wild.&lt;/p>
&lt;p>Suggestions for next posts are welcome, for now i intend to make some scripts for automated benchmarks w/ &lt;code>llama-bench&lt;/code> and gather some data.
I&amp;rsquo;ll try to keep this post up-to-date and &lt;em>maybe&lt;/em> add some stuff if it&amp;rsquo;s requested.
Questions are welcome too, preferably in the comments section.
Have a nice one.&lt;/p>
&lt;h3 id="bonus-where-to-find-models-and-some-recommendations">bonus: where to find models, and some recommendations&lt;/h3>
&lt;p>My favorite site for finding models and comparing them is &lt;a href="https://llm.extractum.io">LLM Explorer&lt;/a>.
It&amp;rsquo;s basically a search engine for models.
The UI is not exactly &lt;em>great&lt;/em> but it&amp;rsquo;s &lt;em>good enough&lt;/em>, it has the most comprehensive list of LLMs i&amp;rsquo;ve seen, and lots of search options.&lt;/p>
&lt;p>As for my recommendations, some relatively recent models i&amp;rsquo;ve tried that made a positive impression upon me are:&lt;/p>
&lt;ul>
&lt;li>Google Gemma 2 9B SimPO - a fine-tune of Google Gemma model. Gemma models are pretty interesting, and their responses are noticeably different from other models.&lt;/li>
&lt;li>Meta Llama 3.1/3.2 - i recommend trying out Llama 3.1 8B Instruct, as it&amp;rsquo;s the default go-to model for most LLM applications. There&amp;rsquo;s also many finetunes and &lt;em>abliterated&lt;/em> versions that don&amp;rsquo;t have any built-in restrictions available publicly.&lt;/li>
&lt;li>Microsoft Phi 3.5 - a series of models from Microsoft. Most of them are small, but there&amp;rsquo;s also big MoE (Mixture of Experts) version available.&lt;/li>
&lt;li>Qwen/CodeQwen 2.5 - series of models from Alibaba, currently one of the best open-source models available. At the moment of writing this, CodeQwen 14B is my daily driver model.&lt;/li>
&lt;/ul>
&lt;h3 id="post-mortem">post-mortem&lt;/h3>
&lt;p>I&amp;rsquo;ve shared this post on Reddit and after getting the feedback, i managed to fix multiple issues with this post. Thanks!
I&amp;rsquo;m currently at the process of &lt;em>refactors&lt;/em>, so many small things may change, and many other small things may be added in following days.
I&amp;rsquo;ll update this section when i&amp;rsquo;m finished.&lt;/p>
&lt;ul>
&lt;li>03.12.2024 - Updated the part about LLM text generation.
I have made some mistakes there, so i&amp;rsquo;ve updated my knowledge and the post.
Thanks /r/AbaGuy17, /r/Oscylator and others for pointing stuff up.&lt;/li>
&lt;li>10.12.2024 - Updated the back-end part, added info about CPU optimizations.&lt;/li>
&lt;li>25.12.2024 - Merry christmas, i just realized that Python 3.13 doesn&amp;rsquo;t have working PyTorch package, and i&amp;rsquo;ve been running Py3.12 all along! Sorry, fixed that!&lt;/li>
&lt;/ul></content></item><item><title>Making C/C++ project template in Meson - part 2.5</title><link>https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-2-5/</link><pubDate>Thu, 29 Feb 2024 00:00:00 +0000</pubDate><guid>https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-2-5/</guid><description>&lt;h2 id="fixing-cpputest-wrap-and-contributing-to-wrapdb">Fixing CppUTest wrap and contributing to WrapDB&lt;/h2>
&lt;p>As per &lt;a href="https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-2/">my previous post&lt;/a>, &lt;a href="https://github.com/mesonbuild/wrapdb/pull/1398">I have fixed&lt;/a> the CppUTest wrap and it&amp;rsquo;s now available on WrapDB!
Since this post series is strictly related to Meson, I want to talk a bit more about creating wraps and contributing there.
If you don&amp;rsquo;t care, and just want to continue to the guide part - jump to the &lt;a href="https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-2-5/#adding-unit-tests-support">next section&lt;/a>.&lt;/p>
&lt;p>I have started the process of creating this wrap by making a very basic example project with a single binary that uses CppUTest.
Then, I plugged in CppUTest as a normal subproject, and began rewriting the &lt;code>meson.build&lt;/code> files, along with the options.
Eventually I&amp;rsquo;ve managed to get to a point where it compiled successfully on my setup, linked to an app, and I was able to run it.
Thinking that&amp;rsquo;s good enough, I&amp;rsquo;ve started worrying about wrapping it properly.&lt;/p></description><content>&lt;h2 id="fixing-cpputest-wrap-and-contributing-to-wrapdb">Fixing CppUTest wrap and contributing to WrapDB&lt;/h2>
&lt;p>As per &lt;a href="https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-2/">my previous post&lt;/a>, &lt;a href="https://github.com/mesonbuild/wrapdb/pull/1398">I have fixed&lt;/a> the CppUTest wrap and it&amp;rsquo;s now available on WrapDB!
Since this post series is strictly related to Meson, I want to talk a bit more about creating wraps and contributing there.
If you don&amp;rsquo;t care, and just want to continue to the guide part - jump to the &lt;a href="https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-2-5/#adding-unit-tests-support">next section&lt;/a>.&lt;/p>
&lt;p>I have started the process of creating this wrap by making a very basic example project with a single binary that uses CppUTest.
Then, I plugged in CppUTest as a normal subproject, and began rewriting the &lt;code>meson.build&lt;/code> files, along with the options.
Eventually I&amp;rsquo;ve managed to get to a point where it compiled successfully on my setup, linked to an app, and I was able to run it.
Thinking that&amp;rsquo;s good enough, I&amp;rsquo;ve started worrying about wrapping it properly.&lt;/p>
&lt;p>There are few different kinds of wraps that Meson supports.
If the project supports Meson, CMake or Cargo it can be wrapped directly, either as an archive or a specific revision of code on a supported repository.
Otherwise, it&amp;rsquo;s possible to provide an &amp;ldquo;overlay&amp;rdquo; patch that adds/replaces the files of the library in order to provide Meson support - that&amp;rsquo;s what file-wraps are.&lt;/p>
&lt;p>I&amp;rsquo;ve considered it before, but to be clear - CppUTest&amp;rsquo;s primary build system is CMake, but I don&amp;rsquo;t have much pleasant memories related to CMake and I&amp;rsquo;d prefer to avoid interacting with it.&lt;/p>
&lt;p>I already had the files necessary to create a file-wrap, so all I needed to do was modifying &lt;code>cpputest.wrap&lt;/code>&amp;hellip; Right?
Wrong!
This is where I&amp;rsquo;ve spent an hour or two, thinking that I can host this on my Github repo instead of contributing to WrapDB.
And this is my first major complaint about Meson!&lt;/p>
&lt;p>First thing I wanted to try was putting the wrap patch files on my Github repository, and pointing the wrap to it.
I&amp;rsquo;ve figured that this way should be supported, because it enables hosting wraps on private repositories, for example in company environments.
So i did that, created a release to have an archive, pointed the wrap to it, and&amp;hellip; It did not work, because Meson kept putting the patch files in different directory than source files.&lt;/p>
&lt;p>I was thinking that &lt;code>directory&lt;/code> option in &lt;code>.wrap&lt;/code> file defines the directory both archives will be extracted to, but that&amp;rsquo;s not the case!
This is the option that defines where the &lt;strong>source archive&lt;/strong> will be extracted to, but not the &lt;strong>patch archive&lt;/strong>.
And the output directory for patch was dependent on&amp;hellip; The name of my repository. Duh.
Effectively, I think that I&amp;rsquo;d have to name my repository with the wrap &lt;code>cpputest&lt;/code> to make it working that way, and I&amp;rsquo;d rather not do that&amp;hellip;
So I&amp;rsquo;ve abandoned this idea and proceeded to read &lt;a href="https://mesonbuild.com/Adding-new-projects-to-wrapdb.html">how to contribute to WrapDB&lt;/a>.
Alas, I believe that this way of providing files for file-wraps should be supported.&lt;/p>
&lt;p>The contribution itself was rather pleasant experience - after moving the wrap files to the WrapDB fork, I&amp;rsquo;ve made a pull request and waited for CI to pass.
I was happy to see the CI checked the code by running a Python script with no external dependencies, so I could run the checks locally.
I was even more happy to see that this script actually downloaded and built every library WrapDB supports, and CI was running it on Windows, Linux and MacOS runners with Clang, GCC and MSVC.
That is a nice thing, but as we experienced, it&amp;rsquo;s not quite enough to guarantee quality - even practically unusable wraps can build with no issues.
I was less happy when I&amp;rsquo;ve enabled the CI on my fork and it ate 1/4th of my monthly free GitHub runner limit after a single full run.
Oh well.
Good thing they do incremental builds on PRs.&lt;/p>
&lt;p>Of course, my PR it did not pass the CI first time - it forced me to add some functionality to the wrap (platform auto-detection), but after that it was good to go and got merged.
The whole process took only a few hours, the PR was reviewed, the CI did it&amp;rsquo;s job - no complains there.&lt;/p>
&lt;h2 id="adding-unit-tests-support">Adding unit tests support&lt;/h2>
&lt;p>Now we can proceed with the actual subject of this post.&lt;/p>
&lt;p>Remove everything from the &lt;code>subprojects/&lt;/code> directory, open a terminal and run &lt;code>meson wrap install cpputest&lt;/code>.
This is the preferred method of installing wraps, and I forgot to mention it earlier.
I don&amp;rsquo;t quite understand why it&amp;rsquo;s not able to create &lt;code>subprojects/&lt;/code> directory by itself by default, but it&amp;rsquo;s a non-issue.&lt;/p>
&lt;p>I assume that you&amp;rsquo;ve followed the previous part, and already added &lt;code>tests&lt;/code> directory to main &lt;code>meson.build&lt;/code>, and created example test in &lt;code>tests/calc/&lt;/code>.
If not, &lt;a href="https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-2/#managing-external-dependencies-with-meson">go back and do that&lt;/a>.
If you already did that, then the project should compile successfully.
The only thing I&amp;rsquo;d like to change is &lt;code>cpputest_dependency&lt;/code> to just &lt;code>cpputest&lt;/code>, because it looks better.&lt;/p>
&lt;p>tests/meson.build:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>cpputest_project &lt;span style="color:#f92672">=&lt;/span> subproject(&lt;span style="color:#e6db74">&amp;#39;cpputest&amp;#39;&lt;/span>, required: &lt;span style="color:#66d9ef">true&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cpputest &lt;span style="color:#f92672">=&lt;/span> cpputest_project.get_variable(&lt;span style="color:#e6db74">&amp;#39;cpputest_dep&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;calc&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;greeter&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>tests/calc/meson.build:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>calc_test_exec &lt;span style="color:#f92672">=&lt;/span> executable(&lt;span style="color:#e6db74">&amp;#39;calc_test&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;test.cpp&amp;#39;&lt;/span>, dependencies: cpputest)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>test(&lt;span style="color:#e6db74">&amp;#39;calc test&amp;#39;&lt;/span>, calc_test_exec)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, we should also be able to run our test via &lt;code>meson test&lt;/code> command.&lt;/p>
&lt;pre tabindex="0">&lt;code>&amp;gt; meson test -C builddir
ninja: Entering directory `\builddir&amp;#39;
ninja: no work to do.
1/1 calc test FAIL 0.01s exit status 1
&amp;gt;&amp;gt;&amp;gt; MALLOC_PERTURB_=245 ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1 UBSAN
_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1 \builddir\tests\calc\calc_test.exe
Ok: 0
Expected Fail: 0
Fail: 1
Unexpected Pass: 0
Skipped: 0
Timeout: 0
Full log written to \builddir\meson-logs\testlog.txt
&lt;/code>&lt;/pre>&lt;p>It will fail, because in the previous part we&amp;rsquo;ve written a test that should fail, so it&amp;rsquo;s all good.
Your &lt;code>MALLOC_PERTURB_&lt;/code> may be different, as it&amp;rsquo;s randomly chosen every test run.
It&amp;rsquo;s used for some magic involving &lt;code>malloc&lt;/code> that helps detecting memory-related issues.&lt;/p>
&lt;p>So, it seems like we have a working unit test setup.
Let&amp;rsquo;s write some meaningful tests for both modules and we&amp;rsquo;ll check if we can call them separately.
We also have to finish up our Meson script for &lt;code>calc&lt;/code> test, because we&amp;rsquo;re not linking to &lt;code>calc&lt;/code> yet, and we should!
And we also have to add &lt;code>libs_includes&lt;/code> to includes for our test exec.&lt;/p>
&lt;p>tests/calc/meson.build&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>calc_test_exec &lt;span style="color:#f92672">=&lt;/span> executable(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;calc_test&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sources: &lt;span style="color:#e6db74">&amp;#39;test.cpp&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dependencies: cpputest,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> link_with: calc_lib,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> include_directories: libs_includes,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>test(&lt;span style="color:#e6db74">&amp;#39;calc test&amp;#39;&lt;/span>, calc_test_exec)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And our test may look like this:&lt;/p>
&lt;p>tests/calc/test.cpp&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;CppUTest/CommandLineTestRunner.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;CppUTest/TestHarness.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;calc/calc.hpp&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>TEST_GROUP(TemperatureCalcTests){};
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>TEST(TemperatureCalcTests, convertsCelsiusToFahrenheit) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> givenCelsius &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">37.7778&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> expectedFahrenheit &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">100.0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> gotFahrenheit &lt;span style="color:#f92672">=&lt;/span> celsius_to_fahrenheit(givenCelsius);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DOUBLES_EQUAL(expectedFahrenheit, gotFahrenheit, &lt;span style="color:#ae81ff">0.001&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>TEST(TemperatureCalcTests, convertsFahrenheitToCelsius) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> givenFahrenheit &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">212&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> expectedCelsius &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">100.0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> gotCelsius &lt;span style="color:#f92672">=&lt;/span> fahrenheit_to_celsius(givenFahrenheit);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DOUBLES_EQUAL(expectedCelsius, gotCelsius, &lt;span style="color:#ae81ff">0.001&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> ac, &lt;span style="color:#66d9ef">char&lt;/span>&lt;span style="color:#f92672">**&lt;/span> av) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> CommandLineTestRunner&lt;span style="color:#f92672">::&lt;/span>RunAllTests(ac, av);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We should create similar meson and source file for greeter.&lt;/p>
&lt;p>tests/greeter/meson.build&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>greeter_test_exec &lt;span style="color:#f92672">=&lt;/span> executable(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;greeter_test&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sources: &lt;span style="color:#e6db74">&amp;#39;test.cpp&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dependencies: cpputest,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> link_with: greeter_lib,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> include_directories: libs_includes,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>test(&lt;span style="color:#e6db74">&amp;#39;greeter test&amp;#39;&lt;/span>, greeter_test_exec)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>tests/greeter/test.cpp&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;CppUTest/CommandLineTestRunner.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;CppUTest/TestHarness.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;greeter/greeter.hpp&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;string&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>TEST_GROUP(GreeterTests){};
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>TEST(GreeterTests, greetsUser) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> user &lt;span style="color:#f92672">=&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>string(&lt;span style="color:#e6db74">&amp;#34;user&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> expectedMessage &lt;span style="color:#f92672">=&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>string(&lt;span style="color:#e6db74">&amp;#34;Hello user&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">auto&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> actualMessage &lt;span style="color:#f92672">=&lt;/span> greet(user);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CHECK_EQUAL(expectedMessage, actualMessage);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> ac, &lt;span style="color:#66d9ef">char&lt;/span>&lt;span style="color:#f92672">**&lt;/span> av) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> CommandLineTestRunner&lt;span style="color:#f92672">::&lt;/span>RunAllTests(ac, av);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And now, after running &lt;code>meson test -C builddir&lt;/code>, we should get something like this:&lt;/p>
&lt;pre tabindex="0">&lt;code>PS .&amp;gt; meson test -C builddir
ninja: Entering directory `.\builddir&amp;#39;
ninja: no work to do.
1/2 calc test OK 0.01s
2/2 greeter test OK 0.01s
Ok: 2
Expected Fail: 0
Fail: 0
Unexpected Pass: 0
Skipped: 0
Timeout: 0
Full log written to .\builddir\meson-logs\testlog.txt
&lt;/code>&lt;/pre>&lt;p>And here we go, we finally have working unit tests in our project.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>Oh well, this part took way too long to publish.
I blame Helldivers II for that.
To be fair, I finished writing it in a manner of two weeks, I just didn&amp;rsquo;t have energy to finish it and push it.
Still, I&amp;rsquo;m gonna try to move it forward faster because my backlog of stuff to do is incredibly long, and this template could help.
For anyone interested, &lt;a href="https://github.com/SteelPh0enix/meson_c_cpp_project_template/releases/tag/part-2">there&amp;rsquo;s a tag&lt;/a> in the repository with it&amp;rsquo;s current state.
In the next part, i will show you how to configure Doxygen with Meson to generate documentation of our modules and applications functions.&lt;/p></content></item><item><title>Making C/C++ project template in Meson - part 2</title><link>https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-2/</link><pubDate>Wed, 07 Feb 2024 00:00:00 +0000</pubDate><guid>https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-2/</guid><description>&lt;h2 id="intro">Intro&lt;/h2>
&lt;p>In &lt;a href="https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-1/">previous part&lt;/a>, we&amp;rsquo;ve created the base of our project by defining it&amp;rsquo;s structure and making our first modules and executable.
In this one, we&amp;rsquo;re gonna add unit tests support.&lt;/p>
&lt;p>But before that&amp;hellip;&lt;/p>
&lt;h3 id="fixing-our-mistakes">Fixing our mistakes&lt;/h3>
&lt;p>So, uhh, let&amp;rsquo;s look at our project.
Specifically, at our &lt;code>hello_world.cpp&lt;/code>.
More specifically, at includes.
It&amp;rsquo;s not that something is wrong with them &lt;em>right now&lt;/em>, but think about it - what would happen if we&amp;rsquo;d put an &lt;code>utils.hpp&lt;/code> file in both libraries?
That is actually a question that can be answered using the &amp;ldquo;fuck around and find out&amp;rdquo; method, so let&amp;rsquo;s find out.&lt;/p></description><content>&lt;h2 id="intro">Intro&lt;/h2>
&lt;p>In &lt;a href="https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-1/">previous part&lt;/a>, we&amp;rsquo;ve created the base of our project by defining it&amp;rsquo;s structure and making our first modules and executable.
In this one, we&amp;rsquo;re gonna add unit tests support.&lt;/p>
&lt;p>But before that&amp;hellip;&lt;/p>
&lt;h3 id="fixing-our-mistakes">Fixing our mistakes&lt;/h3>
&lt;p>So, uhh, let&amp;rsquo;s look at our project.
Specifically, at our &lt;code>hello_world.cpp&lt;/code>.
More specifically, at includes.
It&amp;rsquo;s not that something is wrong with them &lt;em>right now&lt;/em>, but think about it - what would happen if we&amp;rsquo;d put an &lt;code>utils.hpp&lt;/code> file in both libraries?
That is actually a question that can be answered using the &amp;ldquo;fuck around and find out&amp;rdquo; method, so let&amp;rsquo;s find out.&lt;/p>
&lt;p>This is the content of our &lt;code>utils.hpp&lt;/code>.
Adjust the returned string appropriately for the other module.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#pragma once
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;string&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">inline&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>string what_am_i() { &lt;span style="color:#66d9ef">return&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>string(&lt;span style="color:#e6db74">&amp;#34;i am calc&amp;#34;&lt;/span>); }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, our new &lt;code>hello_world.cpp&lt;/code> will become this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;calc.hpp&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;greeter.hpp&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;utils.hpp&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> greet(what_am_i()) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;12.5*F == &amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> fahrenheit_to_celsius(&lt;span style="color:#ae81ff">12.5&lt;/span>) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;*C&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;12.5*C == &amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> celsius_to_fahrenheit(&lt;span style="color:#ae81ff">12.5&lt;/span>) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;*F&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Aaand&amp;hellip; In my case, it prints &lt;code>Hello i am calc&lt;/code>, which is caused by the fact that &lt;code>calc_includes&lt;/code> are before &lt;code>greeter_includes&lt;/code> in my &lt;code>hello_world/meson.build&lt;/code>&amp;rsquo;s &lt;code>executable()&lt;/code> call.
After swapping them, &lt;code>Hello i am greeter&lt;/code> is printed.&lt;/p>
&lt;p>Now that we know what happens, we can easily deduce that it&amp;rsquo;s not supposed to work like that - because we don&amp;rsquo;t have a reasonable way to access the other &lt;code>utils.hpp&lt;/code>.
The simplest solution is to move the include directory one level up, to &lt;code>lib&lt;/code> directory.
And in retrospect, that&amp;rsquo;s how it should be done from the beginning, so excuse my blunder there.&lt;/p>
&lt;p>In order to fix our mistake, we have to revisit some &lt;code>meson.build&lt;/code> files.
First, let&amp;rsquo;s create an include directory object in &lt;code>lib/meson.build&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>libs_includes &lt;span style="color:#f92672">=&lt;/span> include_directories(&lt;span style="color:#e6db74">&amp;#39;.&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;calc&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;greeter&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I&amp;rsquo;ve added it before the &lt;code>subdir()&lt;/code> calls just in case some modules would depend on each other.
This is not something we want to do often, because for every dependency we will probably have to create a mock in order to properly test the modules that use them, but it&amp;rsquo;s sometimes necessary.&lt;/p>
&lt;p>After that, remove the &lt;code>include_directories()&lt;/code> calls from &lt;code>greeter&lt;/code> and &lt;code>calc&lt;/code> modules &lt;code>meson.build&lt;/code>, and fix the &lt;code>executable()&lt;/code> call arguments in &lt;code>hello_world/meson.build&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>hello_world &lt;span style="color:#f92672">=&lt;/span> executable(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;hello_world&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;hello_world.cpp&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> link_with: [calc_lib, greeter_lib],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> include_directories: [libs_includes],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And fix the includes in &lt;code>hello_world.cpp&lt;/code>.
We can also remove the &lt;code>utils.hpp&lt;/code> and restore the original &lt;code>hello_world.cpp&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;calc/calc.hpp&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;greeter/greeter.hpp&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> greet(&lt;span style="color:#e6db74">&amp;#34;random developer&amp;#34;&lt;/span>) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;12.5*F == &amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> fahrenheit_to_celsius(&lt;span style="color:#ae81ff">12.5&lt;/span>) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;*C&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;12.5*C == &amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> celsius_to_fahrenheit(&lt;span style="color:#ae81ff">12.5&lt;/span>) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;*F&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Also; let&amp;rsquo;s rename our &lt;code>lib/&lt;/code> directory to &lt;code>libs/&lt;/code>, this is a very small change but my brain automatically tries to write &lt;code>libs/&lt;/code> instead of &lt;code>lib/&lt;/code> because there are &lt;em>multiple&lt;/em> libraries there, so I think it reflects this directory&amp;rsquo;s content better.
Remember to also change the argument of &lt;code>subdir()&lt;/code> in the root &lt;code>meson.build&lt;/code>.&lt;/p>
&lt;p>Verify if the project still builds and the executable still works. Remove the &lt;code>builddir&lt;/code> first, as we made pretty big change and I honestly don&amp;rsquo;t expect Meson to correctly re-generate it.&lt;/p>
&lt;h2 id="harnessing-the-power-of-unit-tests">Harnessing the power of unit tests&lt;/h2>
&lt;p>I want to assume that You have at least minimal experience with testing your code, but i know some people who don&amp;rsquo;t, so i will not.
But I&amp;rsquo;ll also try not to over explain, there are other resources for that.
If you already know a thing or two about unit testing, you can ignore the rest of this chapter.
Or maybe don&amp;rsquo;t, maybe you&amp;rsquo;ll learn something new.
I dunno.&lt;/p>
&lt;p>When You write some code, it&amp;rsquo;s usually a good habit to test it.
After all, &lt;em>how do you know if the code working correctly?&lt;/em>
You run it, and see if it does what it&amp;rsquo;s supposed to.
The simplest way of doing that is writing some small programs that use the code in question in various ways, and check if the outputs of this code are correct.
For example, let&amp;rsquo;s assume you&amp;rsquo;ve written a function that calculates the amount of damage an attack does in an RPG game.
To test it, You can create some attack scenarios with various items, statistics, and whatever else can influence damage.
Then, calculate the results manually beforehand, and write the code that will perform those calculations using that function and verify if they match your calculations.
Tests that verify those small pieces of code, like functions or classes, are usually called &lt;strong>unit tests&lt;/strong>.
The most basic unit test can be something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;assert.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;DamageCalculation.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;test_utils.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Weapon &lt;span style="color:#66d9ef">const&lt;/span> weapon &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">create_test_weapon&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Enemy &lt;span style="color:#66d9ef">const&lt;/span> enemy &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#a6e22e">create_test_enemy&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">assert&lt;/span>(&lt;span style="color:#a6e22e">calculate_damage&lt;/span>(&lt;span style="color:#f92672">&amp;amp;&lt;/span>weapon, &lt;span style="color:#f92672">&amp;amp;&lt;/span>enemy) &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#ae81ff">3.14&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Although I&amp;rsquo;d replace that magic number with some very approximate manual calculation, if possible.
Tests should not be cryptic - this is, but only because it&amp;rsquo;s an unspecified example.&lt;/p>
&lt;p>Fortunately, the humanity have &lt;em>(mostly)&lt;/em> progressed past the need for &lt;code>assert&lt;/code>, and invented &lt;strong>test harnesses&lt;/strong> that are &lt;em>(usually)&lt;/em> less painful to use.
Trust me on that.
Even C guys didn&amp;rsquo;t like rawdogging &lt;code>assert&lt;/code>, and jumped on &lt;a href="http://www.throwtheswitch.org/unity">Unity&lt;/a> (not &lt;a href="https://www.axios.com/2023/09/22/unity-apologizes-runtime-fees">&lt;em>that&lt;/em>&lt;/a> Unity) and other similar &amp;ldquo;wrappers&amp;rdquo;.
In any case, using a test harness is easier than making one ourselves (or not using one at all), so we are going to use one.
I&amp;rsquo;d prefer if it also were multi-platform and supported both C and C++, and fortunately we have some options in the open-source market for that.&lt;/p>
&lt;h2 id="managing-external-dependencies-with-meson">Managing external dependencies with Meson&lt;/h2>
&lt;p>Before we choose a test harness, however, let&amp;rsquo;s check how Meson handles external dependencies.
Fortunately, &lt;a href="https://mesonbuild.com/Dependencies.html">documentation&lt;/a> got us covered.
And it seems that Meson supports &lt;em>a lot&lt;/em> of popular libraries in very specific ways, which is very, very nice.
However, the &lt;code>dependency()&lt;/code> function seems to work only for locally available packages, which would usually mean that we have to manually download and set up the libraries for development each time we set up a new project environment - but there&amp;rsquo;s a nice catch!
Meson also provides &lt;a href="https://mesonbuild.com/Wrap-dependency-system-manual.html">&lt;strong>Wrap dependency system&lt;/strong>&lt;/a>.
I recommend reading the linked documentation, but if you want a TL;DR: this is a package manager.
Sort of.
You put a valid &lt;code>.wrap&lt;/code> file in &lt;code>subprojects/&lt;/code> directory and voila, Meson can download and build the dependency, allowing us to use it in our project - automagically.&lt;/p>
&lt;p>After looking at available &lt;a href="https://mesonbuild.com/Wrapdb-projects.html">wraps&lt;/a>, and considering the fact that I&amp;rsquo;d like to use that template for ARM projects, I have decided I&amp;rsquo;m going with &lt;a href="https://github.com/cpputest/cpputest">CppUTest&lt;/a>.
If you want to try another test harness, feel free to do that and experiment - but keep in mind that the setup process may vary a bit, depending on the library needs.
CppUTest is relatively simple, which is nice if you&amp;rsquo;re looking for something without much complexity, but it won&amp;rsquo;t provide as many features as some other harnessed - like &lt;a href="https://github.com/catchorg/Catch2">Catch2&lt;/a> or &lt;a href="https://github.com/google/googletest">Google Test&lt;/a>.
I like simple, and it should probably make porting everything to ARM easier, so I&amp;rsquo;m going with CppUTest.&lt;/p>
&lt;p>Let&amp;rsquo;s create &lt;code>subprojects/&lt;/code> directory and run &lt;code>meson wrap install cpputest&lt;/code> (or your preferred test harness).
Installation should proceed automatically, and &lt;code>subprojects/cpputest.wrap&lt;/code> file should appear.
Then, to use it, we have to declare a &lt;code>subproject()&lt;/code> in one of our &lt;code>meson.build&lt;/code> files.
Let&amp;rsquo;s do that in our &lt;code>tests/meson.build&lt;/code>.
We&amp;rsquo;ll also add our tests subdirectories, since we&amp;rsquo;re already editing that file.
Add them below the &lt;code>subproject()&lt;/code> to make sure the test harness is available there.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>cpputest_project &lt;span style="color:#f92672">=&lt;/span> subproject(&lt;span style="color:#e6db74">&amp;#39;cpputest&amp;#39;&lt;/span>, required: &lt;span style="color:#66d9ef">true&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cpputest_dependency &lt;span style="color:#f92672">=&lt;/span> cpputest_project.get_variable(&lt;span style="color:#e6db74">&amp;#39;cpputest_dep&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;calc&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;greeter&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, we also have to declare &lt;code>tests&lt;/code> directory as a &lt;code>subdir()&lt;/code> in main &lt;code>meson.build&lt;/code>.
I&amp;rsquo;ve put it after &lt;code>apps&lt;/code>, because integration tests may require built apps as dependencies to run.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;libs&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;apps&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;tests&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And finally, we can check if this works by deleting &lt;code>builddir&lt;/code>, and running &lt;code>meson setup builddir&lt;/code> and &lt;code>meson compile -C builddir&lt;/code>.
You should see some new logs after running &lt;code>setup&lt;/code>:&lt;/p>
&lt;pre tabindex="0">&lt;code>Executing subproject cpputest
cpputest| Project name: cpputest
cpputest| Project version: 4.0
cpputest| C++ compiler for the host machine: ccache c++ (gcc 13.2.0 &amp;#34;c++ (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders) 13.2.0&amp;#34;)
cpputest| C++ linker for the host machine: c++ ld.bfd 2.41
cpputest| Build targets in project: 5
cpputest| Subproject cpputest finished.
Build targets in project: 5
project_template 0.1
Subprojects
cpputest: YES
&lt;/code>&lt;/pre>&lt;p>That tells us Meson found the wrap and should&amp;rsquo;ve downloaded it.
Compilation should also take significantly longer, because our test harness must be compiled for the first time.
If that&amp;rsquo;s the case, our first step is done.
Now, we have to actually write a test.
Create &lt;code>test.cpp&lt;/code> file in &lt;code>tests/calc/&lt;/code> and put this there:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;CppUTest/CommandLineTestRunner.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;CppUTest/TestHarness.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>TEST_GROUP(FirstTestGroup){};
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>TEST(FirstTestGroup, FirstTest) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> FAIL(&lt;span style="color:#e6db74">&amp;#34;Fail me!&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> ac, &lt;span style="color:#66d9ef">char&lt;/span>&lt;span style="color:#f92672">**&lt;/span> av) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> CommandLineTestRunner&lt;span style="color:#f92672">::&lt;/span>RunAllTests(ac, av);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, let&amp;rsquo;s make it compile. Open &lt;code>tests/calc/meson.build&lt;/code> and put it there:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>calc_test_exec &lt;span style="color:#f92672">=&lt;/span> executable(&lt;span style="color:#e6db74">&amp;#39;calc_test&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;test.cpp&amp;#39;&lt;/span>, dependencies: cpputest_dependency)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>test(&lt;span style="color:#e6db74">&amp;#39;calc test&amp;#39;&lt;/span>, calc_test_exec)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In theory, this should build just fine.
However, in practice&amp;hellip;&lt;/p>
&lt;pre tabindex="0">&lt;code>[44/44] Linking target tests/calc/calc_test.exe
FAILED: tests/calc/calc_test.exe
&amp;#34;c++&amp;#34; -o tests/calc/calc_test.exe tests/calc/calc_test.exe.p/test.cpp.obj &amp;#34;-Wl,--allow-shlib-undefined&amp;#34; &amp;#34;-Wl,--start-group&amp;#34; &amp;#34;subprojects/cpputest-4.0/src/CppUTestExt/libCppUTestExt.a&amp;#34; &amp;#34;-Wl,--subsystem,console&amp;#34; &amp;#34;-lkernel32&amp;#34; &amp;#34;-luser32&amp;#34; &amp;#34;-lgdi32&amp;#34; &amp;#34;-lwinspool&amp;#34; &amp;#34;-lshell32&amp;#34; &amp;#34;-lole32&amp;#34; &amp;#34;-loleaut32&amp;#34; &amp;#34;-luuid&amp;#34; &amp;#34;-lcomdlg32&amp;#34; &amp;#34;-ladvapi32&amp;#34; &amp;#34;-Wl,--end-group&amp;#34;
C:/gcc/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: tests/calc/calc_test.exe.p/test.cpp.obj: in function `TEST_FirstTestGroup_FirstTest_Test::testBody()&amp;#39;:
F:\Projects\C_C++\meson_c_cpp_project_template\builddir/../tests/calc/test.cpp:7:(.text+0x11): undefined reference to `UtestShell::getCurrent()&amp;#39;
&lt;/code>&lt;/pre>&lt;p>In practice, I get a massive linking error.
Gee, I wonder why.&lt;/p>
&lt;p>After few minutes of investigation, I&amp;rsquo;ve reached the conclusion: the &lt;em>&amp;ldquo;official&amp;rdquo;&lt;/em> CppUTest wrap is &lt;strong>very bad&lt;/strong>.
And by that, I mean it&amp;rsquo;s completely broken.
If you&amp;rsquo;ve used a different test harness, and it works (i know for a fact that Catch2 should, last i tried at least&amp;hellip;) - congratulations, you can skip the next part!
For the rest of you, don&amp;rsquo;t worry - we&amp;rsquo;re fix that issue. It&amp;rsquo;ll just take a bit longer for you, and much longer for me.&lt;/p>
&lt;h2 id="side-quest---fixing-cpputest">Side quest - fixing CppUTest!&lt;/h2>
&lt;p>Let&amp;rsquo;s look at it&amp;rsquo;s build files, as everything is stored in &lt;code>subprojects/cpputest-4.0&lt;/code>.
First thing that I&amp;rsquo;ve noticed was spelling error - the author used &lt;code>extinctions&lt;/code> instead of &lt;code>extensions&lt;/code> (in multiple places, so this was&amp;hellip; Intentional?)
Then, I&amp;rsquo;ve noticed something worse.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>cpputest_dep &lt;span style="color:#f92672">=&lt;/span> declare_dependency(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> link_with : cpputest_lib,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version : meson.project_version(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> include_directories : cpputest_dirs)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> get_option(&lt;span style="color:#e6db74">&amp;#39;extinctions&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cpputest_dep &lt;span style="color:#f92672">=&lt;/span> declare_dependency(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> link_with : cpputest_ext_lib,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version : meson.project_version(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> include_directories : cpputest_dirs)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">endif&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You see this, right?
Obviously, a typo, someone forgot to add &lt;code>_ext&lt;/code>, but one that completely breaks the wrap.
Extensions are enabled by default, by the way.
See &lt;code>meson_options.txt&lt;/code> in CppUTest directory:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>option(&lt;span style="color:#e6db74">&amp;#39;extinctions&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> type : &lt;span style="color:#e6db74">&amp;#39;boolean&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> value : &lt;span style="color:#66d9ef">true&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> description : &lt;span style="color:#e6db74">&amp;#39;Use the CppUTest extension library&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>So, what happens when we fix that? We can do it pretty easily, just declare a second dependency, we don&amp;rsquo;t even have to link the extensions because at this point we&amp;rsquo;re not using them.
Rename &lt;code>cpputest_dep&lt;/code> for extensions to &lt;code>cpputest_ext_dep&lt;/code> and let&amp;rsquo;s add the second dependency to &lt;code>cpputest.wrap&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> get_option(&lt;span style="color:#e6db74">&amp;#39;extinctions&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cpputest_ext_dep &lt;span style="color:#f92672">=&lt;/span> declare_dependency(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> link_with : cpputest_ext_lib,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version : meson.project_version(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> include_directories : cpputest_dirs)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">endif&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;pre tabindex="0">&lt;code>[provide]
cpputest = cpputest_dep
cpputest_ext = cpputest_ext_dep
&lt;/code>&lt;/pre>&lt;p>And after clean compilation, unfortunately, this doesn&amp;rsquo;t solve my issue.
I get multiple undefined references to platform-related functions, because apparently someone forgot to add platform-specific implementations that CppUTest provides as dependencies&amp;hellip;&lt;/p>
&lt;p>Well, the fix is &lt;em>easy&lt;/em> - I just have to make a proper wrap myself.
To be continued, after I make it working.&lt;/p>
&lt;blockquote>
&lt;p>Yeah, I could jump the ship and just use different library, but &lt;em>where&amp;rsquo;s the fun in that?&lt;/em>
I&amp;rsquo;ve already vibe checked that one.
Also; there is no tag in the repository for now, I&amp;rsquo;m gonna make one after making that wrap and finishing up this part in next post(s).&lt;/p>
&lt;/blockquote></content></item><item><title>Making C/C++ project template in Meson - part 1</title><link>https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-1/</link><pubDate>Sat, 27 Jan 2024 00:00:00 +0000</pubDate><guid>https://steelph0enix.github.io/posts/making-c-cpp-project-template-in-meson-part-1/</guid><description>&lt;h2 id="intro">Intro&lt;/h2>
&lt;p>One of the things that I dislike about C and C++ in particular is lack of standardized build environment, package manager, and all that stuff.
And although CMake is de-facto &amp;ldquo;standard&amp;rdquo; over here, and even tries to double as a &amp;ldquo;package manager&amp;rdquo;, I find it hard to like this tool.
It&amp;rsquo;s not a &lt;em>bad&lt;/em> tool, I&amp;rsquo;ve used it few times, I&amp;rsquo;ve seen some non-trivial projects made with CMake, but I did not like what I&amp;rsquo;ve seen.&lt;/p></description><content>&lt;h2 id="intro">Intro&lt;/h2>
&lt;p>One of the things that I dislike about C and C++ in particular is lack of standardized build environment, package manager, and all that stuff.
And although CMake is de-facto &amp;ldquo;standard&amp;rdquo; over here, and even tries to double as a &amp;ldquo;package manager&amp;rdquo;, I find it hard to like this tool.
It&amp;rsquo;s not a &lt;em>bad&lt;/em> tool, I&amp;rsquo;ve used it few times, I&amp;rsquo;ve seen some non-trivial projects made with CMake, but I did not like what I&amp;rsquo;ve seen.&lt;/p>
&lt;p>I&amp;rsquo;ve also been missing a proper C/C++ project template for a very long time.
I was not aware I&amp;rsquo;m missing one, until i got a hang of the codebase of one of the projects I&amp;rsquo;ve been working on in my work, and experienced the true power of scripting and automation.
I&amp;rsquo;ve been tinkering with many build systems over the years, but I&amp;rsquo;ve never found one that really &amp;ldquo;clicked&amp;rdquo; for me.
And since I still cannot lift the curse of C/C++ from my life, it seems the only alternative I have is to pick &lt;em>something&lt;/em> and force myself through.
And I&amp;rsquo;m taking You for a ride with me.&lt;/p>
&lt;p>So we&amp;rsquo;re going with &lt;a href="https://mesonbuild.com/">Meson Build system&lt;/a>, which was recommended to me by a colleague few years ago.
Since then I&amp;rsquo;ve read the docs and also experimented with it a bit, but I haven&amp;rsquo;t really made a proper project based on it yet, as of writing this sentence.
This is about to change.&lt;/p>
&lt;p>I will try to make this series of blog posts as &amp;ldquo;followable&amp;rdquo; as possible, so You should be able to reproduce most of my work by yourself, and understand the choices I make.
Remember that the best tool for the job is sometimes the one you make yourself, not the one you blindly copy&amp;amp;paste without deeper understanding.
&lt;del>But please, don&amp;rsquo;t reinvent the wheel if that&amp;rsquo;s not necessary.&lt;/del>
&lt;del>Use the right tools for the job, and whatnot.&lt;/del>&lt;/p>
&lt;h2 id="goals">Goals&lt;/h2>
&lt;p>Our main goal here is to &lt;strong>make a generic, compiler/platform-independent, easy-to-expand project template in Meson for small-to-medium size C/C++ projects&lt;/strong>.
I also want it to have some specific features that I believe are &amp;ldquo;must have&amp;rdquo; in any self-respecting project.
In the end, we will use that template to write some kind of application and maybe play with the project&amp;rsquo;s structure afterwards, until I&amp;rsquo;m satisfied with the results.
After that, I have a plan to use this template as a basis for ARM Cortex-M project template, but we&amp;rsquo;ll cross that bridge when we&amp;rsquo;ll get to it.&lt;/p>
&lt;p>The first thing that I&amp;rsquo;m interested in is unit and integration test support.
I don&amp;rsquo;t want to make assumptions about test harnesses though, so we&amp;rsquo;ll just have to make sure that the project&amp;rsquo;s structure is &lt;em>testable&lt;/em> and Meson can build the tests.
Unit tests have built-in support in Meson, so let&amp;rsquo;s go with that.
There&amp;rsquo;s also some support for code coverage, and we will surely explore that too.
Integration tests are much more project-dependent, so the only requirements is that Meson should recognize them as targets, but running them might be done via external script or program (executed by Meson).
Meson is our &lt;em>build&lt;/em> system, not necessarily the runner, nor the harness, so let&amp;rsquo;s not expect it to be able to handle generic integration tests by itself.
Some external tooling/scripting may still be required (especially when we&amp;rsquo;ll get to the embedded part&amp;hellip;), but everything should eventually be held together by Meson.&lt;/p>
&lt;p>The second thing that I wanna see there is support for C/C++ LSP.
My primary choice there is &lt;code>clangd&lt;/code>, so we&amp;rsquo;ll have to generate &lt;code>compile_commands.json&lt;/code> using Meson. Fortunately, it does that automatically.&lt;/p>
&lt;p>The third thing is support for code checks and code formatting.
External tools can be plugged to Meson via &lt;a href="https://mesonbuild.com/Custom-build-targets.html">custom build targets&lt;/a>, and we will configure at least one code checker and autoformatter like that.
Our project should provide commands to perform project-wide code and formatting check (which is very useful in git prehooks).&lt;/p>
&lt;p>And the last, but not the least - documentation.
I want to be able to generate docs for my code via Meson.
Probably using Doxygen, as it&amp;rsquo;s relatively easy to set up.
This also includes coverage reports, and we will most likely use &lt;code>lcov&lt;/code>/&lt;code>gcov&lt;/code> for that.&lt;/p>
&lt;blockquote>
&lt;p>&lt;em>Did I mention that I&amp;rsquo;ve spent most of last year writing Rust code?&lt;/em>
&lt;em>Did you know that Cargo provides most of the stuff that I&amp;rsquo;ve described here out-of-the-box?&lt;/em>
&lt;em>Now you do.&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;h2 id="preparations">Preparations&lt;/h2>
&lt;p>Before we start, let&amp;rsquo;s check our prerequisites, because there&amp;rsquo;s gonna be some.&lt;/p>
&lt;ul>
&lt;li>Meson - on Linux, should be in your package manager. If it&amp;rsquo;s not, i assume you know what to do. On Windows, i recommend grabbing an installer from &lt;a href="https://github.com/mesonbuild/meson/releases">Github&lt;/a>. Just make sure it&amp;rsquo;s in your &lt;code>PATH&lt;/code> variable - check if opening a terminal and running &lt;code>meson --version&lt;/code> returns expected version string. I&amp;rsquo;m currently running 1.3.1. &lt;em>Technically Meson is available via &lt;code>winget&lt;/code>, but their repo currently provides outdated version, so i recommend installing it manually on Windows.&lt;/em>&lt;/li>
&lt;li>Ninja - I&amp;rsquo;ll describe it&amp;rsquo;s purpose in a bit. It should be bundled with Meson on Windows, and should be installed automatically on Linux when installing Meson via package manager. Should be in your &lt;code>PATH&lt;/code> too. In any case, verify if it&amp;rsquo;s installed correctly by running &lt;code>ninja --version&lt;/code>. I&amp;rsquo;m currently running 1.11.1. You can also install it manually if needed &lt;a href="https://github.com/ninja-build/ninja/releases">by downloading a zip from Github&lt;/a> and extracting it into a directory that&amp;rsquo;s in &lt;code>PATH&lt;/code> variable.&lt;/li>
&lt;li>C/C++ toolchain - &lt;strong>TL;DR install latest GCC&lt;/strong>. In theory, we don&amp;rsquo;t have to choose any specific toolchain, because we&amp;rsquo;re using Meson and Ninja which support the popular ones. &lt;strong>However&lt;/strong>, I&amp;rsquo;ve mentioned that I want to have coverage report generation, and the only coverage tools that I&amp;rsquo;m familiar with are &lt;code>lcov&lt;/code>/&lt;code>gcov&lt;/code>, and this pretty much forces me to use GCC. If you don&amp;rsquo;t care about code coverage reports, pick your treat. If you somehow got here as a complete newbie and you don&amp;rsquo;t have any C/C++ toolchain installed, either grab GCC from your package manager if you&amp;rsquo;re running Linux (look for package called &lt;code>build-essentials&lt;/code> or &lt;code>base-devel&lt;/code> or something like that), or - if you&amp;rsquo;re running Windows - &lt;a href="https://steelph0enix.github.io/posts/vscode-cpp-setup/#cc-toolchain">download latest WinLibs package and add it&amp;rsquo;s &lt;code>bin&lt;/code> subdirectory to PATH&lt;/a>. Or install Microsoft&amp;rsquo;s Visual C++ toolchain via Visual Studio Build Tools (you may need to do that even after installing WinLibs, if you want to use &lt;code>clang&lt;/code>, but I recommend &lt;strong>not&lt;/strong> using MSVC for pure C unless you don&amp;rsquo;t value your sanity).&lt;/li>
&lt;li>LLVM tools - mostly &lt;code>clangd&lt;/code>, but we&amp;rsquo;re also gonna use &lt;code>clang-tidy&lt;/code> at some point. If you&amp;rsquo;re running Linux, again - find those tools in your package manager. If you&amp;rsquo;re running Windows and WinLibs, you already got them. If you don&amp;rsquo;t use WinLibs, install latest LLVM release (&lt;code>winget install llvm&lt;/code>, should be &amp;ldquo;recent enough&amp;rdquo;, or get it from &lt;a href="https://github.com/llvm/llvm-project/releases">Github&lt;/a>), it should contain everything we&amp;rsquo;ll need. Again, if manually installed - make sure it&amp;rsquo;s in PATH by running &lt;code>clangd --version&lt;/code>. I&amp;rsquo;m currently using 17.0.5.&lt;/li>
&lt;li>Doxygen - we&amp;rsquo;re not gonna be using it any time soon, but let&amp;rsquo;s make sure it&amp;rsquo;s available. Linux - install from repo, Windows - it&amp;rsquo;s already in WinLibs, if not using WinLibs (or you want the latest version), install manually - &lt;code>winget install doxygen&lt;/code> or grab installer from &lt;a href="https://www.doxygen.nl/download.html">here&lt;/a>. Run &lt;code>doxygen --version&lt;/code> to verify, I&amp;rsquo;m using 1.10.0. &lt;strong>Note - Winget may not add it to PATH, it&amp;rsquo;s installed in &lt;code>Program Files/doxygen&lt;/code> by default. Add &lt;code>bin&lt;/code> subdirectory to &lt;code>PATH&lt;/code> manually if that&amp;rsquo;s the case.&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>Oh, and there&amp;rsquo;s a small issue of &lt;code>lcov&lt;/code> not having official Windows support. There are some unofficial releases, but we&amp;rsquo;re not gonna use them - instead, we&amp;rsquo;re gonna lock the coverage report generation feature to Linux and GCC only.
I don&amp;rsquo;t want to completely lock this template to GCC because of that feature, so we will have to separate this part appropriately.&lt;/p>
&lt;blockquote>
&lt;p>We can always use containers to get this running with all features on Windows.&lt;/p>
&lt;/blockquote>
&lt;p>Also; i &lt;em>assume&lt;/em> that we&amp;rsquo;re gonna be using Ninja as our building &amp;ldquo;back-end&amp;rdquo;, but it doesn&amp;rsquo;t really matter because Meson should handle whatever &amp;ldquo;back-end&amp;rdquo; you&amp;rsquo;d like to use.
Just make sure to check the &amp;ldquo;whatever &amp;ldquo;back-end&amp;rdquo; you&amp;rsquo;d like to use&amp;rdquo; output when I&amp;rsquo;m talking about Ninja output.
This is an &amp;ldquo;I assume you know what you&amp;rsquo;re doing&amp;rdquo; warning.&lt;/p>
&lt;h2 id="hello-world">Hello, world!&lt;/h2>
&lt;p>Assuming that everything is installed, we are ready to get going.
I&amp;rsquo;ve made a &lt;a href="https://github.com/SteelPh0enix/meson_c_cpp_project_template">repository&lt;/a> for this project, I&amp;rsquo;ll try to make a tag after each part so You can easily follow.&lt;/p>
&lt;p>Let&amp;rsquo;s start by initializing a new Meson project in an empty directory.
The arguments don&amp;rsquo;t really matter here - we&amp;rsquo;re gonna be building our &lt;code>meson.build&lt;/code> from scratch anyway, but right now we want to verify if our environment is set up correctly, so let&amp;rsquo;s go with those for now:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>meson init --name project_template --language cpp --type executable
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This should generate two files - &lt;code>meson.build&lt;/code> and &lt;code>project_template.cpp&lt;/code>. &lt;code>meson.build&lt;/code> should contain project definition with some reasonable defaults, executable, and a test:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>project(&lt;span style="color:#e6db74">&amp;#39;project_template&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;cpp&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version : &lt;span style="color:#e6db74">&amp;#39;0.1&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> default_options : [&lt;span style="color:#e6db74">&amp;#39;warning_level=3&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;cpp_std=c++14&amp;#39;&lt;/span>])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>exe &lt;span style="color:#f92672">=&lt;/span> executable(&lt;span style="color:#e6db74">&amp;#39;project_template&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;project_template.cpp&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> install : &lt;span style="color:#66d9ef">true&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>test(&lt;span style="color:#e6db74">&amp;#39;basic&amp;#39;&lt;/span>, exe)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Source file should contain a very simple test program:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#define PROJECT_NAME &amp;#34;project_template&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> argc, &lt;span style="color:#66d9ef">char&lt;/span> &lt;span style="color:#f92672">**&lt;/span>argv) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span>(argc &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> argv[&lt;span style="color:#ae81ff">0&lt;/span>] &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;takes no arguments.&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;This is project &amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> PROJECT_NAME &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;.&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Let&amp;rsquo;s try building it.
But first, we need to generate actual build scripts via Meson, as it&amp;rsquo;s (similarly to CMake) a meta-build system.
Which means that it&amp;rsquo;s only a &lt;em>generator&lt;/em> for an actual build system (like Ninja, or GNU Make) that performs the heavy lifting.
If you&amp;rsquo;re not used to this approach, it may make no sense at first - why use two tools instead of one, right?
Unfortunately, building stuff is not easy in C/C++, especially when you&amp;rsquo;re working on a big project with multiple dependencies, that must be supported on many different platforms.
As far as I know, the earliest successful application of this approach is &lt;a href="https://en.wikipedia.org/wiki/GNU_Autotools">GNU Autotools&lt;/a> project with it&amp;rsquo;s &lt;code>configure&lt;/code>-&lt;code>make&lt;/code>-&lt;code>make install&lt;/code> routine.
Apparently, it&amp;rsquo;s good to have an additional layer of abstraction over the target build system.
I have already mentioned some reasons for that (multi-platform, yadda yadda yadda), also - meta-build systems are usually easier to use and have more features.
Anyway, to generate a build directory with all the stuff required for compilation, run this from project&amp;rsquo;s directory&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>meson setup builddir
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The argument &lt;code>builddir&lt;/code> is the name of our build directory.
If you&amp;rsquo;ve set up your compiler and Ninja correctly, you should get some info about compiler executables and your environment, and &lt;code>builddir&lt;/code> directory should appear with some Meson and Ninja files.&lt;/p>
&lt;pre tabindex="0">&lt;code>PS&amp;gt; meson setup builddir
The Meson build system
Version: 1.3.1
Source dir: F:\Projects\C_C++\meson_c_cpp_project_template
Build dir: F:\Projects\C_C++\meson_c_cpp_project_template\builddir
Build type: native build
Project name: project_template
Project version: 0.1
C++ compiler for the host machine: ccache c++ (gcc 13.2.0 &amp;#34;c++ (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders) 13.2.0&amp;#34;)
C++ linker for the host machine: c++ ld.bfd 2.41
Host machine cpu family: x86_64
Host machine cpu: x86_64
Build targets in project: 1
Found ninja-1.11.1.git.kitware.jobserver-1 at C:\gcc\bin\ninja.EXE
&lt;/code>&lt;/pre>&lt;p>Now, we can tell Meson to compile the project:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>meson compile -C builddir
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>We could also use &lt;code>ninja&lt;/code> directly, but I&amp;rsquo;ll stick to using Meson whenever possible instead, for compatibility reasons.
That&amp;rsquo;s actually a core part of this template - we &lt;strong>want&lt;/strong> Meson to do stuff for us, we don&amp;rsquo;t want to care what happens under the hood.&lt;/p>
&lt;/blockquote>
&lt;p>We should get some output from Ninja.&lt;/p>
&lt;pre tabindex="0">&lt;code>PS&amp;gt; meson compile -C builddir
ninja: Entering directory `F:/Projects/C_C++/meson_c_cpp_project_template/builddir&amp;#39;
[2/2] Linking target project_template.exe
&lt;/code>&lt;/pre>&lt;p>And an executable&lt;/p>
&lt;pre tabindex="0">&lt;code>PS .&amp;gt; cd .\builddir\
PS .\builddir&amp;gt; .\project_template.exe
This is project project_template.
PS .\builddir&amp;gt; .\project_template.exe hello world
F:\Projects\C_C++\meson_c_cpp_project_template\builddir\project_template.exe takes no arguments.
&lt;/code>&lt;/pre>&lt;p>And also a test!&lt;/p>
&lt;pre tabindex="0">&lt;code>meson test
&lt;/code>&lt;/pre>&lt;p>Oh, if you haven&amp;rsquo;t noticed yet - the &lt;code>-C&lt;/code> argument changes the working directory of Meson, so it&amp;rsquo;s necessary only when you&amp;rsquo;re outside of it.
I assume that we &lt;code>cd&lt;/code>&amp;rsquo;d into it in previous step, so we don&amp;rsquo;t need it now.
I&amp;rsquo;ll also stop adding it to future example invocations, just remember that it exists and that Meson&amp;rsquo;s working directory must be the one that was generated via &lt;code>meson setup&lt;/code> for most commands.&lt;/p>
&lt;p>Let&amp;rsquo;s look at the output:&lt;/p>
&lt;pre tabindex="0">&lt;code>PS .\builddir&amp;gt;meson test
ninja: Entering directory `F:\Projects\C_C++\meson_c_cpp_project_template\builddir&amp;#39;
ninja: no work to do.
1/1 basic OK 0.01s
Ok: 1
Expected Fail: 0
Fail: 0
Unexpected Pass: 0
Skipped: 0
Timeout: 0
Full log written to F:\Projects\C_C++\meson_c_cpp_project_template\builddir\meson-logs\testlog.txt
&lt;/code>&lt;/pre>&lt;p>Yeah, that checks out, we have one &amp;ldquo;test&amp;rdquo; and it does nothing and returns 0, so it passes.
If you have encountered any issues until now, take a break and investigate.
Check if everything is where it&amp;rsquo;s supposed to be.
You can also run &lt;code>meson&lt;/code> commands with &lt;code>--help&lt;/code> flag to see what options you have available, just as an exercise.
Continue, when you&amp;rsquo;re ready.&lt;/p>
&lt;h2 id="structuring-our-project">Structuring our project&lt;/h2>
&lt;p>Now it&amp;rsquo;s time to think about the structure of our project.
We must be aware of the fact that there&amp;rsquo;s no one &amp;ldquo;best&amp;rdquo; project structure that would suit every kind of project.
However, due to the fact that I&amp;rsquo;d rather not work on a completely abstract project, we&amp;rsquo;ll have to make some assumptions about it&amp;rsquo;s structure, at least for now.&lt;/p>
&lt;p>Let&amp;rsquo;s gather what we already assumed about our project.
We assumed that it&amp;rsquo;s supposed to be testable.
Unit tests are the bare minimum that we want to support.
We also assumed that we&amp;rsquo;d like our code to be checked by external tools, but we can ignore that, because those tools should work no matter how we structure our project.
It&amp;rsquo;s just the matter of invoking them properly.
Same thing with Doxygen.
As for &lt;code>clangd&lt;/code>, Meson provides &lt;code>compile_commands.json&lt;/code>, therefore it will work as long as the structure is valid.&lt;/p>
&lt;p>Considering the usual good design practices, we&amp;rsquo;ll probably want to split our codebase into modules according to their responsibility.
No, not &lt;a href="https://en.cppreference.com/w/cpp/language/modules">&lt;em>those&lt;/em>&lt;/a> modules, let&amp;rsquo;s ignore the existence of C++20 for now.
We can store each module in a separate directory, with it&amp;rsquo;s own &lt;code>meson.build&lt;/code> file that would define how it&amp;rsquo;s built.
Modules can be built as static libraries and linked to executables.
That way, it&amp;rsquo;s trivial to test them - we can just link the same binary that&amp;rsquo;s used with our program executable to the test executable, and validate it&amp;rsquo;s behavior.&lt;/p>
&lt;blockquote>
&lt;p>This is how it&amp;rsquo;s done in some projects that I&amp;rsquo;ve been working on in my current job, and if it&amp;rsquo;s good enough choice for space-grade projects, it&amp;rsquo;s sure as hell good enough choice for me.
But really, I&amp;rsquo;ve seen this approach in action and it should work well.&lt;/p>
&lt;/blockquote>
&lt;p>Let&amp;rsquo;s also assume that our project can contain more than one executable, because it should be simple to setup for that.
We&amp;rsquo;re gonna put them in &lt;code>apps&lt;/code> directory.
Modules will go to &lt;code>lib&lt;/code>, and tests into &lt;code>tests&lt;/code>.&lt;/p>
&lt;p>Let&amp;rsquo;s create some dummy libs, tests for them, and an application to tie it together.
Also; let&amp;rsquo;s add &lt;code>meson.build&lt;/code> to each directory, empty - for now.
This is how it might look like:&lt;/p>
&lt;pre tabindex="0">&lt;code>meson_c_cpp_project_template
â”‚ meson.build
â”‚
â”œâ”€â”€â”€apps
â”‚ â””â”€â”€â”€hello_world
â”‚ hello_world.cpp
â”‚ meson.build
â”‚
â”œâ”€â”€â”€lib
â”‚ â”œâ”€â”€â”€calc
â”‚ â”‚ calc.cpp
â”‚ â”‚ calc.hpp
â”‚ â”‚ meson.build
â”‚ â”‚
â”‚ â””â”€â”€â”€greeter
â”‚ greeter.cpp
â”‚ greeter.hpp
â”‚ meson.build
â”‚
â””â”€â”€â”€tests
â”œâ”€â”€â”€calc
â”‚ calc_test.cpp
â”‚ meson.build
â”‚
â””â”€â”€â”€greeter
greeter_test.cpp
meson.build
&lt;/code>&lt;/pre>&lt;h3 id="adding-libraries">Adding libraries&lt;/h3>
&lt;p>Let&amp;rsquo;s ignore the fact that we don&amp;rsquo;t have a test harness yet, and focus on making the libraries build.
But first, we have to put some code into them:&lt;/p>
&lt;p>calc.hpp:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#pragma once
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">double&lt;/span> &lt;span style="color:#a6e22e">celsius_to_fahrenheit&lt;/span>(&lt;span style="color:#66d9ef">double&lt;/span> celsius);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">double&lt;/span> &lt;span style="color:#a6e22e">fahrenheit_to_celsius&lt;/span>(&lt;span style="color:#66d9ef">double&lt;/span> fahrenheit);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>calc.cpp:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;calc.hpp&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">double&lt;/span> &lt;span style="color:#a6e22e">celsius_to_fahrenheit&lt;/span>(&lt;span style="color:#66d9ef">double&lt;/span> celsius) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> (celsius &lt;span style="color:#f92672">*&lt;/span> (&lt;span style="color:#ae81ff">9.0&lt;/span> &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">5.0&lt;/span>)) &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#ae81ff">32.0&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">double&lt;/span> &lt;span style="color:#a6e22e">fahrenheit_to_celsius&lt;/span>(&lt;span style="color:#66d9ef">double&lt;/span> fahrenheit) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> (fahrenheit &lt;span style="color:#f92672">-&lt;/span> &lt;span style="color:#ae81ff">32.0&lt;/span>) &lt;span style="color:#f92672">*&lt;/span> (&lt;span style="color:#ae81ff">5.0&lt;/span> &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">9.0&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>greeter.hpp:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#pragma once
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;string&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>std&lt;span style="color:#f92672">::&lt;/span>string greet(std&lt;span style="color:#f92672">::&lt;/span>string &lt;span style="color:#66d9ef">const&lt;/span>&lt;span style="color:#f92672">&amp;amp;&lt;/span> name);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>greeter.cpp:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;greeter.hpp&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>std&lt;span style="color:#f92672">::&lt;/span>string greet(std&lt;span style="color:#f92672">::&lt;/span>string &lt;span style="color:#66d9ef">const&lt;/span>&lt;span style="color:#f92672">&amp;amp;&lt;/span> name) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>string(&lt;span style="color:#e6db74">&amp;#34;Hello &amp;#34;&lt;/span>) &lt;span style="color:#f92672">+&lt;/span> name;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And now, let&amp;rsquo;s fill &lt;code>meson.build&lt;/code> files.
In order to build a library, we have to use &lt;code>library()&lt;/code> function. Duh.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>calc &lt;span style="color:#f92672">=&lt;/span> library(&lt;span style="color:#e6db74">&amp;#39;calc&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;calc.cpp&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The first argument is name of the library, followed by 0 or more sources in next arguments.
There are some options that we can set here using keyword arguments, but we&amp;rsquo;re gonna ignore them for now because we don&amp;rsquo;t need any.
We also save the result of this function to &lt;code>calc&lt;/code> variable, which we&amp;rsquo;re gonna use later.
We need to do the same thing for the other module, and the next step is modifying our top-level &lt;code>meson.build&lt;/code> to recognize this dependency.
Let&amp;rsquo;s remove the &lt;code>executable()&lt;/code> and &lt;code>test()&lt;/code> calls generated by Meson, and call &lt;code>subdir&lt;/code> instead, to include &lt;code>lib&lt;/code> directory.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>project(&lt;span style="color:#e6db74">&amp;#39;project_template&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;cpp&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version : &lt;span style="color:#e6db74">&amp;#39;0.1&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> default_options : [&lt;span style="color:#e6db74">&amp;#39;warning_level=3&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;cpp_std=c++14&amp;#39;&lt;/span>])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;lib&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then, we&amp;rsquo;re gonna create &lt;code>meson.build&lt;/code> in &lt;code>lib&lt;/code>, that will include all the modules we currently have.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;calc&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;greeter&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And now, we should be able to build them.
I recommend removing &lt;code>builddir&lt;/code> directory after every big change, although I&amp;rsquo;m pretty sure that there is a way to do it in more elegant fashion (re-&lt;code>setup&lt;/code>?), and re-generating the build files.
Oh, yeah, it&amp;rsquo;s &lt;code>meson setup --reconfigure builddir&lt;/code>.
There you go.
And I got instantly reminded why I&amp;rsquo;m not using it - it doesn&amp;rsquo;t really reconfigure &lt;em>everything&lt;/em>, so it&amp;rsquo;s better to stick to the ol&amp;rsquo; reliable &lt;code>rm builddir&lt;/code> technique, at least until our project&amp;rsquo;s structure stabilizes a bit.&lt;/p>
&lt;pre tabindex="0">&lt;code>PS&amp;gt; meson setup builddir
The Meson build system
Version: 1.3.1
Source dir: F:\Projects\C_C++\meson_c_cpp_project_template
Build dir: F:\Projects\C_C++\meson_c_cpp_project_template\builddir
Build type: native build
Project name: project_template
Project version: 0.1
C++ compiler for the host machine: ccache c++ (gcc 13.2.0 &amp;#34;c++ (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht
Sanders) 13.2.0&amp;#34;)
C++ linker for the host machine: c++ ld.bfd 2.41
Host machine cpu family: x86_64
Host machine cpu: x86_64
Build targets in project: 2
Found ninja-1.11.1.git.kitware.jobserver-1 at C:\gcc\bin\ninja.EXE
PS&amp;gt; meson compile -C builddir
INFO: autodetecting backend as ninja
INFO: calculating backend command to run: C:\gcc\bin\ninja.EXE -C F:/Projects/C_C++/meson_c_cpp_project_template/
builddir
ninja: Entering directory `F:/Projects/C_C++/meson_c_cpp_project_template/builddir&amp;#39;
[4/4] Linking target lib/greeter/libgreeter.dll
&lt;/code>&lt;/pre>&lt;p>And it seems that it works.
It detected 2 build targets in project, exactly what we wanted to see.
Let&amp;rsquo;s look at &lt;code>builddir&lt;/code> to confirm it.&lt;/p>
&lt;pre tabindex="0">&lt;code>meson_c_cpp_project_template\builddir\lib
â”œâ”€â”€â”€calc
â”‚ â”‚ libcalc.dll
â”‚ â”‚ libcalc.dll.a
â”‚ â”‚
â”‚ â””â”€â”€â”€libcalc.dll.p
â”‚ calc.cpp.obj
â”‚
â””â”€â”€â”€greeter
â”‚ libgreeter.dll
â”‚ libgreeter.dll.a
â”‚
â””â”€â”€â”€libgreeter.dll.p
greeter.cpp.obj
&lt;/code>&lt;/pre>&lt;p>Okay, but we got DLLs (or &lt;code>.so&lt;/code>&amp;rsquo;s, if you&amp;rsquo;re running Linux).
We don&amp;rsquo;t want dynamic libs, we want static libs.
Fortunately, there&amp;rsquo;s an easy fix, straight from the docs - &lt;code>library()&lt;/code> respects the &lt;code>default_library&lt;/code> project option, so we just have to change it in our project&amp;rsquo;s settings before building the libs.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>project(&lt;span style="color:#e6db74">&amp;#39;project_template&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;cpp&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version : &lt;span style="color:#e6db74">&amp;#39;0.1&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> default_options : {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;warning_level&amp;#39;&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;3&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;cpp_std&amp;#39;&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;c++14&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;c_std&amp;#39;&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;c11&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;default_library&amp;#39;&lt;/span>: &lt;span style="color:#e6db74">&amp;#39;static&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I took an opportunity to refactor that ugly array list into a proper dict, and set the default C standard to C11.
You can find the list of available default options &lt;a href="https://mesonbuild.com/Builtin-options.html#base-options">here&lt;/a>, if you&amp;rsquo;re interested.
Let&amp;rsquo;s rebuild the project (again, &lt;code>--reconfigure&lt;/code> may not detect this change, so &lt;code>rm builddir&lt;/code>), and as we can see - we&amp;rsquo;ve got static libs now!&lt;/p>
&lt;pre tabindex="0">&lt;code>meson_c_cpp_project_template\builddir\lib
â”œâ”€â”€â”€calc
â”‚ â”‚ libcalc.a
â”‚ â”‚
â”‚ â””â”€â”€â”€libcalc.a.p
â”‚ calc.cpp.obj
â”‚
â””â”€â”€â”€greeter
â”‚ libgreeter.a
â”‚
â””â”€â”€â”€libgreeter.a.p
greeter.cpp.obj
&lt;/code>&lt;/pre>&lt;h3 id="adding-an-executable">Adding an executable&lt;/h3>
&lt;p>Next, let&amp;rsquo;s use those libs with our executable.
Example &lt;code>hello_world.cpp&lt;/code> may look like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;calc.hpp&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;greeter.hpp&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> greet(&lt;span style="color:#e6db74">&amp;#34;random developer&amp;#34;&lt;/span>) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;12.5*F == &amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> fahrenheit_to_celsius(&lt;span style="color:#ae81ff">12.5&lt;/span>) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;*C&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;12.5*C == &amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> celsius_to_fahrenheit(&lt;span style="color:#ae81ff">12.5&lt;/span>) &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;*F&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Of course, the LSP will not be able to detect those headers properly yet, so ignore all the errors.
Let the toolchain speak the truth.
This is the content of &lt;code>hello_world&lt;/code>&amp;rsquo;s directory &lt;code>meson.build&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>hello_world &lt;span style="color:#f92672">=&lt;/span> executable(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;hello_world&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;hello_world.cpp&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> link_with: [calc, greeter]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notice that we&amp;rsquo;ve used previously defined &lt;code>calc&lt;/code> and &lt;code>greeter&lt;/code> variables.
In order to do that, &lt;code>lib&lt;/code> subdirectory must be evaluated before &lt;code>apps&lt;/code>, and that&amp;rsquo;s why &lt;code>subdir('apps')&lt;/code> call must be put below &lt;code>subdir('lib')&lt;/code> in our main &lt;code>meson.build&lt;/code> file.
&lt;code>subdir&lt;/code> basically evaluates the &lt;code>meson.build&lt;/code> from the directory provided via the argument, and retains the environment after that.
Pretty useful, but we have to be careful not to misuse that feature, otherwise our Meson scripts may become very fragile and complicated to maintain.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;lib&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;apps&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We also have to create &lt;code>meson.build&lt;/code> for &lt;code>apps&lt;/code> directory.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>subdir(&lt;span style="color:#e6db74">&amp;#39;hello_world&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>And yes, we probably could just &lt;code>subdir('apps/hello_world')&lt;/code>, but I wanna keep everything as local as possible.
You may add &lt;code>meson.build&lt;/code> to every directory of the project now, because we&amp;rsquo;re gonna need them.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;code>meson setup builddir&lt;/code> tells me that there are 3 targets now.
That&amp;rsquo;s what we want.
But after running &lt;code>meson compile -C builddir&lt;/code>, it seems that we have a problem.&lt;/p>
&lt;pre tabindex="0">&lt;code>../apps/hello_world/hello_world.cpp:1:10: fatal error: calc.hpp: No such file or directory
1 | #include &amp;lt;calc.hpp&amp;gt;
| ^~~~~~~~~~
compilation terminated.
&lt;/code>&lt;/pre>&lt;p>Yeah, that checks out.
We&amp;rsquo;ve linked the libs, but we never told &lt;code>executable()&lt;/code> where are their include files.
To do that, we need to use &lt;code>include_directories&lt;/code> argument of &lt;code>executable()&lt;/code>, which expects a list of objects created using &lt;code>include_directories()&lt;/code> function.
And &lt;code>include_directories()&lt;/code> accepts string with relative paths, so we can just add them to the &lt;code>meson.build&lt;/code> of our libraries and use them just like the objects returned from &lt;code>library()&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>calc_lib &lt;span style="color:#f92672">=&lt;/span> library(&lt;span style="color:#e6db74">&amp;#39;calc&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;calc.cpp&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>calc_includes &lt;span style="color:#f92672">=&lt;/span> include_directories(&lt;span style="color:#e6db74">&amp;#39;.&amp;#39;&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I&amp;rsquo;ve also added the suffix to lib.
We need to do the same for &lt;code>greeter&lt;/code>, and fix our &lt;code>hello_world&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-meson" data-lang="meson">&lt;span style="display:flex;">&lt;span>hello_world &lt;span style="color:#f92672">=&lt;/span> executable(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;hello_world&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#39;hello_world.cpp&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> link_with: [calc_lib, greeter_lib],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> include_directories: [calc_includes, greeter_includes],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And voila, we should have a green build now!
Let&amp;rsquo;s see if the executable actually works.&lt;/p>
&lt;pre tabindex="0">&lt;code>PS&amp;gt; .\builddir\apps\hello_world\hello_world.exe
Hello random developer
12.5*F == -10.8333*C
12.5*C == 54.5*F
&lt;/code>&lt;/pre>&lt;p>Yeah, the output checks out.
And that is where I&amp;rsquo;d like to finish this part, because it&amp;rsquo;s already long enough, but there&amp;rsquo;s just one tiny thing that annoys me&amp;hellip;&lt;/p>
&lt;h2 id="feeding-the-lsp">Feeding the LSP&lt;/h2>
&lt;p>I assume that you&amp;rsquo;re using an editor/IDE with LSP support, and will be using &lt;code>clangd&lt;/code>.
If that&amp;rsquo;s not the case, i strongly recommend getting one.
I personally use &lt;a href="https://neovim.io/">neovim&lt;/a>, but I cannot really recommend it to everyone, so &lt;a href="https://code.visualstudio.com/">Visual Studio Code&lt;/a> with &lt;a href="https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd">clangd plugin&lt;/a> should be more than enough.
You may want to look at my older guide to see how to setup that combination.
&lt;strong>You can find it &lt;a href="https://steelph0enix.github.io/posts/vscode-cpp-setup/#bonus-clangd-setup">here&lt;/a>.&lt;/strong>
Meson generates &lt;code>compile_commands.json&lt;/code> by default, automatically on every build (i think).
However, &lt;code>clangd&lt;/code> will not be aware of that, because this file will be stored in &lt;code>builddir&lt;/code> instead of our project&amp;rsquo;s root directory, so we have two options:&lt;/p>
&lt;ol>
&lt;li>Tell &lt;code>clangd&lt;/code> that &lt;code>compile_commands.json&lt;/code> is in &lt;code>builddir/&lt;/code>&lt;/li>
&lt;li>Copy/link &lt;code>compile_commands.json&lt;/code> into root directory of our project&lt;/li>
&lt;/ol>
&lt;p>Fortunately, we can go with the first solution, because &lt;code>clangd&lt;/code> supports configuration, and has an option for telling it where the compilation database is.
Let&amp;rsquo;s create &lt;code>.clangd&lt;/code> file in the root of our project, and put it there:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">CompileFlags&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">CompilationDatabase&lt;/span>: &lt;span style="color:#ae81ff">builddir/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can also add some &lt;a href="https://clangd.llvm.org/config">additional config&lt;/a> for the LSP.&lt;/p>
&lt;p>aaaand&amp;hellip; It looks like it&amp;rsquo;s working for me, so that&amp;rsquo;s it, we&amp;rsquo;re done here.
I&amp;rsquo;ve made a tag and release on &lt;a href="https://github.com/SteelPh0enix/meson_c_cpp_project_template/tree/part-1">this project&amp;rsquo;s repository&lt;/a> if you want to go the easy route and just &lt;code>git clone&lt;/code> everything we&amp;rsquo;ve done here.
See you next time.&lt;/p></content></item><item><title>When should you choose C++ as your starting language?</title><link>https://steelph0enix.github.io/posts/choosing-first-language/</link><pubDate>Tue, 14 Nov 2023 00:00:00 +0000</pubDate><guid>https://steelph0enix.github.io/posts/choosing-first-language/</guid><description>&lt;p>When I&amp;rsquo;m lurking through the internet, I often see posts asking about &amp;ldquo;what language should I pick as a beginner???&amp;rdquo;. As someone who struggled a lot with this choice, and ultimately picked C++ (for reasons that made no real sense—but, of course, I didn&amp;rsquo;t know that back then), I think I can say a few words about this specific choice and how it can affect the learning process of an individual.&lt;/p></description><content>&lt;p>When I&amp;rsquo;m lurking through the internet, I often see posts asking about &amp;ldquo;what language should I pick as a beginner???&amp;rdquo;. As someone who struggled a lot with this choice, and ultimately picked C++ (for reasons that made no real sense—but, of course, I didn&amp;rsquo;t know that back then), I think I can say a few words about this specific choice and how it can affect the learning process of an individual.&lt;/p>
&lt;h2 id="its-a-trap">It&amp;rsquo;s a trap!&lt;/h2>
&lt;p>TL;DR of this post - it&amp;rsquo;s usually not a good idea to pick C++ as your starting language. After working with C++ for a long time, and tasting many different programming languages, I feel like C++ is a convoluted mess taped together using subpar-quality duct tape, somehow still holding on, maybe even going in a relatively good direction with recent changes, but certainly not good enough for a beginner to learn the programming principles on at its current state.&lt;/p>
&lt;h2 id="why-would-you-even-want-to-do-that">Why would you even want to do that?&lt;/h2>
&lt;p>Excellent question! In most cases, I hear these specific arguments, trying very hard to justify picking C++ as a starter:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>It&amp;rsquo;s very fast!&lt;/strong>—very common misconception. &lt;strong>Languages are not inherently fast nor slow&lt;/strong>. Sure, some languages can be &lt;em>parsed&lt;/em> or &lt;em>interpreted&lt;/em>, faster than others, but it does not imply that a &lt;strong>program&lt;/strong> written in language A will &lt;em>always&lt;/em> be faster than a program written in language B, or vice versa. Surely, that can be a thing - we absolutely can write a program in C++ that will be faster than an equivalent program in Python or Java, but &lt;strong>it works both ways!&lt;/strong> A good practice is asking yourself &lt;em>&amp;ldquo;why?&amp;rdquo;&lt;/em> and &lt;em>&amp;ldquo;when?&amp;rdquo;.&lt;/em> Why a program written in language &lt;code>A&lt;/code> may be faster/slower than an equivalent program written in language &lt;code>B&lt;/code>? And at what scale does it become meaningful to performance? But that&amp;rsquo;s not something that a newbie should care about. Generally speaking, you should NOT care about &amp;ldquo;language performance&amp;rdquo; as a beginner, as it&amp;rsquo;s one of the last things you&amp;rsquo;ll have to worry about when learning programming. In the beginning, the real performance will lie mostly in data structures and algorithms used in your code, not the language choice.&lt;/li>
&lt;li>&lt;strong>Learning C++ teaches you low-level concepts, like pointers and manual memory management!&lt;/strong> It can, but there&amp;rsquo;s a biiiiiig &lt;strong>but&lt;/strong>. &lt;strong>You don&amp;rsquo;t have to know these low-level concepts to write software and learn programming.&lt;/strong> Every day thousands of programmers write perfectly fine and working code without even knowing what a pointer is, or how to manually manage the memory. It&amp;rsquo;s not something that you absolutely must know in order to write working code. It&amp;rsquo;s not something that may ever be useful for you. I certainly agree with people saying that knowing how pointers and manual memory management work can be useful in many situations, but for a beginner—it&amp;rsquo;s certainly not a must-know, as even in C++ you usually don&amp;rsquo;t want to manually manage raw pointers and memory allocations.&lt;/li>
&lt;li>&lt;strong>Game programming is done in C++&lt;/strong>—usually paired with the &amp;ldquo;much performance&amp;rdquo; argument—is also invalid, as there are plenty of very popular game engines that provide a much more newbie-friendly approach to game development. Check out &lt;a href="https://godotengine.org/">Godot&lt;/a> as one of the best examples, since it has both its own toolset that uses its own GDScript language, but also can be used with other languages and tools if you prefer that approach or you already know them and don&amp;rsquo;t want to learn new stuff. I know there are plenty of people doing gamedev in C++, and they do have a point in that—being relatively close to the hardware it&amp;rsquo;s not the worst choice of a language, but I still strongly believe that it&amp;rsquo;s an overkill if you want to learn &lt;strong>&amp;ldquo;programming&amp;rdquo;&lt;/strong> and &lt;strong>&amp;ldquo;game development&amp;rdquo;&lt;/strong>, as it carries a heavy bag of &lt;strong>&amp;ldquo;having to learn C++&amp;rdquo;&lt;/strong> before learning the stuff you really wanna learn. Don&amp;rsquo;t forget that, if you&amp;rsquo;re thinking that it&amp;rsquo;ll be a breeze and something you&amp;rsquo;ll do &amp;ldquo;on the side&amp;rdquo;, &lt;em>you may lack some crucial knowledge&lt;/em>. Of course, if you wanna do it &amp;ldquo;raw&amp;rdquo; and really refuse to use existing game engines, you absolutely can do it in any language that has &lt;em>some&lt;/em> kind of multimedia API&amp;hellip; so, basically, the most available and popular languages for sane people (brainfuck btfo). If you don&amp;rsquo;t believe me, &lt;a href="https://store.steampowered.com/app/824600/HROT/">this game was written in Pascal, from scratch, in 2017. And it runs great!&lt;/a>&lt;/li>
&lt;li>&lt;strong>I know some C, so C++ is just an extension, so I&amp;rsquo;ll learn it.&lt;/strong> It is not, and thinking that way will force you onto a path from which it&amp;rsquo;s very hard to return, as I have witnessed multiple times with my own eyes. Beware of thinking about C++ as an addition to C, as it&amp;rsquo;s just as foolish as thinking about a dragon being an addition to his precious mountain of gold. &lt;a href="https://en.cppreference.com/w/cpp/language/initialization">You WILL get burned&lt;/a>. &lt;a href="https://en.cppreference.com/w/cpp/language/lifetime">You WILL witness horrors beyond your comprehension&lt;/a>. &lt;a href="https://en.cppreference.com/w/cpp/language/value_category">That last part is also true when talking about C++ in general&lt;/a>. Compare the following articles with their C counterparts (the link is at the bottom of the reference page) and rethink your life choices.&lt;/li>
&lt;/ul>
&lt;p>That list may become longer with time.&lt;/p>
&lt;h2 id="so-what-are-the-alternatives">So, what are the alternatives?&lt;/h2>
&lt;p>Another excellent question! Loving the audience today.&lt;/p>
&lt;p>Obviously, I will have to answer according to my own opinions and beliefs, however, I will also try to explain why I think like I think I think. I think that will make it easier to, at least, continue your research. Remember - t̴h̴e̶ ̷r̷e̵s̷e̴a̸r̸c̴h̸ ̸n̷e̷v̸e̷r̷ ̷e̵n̵d̴s̸.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Python:&lt;/strong> Some people will tell you that Python sucks. Some people will tell you that Python is not a good language for beginners. Some people are wrong and cannot comprehend the simplicity and beauty of whitespace-defined syntax. I, however, will tell you that I love Python because it rarely disappoints me. I need to write a simple tool that does some stuff automatically, and I want it to work everywhere? Python is my guy. Its long support, big community, package manager, and existing tools are making it very hard for me not to recommend this language as a starter. The syntax is sure &lt;em>very specific&lt;/em>, some might even say &lt;em>exotic&lt;/em>, but after working with it for a while it annoys me only &lt;em>sometimes&lt;/em> when I have to move a block of code to a different place and manually fix every indent. And it even has type hints and easy-to-use tools that&amp;rsquo;ll make sure you adhere to typing rules, which some people may find very useful. For a beginner though, you&amp;rsquo;ll most likely want to focus on the vast &lt;a href="https://pypi.org/">PyPI&lt;/a> repository to write some useful stuff for yourself. Its &lt;a href="https://docs.python.org/3/index.html">documentation&lt;/a> is also relatively decent and even has a tutorial to get you started, but if you&amp;rsquo;re completely new to the IT and/or programming world I&amp;rsquo;d recommend looking for a first-time tutorial more suited to your level of knowledge, as the &amp;ldquo;official&amp;rdquo; one is not very newbie-oriented.&lt;/li>
&lt;li>&lt;strong>JavaScript&lt;/strong> (or maybe even &lt;strong>TypeScript?&lt;/strong>): It&amp;rsquo;s a very simple gateway to the vast world of web applications, but not only, as you can plug JavaScript almost anywhere nowadays&amp;hellip; I&amp;rsquo;m personally not a fan of this language, but from what I&amp;rsquo;ve seen - people who start from JavaScript have a certain &amp;ldquo;freedom&amp;rdquo; of being able to use this language anywhere they want, which is nice if you don&amp;rsquo;t know what you exactly want to do yet, but usually comes with additional baggage of tools for creating user interfaces - HTML and CSS being the usual ones, but most likely also paired with some fancy frameworks and other funky stuff that—according to many web developers—are needed to write functional pages, for which &lt;a href="https://htmx.org/">more elegant technologies from simpler times&lt;/a> may be more than enough). I&amp;rsquo;m getting out of topic though, I endorse JavaScript as a starter way more than I endorse C++, but be aware of many pitfalls this language comes with. &lt;em>Which can be said about basically any language, but the Big Dev won&amp;rsquo;t tell you about that!&lt;/em>&lt;/li>
&lt;li>&lt;strong>Kotlin:&lt;/strong> If you want to learn Java for whatever god-forsaken reason you may have, don&amp;rsquo;t learn Java—learn Kotlin instead! If you wanna make Android apps and you&amp;rsquo;d rather use official tools than some webdev mumbo-jumbo, Kotlin is your guy! Its Java legacy makes it a reasonably good starting language, as you have both a nice, modern language with nice, modern syntax and useful features that are simply a joy to work with, and a vast repository of libraries, some written long before Kotlin even existed. I haven&amp;rsquo;t worked much with this language, but I enjoyed almost every moment of it.&lt;/li>
&lt;li>&lt;strong>C:&lt;/strong> Yeah, let&amp;rsquo;s put it on the list, why not. &lt;strong>You wanna have a very close relationship with your hardware? Here&amp;rsquo;s your language. Don&amp;rsquo;t tell me I didn&amp;rsquo;t warn you though.&lt;/strong>
&lt;img src="https://steelph0enix.github.io/img/this_machine_does_not_know_the_difference.png" alt="don&amp;#39;t say i didn&amp;#39;t warn you" class="center" />
In all fairness, C is certainly lacking—real generics, for example, but that doesn&amp;rsquo;t stop people from using it even right now. As a language to get the hang of programming and &amp;ldquo;how does stuff work under the hood&amp;rdquo; it&amp;rsquo;s a great choice, as a language to write working applications without having to create a whole universe beforehand&amp;hellip; not so much, unless you work on something that does not require an entire universe to run, but I will get to it soon. I promise.&lt;/li>
&lt;/ul>
&lt;p>This list is by no means comprehensive. There are a lot of languages that are deemed &amp;ldquo;reasonable for beginners&amp;rdquo; that are not on this list, just because I either never worked with them, or worked so long ago that I cannot assume that my knowledge is still applicable to its current state (e.g. C#, which I&amp;rsquo;d like to say something about, but I really can&amp;rsquo;t).&lt;/p>
&lt;p>Notice that my focus here is to recommend a language that will be relatively easy to learn and easy to use, with a big community and lots of resources to learn from—C++ is lacking in many of these aspects, which makes learning modern C++ the &amp;ldquo;correct&amp;rdquo; way surprisingly hard. And, to be fair, it makes doing &lt;em>anything&lt;/em> in C++ unreasonably convoluted and painful. Ever tried to set up a C++ project from scratch with automatic unit and integration tests? I did. And failed. Not because it&amp;rsquo;s impossible, but because I simply rather choose a language that supports that stuff out-of-the-box (unit testing, at least). And I&amp;rsquo;ve seen projects that do have this stuff in C++, and I&amp;hellip; uhh&amp;hellip; I&amp;rsquo;d rather avoid having to jump through the hoops of CMake and similar tools in order to have stuff that&amp;rsquo;s relatively easy to do in other languages &lt;em>usually without having to learn how to use 3rd party tools&lt;/em>. And there&amp;rsquo;s C, as a direct C++ alternative, and I&amp;rsquo;ll explain why it&amp;rsquo;s here—because it&amp;rsquo;s certainly not for the same reason as other languages on this list.&lt;/p>
&lt;h2 id="when-it-actually-makes-sense-to-go-c-head-first">When it actually makes sense to go C++ head-first&lt;/h2>
&lt;p>As with any rules, there are exceptions, and this includes my &amp;ldquo;No C++ for beginners allowed&amp;rdquo; rule.&lt;/p>
&lt;p>The most obvious exception for me is targeting embedded programming as &lt;em>the thing you want to do&lt;/em>. And it&amp;rsquo;s also the reason why C is on my list - because C is &lt;em>much&lt;/em> simpler than C++, it might be a better idea to start with that instead. But if you wanna use Arduino, like most beginners nowadays, sticking only to C makes little sense as there are powerful and relatively easy-to-use features of C++ that you already have access to (and might have to use, depending on the libraries you work with). Sure, you may not have the standard library and its fancy features &lt;em>(and I&amp;rsquo;d argue that it&amp;rsquo;s a good thing)&lt;/em>, but you still have the power of templates, lambda expressions, constexpr, and many more cool features. So yeah, if you wanna tinker with hardware then learning C++ &lt;em>might&lt;/em> be unavoidable for you.&lt;/p>
&lt;p>But only &lt;em>might&lt;/em>—if you ignore the existence of Arduino framework—not the hardware, the hardware is its own thing and it&amp;rsquo;s just a &amp;ldquo;brand&amp;rdquo; name - you may just stick to pure C and write the code from scratch directly for the microcontroller you have, or using vendor-provided tools (like STM32CubeIDE for ST microcontrollers). However, this is substantially harder as it usually requires you to actually read the documentation of hardware you&amp;rsquo;re working on. It&amp;rsquo;s really not something for faint-hearted developers that would rather stick to copying and pasting random pieces of code from the internet or ChatGPT, with the hope that it&amp;rsquo;ll solve all their issues.&lt;/p>
&lt;p>Another obvious scenario is &amp;ldquo;you have to learn it because of school/university/work/side-project I&amp;rsquo;m doing with some C++ developers&amp;rdquo;. Or you may just be very, very, very fiercely and weirdly focused on learning C++. For that, I have no cure. I just have a tip: &lt;em>try to stick to modern sources and don&amp;rsquo;t get sucked by outdated, pre-C++11 tutorials and books that have questionable reputation&lt;/em>. There are many. &lt;a href="https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list/">There are curated lists of sources from which you can start your journey&lt;/a>.&lt;/p>
&lt;p>And I can&amp;rsquo;t really think about any other scenarios where C++ would &lt;em>really&lt;/em> make sense. Some time ago I&amp;rsquo;d say that OS-dev is a good niche for that, but you can do OS-dev in C as well, and there&amp;rsquo;s Rust for the cool kids that sit on the back of the bus—much saner choice than C++ if you ask me. Borrow checker may go hard, but &lt;em>it&amp;rsquo;s there and will never leave you&lt;/em>. Do you want the same commitment from C or C++? Configure it yourself. And it won&amp;rsquo;t be half as good as what Rust does for your code. Graphics programming and game development? Yea, sure, maybe. Maybe if you are new in this world, still with hopes for a better tomorrow, still young and innocent, it will sound like a good choice - and you may even walk through this path relatively unscathed. But then again, you may not. I&amp;rsquo;d still look at alternatives first, &lt;em>just because lots of people do it in C++, doesn&amp;rsquo;t mean that you have to do it in C++ too&lt;/em>.&lt;/p>
&lt;p>I may sound like a big C++ hater, but to be honest, it earned it. I&amp;rsquo;ve spent years with that language, and half of this time I&amp;rsquo;ve spent fighting with it. Do I regret it? I regret not jumping this ship earlier, but I&amp;rsquo;d say that lots of things I&amp;rsquo;ve learned via C++ I&amp;rsquo;ve found useful at some point. Could I learn those things while focusing on different languages? Some of them, yes, but not all of them. Would I go a different path if I could start my journey again? Fuck yes, I&amp;rsquo;d jump straight to Python or webdev, but considering my areas of interests and expertise I&amp;rsquo;d end up with C++ at some point anyway.&lt;/p>
&lt;p>I&amp;rsquo;m also not saying that C++ should &lt;em>never&lt;/em> be learned or used. It&amp;rsquo;s got its things going, and with a certain amount of experience in general programming and with setting up software projects, it&amp;rsquo;s possible to work with it. But you need that experience and knowledge first, and head-slamming into a C++ wall may not help you get that knowledge in comparison with other available methods.&lt;/p></content></item><item><title>The future of this blog</title><link>https://steelph0enix.github.io/posts/the-future-of-this-blog/</link><pubDate>Sat, 28 Oct 2023 00:00:00 +0000</pubDate><guid>https://steelph0enix.github.io/posts/the-future-of-this-blog/</guid><description>&lt;h2 id="the-flashback">The flashback&lt;/h2>
&lt;p>First, quick recap of my last year (or two), to provide some context for this post. This will also be kind-of-a-vent, completely out of topic of this blog, so if you don&amp;rsquo;t care about the drama in my life feel free to scroll right to the end for conclusions.&lt;/p>
&lt;p>I&amp;rsquo;m currently 25 years old. I&amp;rsquo;ve had a pretty good relationship with my parents, although their relationship with each other was pretty rocky, especially in the last few years.&lt;/p></description><content>&lt;h2 id="the-flashback">The flashback&lt;/h2>
&lt;p>First, quick recap of my last year (or two), to provide some context for this post. This will also be kind-of-a-vent, completely out of topic of this blog, so if you don&amp;rsquo;t care about the drama in my life feel free to scroll right to the end for conclusions.&lt;/p>
&lt;p>I&amp;rsquo;m currently 25 years old. I&amp;rsquo;ve had a pretty good relationship with my parents, although their relationship with each other was pretty rocky, especially in the last few years.&lt;/p>
&lt;p>Last year I&amp;rsquo;ve learned that my Mom had cancer. Hidden in gallbladder.&lt;/p>
&lt;p>When I learned that, I&amp;rsquo;ve been working at ST, doing my Master&amp;rsquo;s degree, and also working on a telemedicine-related side-project at the same time. The amount of work and stress, along with the situation at my family&amp;rsquo;s house, made me quit my work at ST so I could focus on finishing my degree and helping Mom and my brother. We had a lot of stuff to do.&lt;/p>
&lt;p>At first, everything seemed relatively okay, as the gallbladder was completely removed, and after the first chemo everything seemed clean and we had hope that it was something she can live with for, at least, few more years.&lt;/p>
&lt;p>Unfortunately, the tumors returned few months later - this time on the liver, and they grew very fast and couldn&amp;rsquo;t be removed surgically - at least not completely.&lt;/p>
&lt;p>Mom died 16th January, 2023. She knew it was coming, she prepared us for it best she could, and I will be forever thankful for everything she has done for us.&lt;/p>
&lt;p>Since then, I&amp;rsquo;ve been trying to move on. I&amp;rsquo;ve been working at N7 for some time and it&amp;rsquo;s been a good distraction so far - and I generally like this job and the people there, so I&amp;rsquo;m not planning to quit any time soon. I&amp;rsquo;ve dropped my science-club related duties, and focused on work and staying mostly sane, most of the time at least.&lt;/p>
&lt;p>For the last few weeks, I had a popup in my mind, constantly appearing and telling me to do some stuff and write about it. I&amp;rsquo;ve also had an urge to do some makeovers in my life and become a productive member of society. Thanks to the privilege I have, as someone who&amp;rsquo;s being paid for creating new spells (writing code) using my elaborate sorcerer&amp;rsquo;s setup (split ortholinear keyboard with RGB strip), and who doesn&amp;rsquo;t have to wonder if he&amp;rsquo;ll have a place to live next month, I&amp;rsquo;ve decided that one of the productive things I can do is writing blog posts to force myself to learn and experience new things, because otherwise I don&amp;rsquo;t have enough motivation to even pick up the stick that I&amp;rsquo;m going to be poking the stuff I want to experience with.&lt;/p>
&lt;p>&lt;a href="https://www.doomworld.com/forum/topic/134292-myhousewad/">Making stuff have helped others cope with loss of the loved ones, so i guess it&amp;rsquo;s worth trying&lt;/a>.&lt;/p>
&lt;h2 id="conclusions">Conclusions?&lt;/h2>
&lt;p>I&amp;rsquo;m planning to be much more active here.&lt;/p>
&lt;p>I&amp;rsquo;ve had lots of ideas for projects and blog posts recently, but due to the fact that my time is finite and I only have few hours per day to do this stuff (in contrast to more than dozen I had, back when I was broke and had no work - hefty price to pay), I need to prioritize and choose something to pour my attention into, one at a time.&lt;/p>
&lt;p>From the top of my (unordered) stack, I can list some of those:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>HTMX&lt;/strong> - &lt;a href="https://twitter.com/steel_ph0enix/status/1716376017340604882">I&amp;rsquo;ve bought a mug&lt;/a>, so might as well learn it. Since I have almost no experience in web or frontend development, it should be a fun ride. The plan is to make a dashboard for my RaspberryPi to control the LED strip from a browser.&lt;/li>
&lt;li>&lt;strong>C/C++ project setup in Meson&lt;/strong> - i, uhh, probably will have to go back to C/C++ at some point so I&amp;rsquo;d rather be ready. I poked Meson with a stick already, and I&amp;rsquo;ve even made some working stuff, but it&amp;rsquo;s not quite finished yet. When I will finish it, I will describe it here.&lt;/li>
&lt;li>&lt;strong>Embedded-related stuff&lt;/strong> - Most of my time I&amp;rsquo;ve spent programming in the last few years were spent on embedded programming. So I guess I can pretend I&amp;rsquo;m qualified to talk about it now. I think that a series on implementing (and using) abstraction over microcontroller&amp;rsquo;s hardware could be nice, considering I&amp;rsquo;ve been developing BSP/HAL libraries for most part of the last year.&lt;/li>
&lt;/ul>
&lt;p>But first, i&amp;rsquo;ll probably do a cleanup of my NeoVim config, as it broke recently (again). Maybe i&amp;rsquo;ll write a post on that too.&lt;/p>
&lt;p>I&amp;rsquo;ve also been considering making some video content, as i personally find this form more appealing, but looking at YouTube and their latest ideas constantly makes me reconsider that. The true reason i haven&amp;rsquo;t done any video content, of course, is lack of motivation, but we don&amp;rsquo;t talk about it here, it&amp;rsquo;s easier to blame The Corporation.&lt;/p>
&lt;p>That&amp;rsquo;s basically it for now. I&amp;rsquo;m slowly getting up. I need to create and get used to a new daily routine. And everything will be fine, eventually. Hopefully, at some point in the future, this blog will not be a graveyard of few outdated and mediocre-quality posts, but rather an actual source of knowledge for people looking for it.&lt;/p></content></item><item><title>VSCode CubeMX Project Setup</title><link>https://steelph0enix.github.io/posts/vscode-cubemx-setup/</link><pubDate>Thu, 16 Sep 2021 00:00:00 +0000</pubDate><guid>https://steelph0enix.github.io/posts/vscode-cubemx-setup/</guid><description>&lt;p>Up until now, i&amp;rsquo;ve used STM32CubeIDE with STM32CubeMX as my primary development tools for STM32 microcontrollers. CubeIDE has lots of useful features available out-of-the-box. However, it also suffers from pretty feelable bloat, caused by the fact that it&amp;rsquo;s heavily modified Eclipse IDE with lots of plugins that pile up pretty quickly. It also lacks some features that i&amp;rsquo;m used to having in more&amp;hellip; &amp;ldquo;civilized&amp;rdquo; IDE&amp;rsquo;s (dark theme which is not hurting the eyes with contrast by default? better Git integration? and probably lots of other, smaller thingies). So, i&amp;rsquo;ve decided to try something new and the first obvious choice was Visual Studio Code.&lt;/p></description><content>&lt;p>Up until now, i&amp;rsquo;ve used STM32CubeIDE with STM32CubeMX as my primary development tools for STM32 microcontrollers. CubeIDE has lots of useful features available out-of-the-box. However, it also suffers from pretty feelable bloat, caused by the fact that it&amp;rsquo;s heavily modified Eclipse IDE with lots of plugins that pile up pretty quickly. It also lacks some features that i&amp;rsquo;m used to having in more&amp;hellip; &amp;ldquo;civilized&amp;rdquo; IDE&amp;rsquo;s (dark theme which is not hurting the eyes with contrast by default? better Git integration? and probably lots of other, smaller thingies). So, i&amp;rsquo;ve decided to try something new and the first obvious choice was Visual Studio Code.&lt;/p>
&lt;blockquote>
&lt;p>The second choice would be CLion, which has pretty decent CubeMX project support out-of-the-box, but it is sometimes a little bit buggy with debugging the projects&amp;hellip; or at least it was few months ago, when i tried it. Maybe it got fixed.&lt;/p>
&lt;/blockquote>
&lt;p>So, after few hours of tinkering and lurking around for guides and plugins, i&amp;rsquo;ve found my setup. Most of the config i&amp;rsquo;ve taken from two great videos by &lt;a href="https://www.youtube.com/channel/UCuigr_BEzX1g3Qvwq5QjPXg">Embedded Geek&lt;/a> (first one is &lt;a href="https://www.youtube.com/watch?v=PxQw5_7yI8Q">here&lt;/a>, second one - &lt;a href="https://www.youtube.com/watch?v=xaC5oWwzOt0">here&lt;/a>), but i&amp;rsquo;ve changed some things since i&amp;rsquo;m not using the exact same VSCode config as he does (which, actually, makes things easier and less painful to configure for me). I also made sure that it should work on both Windows and Linux (and &lt;em>probably&lt;/em> MacOS).&lt;/p>
&lt;blockquote>
&lt;p>If you are very lazy, you can use &lt;a href="https://marketplace.visualstudio.com/items?itemName=bmd.stm32-for-vscode">&lt;code>stm32-for-vscode&lt;/code>&lt;/a> plugin instead of this guide. It should do most of the work described there automatically. However, it uses default IntelliSense engine, and as i prefer clangd and knowing how to configure the project from scratch (so when something goes wrong, i know how to fix it), i still created this guide. &lt;em>I&amp;rsquo;ve used it in the past, but after trying it again and comparing with setup made using this guide i found out it doesn&amp;rsquo;t offer better experience in the long run&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;p>Anyway, let&amp;rsquo;s get to it.&lt;/p>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>From hardware side, you need to have an STM32 board, and a debugger that&amp;rsquo;s compatible with it. For this guide, i&amp;rsquo;m using Nucleo-G474RE, with STM32G474RET6 microcontroller, and STLink v3 onboard. The exact choice of MCU and debugger doesn&amp;rsquo;t matter, as this setup will work with any STM32 MCU and every debugger supported by OpenOCD. Also, you&amp;rsquo;ll need an USB cable to connect the board to your PC.
&lt;strong>If you&amp;rsquo;re using ST-Link, update it&amp;rsquo;s firmware before proceeding. You can do it with &lt;a href="https://www.st.com/en/development-tools/stsw-link007.html">this official tool&lt;/a>&lt;/strong>. Free ST account is required to download it.&lt;/p>
&lt;p>From software side, these tools are required:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.st.com/en/development-tools/stm32cubemx.html">&lt;strong>STM32CubeMX&lt;/strong>&lt;/a> - STM32 MCU helper and code generator, simplifies the project generation and MCU initialization to maximum - downloadable for free from official ST site &lt;a href="https://www.st.com/en/development-tools/stm32cubemx.html">here&lt;/a>. Requires free ST account to download. &lt;em>PSA for HAL-haters: you don&amp;rsquo;t have to use HAL even if you generate the code using CubeMX - read about LL libraries, you can use them too, and they are as close to the metal as it&amp;rsquo;s sanely possible. I&amp;rsquo;ll put a quick info about it at the end of this guide, as a bonus.&lt;/em>&lt;/li>
&lt;li>&lt;a href="https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads">&lt;strong>ARM-GCC toolchain&lt;/strong>&lt;/a> (&lt;code>arm-none-eabi-gcc&lt;/code> to be specific, along with all the other GCC tools, and GDB). Linux users should have latest version in the repositories - make sure you also install &lt;code>arm-none-eabi-newlib&lt;/code> and &lt;code>arm-none-eabi-binutils&lt;/code> if these are separate packages. Windows users can either use their preferred package manager, or download the latest toolchain manually from &lt;a href="https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads">official ARM site&lt;/a> - MacOS and Linux versions are available there too, in case you don&amp;rsquo;t have one in repositories. &lt;strong>Just make sure it&amp;rsquo;s in your OS PATH variable before proceeding - open terminal and try running &lt;code>arm-none-eabi-gcc --version&lt;/code>. If it fails, add the toolchain&amp;rsquo;s &lt;code>bin&lt;/code> directory to PATH.&lt;/strong>&lt;/li>
&lt;li>&lt;a href="https://openocd.org/pages/getting-openocd.html">&lt;strong>OpenOCD&lt;/strong>&lt;/a> - we&amp;rsquo;ll use it to debug and flash the firmware on our microcontroller. Get it from your package manager, or via one of the unofficial distributions listed &lt;a href="https://openocd.org/pages/getting-openocd.html">here&lt;/a>. Or you can also build it from source, which is fairly easy. &lt;strong>Make sure it&amp;rsquo;s in PATH too - check if &lt;code>openocd --version&lt;/code> prints the info, if not - add the directory with it&amp;rsquo;s executable to PATH.&lt;/strong>&lt;/li>
&lt;li>&lt;a href="https://code.visualstudio.com/">&lt;strong>Visual Studio Code&lt;/strong>&lt;/a> - obviously. If you are new to this editor, i strongly suggest to check out &lt;a href="https://steelph0enix.github.io/posts/vscode-cpp-setup/">my previous guide&lt;/a> about configuring it for C/C++ projects with CMake. We won&amp;rsquo;t use CMake here (although it is possible to do that with a little bit of help from &lt;a href="https://github.com/ObKo/stm32-cmake">&lt;code>stm32-cmake&lt;/code>&lt;/a> repository), but i will use &lt;code>clangd&lt;/code>, as i prefer this language server over the default one, and it&amp;rsquo;s easier to set-up. So, if you want to stay with default language server, keep in mind that you will have to configure include paths manually, which is painful and i won&amp;rsquo;t describe it here. &lt;em>If you have no idea what i&amp;rsquo;m writing about here - just follow this guide and install all the plugins listed below&lt;/em>. I strongly recommend to use these plugins for VSCode:
&lt;ul>
&lt;li>&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools">&lt;strong>C/C++&lt;/strong>&lt;/a> - for general C and C++ language support - &lt;strong>required&lt;/strong>;&lt;/li>
&lt;li>&lt;a href="https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug">&lt;strong>Cortex-Debug&lt;/strong>&lt;/a> - for ARM Cortex-M debugging - &lt;strong>required&lt;/strong>;&lt;/li>
&lt;li>&lt;a href="https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd">&lt;strong>clangd&lt;/strong>&lt;/a> - alternative language server that usually works better than default one, and has easier and more robust config. Also features configurable code auto-formatting. If you don&amp;rsquo;t know what language server does, or you haven&amp;rsquo;t played with default one yet - install and use &lt;code>clangd&lt;/code>, you can find basic setup instructions &lt;a href="https://steelph0enix.github.io/posts/vscode-cpp-setup/#bonus-clangd-setup">here&lt;/a>, and i&amp;rsquo;ll guide you through configuration for this project. If you know what you&amp;rsquo;re doing - have fun.&lt;/li>
&lt;li>&lt;a href="https://marketplace.visualstudio.com/items?itemName=dan-c-underwood.arm">&lt;strong>ARM&lt;/strong>&lt;/a> - for ARM assembly language support. Useful when lurking around *.s files - optional;&lt;/li>
&lt;li>&lt;a href="https://marketplace.visualstudio.com/items?itemName=ZixuanWang.linkerscript">&lt;strong>LinkerScript&lt;/strong>&lt;/a> - for linker script language support. Useful when lurking around *.ld files - optional;&lt;/li>
&lt;li>And also plugins from my list of recommended C/C++ plugins, which you can find &lt;a href="https://steelph0enix.github.io/posts/vscode-cpp-setup/#vscode-essential-plugins">here&lt;/a>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;a href="https://www.st.com/en/development-tools/stsw-link009.html">&lt;strong>ST-Link (or your preferred debugger) drivers&lt;/strong>&lt;/a> - if you already have &lt;a href="https://www.st.com/en/development-tools/stm32cubeide.html">STM32CubeIDE&lt;/a> or &lt;a href="https://www.st.com/en/development-tools/stm32cubeprog.html">STM32CubeProgrammer&lt;/a> installed on your system, you should already have them. If that&amp;rsquo;s not the case, then either install STM32CubeProgrammer to get all the drivers that you&amp;rsquo;d need for most STM32-compatible debuggers, or download and install the drivers for your specific debugger and OS manually. &lt;strong>Just make sure that your debugger is detected correctly by your operating system before proceeding&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;p>And that should be just about it. If you have everything set-up, can get to work on our project.&lt;/p>
&lt;h2 id="creating-a-project">Creating a project&lt;/h2>
&lt;p>Open up STM32CubeMX and make a new project. I&amp;rsquo;ll assume that you already have basic knowledge of STM32CubeMX usage, so you can create a project for your board. After that, set up your peripherals (i&amp;rsquo;ll just use onboard LED which is set up by default to test stuff out), make sure the debug is enabled (System Core -&amp;gt; SYS -&amp;gt; Debug), clocks are configured correctly (Clock Configuration tab on the top of the window), and go to Project Manager tab. Fill up the project name and location, and set &lt;strong>Toolchain/IDE&lt;/strong> to &lt;strong>Makefile&lt;/strong>. Save the project after that.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/cubemx-config-1.png" alt="cubemx-project-config-1">&lt;/p>
&lt;p>Next, i recommend going to &lt;strong>Code Generator&lt;/strong> tab on the left and selecting all the checkboxes in &lt;strong>Generated files&lt;/strong> section. I do it as force of habit, to have a bit better organized project.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/cubemx-config-2.png" alt="cubemx-project-config-2">&lt;/p>
&lt;p>When you&amp;rsquo;re done with CubeMX config, &lt;strong>Generate the code&lt;/strong> and open the directory with the project in Visual Studio Code.&lt;/p>
&lt;h2 id="setting-up-the-project-in-vscode">Setting up the project in VSCode&lt;/h2>
&lt;h3 id="building-the-project">Building the project&lt;/h3>
&lt;p>Now, time for the fun part. First, we&amp;rsquo;ll make sure we can build the project. Open integrated terminal (&lt;code>Ctrl+` &lt;/code> to open existing one, or &lt;code>Ctrl+Shift+` &lt;/code> to create a new one) and run &lt;code>make&lt;/code>. You should see some output - the commands ran by Make, and size of output binary - and a new directory called &lt;code>build&lt;/code> should appear. The project directory should look like this now:&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/post-build-files-tree.png" alt="post-build-file-tree">&lt;/p>
&lt;blockquote>
&lt;p>If the build failed, read the error message, check if you have all required prerequisites, and look around the internet for support - most of basic issues have been solved by someone already, and solutions are out there.&lt;/p>
&lt;/blockquote>
&lt;p>The &lt;code>build&lt;/code> directory should contain a lot of object (&lt;code>*.o&lt;/code>) , listing (&lt;code>*.lst&lt;/code>), and &lt;code>*.d&lt;/code> files, along with few binaries with the same name as your project, and also a &lt;code>*.map&lt;/code> file, which contains the memory map of whole program.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/post-build-files-out.png" alt="post-build-file-out">&lt;/p>
&lt;p>The only file here that&amp;rsquo;s currently interesting for us is the &lt;code>*.elf&lt;/code> file - this is the actual compiled binary we&amp;rsquo;ll flash to MCU using OpenOCD. &lt;code>*.bin&lt;/code> and &lt;code>*.hex&lt;/code> files are compiled binary files too, created out of the &lt;code>*.elf&lt;/code> file, but they are usually used in different scenarios (for example, &lt;code>*.bin&lt;/code> file can easily be used by custom bootloaders, as it&amp;rsquo;s basically raw compiled code, stripped out of unnecessary stuff).&lt;/p>
&lt;h3 id="configuring-the-language-server-clangd">Configuring the language server (clangd)&lt;/h3>
&lt;p>If you&amp;rsquo;re able to build project successfully, then it&amp;rsquo;s time to set up our language server. Clangd requires &lt;code>compile_commands.json&lt;/code> file to work properly - this file contains translated output from your build system, that tells Clangd how the project is built. Thanks to that, Clangd can automatically detect the configuration you&amp;rsquo;re using and you doesn&amp;rsquo;t have to manually set anything up, unlike in default language server, where you&amp;rsquo;d have to set the include directories, flags and toolchain manually in JSON file.&lt;/p>
&lt;p>There are few ways to generate &lt;code>compile_commands.json&lt;/code>. As i&amp;rsquo;ve described in &lt;a href="https://steelph0enix.github.io/posts/vscode-cpp-setup/#bonus-clangd-setup">my previous blog post&lt;/a>, CMake generates this file automatically. However, we are not using CMake, so we have to use external tool for that. I&amp;rsquo;ve know of two, that i managed to get working without issues: &lt;a href="https://github.com/rizsotto/Bear">&lt;strong>Bear&lt;/strong>&lt;/a>, which supposedly works only on Linux, but apparently there is a version available in &lt;code>winget&lt;/code> repository, and &lt;a href="https://github.com/nickdiego/compiledb">&lt;strong>compiledb&lt;/strong>&lt;/a> which should work anywhere, and is written in Python, so you need it installed to use it. &lt;strong>Note: install &lt;code>compiledb&lt;/code> IN USER DIRECTORY (&lt;code>python3 -m pip install --user compiledb&lt;/code>), because it may not work when installed globally due to permission issues! If you encounter permission-related issues, make sure all of it&amp;rsquo;s dependencies are installed in user directories too! And make sure it&amp;rsquo;s in your PATH variable! Same goes for Bear.&lt;/strong>&lt;/p>
&lt;p>The usage of these tools is simple: you run them with your build command, so they take it&amp;rsquo;s output and convert it to Clangd&amp;rsquo;s &lt;code>compile_commands.json&lt;/code>. In case of Bear, you also have to make clean-build (run &lt;code>make clean&lt;/code> before running it), which makes it troublesome to integrate with our setup, so i&amp;rsquo;m going to use &lt;code>compiledb&lt;/code> here.&lt;/p>
&lt;p>To build the project and generate the &lt;code>compile_commands.json&lt;/code>, run &lt;code>compiledb make -j8&lt;/code>.&lt;/p>
&lt;blockquote>
&lt;p>Tip: You can tell &lt;code>make&lt;/code> to build using multiple threads with &lt;code>-j&lt;/code> argument. This will &lt;strong>significantly&lt;/strong> decrease compilation time. For example: &lt;code>make -j8&lt;/code> will build the program using 8 threads. Modify this argument according to your CPU capabilities (amount of cores and thread per core).&lt;/p>
&lt;/blockquote>
&lt;p>Compiledb will run &lt;code>make&lt;/code> and pass all the arguments to it, &lt;code>-j8&lt;/code> in our case. After running this command from the project root, the &lt;code>compile_commands.json&lt;/code> should appear there. Open any &lt;code>*.c&lt;/code> or &lt;code>*.h&lt;/code> file, or re-open, if you opened them already, and check if the language server works - you should now see no errors related to missing includes or unrecognized function, see the tooltips for code on hover, and have all the other language-server-related features working.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/clangd-working.png" alt="clangd-working">&lt;/p>
&lt;h4 id="configuring-standard-library-visibility">Configuring standard library visibility&lt;/h4>
&lt;p>After playing with &lt;code>clangd&lt;/code> a bit more in this project, i found out that even though project builds successfully when i use standard library, clangd doesn&amp;rsquo;t recognize it&amp;rsquo;s headers for some reason. I suppose it&amp;rsquo;s because the target platform is not the native one, so it doesn&amp;rsquo;t know where to look for headers. I&amp;rsquo;ve fixed it by adding include path with stdlib headers to &lt;code>C_INCLUDES&lt;/code> variable in Makefile, and clean-building the project.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-make" data-lang="make">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># C includes
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>C_INCLUDES &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span>-ICore/Inc &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span>-IDrivers/STM32G4xx_HAL_Driver/Inc &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span>-IDrivers/STM32G4xx_HAL_Driver/Inc/Legacy &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span>-IDrivers/CMSIS/Device/ST/STM32G4xx/Include &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span>-IDrivers/CMSIS/Include &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">-I/usr/arm-none-eabi/include&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="flashing-the-binary">Flashing the binary&lt;/h3>
&lt;p>We&amp;rsquo;ll create a rule in Makefile to flash our program to MCU. You could do this as VSCode task too, if you&amp;rsquo;d like, with slight modifications.
Open the &lt;code>Makefile&lt;/code>, and somewhere on the bottom, before EOF marker, add a new rule that will run OpenOCD:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-make" data-lang="make">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">flash&lt;/span>&lt;span style="color:#f92672">:&lt;/span> all
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> openocd -f interface/stlink.cfg -f target/stm32g4x.cfg -c &lt;span style="color:#e6db74">&amp;#34;program &lt;/span>&lt;span style="color:#66d9ef">$(&lt;/span>BUILD_DIR&lt;span style="color:#66d9ef">)&lt;/span>&lt;span style="color:#e6db74">/&lt;/span>&lt;span style="color:#66d9ef">$(&lt;/span>TARGET&lt;span style="color:#66d9ef">)&lt;/span>&lt;span style="color:#e6db74">.elf verify reset exit&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>If you are not using ST-Link, change the interface config file to proper one. Same applies to MCU config file - switch it to one that&amp;rsquo;s for MCU you&amp;rsquo;re working on.&lt;/strong> All the configuration files are stored in OpenOCD installation directory. If you&amp;rsquo;re on Linux, they should be stored in &lt;code>/usr/share/openocd/&lt;/code> by default.
The rest of this command tells OpenOCD what to do - program the specific ELF file to MCU flash memory, verify the memory integrity, reset the MCU and exit.
It also requires &lt;code>all&lt;/code> rule, so the project will build itself automatically and upload the ELF file to MCU every time you run &lt;code>make flash&lt;/code> now. &lt;strong>This does NOT disappear during CubeMX project re-generation, so you don&amp;rsquo;t have to worry about copy&amp;amp;pasting it every time you do this&lt;/strong>.
To delete all the build files (to do a clean build for example), use &lt;code>make clean&lt;/code> command.&lt;/p>
&lt;h3 id="configuring-vscode-tasks">Configuring VSCode tasks&lt;/h3>
&lt;p>To get some degree of automation, we&amp;rsquo;ll create VSCode tasks for building, cleaning and flashing the project. Open command prompt in VSCode (&lt;code>Ctrl+Shift+P&lt;/code>), and look for &lt;code>Tasks: Configure Default Build Task&lt;/code> command (or &lt;code>Task: Configure Task&lt;/code>).&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/make-build-task-1.png" alt="create-build-task-1">&lt;/p>
&lt;p>Then, select &lt;code>Create tasks.json file from template&lt;/code>, and select &lt;code>Others&lt;/code> template.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/make-build-task-2.png" alt="create-build-task-2">
&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/make-build-task-3.png" alt="create-build-task-3">&lt;/p>
&lt;p>After that, you should see this &lt;code>tasks.json&lt;/code> file, which happened to be added to &lt;code>.vscode&lt;/code> directory in project root. If you&amp;rsquo;re using VCS (Git or Mercurial for example), you should be able to store it in repository without any issues, so don&amp;rsquo;t wildcard out the entire &lt;code>.vscode&lt;/code> directory in your &lt;code>.gitignore&lt;/code> (or any other VCS-related ignorefile) to keep the tasks.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/make-build-task-4.png" alt="create-build-task-4">&lt;/p>
&lt;p>Copy this task and modify label and command accordingly, to create a build, clean and flash task. After that, run &lt;code>Tasks: Configure Default Build Task&lt;/code> command again, and select your build task. &lt;strong>Remember that we&amp;rsquo;re using &lt;code>Bear&lt;/code>/&lt;code>compiledb&lt;/code> to generate compilation database for Clangd! Don&amp;rsquo;t call &lt;code>make&lt;/code> for building the project directly, to always have updated and correct database!&lt;/strong> Now, your &lt;code>tasks.json&lt;/code> file should look similar to this (change the thread amount accordingly to your CPU capabilities):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// See https://go.microsoft.com/fwlink/?LinkId=733558
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// for the documentation about the tasks.json format
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#f92672">&amp;#34;version&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;2.0.0&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;tasks&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;label&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Build&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;shell&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;command&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;compiledb&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;args&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;make&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;-j8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;problemMatcher&amp;#34;&lt;/span>: [],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;group&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;kind&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;build&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;isDefault&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;label&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Build &amp;amp; Flash&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;shell&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;command&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;compiledb&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;args&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;make&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;flash&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;-j8&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;label&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Clean&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;shell&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;command&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;make&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;args&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;clean&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Test the tasks out by running &lt;code>Clean&lt;/code> task (&lt;code>Tasks: Run Task&lt;/code> command -&amp;gt; select &lt;code>Clean&lt;/code> task -&amp;gt; don&amp;rsquo;t scan the output, if you&amp;rsquo;re asked for that), and using &lt;code>Tasks: Run Build Task&lt;/code> shortcut - &lt;code>Ctrl+Shift+B&lt;/code>. The program should first clean, then build itself, and you should see &lt;code>make&lt;/code> output in integrated console window.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/task-output.png" alt="task-output">&lt;/p>
&lt;p>Make a backup of this &lt;code>tasks.json&lt;/code> file, because it should be exactly the same for every project configured this way.
Also, reminder: instead of putting &lt;code>openocd&lt;/code> call in &lt;code>Makefile&lt;/code>, you can do it directly in VSCode task if you want - just split the arguments correctly (the command after &lt;code>-c&lt;/code> should be a single argument), and set the &lt;code>Build&lt;/code> task as &lt;code>Flash&lt;/code> task dependency &lt;a href="https://code.visualstudio.com/docs/editor/tasks#_compound-tasks">with &lt;code>dependsOn&lt;/code> list option&lt;/a>. You can also create a flash-only task like that. It&amp;rsquo;s up to your preference.&lt;/p>
&lt;h3 id="configuring-the-debugger">Configuring the debugger&lt;/h3>
&lt;p>Time to configure the debugging task. Go to the debug tab, and click on &lt;code>create a launch.json file&lt;/code>. Then, from the list, select &lt;code>Cortex Debug&lt;/code> option.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/debug-task-1.png" alt="debug-task-1">&lt;/p>
&lt;p>You should now have this file created, with default task for Cortex Debug plugin. We&amp;rsquo;ll have to change few things to make it working with OpenOCD, our debugger and MCU.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/debug-task-2.png" alt="debug-task-2">&lt;/p>
&lt;p>First, we&amp;rsquo;ll have to change the path to &lt;code>executable&lt;/code>, so it points to the &lt;code>elf&lt;/code> file with our program, which is in &lt;code>${workspaceRoot}/build/&lt;/code> directory. Then, we have to change &lt;code>servertype&lt;/code> to &lt;code>openocd&lt;/code>. It&amp;rsquo;s not the only debugger that Cortex Debug can use, you can see the whole list by calling autocompletion in empty field. Most of them should work with STM32, but i&amp;rsquo;ll focus on OpenOCD.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/debug-task-3.png" alt="debug-task-3">&lt;/p>
&lt;p>Then, we have to configure some OpenOCD-related stuff. We have to specify &lt;code>device&lt;/code>, and put the microcontroller name in it (without &lt;code>Tx&lt;/code> suffix, which corresponds to the temperature version of chip: for example, i&amp;rsquo;m using &lt;code>STM32G474RET6&lt;/code>, but in &lt;code>device&lt;/code> i have to put only &lt;code>STM32G474RE&lt;/code>, because all the temperature versions have exactly the same silicon inside). We also have to specify the configuration files for OpenOCD - the same ones that we specified for flashing. You can do it via &lt;code>configFiles&lt;/code> list.&lt;/p>
&lt;p>You should also specify &lt;code>preLaunchTask&lt;/code>, so VSCode will &lt;strong>always&lt;/strong> build and flash the binary before starting debug session. It&amp;rsquo;s important, because if you&amp;rsquo;d forget about it and start debugging session with different program on MCU than on your PC, debugger - at some point - will start showing you complete junk and unreasonable code flow, and if you are not aware of that, you will probably spend &lt;strong>a lot&lt;/strong> of time thinking what&amp;rsquo;s wrong with your code or MCU.&lt;/p>
&lt;p>The &lt;code>launch.json&lt;/code> file content should now look like this, except the &lt;code>device&lt;/code> and target config files should conform to MCU and debugger you&amp;rsquo;re using, &lt;code>preLaunchTask&lt;/code> should have the same name as your flash task, and &lt;code>executable&lt;/code> should point to your ELF file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// Use IntelliSense to learn about possible attributes.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// Hover to view descriptions of existing attributes.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#f92672">&amp;#34;version&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;0.2.0&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;configurations&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Cortex Debug&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;cwd&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;${workspaceRoot}&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;executable&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;${workspaceRoot}/build/blog-vscode-cubemx-example.elf&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;request&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;launch&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;cortex-debug&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;servertype&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;openocd&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;device&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;STM32G474RE&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;configFiles&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;interface/stlink.cfg&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;target/stm32g4x.cfg&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;preLaunchTask&amp;#34;&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;Build &amp;amp; Flash&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If everything has been configured correctly, you should be able to debug your program now. Add a breakpoint in the first line of &lt;code>main&lt;/code> function (click left to line number, red dot should appear) and run the debugging session (&lt;code>F5&lt;/code> by default, or click a button on top of Debug menu).&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/debug-session.png" alt="debug-session">&lt;/p>
&lt;p>VSCode should switch to debug perspective, and you should be able to see variables/objects (expand Global section), call stack, and add variables/objects to the watch. You should also be able to step through the program - either via controls on top, or keyboard shortcuts (&lt;code>F5&lt;/code> to let the program run until the next breakpoint, &lt;code>F10&lt;/code> to step over current line, &lt;code>F11&lt;/code> to step into current line, &lt;code>Shift+F11&lt;/code> to step out of current scope, &lt;code>Ctrl+Shift+F5&lt;/code> to restart debugging session, and &lt;code>Shift+F5&lt;/code> to stop debugging session). There&amp;rsquo;s also a button to reset the MCU (first from left).&lt;/p>
&lt;h4 id="configuring-the-peripheral-view">Configuring the peripheral view&lt;/h4>
&lt;p>Finally, to see all the peripherals of our MCU and their registers content, we can download and specify SVD (or rather, to be precise, CMSIS-SVD - System View Description) file. &lt;strong>This step is optional, but being able to see the peripherals is useful, so i recommend doing this anyway&lt;/strong>. For ST MCU&amp;rsquo;s, you can get SVD files from the MCU page on st.com. For example, in case of my MCU i go &lt;a href="https://www.st.com/en/microcontrollers-microprocessors/stm32g474re.html">here&lt;/a>, into &lt;code>CAD Resources&lt;/code> tab, and i download &lt;code>STM32G4 System view description&lt;/code> (ST account not required).&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/svd-config-1.png" alt="svd-config-1">&lt;/p>
&lt;p>After that, i unpack it. Inside unpacked archive, there should be a directory wih &lt;code>*.svd&lt;/code> files - find one compatible with your MCU (in my case it&amp;rsquo;s named &lt;code>STM32G474xx.svd&lt;/code>) and copy it to project root directory. Now, open &lt;code>launch.json&lt;/code> and add new option in debug configuration:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;svdFile&amp;#34;&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">:&lt;/span> &lt;span style="color:#e6db74">&amp;#34;${workspaceRoot}/STM32G474xx.svd&amp;#34;&lt;/span>&lt;span style="color:#960050;background-color:#1e0010">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Of course, change the name of the file to correct one. To check if it&amp;rsquo;s working, start debug session (&lt;code>F5&lt;/code>) and look for &lt;code>Cortex Peripherals&lt;/code> section in debug view. Open it and check if you can see the peripherals, along with their addresses, registers and register fields content.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/peripherals-view.png" alt="peripherals-view">&lt;/p>
&lt;p>And that&amp;rsquo;s all. If you did all the steps, and verified they&amp;rsquo;re working, congratulations - you now have an alternative for CubeIDE for STM32 development with CubeMX, that isn&amp;rsquo;t very painful to configure and has most of the features you&amp;rsquo;ll need. I&amp;rsquo;ve heard that Cortex Debug also allows to configure ITM output, but i haven&amp;rsquo;t tried that yet, so i&amp;rsquo;ll probably describe it in different blog post in the future.&lt;/p>
&lt;p>Now, for the bonus part&amp;hellip;&lt;/p>
&lt;h2 id="stm32-hal-vs-ll-libraries-and-how-to-change-between-them-in-cubemx">STM32 HAL vs LL libraries, and how to change between them in CubeMX&lt;/h2>
&lt;p>If you&amp;rsquo;re using CubeMX for STM32 projects, you are (most probably) inherently using HAL libraries to work with the MCU. HAL is an Hardware Abstraction Layer - set of libraries which abstract operations on the hardware, that are easy to use for someone who doesn&amp;rsquo;t want to dig through the manual to do even simplest things. Also worth mentioning, that STM32 HAL is &lt;em>mostly&lt;/em> the same under all STM32 MCUs, therefore moving project that relies on HAL between different STM32 MCUs in the same family, or even between different families of STM32 MCUs, is fairly easy. However, it do have some issues - mainly, the memory bloat that&amp;rsquo;s sometimes really noticeable (especially on low-end MCUs with very little memory - some of them even can&amp;rsquo;t use HAL at all, because it takes more memory than they have for simple project with just the initialization code). It can also be slower than dedicated solutions, due to handling almost everything that can happen every time.&lt;/p>
&lt;p>The solution of these issues doesn&amp;rsquo;t have to be throwing away CubeMX, and going back to the registers and CMSIS level, writing everything manually. There is an alternative - Low Layer libraries. These exist for all the peripherals that don&amp;rsquo;t require upper-level stack (like USB or Ethernet), and are significantly lighter than HAL. However, they are also more complicated to use, as they don&amp;rsquo;t cover the functionality as HAL libraries do. LL offers a low-level API that&amp;rsquo;s just above the registers. In other words, LL is an almost unnoticeable (performance and memory-wise) layer that abstracts the registers away, giving you fairly well documented and more readable API instead, that doesn&amp;rsquo;t take away much control from you.&lt;/p>
&lt;p>To learn about HAL and LL differences in detail, i suggest going to CubeMX package site for your STM32 MCU (in my case: &lt;a href="https://www.st.com/en/embedded-software/stm32cubeg4.html">this one&lt;/a>), into &lt;code>Documentation&lt;/code> section, and reading User Manual called &lt;code>Description of STM32XY HAL and low-layer drivers&lt;/code>, where &lt;code>XY&lt;/code> symbolize you series. You can find full documentation of both HAL and LL libraries there, along with their philosophy.&lt;/p>
&lt;p>There is an easy way of changing between HAL and LL drivers in CubeMX: open the project and go to &lt;code>Project Manager -&amp;gt; Advanced Settings&lt;/code>.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cubemx-setup/cubemx-hal-ll.png" alt="cubemx-hal-ll">&lt;/p>
&lt;p>You can do three things there:&lt;/p>
&lt;ul>
&lt;li>Change between HAL and LL drivers for every peripheral, and even every instance of peripheral;&lt;/li>
&lt;li>Configure the initialization order of used peripherals (very useful for issues with DMA, when it&amp;rsquo;s wrongly initialized after the peripheral that&amp;rsquo;s using it);&lt;/li>
&lt;li>Enable or disable callback registration per peripheral (as an alternative to global interrupt callbacks).&lt;/li>
&lt;/ul>
&lt;p>While you still might not want to use LL, it&amp;rsquo;s a good idea to take a look there if you look for memory to free. For example, if you are not using any peripheral-related functions in code, yet you need that peripheral initialized (which can be a case for RCC for example), you can switch it from HAL to LL to save few bytes of memory without touching the code yourself. &lt;strong>But remember: HAL and LL functions ARE NOT inter-compatible. You CANNOT use both HAL and LL for the same peripheral at the same time (without proper precautions), as HAL requires a handle that&amp;rsquo;s state reflects the current state of peripheral. If you start using non-HAL code along with HAL, you HAVE TO make sure the state in HAL handle will not be invalid when HAL functions are being called (in interrupts that use HAL handlers too!), otherwise HAL code can break the program.&lt;/strong>&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>And that would be it. Thank you for reading, and if you have questions or issues, feel free to &lt;a href="https://steelph0enix.github.io/about/">contact me&lt;/a> or drop an issue/pull request in &lt;a href="https://github.com/SteelPh0enix/steelph0enix.github.io">the blog&amp;rsquo;s repository&lt;/a>.&lt;/p></content></item><item><title>Visual Studio Code - C/C++ Setup</title><link>https://steelph0enix.github.io/posts/vscode-cpp-setup/</link><pubDate>Tue, 08 Jun 2021 00:00:00 +0000</pubDate><guid>https://steelph0enix.github.io/posts/vscode-cpp-setup/</guid><description>&lt;p>Visual Studio Code is a great open-source editor with plenty of useful plugins for insane amount of languages and frameworks.&lt;/p>
&lt;p>However, as C and C++ environment is pretty janky for today&amp;rsquo;s standards, so is the configuration. So i made this guide to streamline the process and make it easy for somebody new in C, C++ or VSCode to setup a reasonably working dev environment with some useful quality-of-life tools. It might not be IntelliJ-level of quality, but hey - it&amp;rsquo;s free.&lt;/p></description><content>&lt;p>Visual Studio Code is a great open-source editor with plenty of useful plugins for insane amount of languages and frameworks.&lt;/p>
&lt;p>However, as C and C++ environment is pretty janky for today&amp;rsquo;s standards, so is the configuration. So i made this guide to streamline the process and make it easy for somebody new in C, C++ or VSCode to setup a reasonably working dev environment with some useful quality-of-life tools. It might not be IntelliJ-level of quality, but hey - it&amp;rsquo;s free.&lt;/p>
&lt;p>In this guide, i will tell you:&lt;/p>
&lt;ul>
&lt;li>What tools you&amp;rsquo;ll need to start developing C and C++ apps in VSCode&lt;/li>
&lt;li>What extensions you might want to install to ease up the code writing process, and how to configure them&lt;/li>
&lt;li>How do you create a C/C++ project in VSCode (with CMake) and integrate it with VSCode&lt;/li>
&lt;/ul>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;h3 id="vscode">VSCode&lt;/h3>
&lt;p>The obvious prerequisite is Visual Studio Code. Install it from the official site: &lt;a href="https://code.visualstudio.com/">https://code.visualstudio.com/&lt;/a>, or from a repository if you&amp;rsquo;re using a package manager.&lt;/p>
&lt;p>&lt;strong>For Windows&lt;/strong>: either download the installer from official site and run it, or install VSCode via &lt;a href="https://scoop.sh/">scoop&lt;/a>: &lt;code>scoop install vscode&lt;/code>. &lt;em>btw; i strongly recommend &lt;code>scoop&lt;/code> - great package manager&lt;/em>&lt;/p>
&lt;p>&lt;strong>For Linux&lt;/strong>: if you have it in your package manager repository, install it from there. Otherwise, use the installer from official site.&lt;/p>
&lt;p>&lt;strong>For MacOS&lt;/strong>: same as for Linux. Probably. I don&amp;rsquo;t use MacOS so i can&amp;rsquo;t really tell.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Fun fact&lt;/strong>; there are two versions of VSCode you can find on the internet and in package managers - OSS version, and Non-OSS. The OSS version is basically the VSCode you&amp;rsquo;d get by downloading it from &lt;a href="https://github.com/microsoft/vscode">official repository&lt;/a> and building it yourself. Non-OSS version is the one from Microsoft distribution (for example, their official site), and the only difference between them is that Non-OSS version contains some Microsoft proprietary code, while OSS doesn&amp;rsquo;t. There is some functional difference (IIRC, OSS version lacks proprietary features like Settings Sync or Remote WSL/SSH/Containers), but both are fully compatible in terms of plugins and configuration, so you usually don&amp;rsquo;t need to worry about the exact version you&amp;rsquo;ve installed.&lt;/p>
&lt;/blockquote>
&lt;h3 id="cc-toolchain">C/C++ Toolchain&lt;/h3>
&lt;p>There are many C/C++ toolchains available, and i&amp;rsquo;m not gonna enforce one, because this guide is mostly toolchain-independent (in the end we&amp;rsquo;re gonna use build system, but most of the examples are for GCC). However, if you are new to C/C++, i&amp;rsquo;d recommend starting with GCC (or MinGW, if you&amp;rsquo;re on Windows) as it&amp;rsquo;s easy to install and use out of the box.&lt;/p>
&lt;p>If you already have your preferred toolchain installed and configured, feel free to go to the next step. If not, here&amp;rsquo;s the guide:&lt;/p>
&lt;p>&lt;strong>For Windows&lt;/strong>:&lt;/p>
&lt;p>My usual way of installing C++ toolchain on Windows is via &lt;a href="https://winlibs.com/">WinLibs&lt;/a> package. To install it:&lt;/p>
&lt;ul>
&lt;li>Download latest UCRT runtime version for Win64 (i assume you&amp;rsquo;re running at least Win10 64bit, otherwise read the instructions on WinLibs page). &lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/winlibs_download.png" alt="winlibs download">&lt;/li>
&lt;li>Extract it somewhere - preferably directly to a hard drive, for example &lt;code>C:\&lt;/code>, but it can be anywhere. I recommend avoiding paths with spaces and non-ASCII characters though, they may cause headaches in the future. &lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/mingw_unzipped.png" alt="mingw unzipped">&lt;/li>
&lt;li>Add &lt;code>/bin&lt;/code> subdirectory to system or user &lt;code>PATH&lt;/code> variable. &lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/env_vars.png" alt="env vars">&lt;/li>
&lt;li>Open terminal and run &lt;code>gcc --version&lt;/code> command. You should see similar message to the one below. You may also want to run &lt;code>where.exe gcc&lt;/code> to make sure you&amp;rsquo;re using correct GCC executable, in case you&amp;rsquo;d have other GCC installation in the system that you forgot about - usually, WinLibs provides most recent one, so this is the one you should be using. &lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/gcc_version.png" alt="check install">&lt;/li>
&lt;/ul>
&lt;p>If you&amp;rsquo;d rather use different toolchain than GCC, i recommend Visual C++, which you can install via Build Tools for Visual Studio 2022. You can download it from &lt;a href="https://aka.ms/vs/17/release/vs_BuildTools.exe">here&lt;/a>. To install C++ toolchain, run the installer and select the option &amp;ldquo;Desktop development with C++&amp;rdquo;. &lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/msvc_cpp.png" alt="msvc install">
Note that &lt;strong>you don&amp;rsquo;t need both toolchains, one is enough&lt;/strong>, and that build tools do not include Visual Studio - if you want Visual Studio, you can install it as separate product using the same installer. Since this guide is about &lt;strong>Visual Studio Code&lt;/strong>, which is a different product, i&amp;rsquo;m not going to provide instructions for that.&lt;/p>
&lt;p>&lt;strong>For Linux&lt;/strong>:&lt;/p>
&lt;p>Usually, you should have GCC in your repository. On Ubuntu, Debian and similar distributions (Mint, Kubuntu, Lubuntu, PopOS!, Zorin, and so on), you have &lt;code>build-essential&lt;/code> package with most tools needed to build C/C++ programs.&lt;/p>
&lt;p>On Arch Linux and similar distributions (Manjaro), you have &lt;code>base-devel&lt;/code> package.&lt;/p>
&lt;p>On other distributions, search for similar package or install latest &lt;code>gcc&lt;/code> and &lt;code>g++&lt;/code> packages from your repository.&lt;/p>
&lt;p>Test it the same way as on Windows - open terminal and try &lt;code>gcc --version&lt;/code>, see what happens.&lt;/p>
&lt;p>&lt;strong>You also have to install &lt;code>gdb&lt;/code> (GCC debugger) separately, as it may not come with the base development packages, and you definitely do want to have it and use it.&lt;/strong>&lt;/p>
&lt;p>&lt;strong>For MacOS&lt;/strong>: probably same thing as on Linux, look for GCC (or any other preferred toolchain) in package manager and install it from there. Verify the installation the same way as on Linux.&lt;/p>
&lt;h2 id="vscode-essential-plugins">VSCode Essential Plugins&lt;/h2>
&lt;p>If you already have working C/C++ toolchain, time to run VSCode and install some plugins. Run VSCode and go to the &lt;em>Extensions&lt;/em> menu.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/vscode-ext-1.png" alt="vsc-ext-1">&lt;/p>
&lt;p>Now, for some general C++ plugins:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>C/C++&lt;/strong> - that&amp;rsquo;s the core extension we&amp;rsquo;ll need. It contains the fundamental stuff to work with C/C++ in VSCode.&lt;/li>
&lt;li>&lt;strong>Better C++ Syntax&lt;/strong> - it&amp;rsquo;s always nice to have better syntax coloring, so i strongly recommend that one. &lt;em>You might want to use one of the themes from this extension description to get full experience.&lt;/em>&lt;/li>
&lt;li>&lt;strong>C/C++ Snippets&lt;/strong> - pretty useful extension that adds automatic generation of snippets in C/C++ code - instead of writing loops, structures and class definitions by hand, you can generate them with autocompletion support.&lt;/li>
&lt;li>&lt;strong>C++ Intellisense&lt;/strong> - pretty good plugin with some intelligent autocompletion features.&lt;/li>
&lt;li>&lt;strong>C++ Helper&lt;/strong> - simple extension which adds automatic function definition generation feature.&lt;/li>
&lt;li>&lt;strong>C-mantic&lt;/strong> - very useful plugin that adds auto-generation of function definitions, getters, setters and more. &lt;strong>An alternative to C++ Helper - pick whatever seems more ergonomic for you&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;p>And i&amp;rsquo;d also recommend these:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Bracket Pair Colorizer 2&lt;/strong> - very useful extension which colorize the matching bracket pairs, increasing code clarity. Strongly recommended.&lt;/li>
&lt;li>&lt;strong>GitLens&lt;/strong> - if you want to work with Git repositories, that&amp;rsquo;s the extension you&amp;rsquo;re looking for. &lt;strong>You need &lt;code>git&lt;/code> installed to use it!&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Material Icon Theme&lt;/strong> - better looking than default ones&lt;/li>
&lt;/ul>
&lt;p>With these plugins, you will have a pretty decent bare-bones environment to work with C and C++. You&amp;rsquo;ll have autocompletion, some refactoring features, some code generation and a pretty decent syntax highlighting. &lt;em>The screenshot below is outdated - use the list above for updated recommended plugins&lt;/em>&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/installed-ext-vscode.png" alt="installed-ext">&lt;/p>
&lt;blockquote>
&lt;p>I have one more plugin to show you, an alternative language server with many useful features, but i&amp;rsquo;ll leave it as a bonus at the end of this guide, because it needs a bit more configuration. Make sure to check it out!&lt;/p>
&lt;/blockquote>
&lt;h2 id="creating-a-project---vscode-and-cmake">Creating a project - VSCode and CMake&lt;/h2>
&lt;p>We&amp;rsquo;ll start with something simple. As i&amp;rsquo;ve mentioned before, i&amp;rsquo;m not gonna teach you how to make a &lt;em>raw&lt;/em> VSCode project, which builds the app from scratch and without any other tools, because that&amp;rsquo;s simply painful, not really scalable, and not worth the trouble.&lt;/p>
&lt;p>Instead, i&amp;rsquo;m gonna teach you how to use a build system.&lt;/p>
&lt;h3 id="but-whats-a-build-system">But what&amp;rsquo;s a &lt;em>build system&lt;/em>?&lt;/h3>
&lt;p>Well, build system is a tool that tells the toolchain (compiler and his friends) how to create a program out of all the source (and resource) files you&amp;rsquo;ve created. And sometimes does other things, but that&amp;rsquo;s out of this tutorial scope.&lt;/p>
&lt;p>Without it, you&amp;rsquo;d have to enter the toolchain commands manually each time you&amp;rsquo;d want to build the application. That&amp;rsquo;s fine for small apps with one, two or maybe five files. But it gets messy when your program starts to grow.&lt;/p>
&lt;p>So, for example, assuming you use &lt;code>gcc&lt;/code>, to build your C program manually, you&amp;rsquo;d have to enter something like this:&lt;/p>
&lt;p>&lt;code>gcc [list of your source files] [some fancy flags for your compiler] [maybe some flags telling the compiler where the libraries are] -o program.exe&lt;/code>&lt;/p>
&lt;p>An actual example would be:&lt;/p>
&lt;p>&lt;code>gcc main.c lib.c lib2.c -O2 -Wall -Wextra -L./some/lib -lmylib -lsomeotherlib -o program.exe&lt;/code>&lt;/p>
&lt;p>That doesn&amp;rsquo;t look so bad, right? You could even put this command in some shell script and easily run it every time you&amp;rsquo;d like to build the code. But then, you&amp;rsquo;d have to change this command every time your project structure changes - so, every time you add a new file, or library, or change some directory name, you gotta edit this script. &lt;em>And yes, i know that wildcards exist, but for the sake of this example i&amp;rsquo;m gonna ignore them.&lt;/em>&lt;/p>
&lt;p>Another issue is that if you&amp;rsquo;d want to give this code to your friend or teacher, he would either have to use the same shell as you, and &lt;code>gcc&lt;/code>, or write his own build script (or project) for the compiler/shell he&amp;rsquo;s using, and that&amp;rsquo;s not very user-friendly (or, rather, programmer-friendly) solution.&lt;/p>
&lt;p>There are also some other issues with manual building, but the point is: &lt;strong>manual building is not comfortable or scalable on a larger scale&lt;/strong>. So, we&amp;rsquo;ll use a build system to do it for us and make everyone&amp;rsquo;s life easier!&lt;/p>
&lt;p>&lt;em>Bonus note: Tool i&amp;rsquo;m going to talk about next - CMake - isn&amp;rsquo;t technically a build system. It&amp;rsquo;s a meta-build system. The difference between those is that build system runs the toolchain commands directly, while meta-build system generates the build system files. Basically, meta-build systems are more flexible, and sometimes easier to use, therefore we&amp;rsquo;re gonna use one.&lt;/em>&lt;/p>
&lt;p>&lt;em>Bonus note number two: originally, i was going to show how to use Premake and CMake, but due to some &lt;a href="https://github.com/premake/premake-core/issues/1640">pretty bad issues with Premake&lt;/a> i&amp;rsquo;ve decided to stay with CMake. I&amp;rsquo;ll probably make a Premake guide in the future.&lt;/em>&lt;/p>
&lt;h4 id="okay-how-do-i-use-it">Okay, how do i use it?&lt;/h4>
&lt;p>Well, let&amp;rsquo;s start with installation. You can either install it from your package manager, just like VSCode, or download from &lt;a href="https://cmake.org/download/">official site&lt;/a>. &lt;strong>Make sure to add CMake to your PATH variable if you&amp;rsquo;re using an official installer!&lt;/strong>.&lt;/p>
&lt;blockquote>
&lt;p>Important note: if you&amp;rsquo;re using your package manager, check the CMake version after installation (&lt;code>cmake --version&lt;/code> command). I&amp;rsquo;m gonna use some stuff that was added in CMake 3.12, but from what i can see, some older Linux distributions (like Ubuntu 18.04) &lt;a href="https://packages.ubuntu.com/search?keywords=cmake">still have CMake 3.10 in their repositories&lt;/a>, so if that&amp;rsquo;s the case i strongly recommend installing newer version manually. If that&amp;rsquo;s not possible, i&amp;rsquo;ll tell what things come from CMake 3.12 and how to make a workaround.&lt;/p>
&lt;/blockquote>
&lt;p>Done? Great. Now a little bit of theory.&lt;/p>
&lt;h4 id="how-does-cmake-work">How does CMake work?&lt;/h4>
&lt;p>CMake is a meta-build system. As i&amp;rsquo;ve mentioned earlier, it means that when we run it, it should give us a project for a build system of our choice, which we can use to build our application. To tell CMake how it should generate the project, a &lt;code>CMakeLists.txt&lt;/code> file is used. This file includes the project configuration, written in CMake&amp;rsquo;s scripting language.&lt;/p>
&lt;p>Some toolchains - like GCC, MinGW or Visual C++ - come with their own build systems. In case of GCC/MinGW, it&amp;rsquo;s GNU Make. In case of Visual C++, it&amp;rsquo;s MSBuild. CMake can generate the necessary files for these build systems, and then we can use them to build the whole project with a single command. Pretty convenient.&lt;/p>
&lt;h3 id="creating-a-simple-project">Creating a simple project&lt;/h3>
&lt;p>Let&amp;rsquo;s open VSCode (or restart, if you had it opened while installing CMake) and add some extensions.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/cmake-ext.png" alt="cmake-ext">&lt;/p>
&lt;p>These two plugins will enable &lt;code>CMakeLists.txt&lt;/code> syntax highlighting and CMake integration for VSCode. And this integration is a very powerful and helpful tool, as we&amp;rsquo;ll see in a bit. &lt;em>You can also install a &lt;code>cmake-format&lt;/code> extension, if you have Python installed and follow the &lt;a href="https://marketplace.visualstudio.com/items?itemName=cheshirekow.cmake-format">plugin&amp;rsquo;s installation guide&lt;/a>&lt;/em>&lt;/p>
&lt;p>Now, make a folder for our new project and create a simple &lt;code>main.cpp&lt;/code> (or &lt;code>main.c&lt;/code>, if you want to code in C):&lt;/p>
&lt;p>C++ version:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;Hello, world!&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>C version:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">printf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Hello, world!&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can check if your toolchain works correctly by building the app manually. If you&amp;rsquo;re using GCC or MinGW, open up integrated VSCode terminal with &lt;code>Ctrl+` &lt;/code> command (or &lt;code>Ctrl+Shift+` &lt;/code> to create a new one), and run:&lt;/p>
&lt;p>C++: &lt;code>g++ main.cpp -o main&lt;/code>&lt;/p>
&lt;p>C: &lt;code>gcc main.c -o main&lt;/code>&lt;/p>
&lt;p>And then run the app with &lt;code>./main&lt;/code> command. You should see your Hello World printed in terminal.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/hello-world-first-run.png" alt="hello-world-manual">&lt;/p>
&lt;p>&lt;em>If that doesn&amp;rsquo;t work, make sure you have correctly added your toolchain to PATH variable.&lt;/em>&lt;/p>
&lt;p>Now, we can proceed with CMake. Delete the compiled program &lt;code>main&lt;/code> you just created and tested, and create a &lt;code>CMakeLists.txt&lt;/code> file in the same directory as your code file. We&amp;rsquo;ll start with bare-bones template and then we&amp;rsquo;ll expand it a little.&lt;/p>
&lt;p>Put this code into &lt;code>CMakeLists.txt&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cmake" data-lang="cmake">&lt;span style="display:flex;">&lt;span>cmake_minimum_required(&lt;span style="color:#e6db74">VERSION&lt;/span> &lt;span style="color:#e6db74">3.12&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#75715e"># Change CXX to C, if you&amp;#39;re making a C program
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>project(&lt;span style="color:#e6db74">HelloWorld&lt;/span> &lt;span style="color:#e6db74">LANGUAGES&lt;/span> &lt;span style="color:#e6db74">CXX&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#75715e"># Change main.cpp to main.c, if you&amp;#39;re making a C program
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>add_executable(&lt;span style="color:#f92672">${&lt;/span>PROJECT_NAME&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#e6db74">main.cpp&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And then open up the VSCode command list with &lt;code>Ctrl+Shift+P&lt;/code> shortcut, and look for &lt;code>CMake: Configure&lt;/code> option. Run it.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/cmake-configure-cmd.png" alt="cmake-config">&lt;/p>
&lt;p>Next, you should see a list of detected toolchains installed in your system. Pick one.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/cmake-select-kit.png" alt="cmake-select-kit">&lt;/p>
&lt;p>After that, VSCode will run CMake and configure the project for the first time. You should see similar output in your VSCode output window:&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/cmake-output.png" alt="cmake-config-out">&lt;/p>
&lt;p>From now on, VSCode will automatically run CMake every time you change &lt;code>CMakeLists.txt&lt;/code> to re-generate the project files. You should also see a new menu on left-side toolbar&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/cmake-menu.png" alt="cmake-menu">&lt;/p>
&lt;h4 id="building-your-program">Building your program&lt;/h4>
&lt;p>So, we have a project now. How do we build it, and how do we run our program?&lt;/p>
&lt;p>Thankfully, CMake plugin for VSCode got us covered. To build the program, either use &lt;code>F7&lt;/code> shortcut, look for the &lt;code>CMake: Build&lt;/code> command in command list, or press the &lt;code>Build&lt;/code> button either on VSCode bottom bar, or in CMake menu.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/build-bottom-bar.png" alt="build-from-bar">&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/build-cmake-menu.png" alt="build-from-menu">&lt;/p>
&lt;p>You should see similar output in VSCode output window:&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/build-output.png" alt="build-output">&lt;/p>
&lt;h4 id="running-your-program">Running your program&lt;/h4>
&lt;p>Now, let&amp;rsquo;s run it. Press the &lt;code>Run&lt;/code> button on the bottom VSCode bar:&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/run-bottom-bar.png" alt="run-app">&lt;/p>
&lt;p>And the app should run without any issues. The output should be in terminal window.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/vscode-run.png" alt="app-run">&lt;/p>
&lt;h4 id="is-that-it">Is that it?&lt;/h4>
&lt;p>&lt;strong>Of course not - there&amp;rsquo;s always more!&lt;/strong>&lt;/p>
&lt;p>But yeah, we just created a very simple project with CMake + VSCode, built it and ran it. Now, let&amp;rsquo;s talk what actually happened and how can we expand our project.&lt;/p>
&lt;h2 id="cmake-101---how-does-it-work">CMake 101 - how does it work?&lt;/h2>
&lt;p>CMake, as i&amp;rsquo;ve mentioned multiple times, is a tool that generates the project files used to build it. However, it can also be used for some other things - like application packaging, managing tests, configuring the project, and so on. At the moment CMake is an &lt;em>industry standard&lt;/em>, which means that most of the C and C++ projects have CMake support, therefore it&amp;rsquo;s a very versatile tool. Not the best, but versatile.&lt;/p>
&lt;p>You may notice that after configuring the project a new directory appeared in your project folder, called &lt;code>build&lt;/code>. Let&amp;rsquo;s peek into it.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/cmake-build-dir.png" alt="cmake-build-dir">&lt;/p>
&lt;p>You are usually not supposed to touch and modify these files, as CMake manages them, but i&amp;rsquo;ll still explain the function of some of the more important ones:&lt;/p>
&lt;ul>
&lt;li>&lt;code>CMakeCache.txt&lt;/code> - inside that file, you can find a list of CMake variables that are used in project generation process. You can find the toolchain paths, compiler flags, user and many different configuration variables there.&lt;/li>
&lt;li>&lt;code>compile_commands.json&lt;/code> - that file contains a list of commands used to build your program. This list can be used by some code analyzing tools (and i&amp;rsquo;ll describe one at the end of this guide).&lt;/li>
&lt;li>&lt;code>HelloWorld.exe&lt;/code> - hey, that&amp;rsquo;s our program!&lt;/li>
&lt;li>&lt;code>Makefile&lt;/code> - this file is used by GNU Make to build our program. Basically, that&amp;rsquo;s the final CMake output. If you&amp;rsquo;re using a different toochain with different build system, like Visual C++, you will get a different file (usually, a whole Visual Studio solution, so maybe even a whole directory).&lt;/li>
&lt;/ul>
&lt;h3 id="cmakelists---whats-inside">CMakeLists - what&amp;rsquo;s inside?&lt;/h3>
&lt;p>Let&amp;rsquo;s analyze our &lt;code>CMakeLists.txt&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cmake" data-lang="cmake">&lt;span style="display:flex;">&lt;span>cmake_minimum_required(&lt;span style="color:#e6db74">VERSION&lt;/span> &lt;span style="color:#e6db74">3.12&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#75715e"># Change CXX to C, if you&amp;#39;re making a C program
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>project(&lt;span style="color:#e6db74">HelloWorld&lt;/span> &lt;span style="color:#e6db74">LANGUAGES&lt;/span> &lt;span style="color:#e6db74">CXX&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#75715e"># Change main.cpp to main.c, if you&amp;#39;re making a C program
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>add_executable(&lt;span style="color:#f92672">${&lt;/span>PROJECT_NAME&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#e6db74">main.cpp&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>cmake_minimum_required&lt;/code> defines the minimal version of CMake required to generate this project. We&amp;rsquo;ll use 3.12 because i&amp;rsquo;m gonna show you some stuff that was added in this version.&lt;/li>
&lt;li>&lt;code>project&lt;/code> defines our project. We can also add information about version, project description, homepage and used languages - which we do in that case.&lt;/li>
&lt;li>&lt;code>add_executable&lt;/code> tells CMake to generate a code that will build an executable file with specified name from specified source files. &lt;code>${PROJECT_NAME}&lt;/code> is a CMake variable containing the project name defined by &lt;code>project&lt;/code> command.&lt;/li>
&lt;/ul>
&lt;p>It&amp;rsquo;s important that &lt;code>add_executable&lt;/code> requires a full list of all the source files making up the program. So, in theory, you would have to add every single source file to the list manually, like that: &lt;code>add_executable(${PROJECT_NAME} main.cpp a.cpp b.cpp [...])&lt;/code>, but fortunately that&amp;rsquo;s not necessary. We can use &lt;code>file&lt;/code> command to generate that list for us.&lt;/p>
&lt;h3 id="expanding-our-project---adding-more-files">Expanding our project - adding more files&lt;/h3>
&lt;p>Let&amp;rsquo;s add some more files for our project. First, create two new directories - &lt;code>include&lt;/code> and &lt;code>src&lt;/code> in root project directory:&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/src-include-dirs.png" alt="src-include-dirs">&lt;/p>
&lt;p>Now, move the &lt;code>main.cpp&lt;/code> (or &lt;code>main.c&lt;/code>) to &lt;code>src&lt;/code> directory, and make a new file called &lt;code>lib.cpp&lt;/code> (or &lt;code>lib.c&lt;/code>) there. Put this code inside:&lt;/p>
&lt;p>C++ version:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;lib.hpp&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">f&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> x) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;Hello, i&amp;#39;m a library!&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">x = &amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> x &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>C version:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;lib.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">f&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> x) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">printf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Hello, i&amp;#39;m a library!&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">x = %d&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, x);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then, add a &lt;code>lib.hpp&lt;/code> (or &lt;code>lib.h&lt;/code>) file inside &lt;code>include&lt;/code> directory. Put this inside:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#pragma once
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">void&lt;/span> &lt;span style="color:#a6e22e">f&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span> x);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The project should look like this now:&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/project-new-files.png" alt="project-expanded">&lt;/p>
&lt;p>Let&amp;rsquo;s update the &lt;code>CMakeLists.txt&lt;/code>.&lt;/p>
&lt;p>We&amp;rsquo;ll use two new commands now: &lt;code>file&lt;/code> and &lt;code>include_directories&lt;/code>. &lt;code>file&lt;/code> command will generate the list of source files, while &lt;code>include_directories&lt;/code> will tell our toolchain where to look for header files.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cmake" data-lang="cmake">&lt;span style="display:flex;">&lt;span>cmake_minimum_required(&lt;span style="color:#e6db74">VERSION&lt;/span> &lt;span style="color:#e6db74">3.12&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#75715e"># Change CXX to C, if you&amp;#39;re making a C program
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>project(&lt;span style="color:#e6db74">HelloWorld&lt;/span> &lt;span style="color:#e6db74">LANGUAGES&lt;/span> &lt;span style="color:#e6db74">CXX&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#75715e"># Change *.cpp to *.c, if you&amp;#39;re making a C program
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>file(&lt;span style="color:#e6db74">GLOB&lt;/span> &lt;span style="color:#e6db74">PROJECT_SOURCE_FILES&lt;/span> &lt;span style="color:#e6db74">CONFIGURE_DEPENDS&lt;/span> &lt;span style="color:#e6db74">src/*.cpp&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>include_directories(&lt;span style="color:#e6db74">include/&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">&lt;/span>&lt;span style="color:#75715e"># Change main.cpp to main.c, if you&amp;#39;re making a C program
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>add_executable(&lt;span style="color:#f92672">${&lt;/span>PROJECT_NAME&lt;span style="color:#f92672">}&lt;/span> &lt;span style="color:#f92672">${&lt;/span>PROJECT_SOURCE_FILES&lt;span style="color:#f92672">}&lt;/span>)&lt;span style="color:#960050;background-color:#1e0010">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>file(GLOB PROJECT_SOURCE_FILES CONFIGURE_DEPENDS src/*.cpp)&lt;/code> - this command generates the list of files from &lt;code>src&lt;/code> dir with &lt;code>.cpp&lt;/code> extension, and stores it in &lt;code>PROJECT_SOURCE_FILES&lt;/code> variable. The &lt;code>CONFIGURE_DEPENDS&lt;/code> flag was added in CMake 3.12, and thanks to it, the list of files will be automatically re-generated every time we add a new file to the project. The command will work without it, but then you would have to manually regenerate CMake project after adding new files.&lt;/li>
&lt;li>&lt;code>include_directories&lt;/code> tells the toolchain where to look for header files.&lt;/li>
&lt;/ul>
&lt;p>The &lt;code>PROJECT_SOURCE_FILES&lt;/code> variable should have a list of all of our code files, and we&amp;rsquo;ll pass it to &lt;code>add_executable&lt;/code> instead of adding a new file manually.
Of course, this variable can be called anyhow you want - it&amp;rsquo;s not a special name or anything.&lt;/p>
&lt;p>Now, let&amp;rsquo;s configure our project again (VSCode should do this automatically, but i wanna show you how to do it in case it doesn&amp;rsquo;t). Right-click on &lt;code>CMakeLists.txt&lt;/code> and select &amp;ldquo;Configure All Projects&amp;rdquo;&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/cmakelists-config.png" alt="cmake-reconfiguration">&lt;/p>
&lt;blockquote>
&lt;p>Sometimes, when something goes wrong and CMake or VSCode starts behaving strange or getting buggy, it&amp;rsquo;s good to clean up and reconfigure the project (&amp;ldquo;Clean Reconfigure All Projects&amp;rdquo; and &amp;ldquo;Clean Rebuild All Projects&amp;rdquo;). This can sometimes happen when playing with CMakeLists.&lt;/p>
&lt;/blockquote>
&lt;p>Let&amp;rsquo;s add some code to our &lt;code>main.cpp&lt;/code> (or &lt;code>main.c&lt;/code>) to test if additional files are added properly to our project:&lt;/p>
&lt;p>C++ version:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;lib.hpp&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> std&lt;span style="color:#f92672">::&lt;/span>cout &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;Hello, world&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&amp;lt;&lt;/span> std&lt;span style="color:#f92672">::&lt;/span>endl;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> f(&lt;span style="color:#ae81ff">42&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>C version:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-c" data-lang="c">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;lt;stdio.h&amp;gt;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#include&lt;/span> &lt;span style="color:#75715e">&amp;#34;lib.h&amp;#34;&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">int&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">printf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Hello, world!&lt;/span>&lt;span style="color:#ae81ff">\n&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">f&lt;/span>(&lt;span style="color:#ae81ff">42&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, we should be able to build our program&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/lib-build.png" alt="lib-build">&lt;/p>
&lt;p>And run it&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/lib-run.png" alt="lib-run">&lt;/p>
&lt;p>At this point, everything should work automatically. You should be able to add new source files to &lt;code>src&lt;/code> dir, and new headers to &lt;code>include&lt;/code>, and CMake should handle them without having to touch &lt;code>CMakeLists.txt&lt;/code> manually. Adding new directories with source/include files is done the same way - just &lt;code>file&lt;/code> the source files, add include path with &lt;code>include_directories&lt;/code> and voila.&lt;/p>
&lt;h3 id="debugging-our-code">Debugging our code&lt;/h3>
&lt;p>Time for a last step in our setup - using a debugger. Fortunately, CMake integration does most of the work for us here.&lt;/p>
&lt;p>Let&amp;rsquo;s add a breakpoint in the first line of our &lt;code>main&lt;/code> function - click on the left of line number, you should see a red dot:&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/vsc-breakpoint.png" alt="vsc-breakpoint">&lt;/p>
&lt;p>And start a debugging session by pressing a little bug icon on the bottom of VSCode window, or by using &lt;code>CMake: Debug&lt;/code> command&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/vsc-debug-icon.png" alt="vsc-debug-cmd">&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/vscode-debug.png" alt="vsc-debug-icon">&lt;/p>
&lt;p>VSCode should now switch into debugging perspective and program should stop at breakpoint.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/vscode-debug-perspective.png" alt="vsc-debug-perspective">&lt;/p>
&lt;p>And that&amp;rsquo;s basically it. Don&amp;rsquo;t worry about lack of configuration for run/debug in VSCode - CMake integration does everything for us, so there&amp;rsquo;s no need to make them. Just make sure you run/debug the program via CMake commands in VSCode, instead of it&amp;rsquo;s own. Setting up the run/debug configs manually can be pretty annoying at the times, so that&amp;rsquo;s a subject for another guide.&lt;/p>
&lt;p>There&amp;rsquo;s one subject i haven&amp;rsquo;t touched yet, and it&amp;rsquo;s adding external libraries. Since this guide is already long enough, i&amp;rsquo;ll make another one for that.
Now, it&amp;rsquo;s the time for some bonus content.&lt;/p>
&lt;h2 id="bonus-clangd-setup">Bonus: clangd setup&lt;/h2>
&lt;p>&lt;code>clangd&lt;/code> is a language server for C++. It provides functionalities like code completion, code linting (showing warning and errors in real-time), simple refactoring, and so on. There is a C++ language server in VSCode already, as we installed &lt;code>C/C++&lt;/code> extension, but &lt;code>clangd&lt;/code> is better in some ways, and it&amp;rsquo;s more multi-platform (default VSCode C++ language server doesn&amp;rsquo;t work on ARM yet, so if you wanna code on RaspberryPi via SSH - &lt;code>clangd&lt;/code> is your best friend).&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/ext-clang.png" alt="clangd-plugin">&lt;/p>
&lt;p>In order to work, &lt;code>clangd&lt;/code> requires &lt;code>compile_commands.json&lt;/code> file to know how your code is compiled and with what flags. Fortunately for us, CMake generates that file automatically, so no further configuration is required on our side!&lt;/p>
&lt;p>Right after the plugin installation, you should see a popup like this one:&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/clangd-install.png" alt="clangd-install">&lt;/p>
&lt;p>In order for &lt;code>clangd&lt;/code> to work, you have to disable the default Intellisense server (press &lt;code>Disable IntelliSense&lt;/code>), and download &lt;code>clangd&lt;/code> binary (which VSCode does for us). After it&amp;rsquo;s downloaded, you should see this popup:&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/clangd-installed.png" alt="clangd-installed">&lt;/p>
&lt;p>Reload the window, open up a C/C++ source file, and check if &lt;code>clangd&lt;/code> info is displayed on the bottom of the screen&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/clangd-idle.png" alt="clangd-idle">&lt;/p>
&lt;p>If that&amp;rsquo;s the case, congratulations, it should work now. Check if there&amp;rsquo;s no &lt;code>include&lt;/code> errors, and if the autocompletion works (you can manually trigger autocompletion with &lt;code>Ctrl+Space&lt;/code> shortcut). There should also be a &lt;code>.cache&lt;/code> directory in your project folder now, with &lt;code>clangd&lt;/code>&amp;rsquo;s cache files - make sure to add it to &lt;code>.gitignore&lt;/code> before making a commit :P&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/clangd-cache.png" alt="clangd-cache">&lt;/p>
&lt;p>Full list of &lt;code>clangd&lt;/code> features can be found here: &lt;a href="https://clangd.llvm.org/features.html">https://clangd.llvm.org/features.html&lt;/a>. I strongly recommend checking it out.&lt;/p>
&lt;h3 id="clang-format-setup">Clang-Format setup&lt;/h3>
&lt;p>&lt;code>clangd&lt;/code> embeds &lt;code>clang-format&lt;/code> support, which is a code formatting tool - one of the best in C and C++ environment. &lt;code>clang-format&lt;/code> allows you to manually or automatically format the code (for example, on-save, format-while-typing is not yet supported with &lt;code>clangd&lt;/code>) according to your preferences, which you can set by creating &lt;code>.clang-format&lt;/code> file in your workspace root, with configuration options for &lt;code>clang-format&lt;/code>.&lt;/p>
&lt;p>The list of configuration options is pretty long and can be found here: &lt;a href="https://clang.llvm.org/docs/ClangFormatStyleOptions.html">https://clang.llvm.org/docs/ClangFormatStyleOptions.html&lt;/a>. I&amp;rsquo;m not gonna describe them all of course, i&amp;rsquo;ll just show an setup example.&lt;/p>
&lt;p>Let&amp;rsquo;s create a &lt;code>.clang-format&lt;/code> file in our workspace root dir, and put this inside:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-clang-format" data-lang="clang-format">BasedOnStyle: Chromium
IndentWidth: 2
Standard: c++17
BreakBeforeBraces: Linux
&lt;/code>&lt;/pre>&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/clang-format.png" alt="clang-format">&lt;/p>
&lt;p>If &lt;code>clangd&lt;/code> is the only formatter you have installed, it should be treated as default. If not, make sure it is by opening command list (&lt;code>Ctrl+Shift+P&lt;/code>), looking for &lt;code>Format Document With...&lt;/code> option, and setting default formatter to &lt;code>clangd&lt;/code>.&lt;/p>
&lt;p>Now, open a &lt;code>main&lt;/code> file and either right-click and pick &lt;code>Format document&lt;/code> option, or use a shortcut (&lt;code>Shift+Alt+F&lt;/code> on Windows by default). The code should now look like this:&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/main-formatted.png" alt="main-formatted">&lt;/p>
&lt;p>If it had been formatted differently, check the &lt;code>clangd&lt;/code> output in VSCode window. All the issues and errors with &lt;code>.clang-format&lt;/code> file should be there.&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/clangd-output.png" alt="clangd-output">&lt;/p>
&lt;p>You can set autoformatting in VSCode settings - open settings window with &lt;code>Ctrl+,&lt;/code> or &lt;code>File -&amp;gt; Preferences -&amp;gt; Settings&lt;/code>, and look for &lt;code>format on&lt;/code> options&lt;/p>
&lt;p>&lt;img src="https://steelph0enix.github.io/img/vscode-cpp-setup/vsc-format-settings.png" alt="vsc-format-on">&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>And that would be it. I hope it&amp;rsquo;s not too long.&lt;/p>
&lt;p>If there are any issues or questions about the guide itself or the setup process, feel free to &lt;a href="https://steelph0enix.github.io/about/">contact me&lt;/a>!&lt;/p></content></item></channel></rss>