Nat!'s JournalJekyll2024-01-25T23:52:21+01:00https://www.mulle-kybernetik.com/weblog/Nat!https://www.mulle-kybernetik.com/weblog/https://www.mulle-kybernetik.com/weblog/2024/mulle_objc_0_23_release.html2024-01-23T09:48:00+01:002024-01-23T09:48:00+01:00Nat!https://www.mulle-kybernetik.com/weblog<figure>
<a href="//mulle-objc.github.io"><img src="/weblog/images/35923740.png" alt="mulle-objc logo and link to homepage" /></a>
<figcaption><a href="//mulle-objc.github.io">mulle-objc project homepage</a></figcaption>
</figure>
<h2 id="how-to-update">How to update</h2>
<p>Follow the instructions in <a href="/github.com/MulleFoundation/foundation-developer">foundation-developer</a>
to install a new development environment.</p>
<p>To update an existing project, you should clean the (global)
archive and mirror cache.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mulle-sde <span class="nt">-f</span> clean cache
</code></pre></div></div>
<p>Then upgrade each project and rebuild:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mulle-sde upgrade
mulle-sde craft <span class="nt">-g</span>
</code></pre></div></div>
<h2 id="important-changes-for-existing-projects">Important changes for existing projects</h2>
<p>There have been some API changes and changes of conventions to be aware
of.</p>
<h3 id="move-mulle-testallocator-dependencies-below-mulle-core">Move mulle-testallocator dependencies below mulle-core</h3>
<p>In your tests and wherever you use the <code class="language-plaintext highlighter-rouge">mulle-testallocator</code> in conjunction
with the <code class="language-plaintext highlighter-rouge">mulle-core</code> library, you have to have an entry in the
sourcetree for <code class="language-plaintext highlighter-rouge">mulle-core</code> before <code class="language-plaintext highlighter-rouge">mulle-testallocator</code>. Either by explicitly
adding and moving it there, or by virtue of another dependency doing it.
As an example, if your test dependencies look like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># address supermarks aliases include
- ------- ---------- ------- -------
0 mulle-testallocator C,LinkForce
1 MulleObjC-startup ObjC,Serial,Startup
2 MulleThread ObjC
</code></pre></div></div>
<p>then move <code class="language-plaintext highlighter-rouge">mulle-testallocator</code> to position 1 or 2, since both of these
projects will depend on the mulle-objc-runtime, which in turn depends on
mulle-core. (<code class="language-plaintext highlighter-rouge">mulle-sde dep move 0 to 1</code>)</p>
<h3 id="avoid-doubling-of-mulle-core">Avoid doubling of mulle-core</h3>
<p>(like anything that is based on mulle-objc-runtime).</p>
<p>What doesn’t work currently is “doubling” what’s in mulle-core. So
if you have done <code class="language-plaintext highlighter-rouge">mulle-sde add github:mulle-c/mulle-core</code>, you can not add
a constituent library like <code class="language-plaintext highlighter-rouge">mulle-sde add github:mulle-c/mulle-allocator</code>,
as the cmake reflection will still try to locate <code class="language-plaintext highlighter-rouge">-lmulle-allocator</code>. This
will be fixed though, hopefully in the next release.</p>
<h3 id="generated-identifiers-for-c-code-have-changed">Generated identifiers for C code have changed</h3>
<p>The way install shield identifiers and version numbers as well as sourcetree
macro names are generated has changed again. A file named ‘mulle-c11.h’ used
to have <code class="language-plaintext highlighter-rouge">#ifndef mulle_c_h__</code> but now has <code class="language-plaintext highlighter-rouge">#ifndef mulle__c_h__</code>. This was
necessary, to be able to distinguish projects named “mulle-thread” against
“MulleThread”. If naming projects like that is good practice is questionable
though, so maybe the “Mulle” prefix for Objective-C might fall to the wayside
and something else will be used…</p>
<h3 id="mulle-sde-run-is-now-a-different-command"><code class="language-plaintext highlighter-rouge">mulle-sde run</code> is now a different command</h3>
<p>This may affect existing scripts, where you have to replace <code class="language-plaintext highlighter-rouge">run</code> with <code class="language-plaintext highlighter-rouge">exec</code>.</p>
<h2 id="whats-new-">What’s new ?</h2>
<p>Some of the highlights of this new release are described in separate articles,
to keep this blog entry compact:</p>
<ul>
<li>Supercharge your C code with <a href="https://www.mulle-kybernetik.com/weblog/2024/mulle_alloca.html">mulle-alloca</a></li>
<li>Pervasive use of <a href="https://www.mulle-kybernetik.com/weblog/2024/do_for.html">do / for </a> blocks</li>
<li>Die neue <a href="https://www.mulle-kybernetik.com/weblog/2024/die_neue_s_klasse.html">S Klasse</a></li>
</ul>
<h3 id="-mulleweb">🎒 MulleWeb</h3>
<p><a href="//github.com/MulleWeb/mulle-scion">mulle-scion</a> got the “S Klasse” treatment.</p>
<h3 id="-mullefoundation">👑 MulleFoundation</h3>
<p>There is now regular expression and wildcards support for <code class="language-plaintext highlighter-rouge">NSString</code>.</p>
<p>The thread class used by <a href="//github.com/MulleFoundation/MulleInvocationQueue">MulleInvocationQueue</a>
has been split off into a separate project <a href="//github.com/MulleFoundation/MulleThread">MulleThread</a>.</p>
<h3 id="-mulle-core">🤠 mulle-core</h3>
<p>There is a new developer in town and the name is
<a href="//github.com/mulle-core/mulle-core-developer">mulle-core</a>. If
you are writing pure C code, make this your go-to library. You won’t be able to
live with out it, once you have used it.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mulle-sde init <span class="nt">-d</span> demo <span class="nt">-m</span> mulle-core/c-developer executable
</code></pre></div></div>
<h3 id="-mulle-c">🧢 mulle-c</h3>
<p>There are a few new libraries. <strong>mulle-regex</strong>, <strong>mulle-rbtree</strong>,
<strong>mulle-storage</strong>, <strong>mulle-linkedlist</strong>.</p>
<p><strong>mulle-container</strong> has undergone once again a large expansion and saw some
rewriting, all to make everything more orthogonal. If you learned one of the
data-structures, the other should be as similar as possible. With do/for
macros, mulle-container data structures are now convenient and concise in
day to day usage, but remain as expressive and tweakable as they were before.</p>
<h3 id="-mulle-sde">💠 mulle-sde</h3>
<p><a href="//github.com/mulle-sde">mulle-fetch</a> has the breaking change, that the “run”
command is now called “exec”, and “run” now executes the “product”
(i.e. the executable).</p>
<p><code class="language-plaintext highlighter-rouge">mulle-sde init</code> has now some dialog options, which make this command easier and
faster to use, since you don’t have to memorize
<code class="language-plaintext highlighter-rouge">-m mulle-foundation/c-developer</code> any more. <code class="language-plaintext highlighter-rouge">mulle-sde extension add</code> also has
a dialog option for convenience. More commands might get this treatment in
the future.</p>
<p><a href="//github.com/mulle-sde/mulle-fetch">mulle-fetch</a> and
<a href="//github.com/mulle-sde/mulle-domain">mulle-domain</a> are now even better at
understanding convenience dependency specifiers like
<code class="language-plaintext highlighter-rouge">github:mulle-c/mulle-container.zip@latest</code>
or <code class="language-plaintext highlighter-rouge">zlib:mulle-core/mulle-core</code>.</p>
<p>There is a new project <a href="//github.com/mulle-sde/mulle-menu">mulle-menu</a>,
which is a nice way for the user to make a choice from a menu of options.</p>
<h2 id="release-notes">Release Notes</h2>
<p>This is the list of release notes of the various projects. Enjoy the read :)
Projects with only a small bug fix have been omitted for brevity.</p>
<h3 id="changes-for-0231">Changes for 0.23.1</h3>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>Last Release</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-core/mulle-core-developer/blob/release/RELEASENOTES.md">mulle-core-developer</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-domain/blob/release/RELEASENOTES.md">mulle-domain</a></td>
<td>1.5.0</td>
<td>1.4.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-env/blob/release/RELEASENOTES.md">mulle-env</a></td>
<td>5.2.0</td>
<td>5.1.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-fetch/blob/release/RELEASENOTES.md">mulle-fetch</a></td>
<td>4.0.0</td>
<td>3.2.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-sde-developer/blob/release/RELEASENOTES.md">mulle-sde-developer</a></td>
<td>0.27.0</td>
<td>0.26.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-sde/blob/release/RELEASENOTES.md">mulle-sde</a></td>
<td>3.0.0</td>
<td>2.4.0</td>
</tr>
</tbody>
</table>
<h3 id="changes-for-0230">Changes for 0.23.0</h3>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>Last Release</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/MulleFoundation/foundation-developer/blob/release/RELEASENOTES.md">foundation-developer</a></td>
<td>0.22.0</td>
<td>0.21.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-allocator/blob/release/RELEASENOTES.md">mulle-allocator</a></td>
<td>6.0.0</td>
<td>5.0.1</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleWeb/MulleBashStringExpansion/blob/release/RELEASENOTES.md">MulleBashStringExpansion</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-buffer/blob/release/RELEASENOTES.md">mulle-buffer</a></td>
<td>3.5.0</td>
<td>3.4.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-c11/blob/release/RELEASENOTES.md">mulle-c11</a></td>
<td>4.4.0</td>
<td>4.3.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-container/blob/release/RELEASENOTES.md">mulle-container</a></strong></td>
<td><strong>8.0.0</strong></td>
<td>7.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-core/blob/release/RELEASENOTES.md">mulle-core</a></td>
<td>0.2.0</td>
<td>0.1.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleFoundationBase/blob/release/RELEASENOTES.md">MulleFoundationBase</a></td>
<td>0.23.0</td>
<td>0.22.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-concurrent/mulle-linkedlist/blob/release/RELEASENOTES.md">mulle-linkedlist</a></strong></td>
<td><strong>0.0.2</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-developer/blob/release/RELEASENOTES.md">mulle-objc-developer</a></td>
<td>0.26.0</td>
<td>0.25.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleObjCRegexFoundation/blob/release/RELEASENOTES.md">MulleObjCRegexFoundation</a></strong></td>
<td><strong>0.20.5</strong></td>
<td>-</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-rbtree/blob/release/RELEASENOTES.md">mulle-rbtree</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-regex/blob/release/RELEASENOTES.md">mulle-regex</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-sprintf/blob/release/RELEASENOTES.md">mulle-sprintf</a></td>
<td>3.1.0</td>
<td>3.0.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-storage/blob/release/RELEASENOTES.md">mulle-storage</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleThread/blob/release/RELEASENOTES.md">MulleThread</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-utf/blob/release/RELEASENOTES.md">mulle-utf</a></td>
<td>4.0.0</td>
<td>3.1.3</td>
</tr>
</tbody>
</table>
<h2 id="cosmopolitan-is-still-stale-but-not-much-longer">Cosmopolitan is still stale, but not much longer</h2>
<p>Cosmopolitan building on the fly is still prohibitive, but its probably easily
fixable. But not yet in this release.</p>
https://www.mulle-kybernetik.com/weblog/2024/do_for.html2024-01-20T09:48:00+01:002024-01-20T09:48:00+01:00Nat!https://www.mulle-kybernetik.com/weblog<p><a href="//mulle-objc.github.io"><img src="/weblog/images/35923740.png" alt="logo" /></a></p>
<p>There are two ongoing trends in the mulle libraries. One is to wrap short
lived data structures into a “do” block. The other is to hide the enumerators
in “for” blocks.</p>
<p>As an example, lets write a function, that accepts some variable arguments,
puts them into a <code class="language-plaintext highlighter-rouge">mulle_pointerarray</code>. Then the array is reverse enumerated,
and the contents printed line by line:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="nf">reverse_print</span><span class="p">(</span> <span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span><span class="p">,</span> <span class="p">...)</span>
<span class="p">{</span>
<span class="kt">va_list</span> <span class="n">args</span><span class="p">;</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">s</span><span class="p">;</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">item</span><span class="p">;</span>
<span class="n">va_start</span><span class="p">(</span> <span class="n">args</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span>
<span class="n">mulle_pointerarray_do</span><span class="p">(</span> <span class="n">array</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// fill array with arguments</span>
<span class="k">while</span><span class="p">(</span> <span class="p">(</span><span class="n">s</span> <span class="o">=</span> <span class="n">va_arg</span><span class="p">(</span> <span class="n">args</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="p">)))</span>
<span class="p">{</span>
<span class="n">mulle_pointerarray_add</span><span class="p">(</span> <span class="n">array</span><span class="p">,</span> <span class="n">s</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// now go from back to front and print</span>
<span class="n">mulle_pointerarray_for_reverse</span><span class="p">(</span> <span class="n">array</span><span class="p">,</span> <span class="n">item</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">fprintf</span><span class="p">(</span> <span class="n">fp</span><span class="p">,</span> <span class="s">"%s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">item</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// the array contents are freed and variable "array" is undefined outside</span>
<span class="c1">// of the block</span>
<span class="n">va_end</span><span class="p">(</span> <span class="n">args</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The opportunities for bugs aren’t eliminated, but things look tidier and
tighter.</p>
<p>All data structures of mulle-container now can be used in this “do/for” manner.
And many other data structures in other parts of mulle-core and even the
MulleFoundation (like <code class="language-plaintext highlighter-rouge">NSLockingDo</code>) partake in the scheme.</p>
https://www.mulle-kybernetik.com/weblog/2024/mulle_alloca.html2024-01-19T09:48:00+01:002024-01-19T09:48:00+01:00Nat!https://www.mulle-kybernetik.com/weblog<p><a href="//mulle-objc.github.io"><img src="/weblog/images/35923740.png" alt="logo" /></a></p>
<h2 id="use-mulle_alloca_do-like-a-better-alloca">Use <code class="language-plaintext highlighter-rouge">mulle_alloca_do</code> like a better alloca</h2>
<p>mulle-allocator provides the <code class="language-plaintext highlighter-rouge">mulle_alloca_do</code> macro for stack based
storage, that gets swapped to heap based when the size exceeds a certain
compile time size.</p>
<blockquote>
<h4 id="what-is-alloca--excerpts-from-man-alloca">What is <code class="language-plaintext highlighter-rouge">alloca</code> ? Excerpts from <code class="language-plaintext highlighter-rouge">man alloca</code></h4>
<p>The alloca() function allocates size bytes of space in the stack frame
of the caller. This temporary space is automatically freed the
function that called alloca() returns to its caller… There is no error
indication, if the stack frame cannot be extended… For certain applications,
its use can improve efficiency compared to the use of malloc plus free…
Otherwise, its use is discouraged…</p>
</blockquote>
<p>Here is an example function, where the use of <code class="language-plaintext highlighter-rouge">alloca</code> avoids a <code class="language-plaintext highlighter-rouge">malloc</code>
and <code class="language-plaintext highlighter-rouge">free</code> call. Getting stack space is super cheap and the
automatic reclamation makes the code easier to write:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">print_uppercase</span><span class="p">(</span> <span class="kt">char</span> <span class="o">*</span><span class="n">s</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">copy</span><span class="p">;</span>
<span class="kt">size_t</span> <span class="n">i</span><span class="p">;</span>
<span class="kt">size_t</span> <span class="n">len</span><span class="p">;</span>
<span class="n">len</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span> <span class="n">s</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">copy</span> <span class="o">=</span> <span class="n">alloca</span><span class="p">(</span> <span class="n">len</span><span class="p">);</span>
<span class="k">for</span><span class="p">(</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">len</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="n">copy</span><span class="p">[</span> <span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">toupper</span><span class="p">(</span> <span class="n">s</span><span class="p">[</span> <span class="n">i</span><span class="p">]);</span>
<span class="n">printf</span><span class="p">(</span> <span class="s">"%s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">copy</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The problems are two-fold: <code class="language-plaintext highlighter-rouge">alloca</code> may not be available (or is
“hidden” in a non standard header, which decreases portability). The length
of <code class="language-plaintext highlighter-rouge">s</code> is unknown and the available stack size is also unknown. So this code
is likely to crash for large strings. How large ? Hard to say…</p>
<p><code class="language-plaintext highlighter-rouge">mulle_alloca_do</code> uses a certain safe amount (the default is 128 bytes) of
stack space for a temporary allocation.</p>
<blockquote>
<h3 id="note">Note</h3>
<p>What is a safe amount ? The author thinks <code class="language-plaintext highlighter-rouge">double[8]</code> should be fine.
If your system is very tiny or very large, you can set MULLE_ALLOCA_STACKSIZE
to a size of your liking. Remember though, that if you call other functions
that also use <code class="language-plaintext highlighter-rouge">mulle_alloca_do</code> the reasonable stack size is halved (and
so on). Use <code class="language-plaintext highlighter-rouge">mulle_alloca_do_flexible</code> if you want to set the stack size
per macro invocation.</p>
</blockquote>
<p>But if the requests exceed this amount <code class="language-plaintext highlighter-rouge">mulle_alloca_do</code> will fallback to
malloc/free. See above code transformed to <code class="language-plaintext highlighter-rouge">mulle_alloca_do</code>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">print_uppercase</span><span class="p">(</span> <span class="kt">char</span> <span class="o">*</span><span class="n">s</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// char *copy; // no longer needed, will be declared in mulle_alloca_do</span>
<span class="kt">size_t</span> <span class="n">i</span><span class="p">;</span>
<span class="kt">size_t</span> <span class="n">len</span><span class="p">;</span>
<span class="n">len</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span> <span class="n">s</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">mulle_alloca_do</span><span class="p">(</span> <span class="n">copy</span><span class="p">,</span> <span class="kt">char</span><span class="p">,</span> <span class="n">len</span><span class="p">)</span> <span class="c1">// need type to alloca for alignment</span>
<span class="p">{</span>
<span class="k">for</span><span class="p">(</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">len</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="n">copy</span><span class="p">[</span> <span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">toupper</span><span class="p">(</span> <span class="n">s</span><span class="p">[</span> <span class="n">i</span><span class="p">]);</span>
<span class="n">printf</span><span class="p">(</span> <span class="s">"%s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">copy</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The scope of <code class="language-plaintext highlighter-rouge">copy</code> is now tied to <code class="language-plaintext highlighter-rouge">mulle_alloca_do</code> and not the function!</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* copy not yet in scope */</span>
<span class="n">mulle_alloca_do</span><span class="p">(</span> <span class="n">copy</span><span class="p">,</span> <span class="kt">char</span><span class="p">,</span> <span class="n">size</span><span class="p">)</span>
<span class="p">{</span>
<span class="cm">/* code block, where copy is valid */</span>
<span class="p">}</span>
<span class="cm">/* copy no longer in scope (and freed) */</span>
</code></pre></div></div>
https://www.mulle-kybernetik.com/weblog/2024/die_neue_s_klasse.html2024-01-18T09:48:00+01:002024-01-18T09:48:00+01:00Nat!https://www.mulle-kybernetik.com/weblog<p><a href="//mulle-objc.github.io"><img src="/weblog/images/35923740.png" alt="logo" /></a></p>
<h2 id="bash-like-string-expansion-in-objective-c">Bash like string expansion in Objective-C</h2>
<p>So lets dive right in with a real world example. <a href="https://github.com/mulle-nat/mulle-readme-cms">mulle-readme-cms</a>,
uses the templating language interpreter <a href="https://github.com/mulle-nat/mulle-readme-cms">mulle-scion</a> to generate a <code class="language-plaintext highlighter-rouge">README.md</code> file from constituent files called “buds”.</p>
<p>In the <a href="https://github.com/mulle-core/mulle-core/blob/release/README.md">README.md</a> of <a href="https://github.com/mulle-core/mulle-core">mulle-core</a>,
the “bud”
<a href="https://github.com/mulle-core/mulle-core/blob/release/cola/constituents.md.bud">constituents.bud.md</a>
is responsible for providing the list of constituents,
that look like this:</p>
<table>
<thead>
<tr>
<th>Constituent</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>mulle-allocator</td>
<td>🔄 Flexible C memory allocation scheme</td>
</tr>
<tr>
<td>mulle-buffer</td>
<td>↗️ A growable C char array and also a stream</td>
</tr>
<tr>
<td>…</td>
<td>…</td>
</tr>
<tr>
<td>mulle-time</td>
<td>🕕 Simple time types with arithmetic on timespec and timeval</td>
</tr>
</tbody>
</table>
<p>Now the code that generates the table is this part of “constituents.bud.md”:</p>
<pre><code class="language-objective-c">
{% if [amalgamated count] %}
| Constituent | Description
|----------------------------------------------|-----------------------
{%
sorter = [NSSortDescriptor sortDescriptorWithKey:@"url" ascending:YES]
for item in [amalgamated sortedArrayUsingDescriptors:[NSArray arrayWithObject:sorter]] %}
| [{{ [S $:"item.name#src/"] }}]({{ [S $:"item.url%@*"] }}) | {{ item.description }}
{% endfor %}
{% endif %}
</code></pre>
<p>The mulle-scion executable is linked against the MulleFoundation which has gained <a href="https://en.wikipedia.org/wiki/Regular_expression">“Regular Expressions”</a> and <a href="https://en.wikipedia.org/wiki/Matching_wildcards">“Wildcards”</a>
support for <code class="language-plaintext highlighter-rouge">NSString</code> and now also links against <a href="https://github.com/MulleWeb/MulleBashStringExpansion">MulleBashStringExpansion</a>, which contains a class named “S”.</p>
<p>Inside the loop you can see the new “S Klasse” put to work. If you know your
bashs string expansion, you will recognize how <code class="language-plaintext highlighter-rouge">#src/</code> removes a prefix from
a string, and <code class="language-plaintext highlighter-rouge">%@*</code> removes a suffix with wildcards.</p>
<blockquote>
<h4 id="where-does-the-data-actually-come-from">Where does the data actually come from?</h4>
<p>The value for <code class="language-plaintext highlighter-rouge">amalgamated</code> is taken from <a href="https://github.com/mulle-core/mulle-core/blob/release/cola/properties.plist"><code class="language-plaintext highlighter-rouge">properties.plist</code></a>, which in turn is generated by <a href="https://github.com/mulle-nat/mulle-project/blob/release/mulle-project-properties-plist"><code class="language-plaintext highlighter-rouge">mulle-project-properties-plist</code></a>, which
the README CMS calls on demand.</p>
</blockquote>
https://www.mulle-kybernetik.com/weblog/2023/amzn_music_player.html2023-10-17T21:00:00+02:002023-10-17T21:00:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p><img src="/weblog/images/screeny-amznmusic.png" alt="Screenshot of Amazon Music Player" /></p>
<p>Granted the Spotify player (App) is much, much better than the Amazon Music
Player (Web).</p>
<p>But just for listening to some random music, Amazon isn’t too bad. Since
I already have Prime, I don’t get ads. Whereas Spotify gives me an ad like
every 10 minutes (and always the same ads…).</p>
<p>I use Amazon Music as a “radio” and Spotify, if I want to listen to a specific
track. So whats wrong with Amazon Music in a Firefox browser tab ?</p>
<ul>
<li>it’s inconvenient to locate the tab quickly, to skip a song</li>
<li>music stops, when I restart the browser or accidentally close too many tabs</li>
<li>can’t see whats playing at a glance</li>
<li>lame look and feel</li>
<li>my Firefox clears cookies, so I have to re-login every time</li>
</ul>
<h2 id="fail-1--amazon-music-player-app">Fail 1 : Amazon Music Player app</h2>
<p>There is an actual “Amazon Music Player” app, but it is just a electron junk
app that wraps the website. And it’s not available on Linux.
I tried running it in a Windows VM, but this is just not convenient enough
to bother with.</p>
<h2 id="fail-2--custom-service-in-ferdium">Fail 2 : Custom service in Ferdium</h2>
<p>I thought it would be kinda neat to create a service in <a href="https://github.com/ferdium">Ferdium</a> for Amazon Music. That wasn’t very hard, but the Ferdium browser
couldn’t provide the required DRM plugin. I didn’t try very hard to figure out,
how to enable DRM in Ferdium though, because I got a better idea…</p>
<h2 id="success--custom-firefox-profile">Success : Custom Firefox profile</h2>
<p>You can run multiple Firefox instances with their own <a href="https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data">profiles</a>.</p>
<ol>
<li>Use the <code class="language-plaintext highlighter-rouge">firefox --ProfileManager</code> to create a <code class="language-plaintext highlighter-rouge">Amazon Music</code> profile. Before saving, mark the original “default” profile as the “Use the selected profile without asking at startup” and not the new “Amazon Music” profile!</li>
<li>Start Firefox with the new profile <code class="language-plaintext highlighter-rouge">firefox -P 'Amazon Music'</code>. Configure this Firefox profile, so everything is super permissive. This instance won’t be used for browsing.</li>
<li>Open Amazon Music Player in this profile, accept all cookies. Let Firefox install DRM extensions. Login and navigate to the place which you want to be your home page. Make this your home page in Firefox settings.</li>
<li>Remove all GUI elements manually, except the navigation bar which contains the search bar.</li>
<li>In <code class="language-plaintext highlighter-rouge">about:config</code> set <code class="language-plaintext highlighter-rouge">toolkit.legacyUserProfileCustomization.stylesheets</code> to <code class="language-plaintext highlighter-rouge">true</code></li>
<li>Copy MrOtherGuys <a href="https://github.com/MrOtherGuy/firefox-csshacks/tree/master/chrome/autohide_toolbox.css">userChrome.css</a> into <code class="language-plaintext highlighter-rouge">~/.mozilla/firefox/*.Amazon\ Music/chrome/userChrome.css</code></li>
<li>Create desktop file for GNOME (see below)</li>
<li>Restart and enjoy</li>
</ol>
<h4 id="amazon-music-gnome-desktop-file">Amazon Music GNOME desktop file</h4>
<p>Place this text into a <code class="language-plaintext highlighter-rouge">~/.local/share/applications/amazon-music.desktop</code> file:</p>
<pre><code class="language-desktop">[Desktop Entry]
Version=1.0
Type=Application
Name=Amazon Music Player
Comment=Play Music with Amazon Music Prime
GenericName=Music Player
Categories=GNOME;GTK;Network;Music
Keywords=Amazon;Player;Music
Icon=amazon-music
Exec=firefox -P Amazon\ Music --class AmazonMusic %u
StartupWMClass=AmazonMusic
Terminal=false
StartupNotify=true
</code></pre>
<p>Place this <a href="https://upload.wikimedia.org/wikipedia/commons/9/92/Amazon_Music_logo.svg"><img src="https://upload.wikimedia.org/wikipedia/commons/9/92/Amazon_Music_logo.svg" alt="SVG" /></a> SVG file (Wikipedia) into <code class="language-plaintext highlighter-rouge">~/.local/share/icons/hicolor/symbolic/apps/amazon-music.svg</code>.</p>
https://www.mulle-kybernetik.com/weblog/2023/steam_in_distrobox.html2023-05-13T02:00:00+02:002023-05-13T02:00:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p>I like to keep my main system as bare bones as possible (it is not though).
Projects that come with a plethora of libraries, I try to containerize. My
convenient <a href="https://github.com/mulle-nat/mulle-dockerize">mulle-dockerize</a>
script wraps commands such as <code class="language-plaintext highlighter-rouge">jekyll</code> into commands that then start and
execute a container, but it still was some work to get the Dockerfiles up.</p>
<p>But today I tried something different and I think my old ways <em>may</em> be obsolete
now. The name of the possible gamechanger is
<a href="https://github.com/89luca89/distrobox">distrobox</a>.</p>
<p><img src="/weblog/images//distrobox-steam.jpg" alt="Steam Client running in Ubuntu" /></p>
<h2 id="install-stable-distrobox-1421">Install stable distrobox (1.4.2.1)</h2>
<p>I used the lazy install method, after all distrobox is just a bunch of shell
scripts:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> https://raw.githubusercontent.com/89luca89/distrobox/main/install | <span class="nb">sudo </span>sh
</code></pre></div></div>
<h2 id="create-a-containerdistrobox">Create a container/distrobox</h2>
<blockquote>
<h4 id="note">Note</h4>
<p>See the <a href="#A-distrobox-with-explicit-nvidia-support">end of this article</a>
if you have a NVidia card.</p>
</blockquote>
<p>Now let’s create an ubuntu container named “steam” to host
the <a href="https://repo.steampowered.com/steam/">steam</a> executable:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>distrobox create <span class="nt">-n</span> steam <span class="se">\</span>
<span class="nt">--image</span> ubuntu:latest <span class="se">\</span>
<span class="nt">-p</span> <span class="se">\</span>
<span class="nt">--yes</span> <span class="se">\</span>
<span class="nt">--home</span> ~/.distrobox/steam
distrobox enter steam
</code></pre></div></div>
<blockquote>
<h4 id="memo">Memo</h4>
<p><code class="language-plaintext highlighter-rouge">-n steam</code> : name of the container,
<code class="language-plaintext highlighter-rouge">--image ubuntu:latest</code> : docker image to base the container on,
<code class="language-plaintext highlighter-rouge">-p</code> <code class="language-plaintext highlighter-rouge">--yes</code> : pull, without asking,
–home ~/.distrobox/steam : use a custom home directory for less trouble</p>
</blockquote>
<p>I am gonna call a container managed by <code class="language-plaintext highlighter-rouge">distrobox</code> a “distrobox” from here on.</p>
<h2 id="install-steam-in-distrobox">Install Steam in distrobox</h2>
<p>Now in the distrobox:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-L</span> <span class="nt">-O</span> https://repo.steampowered.com/steam/archive/stable/steam_latest.deb
<span class="nb">sudo </span>dpkg <span class="nt">--add-architecture</span> i386
<span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt <span class="nt">-y</span> dist-upgrade
<span class="nb">sudo </span>apt <span class="nt">-y</span> <span class="nb">install </span>pciutils udev libcanberra-gtk-module
<span class="c"># install and fix missing dependencies afterwards</span>
<span class="nb">sudo </span>dpkg <span class="nt">-i</span> steam_latest.deb
<span class="nb">sudo </span>apt <span class="nt">-y</span> <span class="nt">-f</span> <span class="nb">install
sudo </span>apt <span class="nb">install </span>libc6-i386 <span class="c"># libgl1:i386 libdrm2:i386 libegl1:i386 libgbm1:i386</span>
<span class="c"># depends on ubuntu version seemingly</span>
<span class="nb">sudo </span>apt <span class="nb">install </span>steam-libs-amd64 steam-libs-i386
<span class="nb">sudo </span>ldconfig
</code></pre></div></div>
<h2 id="export-steam-as-a-local-command">Export steam as a local command</h2>
<p>The command will be available in a <code class="language-plaintext highlighter-rouge">bin</code> folder of my choosing that
is reachable from the distrobox. So still in the distrobox:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>distrobox-export <span class="nt">--bin</span> <span class="sb">`</span>which steam<span class="sb">`</span> <span class="nt">--export-path</span> /home/nat/bin
</code></pre></div></div>
<p>Once it’s out there on the host, I can move the command to any place I like.</p>
<h2 id="run-steam">Run steam</h2>
<p>Now back on the host:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>steam
</code></pre></div></div>
<p>This will also work, if the distrobox is not running anymore.
The output should look something like this:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Container steam is not running.
Starting container steam
run this command to follow along:
docker logs -f steam
Starting container... [ OK ]
Installing basic packages... [ OK ]
Setting up read-only mounts... [ OK ]
Setting up read-write mounts... [ OK ]
Setting up host's sockets integration... [ OK ]
Integrating host's themes, icons, fonts... [ OK ]
Setting up package manager exceptions... [ OK ]
Setting up dpkg exceptions... [ OK ]
Setting up apt hooks... [ OK ]
Setting up sudo... [ OK ]
Setting up groups... [ OK ]
Setting up users... [ OK ]
Executing init hooks... [ OK ]
Container Setup Complete!
Unable to determine whether the expected Nvidia drivers are available.
The Steam client may have limited functionality.
steam.sh[1538712]: Running Steam on ubuntu 22.04 64-bit
steam.sh[1538712]: STEAM_RUNTIME is enabled automatically
setup.sh[1538875]: Steam runtime environment up-to-date!
steam.sh[1538712]: Steam client's requirements are satisfied
WARNING: setlocale('en_US.UTF-8') failed, using locale: 'C'. International characters may not work.
[2023-05-13 00:57:28] Startup - updater built Apr 28 2023 18:32:42
[2023-05-13 00:57:28] Startup - Steam Client launched with: '/home/nat/.distrobox/steam/.local/share/Steam/ubuntu12_32/steam'
...
</code></pre></div></div>
<p>Patience is a virtue here, as steam will take its time…</p>
<p>Then I installed one of my Linux compatible games, a small puzzler called
<a href="https://store.steampowered.com/app/96000/The_Tiny_Bang_Story/">The Tiny Bang Story</a>.
To my surprise it worked. Graphics worked without glitching, Sound worked.
It was playable.</p>
<h2 id="remaining-problems">Remaining problems</h2>
<h3 id="x11-warnings">X11 warnings</h3>
<p>Though everything seems to work, I see a lot of errors like these,
which presumably slow down the proceedings:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>_IceTransSocketUNIXConnect: Cannot connect to non-local host myhost
Could not connect to X session manager: Could not open network socket
_IceTransSocketUNIXConnect: Cannot connect to non-local host myhost
_IceTransSocketUNIXConnect: Cannot connect to non-local host myhost
Could not connect to X session manager: Could not open network socket
</code></pre></div></div>
<h3 id="firewall-problems">Firewall problems</h3>
<p>I have the Portmaster firewall running. If I turn it off, the process is quite
snappy. But the X11 warnings remain.</p>
<h2 id="and-the-fix">And the fix!</h2>
<p>Change <code class="language-plaintext highlighter-rouge">/etc/X11/xinit/xserverrc</code> so tcp connections are accepted. (I have a
firewall, so I don’t mind):</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="nb">exec</span> /usr/bin/X <span class="nt">-listen</span> tcp <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span>
</code></pre></div></div>
<p>Now change the <code class="language-plaintext highlighter-rouge">steam</code> command, that was exported in a previous step, so
the <code class="language-plaintext highlighter-rouge">SESSION_MANAGER</code> environment variable is no longer exported:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/sh
# distrobox_binary
# name: steam
if [ ! -f /run/.containerenv ] && [ ! -f /.dockerenv ]
then
unset SESSION_MANAGER
exec /usr/local/bin/distrobox-enter -n steam -- /usr/bin/steam "$@"
else
exec /usr/bin/steam "$@"
fi
</code></pre></div></div>
<p>Now everything works <strong>smoothly</strong>.</p>
<p>This also fixes my Portmaster firewall interference problem. It doesn’t
matter anymore, if the firewall is up or not!</p>
<p><a id="A-distrobox-with-explicit-nvidia-support"></a></p>
<h1 id="a-distrobox-with-explicit-nvidia-support">A distrobox with explicit nvidia support</h1>
<h2 id="install-unstable-distrobox">Install unstable distrobox</h2>
<p>For this I needed a more cutting-edge version of “distrobox”:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> https://raw.githubusercontent.com/89luca89/distrobox/main/install <span class="se">\</span>
| <span class="nb">sudo </span>sh <span class="nt">-s</span> <span class="nt">--</span> <span class="nt">--next</span> <span class="nt">--prefix</span> /usr/local
</code></pre></div></div>
<h2 id="create-a-containerdistrobox-1">Create a container/distrobox</h2>
<p>The <code class="language-plaintext highlighter-rouge">--privileged</code> flag is admittedly just some voodoo from earlier attempts,
that may or may not be needed.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>distrobox create <span class="nt">-n</span> steam-nvidia <span class="se">\</span>
<span class="nt">--image</span> ubuntu:latest <span class="se">\</span>
<span class="nt">--nvidia</span> <span class="se">\</span>
<span class="nt">-p</span> <span class="se">\</span>
<span class="nt">--additional-flags</span> <span class="s2">"--privileged"</span> <span class="se">\</span>
<span class="nt">--yes</span> <span class="se">\</span>
<span class="nt">--home</span> ~/.distrobox/steam
distrobox enter steam-nvidia
</code></pre></div></div>
<h2 id="install-steam-in-distrobox-1">Install Steam in distrobox</h2>
<p>The next part is pretty much unchanged, but the command order is maybe a bit
more sensible:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-L</span> <span class="nt">-O</span> https://repo.steampowered.com/steam/archive/stable/steam_latest.deb
<span class="nb">sudo </span>dpkg <span class="nt">--add-architecture</span> i386
<span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt <span class="nt">-y</span> dist-upgrade
<span class="nb">sudo </span>apt <span class="nt">-y</span> <span class="nb">install </span>pciutils udev libcanberra-gtk-module
<span class="nb">sudo </span>apt <span class="nt">-y</span> <span class="nb">install </span>libc6-i386
<span class="c"># not needed with nvidia image nvidia/opengl:1.2-glvnd-runtime-ubuntu22.04</span>
<span class="nb">sudo </span>apt <span class="nt">-y</span> libgl1:i386 libdrm2:i386 libegl1:i386 libgbm1:i386
<span class="c"># install and fix missing dependencies afterwards</span>
<span class="nb">sudo </span>dpkg <span class="nt">-i</span> steam_latest.deb
<span class="nb">sudo </span>apt <span class="nt">-y</span> <span class="nt">-f</span> <span class="nb">install
sudo </span>ldconfig
</code></pre></div></div>
<p>Everything else is, as explained before.</p>
<p>Then I downloaded and executed <a href="https://steamdb.info/app/1089130/">Quake 2 RTX</a>
and in the console, it recognized my NVidia card. So I would call this a
success.</p>
<h2 id="remaining-problem">Remaining problem</h2>
<p>For some reason I always get a window popping up saying, that some packages
are out of date. I don’t know how to fix this yet.</p>
https://www.mulle-kybernetik.com/weblog/2023/mulle_objc_0_22_1_release.html2023-04-22T10:48:00+02:002023-04-22T10:48:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p><a href="//mulle-objc.github.io"><img src="/weblog/images/35923740.png" alt="logo" /></a></p>
<h2 id="how-to-update">How to update</h2>
<p>Follow the instructions in <a href="/github.com/MulleFoundation/foundation-developer">foundation-developer</a>
to install a new development environment.</p>
<p>To update an existing mulle-objc project, you should clean the (global)
archive and mirror cache.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mulle-sde <span class="nt">-f</span> clean cache
</code></pre></div></div>
<p>Then upgrade each project and rebuild:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mulle-sde upgrade
mulle-sde craft <span class="nt">-g</span>
</code></pre></div></div>
<p>This should give you no trouble.</p>
<h2 id="whats-new-">What’s new ?</h2>
<p>Most of the changes have been in the tooling as described here:</p>
<ul>
<li><a href="https://www.mulle-kybernetik.com/weblog/2023/mulle_objc_0_22_1_1release.html">Cross Platform</a></li>
<li><a href="https://www.mulle-kybernetik.com/weblog/2023/mulle_objc_0_22_1_2release.html">Amalgamation</a></li>
<li><a href="https://www.mulle-kybernetik.com/weblog/2023/mulle_objc_0_22_1_3release.html">README.md CMS</a></li>
<li><a href="https://www.mulle-kybernetik.com/weblog/2023/mulle_objc_0_22_1_4release.html">Supermarks</a></li>
</ul>
<p>Pretty much from the start there was an attempt to furnish <strong>mulle-sde</strong> with
completion support for bash. That has never really panned out though, so I
removed it in this version!</p>
<p>What follows are the changes that pertain to Objective-C: the runtime, the
language, the compiler, the libraries. Since this is only a patch release,
sort of, there are few noteworthy changes.</p>
<h2 id="release-notes">Release Notes</h2>
<p>This is the list of release notes of the various projects. Enjoy the read :)
Projects with only a small bug fix have been omitted for brevity.</p>
<h3 id="mullefoundation">MulleFoundation</h3>
<p>MulleFoundation uses “Almagamation” to combine various projects into
<strong>MulleFoundationBase</strong>. The main advantage gained by this is build speed and
less effort for “legacy” development.</p>
<h3 id="mulle-core">mulle-core</h3>
<p><strong>mulle-core</strong> is an amalgamation of most of the libraries, that are
found under <a href="//github.com/mulle-c/">mulle-c</a>,
<a href="//github.com/mulle-concurrent/">mulle-concurrent</a>. For “legacy” projects,
you can just git clone the repo and run cmake and everything will be there.</p>
<h3 id="mulle-c">mulle-c</h3>
<p>There is a new project <strong>mulle-slug</strong> that turns arbitrary strings into
a URL fragment compatible string. For example the slug of “## Hello World !”
is “Hello-World”. This is used in mulle-markdown to create HTML anchors.</p>
<p><strong>mulle-container</strong> has some important fixes for mulle_range_set and a
breaking API change, pushing it to 7.0.0. <strong>mulle-data</strong> has improved support
for <code class="language-plaintext highlighter-rouge">struct ns_range</code> with some error checks now. <strong>mulle-sprintf</strong> now doesn’t
use constructors anymore.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>Last Release</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/mulle-c/mulle-buffer/blob/release/RELEASENOTES.md">mulle-buffer</a></td>
<td>3.4.0</td>
<td>3.3.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-container/blob/release/RELEASENOTES.md">mulle-container</a></td>
<td>7.0.0</td>
<td>6.1.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-core/mulle-core/blob/release/RELEASENOTES.md">mulle-core</a></strong></td>
<td><strong>0.1.0</strong></td>
<td>-</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleFoundationBase/blob/release/RELEASENOTES.md">MulleFoundationBase</a></strong></td>
<td><strong>0.22.0</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-developer/blob/release/RELEASENOTES.md">mulle-objc-developer</a></td>
<td>0.25.0</td>
<td>0.24.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleScion/blob/release/RELEASENOTES.md">MulleScion</a></td>
<td>1860.0.0</td>
<td>1859.1.10</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/mulle-scion/blob/release/RELEASENOTES.md">mulle-scion</a></td>
<td>1860.0.0</td>
<td>1859.1.9</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-slug/blob/release/RELEASENOTES.md">mulle-slug</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-sprintf/blob/release/RELEASENOTES.md">mulle-sprintf</a></td>
<td>3.0.0</td>
<td>2.2.0</td>
</tr>
</tbody>
</table>
<h3 id="warning-cosmopolitan-is-a-stale-project-at-the-moment">warning: cosmopolitan is a stale project at the moment</h3>
<p>The way cosmopolitan is built has changed in a way, that building it on
the fly is prohibitive. Since AFAIK no one really <em>needs</em> it, I didn’t
invest time to update from cosmopolitan 2.1 to 2.2.</p>
https://www.mulle-kybernetik.com/weblog/2023/mulle_objc_0_22_1_4release.html2023-04-18T00:00:00+02:002023-04-18T00:00:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p>I finally found the time to implement the last feature as specified in the “master-plan”:</p>
<h2 id="supermarks">Supermarks</h2>
<p>As you may or may not know, mulle-sde uses a tagging mechanism called “marks”
for dependencies. For example the marks for the two dependencies
<a href="https://github.com/mulle-c/mulle-c11">mulle-c11</a>
and <a href="https://github.com/mulle-c/mulle-farmhash">farmhash-c</a> for
<a href="https://github.com/mulle-c/mulle-data">mulle-data</a> looks like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>address marks
------- -----
mulle-c11 no-all-load,no-cmake-inherit,no-import,no-link,no-recurse,no-singlephase
src/farmhash-c no-all-load,no-build,no-clobber,no-header,no-import,no-link,no-readwrite,no-share,no-share-shirk
</code></pre></div></div>
<p>A mark directly steers the code generation or the way a dependency is fetched
and linked. Though the meaning of the marks is documented, parsing them
mentally is still burdensome. Supermarks are macros, that combine one or more
marks. With output as supermarks the picture becomes clearer:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>address supermarks
------- ----------
mulle-c11 C,TreeLeaf
src/farmhash-c Amalgamated
</code></pre></div></div>
<p>Here you just have to know that an amalgamated dependency is source code,
that is embedded in your project, but that also resides in a sibling project.</p>
<p>Given the power of the mulle-sourcetree, that is about as easy as it can get :)</p>
<h2 id="the-end-of-mulle-sde-development-">The end of mulle-sde development ?</h2>
<p>So for what it was originally intended, I see mulle-sde as feature complete.
Of course there will be bugs fixes and code refinements.
But for the foreseeable future I would prefer to not touch it and concentrate
on mulle-objc exclusively now.</p>
https://www.mulle-kybernetik.com/weblog/2023/mulle_objc_0_22_1_3release.html2023-04-17T02:01:00+02:002023-04-17T02:01:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p><a href="//mulle-objc.github.io"><img src="/weblog/images/35923740.png" alt="logo" /></a></p>
<h2 id="a-readmemd-cms">A README.md CMS</h2>
<p>As talked about often on these pages, the number of github projects that
are affected with each release is huge. For this release it’s close to
a hundred projects, each with its own versioning and <code class="language-plaintext highlighter-rouge">README.md</code>. Certain
other projects, like MulleUI, aren’t yet in a releaseable state, but they
will appear eventually. These “below the surface” projects add up to maybe
another hundred projects.</p>
<p>Maintaining all these README.mds by hand takes a lot of time during a
release. But these README.mds have a lot in common and some of the information
can be generated from the project information by scripts.</p>
<p>That’s all perfect for templating. As the project already has a
sophisticated template generator <a href="https://github.com/MulleWeb/mulle-scion">mulle-scion</a>, it was fairly easy
to create the <a href="https://github.com/mulle-nat/mulle-readme-cms">mulle-readme-cms</a>
project.</p>
<h3 id="mulle-readme-cms">mulle-readme-cms</h3>
<p>Drawing from biology terminology, each <code class="language-plaintext highlighter-rouge">README.md</code> is created with a
<code class="language-plaintext highlighter-rouge">README.md.scion</code> template, that pulls <strong>buds</strong> and <strong>plist</strong> files (JSON or XML would be OK too) from a <strong>cola</strong> directory, and combines them to form the
<code class="language-plaintext highlighter-rouge">README.md</code> content.</p>
<p>As an example here is a snippet off <code class="language-plaintext highlighter-rouge">README.md.scion</code> as used by <strong>mulle-c</strong>:</p>
<div class="language-twig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">{%</span> <span class="k">if</span> <span class="ow">not</span> <span class="nv">config.skipDescription</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="nv">includes</span> <span class="nv">optionally</span> <span class="s2">"description.md.bud"</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">if</span> <span class="ow">not</span> <span class="nv">config.skipAuthor</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="nv">includes</span> <span class="nv">optionally</span> <span class="s2">"author.md.bud"</span> <span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
</code></pre></div></div>
<p>The description is germane to each individual prioject, but the author
information is the same most of the time.</p>
<p>So in a central <code class="language-plaintext highlighter-rouge">cola</code> shared by all <em>mulle-c</em> projects there is</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cola
├── author.md.bud
├── author.plist
└── README.md.scion
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">author.md.bud</code>:</p>
<div class="language-twig highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## Author</span>
<span class="cp">{%</span> <span class="k">for</span> <span class="nv">author</span> <span class="ow">in</span> <span class="nv">authors</span> <span class="cp">%}</span>
[<span class="cp">{{</span> <span class="nv">author.name</span> <span class="cp">}}</span>](<span class="cp">{{</span> <span class="nv">author.url</span> <span class="cp">}}</span>)<span class="cp">{{</span> <span class="nv">author.organization</span> <span class="err">?</span> <span class="p">[</span><span class="err">@</span><span class="s2">" for "</span> <span class="nv">stringByAppendingString</span><span class="err">:</span><span class="nv">author.organization</span><span class="p">]</span> <span class="err">:</span> <span class="s2">""</span> <span class="cp">}}</span>
<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
</code></pre></div></div>
<p>and <code class="language-plaintext highlighter-rouge">authors.plist</code>:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="err">authors</span><span class="w"> </span><span class="err">=</span><span class="w">
</span><span class="err">(</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="err">name=</span><span class="s2">"Nat!"</span><span class="err">;</span><span class="w">
</span><span class="err">url=</span><span class="s2">"https://mulle-kybernetik.com/weblog"</span><span class="err">;</span><span class="w">
</span><span class="err">organization=</span><span class="s2">"Mulle kybernetiK"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="err">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>These values can then be overridden by <code class="language-plaintext highlighter-rouge">plist</code> files and <code class="language-plaintext highlighter-rouge">bud</code> files in the <code class="language-plaintext highlighter-rouge">cola</code> folder of the project.</p>
<h2 id="long-term-pitfalls">Long term pitfalls</h2>
<p>The information for an individual project is gathered with <code class="language-plaintext highlighter-rouge">mulle-project-properties-plist</code> to create a file like this:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="err">project</span><span class="w"> </span><span class="err">=</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="err">description=</span><span class="s2">"#️⃣ A collection of hash functions"</span><span class="err">;</span><span class="w">
</span><span class="err">domain=</span><span class="s2">"github"</span><span class="err">;</span><span class="w">
</span><span class="err">name=</span><span class="s2">"mulle-data"</span><span class="err">;</span><span class="w">
</span><span class="err">user=</span><span class="s2">"mulle-c"</span><span class="err">;</span><span class="w">
</span><span class="err">repo=</span><span class="s2">"mulle-data"</span><span class="err">;</span><span class="w">
</span><span class="err">homepage=</span><span class="s2">"https://github.com/mulle-c/mulle-data"</span><span class="err">;</span><span class="w">
</span><span class="err">license=</span><span class="s2">"BSD-3-Clause"</span><span class="w">
</span><span class="p">}</span><span class="err">;</span><span class="w">
</span><span class="err">dependencies</span><span class="w"> </span><span class="err">=</span><span class="w">
</span><span class="err">(</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="err">description=</span><span class="s2">"🔀 Cross-platform C compiler glue (and some cpp conveniences)"</span><span class="err">;</span><span class="w">
</span><span class="err">domain=</span><span class="s2">"github"</span><span class="err">;</span><span class="w">
</span><span class="err">name=</span><span class="s2">"mulle-c11"</span><span class="err">;</span><span class="w">
</span><span class="err">repo=</span><span class="s2">"mulle-c11"</span><span class="err">;</span><span class="w">
</span><span class="err">url=</span><span class="s2">"https://github.com/mulle-c/mulle-c11"</span><span class="err">;</span><span class="w">
</span><span class="err">user=</span><span class="s2">"mulle-c"</span><span class="err">;</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="err">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>This information is then transformed into markdown with a <code class="language-plaintext highlighter-rouge">scion</code> template and
<code class="language-plaintext highlighter-rouge">mulle-scion</code>. A problem though appears now, that this <code class="language-plaintext highlighter-rouge">properties.plist</code> file
is now a duplicate of settings contained in the <code class="language-plaintext highlighter-rouge">mulle-sde environment</code> and
the sourcetree <code class="language-plaintext highlighter-rouge">.mulle/etc/sourcetree/config</code> as well as some other files
like <code class="language-plaintext highlighter-rouge">.mulle/etc/project/version.sh</code>.</p>
<p>This is unfortunate as there is a tendendency I have noted in myself, to edit
<code class="language-plaintext highlighter-rouge">properties.plist</code> itself, though I shouldn’t. The lack of a clearly defined
central place for this information (most likely the best place is the
“project” environment) is a blemish I should correct in the next version.</p>
<h2 id="project-related-tools">Project related tools</h2>
<p>The <strong>mulle-project-pacman-pkg</strong> script, can produce arch PKGBUILD files:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">pkgname</span><span class="o">=</span>mulle-buffer
<span class="nv">pkgver</span><span class="o">=</span>3.3.0
<span class="nv">pkgrel</span><span class="o">=</span>1
<span class="nv">pkgdesc</span><span class="o">=</span><span class="s2">"↗️ A growable C char array and also a stream"</span>
<span class="nv">url</span><span class="o">=</span><span class="s2">"https://github.com/mulle-c/mulle-buffer.git"</span>
<span class="nv">license</span><span class="o">=(</span><span class="s1">'BSD-3-Clause'</span><span class="o">)</span>
<span class="nb">source</span><span class="o">=(</span><span class="s2">"https://github.com/mulle-c/mulle-buffer/archive/3.3.0.tar.gz"</span><span class="o">)</span>
<span class="nv">sha256sums</span><span class="o">=(</span><span class="s1">'6e43ecf2f1ae96e604ae53c370cb0e45fd4ec2340974693923af55acca7c39ed'</span><span class="o">)</span>
<span class="nb">arch</span><span class="o">=(</span><span class="s1">'i686'</span> <span class="s1">'x86_64'</span> <span class="s1">'ppc'</span> <span class="s1">'aarm'</span><span class="o">)</span>
<span class="nv">options</span><span class="o">=(</span><span class="s1">'staticlibs'</span><span class="o">)</span>
<span class="nv">depends</span><span class="o">=(</span><span class="s1">'mulle-allocator'</span>
<span class="s1">'mulle-data'</span><span class="o">)</span>
build<span class="o">()</span>
<span class="o">{</span>
cmake <span class="nt">-B</span> build <span class="se">\</span>
<span class="nt">-S</span> <span class="s2">"</span><span class="k">${</span><span class="nv">srcdir</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">pkgname</span><span class="k">}</span><span class="s2">-</span><span class="k">${</span><span class="nv">pkgver</span><span class="k">}</span><span class="s2">"</span> <span class="se">\</span>
<span class="nt">-DCMAKE_INSTALL_PREFIX</span><span class="o">=</span>/usr <span class="se">\</span>
<span class="nt">-DCMAKE_PREFIX_PATH</span><span class="o">=</span>/usr <span class="se">\</span>
<span class="nt">-DCMAKE_BUILD_TYPE</span><span class="o">=</span>Release <span class="o">&&</span>
cmake <span class="nt">--build</span> build <span class="nt">--config</span> Release
<span class="o">}</span>
package<span class="o">()</span>
<span class="o">{</span>
<span class="nv">DESTDIR</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">pkgdir</span><span class="k">}</span><span class="s2">"</span> <span class="se">\</span>
cmake <span class="nt">--install</span> build <span class="nt">--config</span> Release
<span class="o">}</span>
</code></pre></div></div>
<p>The <strong>mulle-project-git-submodule</strong> script produces shell commands to
import dependencies as git submodules if you so desire:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git submodule add https://github.com/mulle-c/mulle-allocator submodule/mulle-allocator
git submodule add https://github.com/mulle-c/mulle-c11 submodule/mulle-c11
git submodule add https://github.com/mulle-c/mulle-data submodule/mulle-data
mulle-sde environment <span class="nt">--global</span> <span class="nb">set </span>MULLE_FETCH_SEARCH_PATH <span class="s1">'${PWD}/submodule:${MULLE_FETCH_SEARCH_PATH}'</span>
</code></pre></div></div>
<p><strong>mulle-project-package-json</strong> produces NPM compatible <code class="language-plaintext highlighter-rouge">package.json</code> output:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"mulle-buffer"</span><span class="p">,</span><span class="w">
</span><span class="nl">"version"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"3.3.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"↗️ A growable C char array and also a stream"</span><span class="p">,</span><span class="w">
</span><span class="nl">"homepage"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://github.com/mulle-c/mulle-buffer"</span><span class="p">,</span><span class="w">
</span><span class="nl">"bugs"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://github.com/mulle-c/mulle-buffer/issues"</span><span class="p">,</span><span class="w">
</span><span class="nl">"keywords"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
</span><span class="nl">"license"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"BSD-3-Clause"</span><span class="p">,</span><span class="w">
</span><span class="nl">"repository"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"git"</span><span class="p">,</span><span class="w">
</span><span class="nl">"url"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"github:mulle-c/mulle-buffer"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"dependencies"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"mulle-allocator"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"git://github.com/mulle-c/mulle-allocator"</span><span class="p">,</span><span class="w">
</span><span class="nl">"mulle-c11"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"git://github.com/mulle-c/mulle-c11"</span><span class="p">,</span><span class="w">
</span><span class="nl">"mulle-data"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"git://github.com/mulle-c/mulle-data"</span><span class="p">,</span><span class="w">
</span><span class="nl">"src/farmhash-c"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"964E1DD0-08BA-4944-B09F-9CBD876383A4"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p><strong>mulle-project-clib-json</strong> produces clib compatible <code class="language-plaintext highlighter-rouge">clib.json</code> output:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"mulle-buffer"</span><span class="p">,</span><span class="w">
</span><span class="nl">"version"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"3.3.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"A growable C char array and also a stream"</span><span class="p">,</span><span class="w">
</span><span class="nl">"keywords"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
</span><span class="nl">"license"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"BSD-3-Clause"</span><span class="p">,</span><span class="w">
</span><span class="nl">"repo"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"mulle-c/mulle-buffer"</span><span class="p">,</span><span class="w">
</span><span class="nl">"src"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"src/generic/include-private.h"</span><span class="p">,</span><span class="w">
</span><span class="s2">"src/generic/include.h"</span><span class="p">,</span><span class="w">
</span><span class="s2">"src/mulle--buffer.c"</span><span class="p">,</span><span class="w">
</span><span class="s2">"src/mulle--buffer.h"</span><span class="p">,</span><span class="w">
</span><span class="s2">"src/mulle-buffer-standalone.c"</span><span class="p">,</span><span class="w">
</span><span class="s2">"src/mulle-buffer.c"</span><span class="p">,</span><span class="w">
</span><span class="s2">"src/mulle-buffer.h"</span><span class="p">,</span><span class="w">
</span><span class="s2">"src/mulle-flexbuffer.h"</span><span class="p">,</span><span class="w">
</span><span class="s2">"src/reflect/_mulle-buffer-include-private.h"</span><span class="p">,</span><span class="w">
</span><span class="s2">"src/reflect/_mulle-buffer-include.h"</span><span class="p">,</span><span class="w">
</span><span class="s2">"src/reflect/_mulle-buffer-versioncheck.h"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"dependencies"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"mulle-c/mulle-allocator"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w">
</span><span class="nl">"mulle-c/mulle-data"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
https://www.mulle-kybernetik.com/weblog/2023/mulle_objc_0_22_1_2release.html2023-04-15T22:00:00+02:002023-04-15T22:00:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p><a href="//mulle-objc.github.io"><img src="/weblog/images/35923740.png" alt="logo" /></a></p>
<h2 id="amalgamated-library-mulle-core">Amalgamated library <code class="language-plaintext highlighter-rouge">mulle-core</code></h2>
<p>I wanted to reduce the compile time, but keep the project structure as is.
As it is now, there are many reusable libraries with a topic (e.g. mulle-fifo)
but the cost of calling <strong>cmake</strong> on each of them add significantly to the
compile time.</p>
<p>The <strong>clib</strong> project provides the infra-structure to embed sources from
other C projects into your own C project. The new library
<a href="https://github.com/mulle-core/mulle-core">mulle-core</a> uses this to
“amalgamate” all mulle-c, mulle-concurrent, mulle-core projects, that do not
need special linker treatment. To enable <strong>mulle-sprintf</strong> to be part of
the amagamation library, I had to let go off the <code class="language-plaintext highlighter-rouge">MULLE_C_CONSTRUCTOR</code> way
to piece the various parts together.</p>
<p>Gone are the <code class="language-plaintext highlighter-rouge">package.json</code> files, which conflict with <strong>clib</strong> which
used <code class="language-plaintext highlighter-rouge">package.json</code> as a synonym for <code class="language-plaintext highlighter-rouge">clib.json</code>.</p>
<p>The net result is, that the compile speed on Linux is now back to the speed
before the Linux compile speed kernel regression, which I am not sure will get
fixed ever.</p>
<p>One downside of a amalgamated library is, that the same source is now
part of two projects. If there was a bug in say mulle-fifo, it’s not
so easy to get to the proper issue tracker. A fictious pull request, could also
not be handled.</p>
<h2 id="amalgamated-library-mullefoundationbase">Amalgamated library <code class="language-plaintext highlighter-rouge">MulleFoundationBase</code></h2>
<p>You can do the same for the Objective-C library, up to the point, where it
diverges into OS specifica (namely MulleObjCOSFoundation, with its subprojects).
The way <strong>MulleObjCOSFoundation</strong> is structured as subprojects and not
as individual projects, becomes more and more of a burden. But all projects
“beneath” it can be combined into a new library. Again build time reduction is
the benefit.</p>
https://www.mulle-kybernetik.com/weblog/2023/mulle_objc_0_22_1_1release.html2023-04-13T14:00:00+02:002023-04-13T14:00:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p><a href="//mulle-objc.github.io"><img src="/weblog/images/35923740.png" alt="logo" /></a></p>
<h2 id="intro">Intro</h2>
<p>This is not the release announcement (yet). I’d like to explain ahead of
time, what’s coming as to not overload the announcement
itself. Ideally this will be a multi-part series, but lets see, how soon I lose
interest in that ;)</p>
<h2 id="cross-platform-expanded">Cross platform expanded</h2>
<h3 id="mulle-sde">mulle-sde</h3>
<p>The main platform in terms of development is and remains Linux. macOS as a
“first class” citizen has been supported for a long time as well. WSL/MiNGW32
has tooling support, but it’s not fully supported as the “Foundation” does not
have enough windows specifica.</p>
<p>I decided to try various other Unix platforms to see, if I could build the
runtime there.</p>
<p>Due to the differences in the development environment, lots of changes had
to be made to the <strong>cmake</strong> files and the <strong>mulle-sde</strong> scripts. So
these additional platforms are now recognized and are expected to compile
the mulle-data:</p>
<ul>
<li>Android (x86)</li>
<li>Dragonfly BSD (5.6)</li>
<li>OpenBSD (7.2)</li>
<li>NetBSD (9.0)</li>
<li>Solaris (11)</li>
</ul>
<p>You’d need to build the mulle-clang compiler yourself. It’s very likely
that OS support is good enough in the Foundation for these OS to be fully
supported, but it hasn’t been tested.</p>
<p>Reliance on external tools in shell scripts like <code class="language-plaintext highlighter-rouge">base64</code> and <code class="language-plaintext highlighter-rouge">uuidgen</code> had to
be removed and recoded in shell script.</p>
<p>To support more platforms there had to be major changes in
<strong>mulle-bashfunctions</strong>, which require a major version bump.
I jettisoned the old bash compatibility requirements for the ancient macOS
shell, where zsh should be used exclusively now. This will free me to use more
actual arrays in bash.</p>
<p>There are now built-in <em>man</em> and <em>apropos</em> commands to quickly look up
functions and function info.</p>
<h3 id="cmake">cmake</h3>
<p>The <strong>cmake</strong> support has been updated to support the more rare OSes.</p>
<p>Changes to the <code class="language-plaintext highlighter-rouge">cmake</code> files facilitate building projects in a legacy
environment, where the dependencies are not local to the project but installed
in a global manner (like <code class="language-plaintext highlighter-rouge">/usr/local</code>). <code class="language-plaintext highlighter-rouge">DEPENDENCY_IGNORE_SYSTEM_LIBARIES</code>
is still the default, but you can turn it off, if you want:</p>
<div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">if</span><span class="p">(</span> DEPENDENCY_IGNORE_SYSTEM_LIBARIES<span class="p">)</span>
<span class="nb">find_library</span><span class="p">(</span> MULLE_DATA_LIBRARY NAMES
<span class="si">${</span><span class="nv">CMAKE_STATIC_LIBRARY_PREFIX</span><span class="si">}</span>mulle-data<span class="si">${</span><span class="nv">CMAKE_DEBUG_POSTFIX</span><span class="si">}${</span><span class="nv">CMAKE_STATIC_LIBRARY_SUFFIX</span><span class="si">}</span>
<span class="si">${</span><span class="nv">CMAKE_STATIC_LIBRARY_PREFIX</span><span class="si">}</span>mulle-data<span class="si">${</span><span class="nv">CMAKE_STATIC_LIBRARY_SUFFIX</span><span class="si">}</span>
mulle-data
NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH
<span class="p">)</span>
<span class="nb">else</span><span class="p">()</span>
<span class="nb">find_library</span><span class="p">(</span> MULLE_DATA_LIBRARY NAMES
<span class="si">${</span><span class="nv">CMAKE_STATIC_LIBRARY_PREFIX</span><span class="si">}</span>mulle-data<span class="si">${</span><span class="nv">CMAKE_DEBUG_POSTFIX</span><span class="si">}${</span><span class="nv">CMAKE_STATIC_LIBRARY_SUFFIX</span><span class="si">}</span>
<span class="si">${</span><span class="nv">CMAKE_STATIC_LIBRARY_PREFIX</span><span class="si">}</span>mulle-data<span class="si">${</span><span class="nv">CMAKE_STATIC_LIBRARY_SUFFIX</span><span class="si">}</span>
mulle-data
<span class="p">)</span>
<span class="nb">endif</span><span class="p">()</span>
</code></pre></div></div>
https://www.mulle-kybernetik.com/weblog/2023/zfs_on_ssd.html2023-02-16T02:29:00+01:002023-02-16T02:29:00+01:00Nat!https://www.mulle-kybernetik.com/weblog<p>Yay, a new SSD is in the house. It’s a <a href="https://www.notebookcheck.com/Deal-Pfeilschnelle-WD-Black-SN850X-2TB-SSD-erreicht-neuen-Bestpreis.694682.0.html">WD SN850X</a>.
Now I want to move those parts of a partition unto it, that hold all my source
code. In my case that is <code class="language-plaintext highlighter-rouge">/home/src</code>. Lets do it with ZFS, which is a first for
me. Why ZFS ? Well the benchmarks seemed enticing (see below).</p>
<h2 id="install">Install</h2>
<blockquote>
<p>Everything has to be run as sudo/root!</p>
</blockquote>
<h3 id="prerequisites">Prerequisites</h3>
<p>I am on Ubuntu 22.04.1 LTS and I need a few packages to get going:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt <span class="nb">install </span>nvme-cli zfsutils-linux gdisk smartmontools
</code></pre></div></div>
<h3 id="setup-ssd">Setup SSD</h3>
<h4 id="find-device">Find device</h4>
<p>I installed the device in a <a href="https://www.amazon.com/M-2-Extension-Hyper-Quad-Card/dp/B082W9Q52V">PCIe expansion card</a>, that can hold up to 4
NVMe SSDs. After I changed the PCIe slot BIOS setting to 4x4x4x4, I can
now see the SSD as <code class="language-plaintext highlighter-rouge">/dev/nvme2n1</code></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nvme list
</code></pre></div></div>
<blockquote>
<h4 id="setup-environment">Setup environment</h4>
<p>To avoid stupid typos, I use a few environment variables. Because ZFS
degrades <em>horribly</em>, if it becomes full, I dedicate 20% to an untouchable
“reservation” that prevents the SSD from becoming more than 80% full.
The reservation is 20% of the SSD size (1800GB) in my case.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">SSD</span><span class="o">=</span><span class="s2">"/dev/nvme2n1"</span>
<span class="nv">PARTITION</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">SSD</span><span class="k">}</span><span class="s2">p1"</span>
<span class="nv">ZFS_RESERVATION</span><span class="o">=</span><span class="s2">"360G"</span>
<span class="nv">ZFS_MOUNTPOINT</span><span class="o">=</span><span class="s2">"/home/src"</span>
<span class="nv">ZFS_POOL</span><span class="o">=</span><span class="s2">"zfs-pool"</span>
</code></pre></div> </div>
</blockquote>
<h4 id="format-ssd-to-4k">Format SSD to 4K</h4>
<p>Figure out the proper block size for the SSD with <code class="language-plaintext highlighter-rouge">nvme id-ns -H ${SSD}</code>
and reformat it:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nvme format <span class="s2">"</span><span class="k">${</span><span class="nv">SSD</span><span class="k">}</span><span class="s2">"</span> <span class="nt">--block-size</span><span class="o">=</span>4096
</code></pre></div></div>
<p><a href="https://www.reddit.com/r/hardware/comments/m18a5c/comment/gqcyjt0/?utm_source=share&utm_medium=web2x&context=3">block-size</a></p>
<h4 id="partition-the-ssd">Partition the SSD</h4>
<p>Partition the SSD:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sgdisk <span class="nt">--zap-all</span> <span class="s2">"</span><span class="k">${</span><span class="nv">SSD</span><span class="k">}</span><span class="s2">"</span>
sgdisk <span class="nt">-a</span> 4096 <span class="nt">-n</span> 1:0.0 <span class="s2">"</span><span class="k">${</span><span class="nv">SSD</span><span class="k">}</span><span class="s2">"</span>
</code></pre></div></div>
<p>Technically you can leave this out and use the unpartitioned SSD as a pool
device, but I prefer it this way, and the next steps depend on a
partition.</p>
<p><a href="https://linux.die.net/man/8/sgdisk">linux.die.net</a></p>
<h3 id="setup-zfs">Setup ZFS</h3>
<h4 id="create-zfs-pool-on-partition">Create zfs pool on partition</h4>
<p>Now add the partition to a fresh ZFS zpool:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>zpool create <span class="nt">-m</span> none <span class="nt">-o</span> <span class="nv">autotrim</span><span class="o">=</span>on <span class="nt">-o</span> <span class="nv">ashift</span><span class="o">=</span>12 <span class="s2">"</span><span class="k">${</span><span class="nv">ZFS_POOL</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PARTITION</span><span class="k">}</span><span class="s2">"</span>
</code></pre></div></div>
<p><a href="https://www.reddit.com/r/zfs/comments/ntinff/comment/h15vaez/?utm_source=share&utm_medium=web2x&context=3">autotrim+ashift</a></p>
<h4 id="create-two-filesystems">Create two filesystems</h4>
<p>Leave out 20% of SSD space as insurance against ZFS degradation:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>zfs create <span class="nt">-o</span> <span class="nv">reservation</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">ZFS_RESERVATION</span><span class="k">}</span><span class="s2">"</span> <span class="se">\</span>
<span class="nt">-o</span> <span class="nv">atime</span><span class="o">=</span>off <span class="se">\</span>
<span class="nt">-o</span> <span class="nv">xattr</span><span class="o">=</span>sa <span class="se">\</span>
<span class="nt">-o</span> <span class="nv">acltype</span><span class="o">=</span>posixacl <span class="se">\</span>
<span class="nt">-o</span> <span class="nv">mountpoint</span><span class="o">=</span>none <span class="se">\</span>
<span class="s2">"</span><span class="k">${</span><span class="nv">ZFS_POOL</span><span class="k">}</span><span class="s2">"</span>/performance-protection
</code></pre></div></div>
<p>Lets not immediately mount to ${ZFS_MOUNTPOINT}, because some data from this
location needs to be copied unto the new file system:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>zfs create <span class="nt">-o</span> <span class="nv">atime</span><span class="o">=</span>off <span class="se">\</span>
<span class="nt">-o</span> <span class="nv">dedup</span><span class="o">=</span>on <span class="se">\</span>
<span class="nt">-o</span> <span class="nv">xattr</span><span class="o">=</span>sa <span class="se">\</span>
<span class="nt">-o</span> <span class="nv">acltype</span><span class="o">=</span>posixacl <span class="se">\</span>
<span class="nt">-o</span> <span class="nv">mountpoint</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">ZFS_MOUNTPOINT</span><span class="k">}</span><span class="s2">-new"</span> <span class="se">\</span>
<span class="s2">"</span><span class="k">${</span><span class="nv">ZFS_POOL</span><span class="k">}</span><span class="s2">/</span><span class="sb">`</span><span class="nb">basename</span> <span class="nt">--</span> <span class="s2">"</span><span class="k">${</span><span class="nv">ZFS_MOUNTPOINT</span><span class="k">}</span><span class="s2">"</span><span class="sb">`</span><span class="s2">"</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">zfs list</code> should now give this:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NAME USED AVAIL REFER MOUNTPOINT
zfs-pool 360G 1.40T 96K none
zfs-pool/performance-protection 96K 1.76T 96K none
zfs-pool/src 96K 1.40T 96K /home/src-new
</code></pre></div></div>
<p>and the filesystem is already mounted!</p>
<p><a href="https://www.stefanux.de/wiki/doku.php/linux/zfs">atime</a>
<a href="https://www.reddit.com/r/zfs/comments/dltik7/comment/f4yl8z8/">xattr+acltype</a></p>
<blockquote>
<p>There is a choice to be made between <code class="language-plaintext highlighter-rouge">dedup</code> and <code class="language-plaintext highlighter-rouge">compression</code>. I chose
<code class="language-plaintext highlighter-rouge">dedup</code>, because we benched compression vs non-compression on another system
and the slowdown was significant. Granted that system isn’t as powerful as
my desktop. The conventional wisdom is <code class="language-plaintext highlighter-rouge">compression</code>, I chose <code class="language-plaintext highlighter-rouge">dedup</code>.
Doing both seems wasteful.</p>
</blockquote>
<h3 id="copy-and-remount">Copy and remount</h3>
<h4 id="rsync-stuff-over">rsync stuff over</h4>
<p>Run this also as sudo (I don’t use the rsync ‘S’ option because its slower, if
you don’t have sparse files (like some VM images):</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rsync <span class="nt">-axHAWX</span> <span class="nt">--numeric-ids</span> <span class="nt">--info</span><span class="o">=</span>progress2 <span class="se">\</span>
<span class="s2">"</span><span class="k">${</span><span class="nv">ZFS_MOUNTPOINT</span><span class="k">}</span><span class="s2">/"</span> <span class="se">\</span>
<span class="s2">"</span><span class="k">${</span><span class="nv">ZFS_MOUNTPOINT</span><span class="k">}</span><span class="s2">-new"</span>
</code></pre></div></div>
<p><a href="https://superuser.com/questions/307541/copy-entire-file-system-hierarchy-from-one-drive-to-another">axHAWXS</a></p>
<blockquote>
<h4 id="note">Note</h4>
<p>This was decidely faster on my initial try, when I had
512B blocks on the SSD still. I suppose its because of a block
size mismatch with my ext4 drive (but I don’t know)</p>
<p>But in the end the 4K blocks give me just a smidgen faster
build times and the wear on the SSD should be better.</p>
</blockquote>
<h3 id="change-the-mount-point">Change the mount point</h3>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mv</span> <span class="s2">"</span><span class="k">${</span><span class="nv">ZFS_MOUNTPOINT</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">ZFS_MOUNTPOINT</span><span class="k">}</span><span class="s2">-old"</span>
zfs <span class="nb">set </span><span class="nv">mountpoint</span><span class="o">=</span>/home/src zfs-pool/<span class="sb">`</span><span class="nb">basename</span> <span class="nt">--</span> <span class="s2">"</span><span class="k">${</span><span class="nv">ZFS_MOUNTPOINT</span><span class="k">}</span><span class="s2">"</span><span class="sb">`</span>
</code></pre></div></div>
<p>And that’s it.</p>
<h2 id="benchmarks-ext4-vs-zfs">Benchmarks Ext4 vs ZFS</h2>
<blockquote>
<p>Caveat: These benchmarks were done with a block size of 512. I later
reformatted the SSD with 4K. I have rerun the last test (ninja) with
4K to show the difference.</p>
</blockquote>
<h2 id="fio">fio</h2>
<p>The “best” test for zfs seems to be <a href="https://github.com/axboe/fio">fio</a> for
some reason. I am using this command:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fio <span class="nt">--randrepeat</span><span class="o">=</span>1 <span class="se">\</span>
<span class="nt">--numjobs</span> 3 <span class="se">\</span>
<span class="nt">--ioengine</span><span class="o">=</span>posixaio <span class="se">\</span>
<span class="nt">--name</span><span class="o">=</span><span class="nb">test</span> <span class="se">\</span>
<span class="nt">--bs</span><span class="o">=</span>4k <span class="se">\</span>
<span class="nt">--iodepth</span><span class="o">=</span>2 <span class="se">\</span>
<span class="nt">--readwrite</span><span class="o">=</span>randrw <span class="se">\</span>
<span class="nt">--rwmixread</span><span class="o">=</span>98 <span class="se">\</span>
<span class="nt">--size</span><span class="o">=</span>1G <span class="se">\</span>
<span class="nt">--filename</span><span class="o">=</span><span class="nv">$PWD</span>/testfile
</code></pre></div></div>
<p>and the results are</p>
<p>ext4s:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>READ: bw=494MiB/s (518MB/s), 164MiB/s-166MiB/s (172MB/s-174MB/s), io=3010MiB (3157MB), run=6057-6099msec
WRITE: bw=10.1MiB/s (10.6MB/s), 3431KiB/s-3486KiB/s (3513kB/s-3570kB/s), io=61.7MiB (64.7MB), run=6057-6099msec
</code></pre></div></div>
<p>zfs:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>READ: bw=1813MiB/s (1902MB/s), 604MiB/s-655MiB/s (634MB/s-686MB/s), io=3010MiB (3157MB), run=1533-1660msec
WRITE: bw=37.2MiB/s (39.0MB/s), 12.5MiB/s-13.4MiB/s (13.1MB/s-14.0MB/s), io=61.7MiB (64.7MB), run=1533-1660msec
</code></pre></div></div>
<p>ZFS crushes this comparison.</p>
<h2 id="mulle-sde-with-cmake--gmake">mulle-sde with cmake / gmake</h2>
<p>I am building a large Objective-C/C project with many dependencies,
the actual command is <code class="language-plaintext highlighter-rouge">mulle-sde clean all ; mulle-sde craft</code>.</p>
<p>ext4:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>real 2m7,117s
user 2m48,457s
sys 0m42,866s
</code></pre></div></div>
<p>zfs:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>real 1m57,936s
user 2m39,020s
sys 0m44,386s
</code></pre></div></div>
<p>Ten seconds better is non-negligble, but it’s not a crushing defeat for ext4
(which was ~50% full).</p>
<h2 id="mulle-sde-with-cmake--ninja">mulle-sde with cmake / ninja</h2>
<p>I noticed at this point, that the ninja version Ubuntu 22 installs is just too
old and my build system doesn’t like it. When it reverted to make, mulle-sde
didn’t use the ‘-j <cores>' option, because ninja doesn't need it...</cores></p>
<blockquote>
<p>Corrected benchmark of mulle-sde with cmake/make with -j set:</p>
<p>zfs:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>real 0m54,466s
user 3m16,574s
sys 0m53,979s
</code></pre></div> </div>
</blockquote>
<p>So I downloaded the newest ninja version and tried again:</p>
<p>ext4:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>real 0m45,373s
user 3m13,417s
sys 0m42,272s
</code></pre></div></div>
<p>zfs:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>real 0m46,803s
user 3m12,275s
sys 0m49,064s
</code></pre></div></div>
<p>zfs: (4K)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>real 0m45,762s
user 3m9,124s
sys 0m45,804s
</code></pre></div></div>
<p>It’s curious and funny, but ext4 is ever so slightly faster in this benchmark.</p>
<h2 id="zfs-considerations">ZFS considerations</h2>
<h3 id="why-lose-20-of-the-ssd-">Why lose 20% of the SSD ?</h3>
<p>ZFS degrades badly, when there is not enough room. What is “not enough room
for ZFS” probably depends on the exact configuration. But folklore (and I) say
that a 80% fill grade is OK, and anything over that is bad.</p>
<p>Here are two fio benchmarks on an actual ZFS system</p>
<p>filled up:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>READ: bw=5347KiB/s (5475kB/s), 1782KiB/s-1785KiB/s (1824kB/s-1828kB/s), io=2764MiB (2898MB), run=528527-529277msec
WRITE: bw=596KiB/s (611kB/s), 197KiB/s-200KiB/s (202kB/s-205kB/s), io=308MiB (323MB), run=528527-529277msec
</code></pre></div></div>
<p>and after clean up:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>READ: bw=240MiB/s (252MB/s), 80.0MiB/s-80.3MiB/s (83.9MB/s-84.2MB/s), io=2764MiB (2898MB), run=11472-11514msec
WRITE: bw=26.8MiB/s (28.1MB/s), 9076KiB/s-9203KiB/s (9294kB/s-9424kB/s), io=308MiB (323MB), run=11472-11514msec
</code></pre></div></div>
<p>To put this into perspective, ZFS managed to turn a NVMe SSD into an ATA/33 drive
and not a particular fast one…</p>
<h3 id="dont-stack-zfs">Don’t stack ZFS</h3>
<p>You have a desktop or server running ZFS, fine. Don’t create VMs with ZFS as
their filesystem. Why ? You are wasting your resources. 20% of the SSD is
gone for the desktop. The usable space is a factor 0.8. Now add a VM with ZFS,
you get another factor 0.8. Combined usable space is now only 0.64 of the
original capacity. Adding insult to injury, you don’t really want your
filesystem to be completely full, lest you might not be able to log in.
What is your comfort zone here ? Maybe 10% ?</p>
<p>That is <code class="language-plaintext highlighter-rouge">(0.8 - (0.8 / 10)) * (0.8 - (0.8 / 10)) ~ 0.5</code>. You halved your SSD.</p>
<p>And that’s just diskspace. ZFS also likes a lot of RAM, again you are paying
twice.</p>
<h3 id="is-zfs-good-or-bad-for-ssd-wear-">Is ZFS good or bad for SSD wear ?</h3>
<p>I couldn’t figure this one out. Here come some open ended musings without
a definite opinion or answer…</p>
<p>With <code class="language-plaintext highlighter-rouge">sudo smartctl -x -A ${SSD} | egrep 'Written|Write|Read'</code>
i can see (amongst other values):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Data Units Read: 150.783 [77,2 GB]
Data Units Written: 1.248.697 [639 GB]
Host Read Commands: 3.342.858
Host Write Commands: 23.090.896
</code></pre></div></div>
<p>Data units are <code class="language-plaintext highlighter-rouge">1000 * 512B</code>. So 150783 comes out to 77.2GB. I did start
out with 512B and copied lots of GB, then reformatted with 4K and copied
again pretty much the same amount. Apparently even if the block size is 4KB
the data units size remains at 512B. Curious.</p>
<p>Supposedly the ratio of “Data Units Written” to “Host Write Commands” is
an indication of <a href="https://en.wikipedia.org/wiki/Write_amplification">Write amplification</a></p>
<p>But the number of “Host Write Commands” doesn’t tell the number of GB that were
transferred. It could be 4K, it could be 512 Bytes, it could be something
else. So if WAF=”Host Write Commands” / “Data Units Written”, then
<code class="language-plaintext highlighter-rouge">23.090.89 / 1.248.697</code> comes out to <code class="language-plaintext highlighter-rouge">0,05</code>. That seems ludicrously low for
WAF.</p>
<p>I checked the SSD before doing a somewhat larger tar operation on ZFS with</p>
<p>before:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Data Units Read: 150.783 [77,2 GB]
Data Units Written: 1.248.741 [639 GB]
Host Read Commands: 3.342.870
Host Write Commands: 23.093.189
</code></pre></div></div>
<p>after:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Data Units Read: 151.358 [77,4 GB]
Data Units Written: 1.251.795 [640 GB]
Host Read Commands: 3.360.901
Host Write Commands: 23.108.672
</code></pre></div></div>
<p>That’s <code class="language-plaintext highlighter-rouge">15483</code> host write commands.
<code class="language-plaintext highlighter-rouge">3054</code> data units were written <code class="language-plaintext highlighter-rouge">3054 * 1000 * 512 = 1.5GB</code>. That matches with the
tar file size.</p>
<p>Let’s try with <code class="language-plaintext highlighter-rouge">ext4</code> (512B formatted):</p>
<p>before:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Data Units Read: 4.602.526 [2,35 TB]
Data Units Written: 13.464.991 [6,89 TB]
Host Read Commands: 47.778.814
Host Write Commands: 169.418.307
</code></pre></div></div>
<p>it’s problematic to catch the right values, as the OS has apparently put a lot
of writes into the queue and is dispensing them slowly, though <em>tar</em> has
finished:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Data Units Read: 4.602.526 [2,35 TB]
Data Units Written: 13.468.907 [6,89 TB]
Host Read Commands: 47.778.814
Host Write Commands: 169.423.843
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">5536</code> host write commands were issued and <code class="language-plaintext highlighter-rouge">3916</code> data units were written.</p>
<p>The relationship between host writes and data units on ext4 vs
zfs is interesting. It is factor 0.7 for ext4. Whereas on zfs its factor 0.2.
Is this WAF, is this better or worse ?</p>
<p>It seems that generally a higher number of “Host Write Commands” compared
to “Data Units Written” is desirable, and therefore ZFS should be a better
choice for SSDs (also <code class="language-plaintext highlighter-rouge">dedup</code> or <code class="language-plaintext highlighter-rouge">compression</code> will be beneficial for SSD wear).</p>
<p><a href="https://serverfault.com/questions/995084/how-to-derive-bytes-written-from-smarts-host-write-commands">stackoverflow</a></p>
https://www.mulle-kybernetik.com/weblog/2022/mulle_objc_0_22_release.html2022-12-20T12:00:00+01:002022-12-20T12:00:00+01:00Nat!https://www.mulle-kybernetik.com/weblog<p><a href="//mulle-objc.github.io"><img src="/weblog/images/35923740.png" alt="logo" /></a></p>
<h2 id="how-to-update">How to update</h2>
<p>Follow the instructions in <a href="/github.com/MulleFoundation/foundation-developer">foundation-developer</a>
to install a new development environment.</p>
<p>To update an existing mulle-objc project, you should clean the (global)
archive and mirror cache.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mulle-sde <span class="nt">-f</span> clean archive
mulle-sde <span class="nt">-f</span> clean mirror
</code></pre></div></div>
<p>Then upgrade each project and rebuild:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mulle-sde <span class="nt">-f</span> clean gravetidy
mulle-sde upgrade
mulle-sde craft
</code></pre></div></div>
<p>This should give you no trouble.</p>
<h2 id="whats-new-">What’s new ?</h2>
<p>As the Foundation stabilizes, the amount of changes to the core mulle-objc
system is not very large. Which is a positive. So maybe the most exciting
aspect of this release is the…</p>
<h3 id="new-documentation">New Documentation</h3>
<p>To ease the first steps into the mulle-objc work, I wrote two small pamphlets
or guides on how to get started with
<a href="//www.mulle-kybernetik.com/mulle-objc-ide/">mulle-objc Development with IDEs</a>:</p>
<p><a href="//www.mulle-kybernetik.com/mulle-objc-ide/"><img src="/weblog/images/dox-preview1.png" alt="mulle-objc Development with IDE" /></a></p>
<p>and how to profit from using mulle-sde in <a href="//www.mulle-kybernetik.com/de-re-mulle-sde/">De Re mulle-sde</a>:</p>
<p><a href="//www.mulle-kybernetik.com/de-re-mulle-sde/"><img src="/weblog/images/dox-preview2.png" alt="De Re mulle-sde" /></a></p>
<p>These are quick reads, so anyone can get going fast and he/she gets a grasp
onto what is going on.</p>
<p>Next year I would like to rework the developer guide. I envision a
reorganization into multiple pamphlets, like: intro the language, intro to the
runtime, intro the MulleObjC.</p>
<h3 id="new-c-library-support">New C library support</h3>
<p>The last release saw the addition of <a href="//musl.libc.org">musl</a> to the list of
supported C libraries. musl allows the production of statically linked mulle-objc
executables, that can be run on pretty much any Linux installation just by
copying the executable there. <a href="//justine.lol/cosmopolitan/index.html">cosmopolitan</a>
takes this one step further and makes it possible to create executables, that
run on macOS, FreeBSD and Linux by just copying the executable there.
<a href="//github.com/MulleWeb/mulle-markdown">mulle-markdown</a> showcases executable
builds with glibc, cosmopolitan, musl.</p>
<h3 id="new-libaries">New Libaries</h3>
<h4 id="mulleinvocationqueue">MulleInvocationQueue</h4>
<p>This is a class related to
<a href="//developer.apple.com/documentation/foundation/nsoperationqueue?language=objc">NSOperationQueue</a>,
but is somewhat more low-level.
You don’t queue <code class="language-plaintext highlighter-rouge">NSOperations</code>, you put in <code class="language-plaintext highlighter-rouge">NSInvocations</code>. Also there
is a defined worker thread, that executes the queue in the background. So
invocations are executed in a serial manner.</p>
<h4 id="mulleobjcuuidfoundation">MulleObjCUUIDFoundation</h4>
<p>This implements the <a href="//developer.apple.com/documentation/foundation/nsuuid?language=objc">NSUUID</a>
class with the help of the <a href="//github.com/rxi/uuid4.git">uuid4</a> library.</p>
<h4 id="mulle_mmapallocator"><code class="language-plaintext highlighter-rouge">mulle_mmapallocator</code></h4>
<p>This library combines a malloc library with shared memory allocation as
a more fine grained mechanism to share data between processes. It’s
still experimental.</p>
<h3 id="improved-mulle-sde-tooling">Improved mulle-sde tooling</h3>
<h4 id="inherited-definitions">Inherited definitions</h4>
<p>As the project scope broadens, the mulle-sde toolset needs to cope with
projects that may be built against different C libraries.
To support mulitple C libraries and their possibly different C compilers,
mulle-craft had to be reworked.
See <a href="//www.mulle-kybernetik.com/de-re-mulle-sde/definition.html">De Re mulle-sde : definition separator</a>
for a quick intro about inherited definitions.</p>
<h4 id="mulle-bash-replaces-lots-of-script-code">mulle-bash replaces lots of script code</h4>
<p>All the various mulle-sde scripts are now more readable, because I could
externalize a lot of boilerplate code. Any mulle-bashfunctions script - which
is the shell script library of mulle-sde - now starts with
<code class="language-plaintext highlighter-rouge">#! /usr/bin/env mulle-bash</code>. This loads mulle-bashfunctions, possibly changes
the shell execution environment to zsh or bash and then executes the script.</p>
<h3 id="mulle-make-rework">mulle-make rework</h3>
<p><strong>mulle-make</strong> has been partially rewritten, to make it somewhat easier to
read. mulle-make now uses <em>cmake</em> exclusively for compilation of cmake projects.
It used to divide this task up to <em>cmake</em> and <em>make</em>/<em>ninja</em>. But that is just
not necessary anymore. Specify the generator you want to use with the
<code class="language-plaintext highlighter-rouge">-DCMAKE_GENERATOR=ninja</code> for example.</p>
<blockquote>
<h3 id="whats-not-new-">What’s not new ?</h3>
<h4 id="windows-support">Windows support</h4>
<p>I wanted to write complete Windows support for the Foundation for this release,
but I didn’t have the time. Since I am rarely on Windows, there was not enough
of a natural impetus to do it.</p>
<h4 id="mulleui">MulleUI</h4>
<p>There was too much work on the fundamental parts of MulleUI, that I could not
get a usable enough widget set together for a release. I feel, that the first
release of MulleUI must be good enough, that it is instantly usable to create
cross-platform applications. That’s still a bit off.</p>
</blockquote>
<h2 id="release-notes">Release Notes</h2>
<p>This is the list of release notes of the various projects. Enjoy the read :)
Projects with only a small bug fix have been omitted for brevity.</p>
<h3 id="mullefoundation">MulleFoundation</h3>
<p>The most interesting changes are the additions of <strong>MulleObjCUUIDFoundation</strong>
and <strong>MulleInvocationQueue</strong>. Otherwise some code moved from
<strong>MulleObjCContainerFoundation</strong> to <strong>MulleObjC</strong>. Overall not a lot has
changed here.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.21 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCLockFoundation/RELEASENOTES.md">MulleObjCLockFoundation</a></td>
<td>0.21.0</td>
<td>0.20.1</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCTimeFoundation/RELEASENOTES.md">MulleObjCTimeFoundation</a></td>
<td>0.1.0</td>
<td>0.0.3</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleObjCUUIDFoundation/RELEASENOTES.md">MulleObjCUUIDFoundation</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCContainerFoundation/RELEASENOTES.md">MulleObjCContainerFoundation</a></td>
<td>0.21.0</td>
<td>0.20.1</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleInvocationQueue/RELEASENOTES.md">MulleInvocationQueue</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/mulle-foundation-developer/RELEASENOTES.md">mulle-foundation-developer</a></td>
<td>0.22.0</td>
<td>0.21.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/foundation-developer/RELEASENOTES.md">foundation-developer</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
</tbody>
</table>
<h3 id="mulleweb">MulleWeb</h3>
<p><strong>mulle-markdown</strong> is now not just a showcase for the use of <strong>musl</strong> with
mulle-objc, but also for the use of <strong>cosmopolitan</strong> with mulle-objc.
<strong>MulleCurl</strong> has an improvement with the way it handles return codes.
Otherwise there have been no real changes to MulleWeb.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.21 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/MulleWeb/MulleCurl/blob/release/RELEASENOTES.md">MulleCurl</a></td>
<td>0.18.0</td>
<td>0.17.5</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleWeb/mulle-markdown/blob/release/RELEASENOTES.md">mulle-markdown</a></strong></td>
<td><strong>0.1.0</strong></td>
<td>0.0.1</td>
</tr>
</tbody>
</table>
<h3 id="mulle-objc">mulle-objc</h3>
<p><strong>MulleObjC</strong> gained some cool features. The actual runtime is pretty much
unchanged and its “+load” version remains as it was before.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.21 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-objc/mulle-objc-runtime/blob/release/RELEASENOTES.md">mulle-objc-runtime</a></strong></td>
<td><strong>0.22.0</strong></td>
<td>0.21.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-objc/MulleObjC/blob/release/RELEASENOTES.md">MulleObjC</a></strong></td>
<td><strong>0.22.0</strong></td>
<td>0.21.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-developer/blob/release/RELEASENOTES.md">mulle-objc-developer</a></td>
<td>0.24.0</td>
<td>0.23.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-cc">mulle-cc</h3>
<p>The <strong>mulle-clang</strong> compiler has two bug fixes. There is now not only support
for <strong>musl</strong> but also for <strong>cosmopolitan</strong>. Some old repositories died, and new
repositories sprang to life.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.21 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-cc/mulle-clang-project/releases/tag/14.0.6.2">mulle-clang-project</a></strong></td>
<td><strong>14.0.6.2</strong></td>
<td>14.0.6.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-cc/mulle-cosmopolitan/blob/release/RELEASENOTES.md">mulle-cosmopolitan</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-cc/mulle-cosmopolitan-cc/blob/release/RELEASENOTES.md">mulle-cosmopolitan-cc</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-cc/mulle-musl-cc/blob/release/RELEASENOTES.md">mulle-musl-cc</a></strong></td>
<td><strong>0.1.0</strong></td>
<td>-</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-cc/mulle-objc-cc/blob/release/RELEASENOTES.md">mulle-objc-cc</a></strong></td>
<td><strong>0.1.0</strong></td>
<td>-</td>
</tr>
</tbody>
</table>
<h3 id="mulle-core">mulle-core</h3>
<p><strong>mulle-dlmalloc</strong> and <strong>mulle-mmapallocator</strong> are new additions. They are
somewhat experimental. Due to changes in <strong>mulle-alloctor</strong>
<strong>mulle-testallocator</strong> has also been affected in an incompatible way.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.21 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-core/mulle-dlmalloc/blob/release/RELEASENOTES.md">mulle-dlmalloc</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-mmap/blob/release/RELEASENOTES.md">mulle-mmap</a></td>
<td>0.2.0</td>
<td>0.1.2</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-core/mulle-mmapallocator/blob/release/RELEASENOTES.md">mulle-mmapallocator</a></strong></td>
<td>0.0.1</td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-sprintf/blob/release/RELEASENOTES.md">mulle-sprintf</a></td>
<td>2.2.0</td>
<td>2.1.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-core/mulle-testallocator/blob/release/RELEASENOTES.md">mulle-testallocator</a></strong></td>
<td>5.0.0</td>
<td>4.2.5</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-time/blob/release/RELEASENOTES.md">mulle-time</a></td>
<td>1.1.0</td>
<td>1.0.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-concurrent">mulle-concurrent</h3>
<p>Due to changes in <strong>mulle-alloctor</strong>, <strong>mulle-aba</strong> freeing API was affected.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.21 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-concurrent/mulle-aba/blob/release/RELEASENOTES.md">mulle-aba</a></strong></td>
<td><strong>3.0.0</strong></td>
<td>2.0.23</td>
</tr>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-fifo/blob/release/RELEASENOTES.md">mulle-fifo</a></td>
<td>0.1.0</td>
<td>0.0.3</td>
</tr>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-thread/blob/release/RELEASENOTES.md">mulle-thread</a></td>
<td>4.5.0</td>
<td>4.4.1</td>
</tr>
</tbody>
</table>
<h3 id="mulle-c">mulle-c</h3>
<p><strong>mulle-musl-gcc</strong> has moved to <strong>mulle-cc/mulle-musl-cc</strong> and is really
quite different now.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.21 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-allocator/blob/release/RELEASENOTES.md">mulle-allocator</a></strong></td>
<td><strong>5.0.0</strong></td>
<td>4.2.5</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-data/blob/release/RELEASENOTES.md">mulle-data</a></strong></td>
<td>0.2.0</td>
<td>0.1.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-buffer/blob/release/RELEASENOTES.md">mulle-buffer</a></strong></td>
<td>3.3.0</td>
<td>3.2.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-container/blob/release/RELEASENOTES.md">mulle-container</a></strong></td>
<td>6.1.0</td>
<td>6.0.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-c-developer/blob/release/RELEASENOTES.md">mulle-c-developer</a></strong></td>
<td><strong>0.15.0</strong></td>
<td>0.14.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-sde">mulle-sde</h3>
<p><strong>mulle-env</strong> has now proper scope loading for additional scope a long standing
bug fix.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.21 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-craft/blob/release/RELEASENOTES.md">mulle-craft</a></strong></td>
<td><strong>3.0.0</strong></td>
<td>2.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-dispense/blob/release/RELEASENOTES.md">mulle-dispense</a></td>
<td>3.2.0</td>
<td>3.1.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-env/blob/release/RELEASENOTES.md">mulle-env</a></td>
<td>4.2.0</td>
<td>4.1.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-make/blob/release/RELEASENOTES.md">mulle-make</a></strong></td>
<td><strong>3.0.0</strong></td>
<td>1.1.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-sde-developer/blob/release/RELEASENOTES.md">mulle-sde-developer</a></td>
<td>0.25.0</td>
<td>0.24.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-sde/blob/release/RELEASENOTES.md">mulle-sde</a></td>
<td>2.0.0</td>
<td>1.1.3</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-sourcetree/blob/release/RELEASENOTES.md">mulle-sourcetree</a></td>
<td>1.2.0</td>
<td>1.1.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-test/blob/release/RELEASENOTES.md">mulle-test</a></td>
<td>6.2.0</td>
<td>6.1.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-nat">mulle-nat</h3>
<p><strong>mulle-bashfunctions</strong> has some removed API, which made a major change
necessary. The way to create mulle-bashfunctions scripts has been greatly
simplified.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.21 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-nat/mulle-bashfunctions/blob/release/blob/release/RELEASENOTES.md">mulle-bashfunctions</a></strong></td>
<td><strong>5.0.0</strong></td>
<td>4.1.0</td>
</tr>
</tbody>
</table>
https://www.mulle-kybernetik.com/weblog/2022/mulle_objc_0_21_release.html2022-07-06T14:20:00+02:002022-07-06T14:20:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p><a href="//mulle-objc.github.io"><img src="/weblog/images/35923740.png" alt="logo" /></a></p>
<h2 id="how-to-update">How to update</h2>
<p>To update an existing mulle-objc project, you should clean the (global)
archive cache and throw away the old downloaded dependencies in <code class="language-plaintext highlighter-rouge">stash</code>. Then
upgrade the project and rebuild:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mulle-sde <span class="nt">-f</span> clean cache
mulle-sde <span class="nt">-f</span> clean tidy
mulle-sde upgrade
mulle-sde craft
</code></pre></div></div>
<p>This should give you no trouble.</p>
<h2 id="whats-new-">What’s new ?</h2>
<p>There are some new features and improvements, including a technological
world premiere and … some breakage.</p>
<h3 id="colorization-for-terminal-output">Colorization for terminal output</h3>
<p>In logging statements color output can be very helpful to quickly
scan output lines for the same object. There is a new method
<code class="language-plaintext highlighter-rouge">-colorizedUTF8String</code> which will be used by the <strong>printf</strong> extension format
“%#@” to print a colorized description of the object.</p>
<p>Implementing a colorization for an object can look like this:</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">-</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="n">colorizerColorCode</span>
<span class="p">{</span>
<span class="k">return</span><span class="p">(</span> <span class="mi">111</span><span class="p">);</span> <span class="c1">// 256 color terminal code</span>
<span class="p">}</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span> <span class="n">colorizerPrefixUTF8String</span>
<span class="p">{</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">s</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">color</span><span class="p">;</span>
<span class="n">color</span> <span class="o">=</span> <span class="p">[</span><span class="n">self</span> <span class="nf">colorizerColorCode</span><span class="p">]</span>
<span class="n">mulle_asprintf</span><span class="p">(</span> <span class="o">&</span><span class="n">s</span><span class="p">,</span> <span class="s">"</span><span class="se">\033</span><span class="s">[38;5;%dm"</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span>
<span class="k">return</span><span class="p">(</span> <span class="n">MulleObjCAutoreleaseAllocation</span><span class="p">(</span> <span class="n">s</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">-colorizerPrefixUTF8String</code> and <code class="language-plaintext highlighter-rouge">-colorizerSuffixUTF8String</code> will be called
by <code class="language-plaintext highlighter-rouge">-[NSObject colorizedUTF8String]</code>.</p>
<p><code class="language-plaintext highlighter-rouge">mulle_asprintf</code> will create a malloced terminal color string, which will
be returned autoreleased.</p>
<h3 id="c-callbacks-for-nsthread">C callbacks for NSThread</h3>
<p>NSThread is very convenient to start a new thread. Yet until now, to just let
a small function run, you needed a class and a method. Now you can just pass
in a C function and some user info.</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">+</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="nf">mulleDetachNewThreadWithFunction</span><span class="p">:(</span><span class="n">MulleThreadFunction_t</span> <span class="o">*</span><span class="p">)</span> <span class="n">f</span>
<span class="n">argument</span><span class="o">:</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span> <span class="n">argument</span><span class="p">;</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span> <span class="nf">mulleInitWithFunction</span><span class="p">:(</span><span class="n">MulleThreadFunction_t</span> <span class="o">*</span><span class="p">)</span> <span class="n">f</span>
<span class="n">argument</span><span class="o">:</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span> <span class="n">argument</span><span class="p">;</span>
</code></pre></div></div>
<h3 id="support-for-static-objective-c-executables-linux">Support for static Objective-C executables (Linux)</h3>
<p>With <a href="https://musl.libc.org/">musl libc</a> instead of glibc as the standard
C library it becomes possible to create Objective-C executables that do not
need <em>any</em> shared libraries. <a href="//github.com/MulleWeb/mulle-markdown">mulle-markdown</a>
is an example of such a standalone executable.</p>
<p>As far as I know, a fully statically linked Objective-C executable has never
been done before, so I claim this as “World Premiere Tech” 😉</p>
<h3 id="support-for-jetbrains--clion">Support for Jetbrains / CLion</h3>
<p>You can now conveniently set up a basic mulle-sde CLion project with</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mulle-sde extension add idea
</code></pre></div></div>
<p>Setup the default toolchain, to use “mulle-clang” as your C compiler and
“mulle-gdb” as your debugger and you should be ready to develop mulle-objc
projects in CLion.</p>
<h3 id="improved-windows-support">Improved Windows support</h3>
<p>Some time as been expended to make <strong>mulle-objc</strong> work on Windows (WIN32).
The tooling should be OK and the libraries up to MulleObjC should work.
There hasn’t been enough effort yet to achieve proper Windows support in
<strong>MulleObjCLockFoundation</strong> and <strong>MulleObjCOSFoundation</strong>.</p>
<p>You use WSL to build native versions of the mulle-objc libraries, and you can
then use the native <strong>mulle-clang-cl.exe</strong> in Visual Studio as the Objective-C
compiler.</p>
<h3 id="mulle-sde-config-switch">mulle-sde config switch</h3>
<p>I am writing a graphics library, that I want to base on multiple backends. This
library called MulleUI has a dependency named MulleUIOS,
which can be based on either
<a href="https://www.libsdl.org/">libsdl</a> or <a href="https://www.glfw.org/">glfw</a>.
With <code class="language-plaintext highlighter-rouge">config select</code> i can now choose between two different sets of
dependencies. MulleUIOS has two sourcetrees alternatives. “config is the
default and “sdl” is the alternative. MulleUIOS usually selects glfw,
and the projects headers and cmake files are “reflected” for it.</p>
<p><img src="//raw.githubusercontent.com/mulle-sde/mulle-sourcetree/release/dox/config-choice.svg" alt="" /></p>
<p>With <code class="language-plaintext highlighter-rouge">mulle-sde config switch</code> you can now choose in “MulleUI” the sourcetree
that “MulleUIOS” should use. This is done via an environment setting in the
main project. This will force on the next <code class="language-plaintext highlighter-rouge">craft</code> a new “reflect” in
“MulleUIOS”, which changes the C headers and cmake files in “MulleUIOS”. So
for example the source code <em>in</em> “MulleUIOS” changes from <code class="language-plaintext highlighter-rouge">#include <libsdl.h></code>
to <code class="language-plaintext highlighter-rouge">#include <GLFW.h></code> or vice versa.</p>
<h3 id="foundation-brew-formula-is-broken-macos">Foundation brew formula is broken (MacOS)</h3>
<p>Unfortunately the ‘homebrew’ formula for <a href="https://github.com/mulle-kybernetik/homebrew-software/blob/master/Foundation.rb.broken">Foundation.rb</a>
is broken. It seems to me, that the brew project really goes out of its way to
make things as cumbersome as possible for formula writers. You can still build
the Foundation with <strong>mulle-sde</strong> with no problems though, but it’s just not
possible to use <strong>mulle-sde</strong> within a brew formula to fetch something via
curl…</p>
<h3 id="raspberry-pi-llvm-build-is-broken">Raspberry PI llvm build is broken</h3>
<p>The llvm code base does not compile with the old gcc compiler delivered with
bullseye and the clang of bullseye crashes when compiling
<code class="language-plaintext highlighter-rouge">lib/Target/AArch64/AArch64ISelLowering.cpp</code>.</p>
<h2 id="release-notes">Release Notes</h2>
<p>This is the list of release notes of the various projects. Enjoy the read :)</p>
<h3 id="mullefoundation">MulleFoundation</h3>
<p>The most interesting change is the addition of <strong>MulleObjCTimeFoundation</strong>,
which was spawned by <strong>MulleObjCOSFoundation</strong> and <strong>MulleObjCValueFoundation</strong>
as a home for <code class="language-plaintext highlighter-rouge">NSDate</code> and <code class="language-plaintext highlighter-rouge">NSTimer</code>. Overall not a lot has changed here.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.20 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCArchiverFoundation/blob/release/RELEASENOTES.md">MulleObjCArchiverFoundation</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCCalendarFoundation/blob/release/RELEASENOTES.md">MulleObjCCalendarFoundation</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCContainerFoundation/blob/release/RELEASENOTES.md">MulleObjCContainerFoundation</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCExpatFoundation/blob/release/RELEASENOTES.md">MulleObjCExpatFoundation</a></td>
<td>0.20.2</td>
<td>0.20.1</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCInetOSFoundation/blob/release/RELEASENOTES.md">MulleObjCInetOSFoundation</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCKVCFoundation/blob/release/RELEASENOTES.md">MulleObjCKVCFoundation</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCLockFoundation/blob/release/RELEASENOTES.md">MulleObjCLockFoundation</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCMathFoundation/blob/release/RELEASENOTES.md">MulleObjCMathFoundation</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleObjCOSFoundation/blob/release/RELEASENOTES.md">MulleObjCOSFoundation</a></strong></td>
<td><strong>0.21.0</strong></td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCPlistFoundation/blob/release/RELEASENOTES.md">MulleObjCPlistFoundation</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCStandardFoundation/blob/release/RELEASENOTES.md">MulleObjCStandardFoundation</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleObjCTimeFoundation/blob/release/RELEASENOTES.md">MulleObjCTimeFoundation</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCUnicodeFoundation/blob/release/RELEASENOTES.md">MulleObjCUnicodeFoundation</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleObjCValueFoundation/blob/release/RELEASENOTES.md">MulleObjCValueFoundation</a></strong></td>
<td><strong>0.21.0</strong></td>
<td>0.20.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/Foundation/blob/release/RELEASENOTES.md">Foundation</a></strong></td>
<td><strong>0.21.0</strong></td>
<td>0.20.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleFoundation/blob/release/RELEASENOTES.md">MulleFoundation</a></strong></td>
<td><strong>0.21.0</strong></td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/objc-compat/blob/release/RELEASENOTES.md">objc-compat</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/mulle-bunchobjects/blob/release/RELEASENOTES.md">mulle-bunchobjects</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/mulle-testgen/blob/release/RELEASENOTES.md">mulle-testgen</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCStandardFoundation-startup/blob/release/RELEASENOTES.md">MulleObjCStandardFoundation-startup</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/Foundation-startup/blob/release/RELEASENOTES.md">Foundation-startup</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleFoundation-startup/blob/release/RELEASENOTES.md">MulleFoundation-startup</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/mulle-foundation-developer/blob/release/RELEASENOTES.md">mulle-foundation-developer</a></strong></td>
<td><strong>0.21.0</strong></td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/foundation-developer/blob/release/RELEASENOTES.md">foundation-developer</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
</tbody>
</table>
<h3 id="mulleweb">MulleWeb</h3>
<p>There is a new project <strong>mulle-markdown</strong>. It’s a small executable to convert
markdown to HTML. It’s a showcase for the use of <strong>musl</strong> with mulle-objc, to
produce a self-contained executable with no shared libraries linked.</p>
<p>Otherwise there have been no real changes to MulleWeb.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.20 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/MulleWeb/MulleObjCHTTPFoundation/blob/release/RELEASENOTES.md">MulleObjCHTTPFoundation</a></td>
<td>0.18.3</td>
<td>0.18.2</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleObjCInetFoundation/blob/release/RELEASENOTES.md">MulleObjCInetFoundation</a></td>
<td>0.18.3</td>
<td>0.18.2</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleObjCJSMNFoundation/blob/release/RELEASENOTES.md">MulleObjCJSMNFoundation</a></td>
<td>0.18.3</td>
<td>0.18.2</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleBase64/blob/release/RELEASENOTES.md">MulleBase64</a></td>
<td>0.0.3</td>
<td>0.0.2</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleCivetWeb/blob/release/RELEASENOTES.md">MulleCivetWeb</a></td>
<td>0.17.7</td>
<td>0.17.6</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleCurl/blob/release/RELEASENOTES.md">MulleCurl</a></td>
<td>0.17.6</td>
<td>0.17.5</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleZlib/blob/release/RELEASENOTES.md">MulleZlib</a></td>
<td>0.15.7</td>
<td>0.15.6</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleHoedown/blob/release/RELEASENOTES.md">MulleHoedown</a></td>
<td>0.2.5</td>
<td>0.2.4</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleWebClient/blob/release/RELEASENOTES.md">MulleWebClient</a></td>
<td>0.0.7</td>
<td>0.0.6</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleWebServer/blob/release/RELEASENOTES.md">MulleWebServer</a></td>
<td>0.0.7</td>
<td>0.0.6</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleScion/blob/release/RELEASENOTES.md">MulleScion</a></td>
<td>1859.1.9</td>
<td>1859.1.8</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleScionHTMLPreprocessor/blob/release/RELEASENOTES.md">MulleScionHTMLPreprocessor</a></td>
<td>0.2.5</td>
<td>0.2.4</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/mulle-scion/blob/release/RELEASENOTES.md">mulle-scion</a></td>
<td>1859.1.8</td>
<td>1859.1.7</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleWeb/mulle-markdown/blob/release/RELEASENOTES.md">mulle-markdown</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
</tbody>
</table>
<h3 id="mulle-objc">mulle-objc</h3>
<p>Finally the synced version numbers are a thing of the past. It’s expected that
<strong>mulle-objc-runtime</strong> and <strong>MulleObjC</strong> remain syned, but the others will
likely fall behind (and have fallen behind).</p>
<p>New is the addition of <a href="">mulle-musl-gcc</a> for support of the <strong>musl</strong> C
library.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.20 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-objc/mulle-objc-runtime/blob/release/RELEASENOTES.md">mulle-objc-runtime</a></strong></td>
<td><strong>0.21.0</strong></td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-debug/blob/release/RELEASENOTES.md">mulle-objc-debug</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-list/blob/release/RELEASENOTES.md">mulle-objc-list</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-objc/mulle-musl-gcc/blob/release/RELEASENOTES.md">mulle-musl-gcc</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-compat/blob/release/RELEASENOTES.md">mulle-objc-compat</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-objc/MulleObjC/blob/release/RELEASENOTES.md">MulleObjC</a></strong></td>
<td><strong>0.21.0</strong></td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/MulleObjC-startup/blob/release/RELEASENOTES.md">MulleObjC-startup</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-runtime-startup/blob/release/RELEASENOTES.md">mulle-objc-runtime-startup</a></td>
<td>0.20.1</td>
<td>0.20.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-objc/mulle-objc-developer/blob/release/RELEASENOTES.md">mulle-objc-developer</a></strong></td>
<td><strong>0.23.0</strong></td>
<td>0.22.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-cc">mulle-cc</h3>
<p><strong>mulle-gdb</strong> remains essentially unchanged, but follows the <strong>gdb</strong> version.
The <strong>mulle-clang</strong> compiler received an update to the newest llvm version and
a code generation bug was fixed.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.20 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-cc/mulle-clang-project/releases/tag/14.0.6.0">mulle-clang-project</a></strong></td>
<td><strong>14.0.6.0</strong></td>
<td>13.0.0.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-cc/mulle-gdb/releases/tag/11.1.0.0">mulle-gdb</a></strong></td>
<td><strong>11.1.0.0</strong></td>
<td>10.1.0.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-core">mulle-core</h3>
<p><strong>mulle-fprintf</strong> and <strong>mulle-sprintf</strong> have seen some significant changes.</p>
<p><strong>mulle-time</strong> now distinguishes between “calendar” time and a process’
“absolute” time.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.20 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/mulle-core/mulle-atexit/blob/release/RELEASENOTES.md">mulle-atexit</a></td>
<td>0.0.11</td>
<td>0.0.10</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-atinit/blob/release/RELEASENOTES.md">mulle-atinit</a></td>
<td>0.0.8</td>
<td>0.0.7</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-dlfcn/blob/release/RELEASENOTES.md">mulle-dlfcn</a></td>
<td>0.0.9</td>
<td>0.0.8</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-core/mulle-fprintf/blob/release/RELEASENOTES.md">mulle-fprintf</a></strong></td>
<td><strong>0.1.0</strong></td>
<td>0.0.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-mmap/blob/release/RELEASENOTES.md">mulle-mmap</a></td>
<td>0.1.2</td>
<td>0.1.1</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-core/mulle-sprintf/blob/release/RELEASENOTES.md">mulle-sprintf</a></strong></td>
<td><strong>2.1.0</strong></td>
<td>2.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-stacktrace/blob/release/RELEASENOTES.md">mulle-stacktrace</a></td>
<td>0.2.5</td>
<td>0.2.4</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-testallocator/blob/release/RELEASENOTES.md">mulle-testallocator</a></td>
<td>4.2.5</td>
<td>4.2.4</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-time/blob/release/RELEASENOTES.md">mulle-time</a></td>
<td>1.0.0</td>
<td>0.1.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-concurrent">mulle-concurrent</h3>
<p>There is nothing new for mulle-concurrent.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.20 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-thread/blob/release/RELEASENOTES.md">mulle-thread</a></td>
<td>4.4.1</td>
<td>4.4.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-fifo/blob/release/RELEASENOTES.md">mulle-fifo</a></td>
<td>0.0.3</td>
<td>0.0.2</td>
</tr>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-aba/blob/release/RELEASENOTES.md">mulle-aba</a></td>
<td>2.0.23</td>
<td>2.0.22</td>
</tr>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-concurrent/blob/release/RELEASENOTES.md">mulle-concurrent</a></td>
<td>2.2.12</td>
<td>2.2.11</td>
</tr>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-multififo/blob/release/RELEASENOTES.md">mulle-multififo</a></td>
<td>0.0.2</td>
<td>0.0.1</td>
</tr>
</tbody>
</table>
<h3 id="mulle-c">mulle-c</h3>
<p><strong>mulle-c11</strong> is now supporting Windows better. There is now support for the
<strong>musl</strong> C library (Linux/FreeBSD only).</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.20 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-c11/blob/release/RELEASENOTES.md">mulle-c11</a></strong></td>
<td><strong>4.2.0</strong></td>
<td>4.1.2</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-allocator/blob/release/RELEASENOTES.md">mulle-allocator</a></td>
<td>4.2.5</td>
<td>4.2.4</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-data/blob/release/RELEASENOTES.md">mulle-data</a></strong></td>
<td>0.1.0</td>
<td>0.0.3</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-buffer/blob/release/RELEASENOTES.md">mulle-buffer</a></strong></td>
<td><strong>3.2.0</strong></td>
<td>3.1.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-container/blob/release/RELEASENOTES.md">mulle-container</a></strong></td>
<td><strong>6.1.0</strong></td>
<td>6.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-http/blob/release/RELEASENOTES.md">mulle-http</a></td>
<td>0.1.4</td>
<td>0.1.3</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-musl-gcc/blob/release/RELEASENOTES.md">mulle-musl-gcc</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-unicode/blob/release/RELEASENOTES.md">mulle-unicode</a></td>
<td>2.4.4</td>
<td>2.4.3</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-url/blob/release/RELEASENOTES.md">mulle-url</a></td>
<td>2.3.4</td>
<td>2.3.3</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-utf/blob/release/RELEASENOTES.md">mulle-utf</a></td>
<td>3.1.1</td>
<td>3.1.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-vararg/blob/release/RELEASENOTES.md">mulle-vararg</a></td>
<td>1.1.3</td>
<td>1.1.2</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-c-developer/blob/release/RELEASENOTES.md">mulle-c-developer</a></strong></td>
<td><strong>0.15.0</strong></td>
<td>0.14.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-sde">mulle-sde</h3>
<p><strong>mulle-sde</strong> sees a major new feature, with the addition of multiple
sourcetrees per project. This makes large projects like the MulleFoundation
more easily configurable.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.20 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-craft/blob/release/RELEASENOTES.md">mulle-craft</a></strong></td>
<td><strong>2.0.0</strong></td>
<td>1.0.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-dispense/blob/release/RELEASENOTES.md">mulle-dispense</a></strong></td>
<td><strong>3.1.0</strong></td>
<td>3.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-domain/blob/release/RELEASENOTES.md">mulle-domain</a></td>
<td>1.0.1</td>
<td>1.0.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-env/blob/release/RELEASENOTES.md">mulle-env</a></strong></td>
<td><strong>4.1.0</strong></td>
<td>4.0.2</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-fetch/blob/release/RELEASENOTES.md">mulle-fetch</a></td>
<td>3.0.1</td>
<td>3.0.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-make/blob/release/RELEASENOTES.md">mulle-make</a></strong></td>
<td><strong>1.1.0</strong></td>
<td>1.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-match/blob/release/RELEASENOTES.md">mulle-match</a></td>
<td>1.0.1</td>
<td>1.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-monitor/blob/release/RELEASENOTES.md">mulle-monitor</a></td>
<td>1.0.1</td>
<td>1.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-platform/blob/release/RELEASENOTES.md">mulle-platform</a></td>
<td>1.0.1</td>
<td>1.0.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-sde/blob/release/RELEASENOTES.md">mulle-sde</a></strong></td>
<td><strong>1.1.0</strong></td>
<td>1.0.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-semver/blob/release/RELEASENOTES.md">mulle-semver</a></td>
<td>1.0.1</td>
<td>1.0.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-sourcetree/blob/release/RELEASENOTES.md">mulle-sourcetree</a></strong></td>
<td><strong>1.1.0</strong></td>
<td>1.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-template/blob/release/RELEASENOTES.md">mulle-template</a></td>
<td>1.0.1</td>
<td>1.0.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-test/blob/release/RELEASENOTES.md">mulle-test</a></strong></td>
<td><strong>6.1.0</strong></td>
<td>6.0.1</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-sde-developer/blob/release/RELEASENOTES.md">mulle-sde-developer</a></strong></td>
<td><strong>0.24.0</strong></td>
<td>0.23.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-nat">mulle-nat</h3>
<p><strong>mulle-project</strong> now contains the convenient <strong>mulle-gitignore</strong> script!</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.20 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-nat/mulle-bashfunctions/blob/release/RELEASENOTES.md">mulle-bashfunctions</a></strong></td>
<td><strong>4.1.0</strong></td>
<td>4.0.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-nat/mulle-project/blob/release/RELEASENOTES.md">mulle-project</a></strong></td>
<td><strong>3.1.0</strong></td>
<td>3.0.0</td>
</tr>
</tbody>
</table>
https://www.mulle-kybernetik.com/weblog/2022/mulle_objc_0_20_release.html2022-01-12T23:30:00+01:002022-01-12T23:30:00+01:00Nat!https://www.mulle-kybernetik.com/weblog<p><a href="//mulle-objc.github.io"><img src="/weblog/images/35923740.png" alt="logo" /></a></p>
<p>Well happy new year to you all.</p>
<p>It’s a new year and lets get it started with the <strong>mulle-objc 0.20</strong> release.</p>
<p>But lets reminisce about the good old days first, which were … 2021!</p>
<h2 id="looking-back-on-2021">Looking back on 2021</h2>
<p>For a “hobby” project, mulle-objc and its toolset are quite beefy. I put the
repositories into <strong>OpenHub</strong>, and these are the results:</p>
<table>
<thead>
<tr>
<th>Organization</th>
<th>Language</th>
<th>Lines of Code</th>
<th>COCOMO Years</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//www.openhub.net/p/MulleWeb">MulleWeb</a></td>
<td>Objective-C</td>
<td>49213</td>
<td>12</td>
</tr>
<tr>
<td><a href="//www.openhub.net/p/MulleFoundation">MulleFoundation</a></td>
<td>Objective-C</td>
<td>135902</td>
<td>34</td>
</tr>
<tr>
<td><a href="//www.openhub.net/p/mulle-objc/">mulle-objc</a></td>
<td>C</td>
<td>74012</td>
<td>18</td>
</tr>
<tr>
<td><a href="//www.openhub.net/p/mulle-core/">mulle-core</a></td>
<td>C</td>
<td>19481</td>
<td>5</td>
</tr>
<tr>
<td><a href="//www.openhub.net/p/mulle-concurrent/">mulle-concurrent</a></td>
<td>C</td>
<td>15500</td>
<td>4</td>
</tr>
<tr>
<td><a href="//www.openhub.net/p/mulle-c/">mulle-c</a></td>
<td>C</td>
<td>40830</td>
<td>10</td>
</tr>
<tr>
<td><a href="//www.openhub.net/p/mulle-sde/">mulle-sde</a></td>
<td>shell script</td>
<td>85485</td>
<td>22</td>
</tr>
<tr>
<td><a href="//www.openhub.net/p/mulle-bashfunctions/">mulle-bashfunctions</a></td>
<td>shell script</td>
<td>5974</td>
<td>2</td>
</tr>
<tr>
<td><a href="//www.openhub.net/p/mulle-bashfunctions/">mulle-project</a></td>
<td>shell script</td>
<td>9389</td>
<td>3</td>
</tr>
</tbody>
<tbody>
<tr>
<td>Total</td>
<td> </td>
<td>435786</td>
<td>110</td>
</tr>
</tbody>
</table>
<p>110 years of developer time have been spent on mulle-objc! And who am I to
disagreee with the COCOMO model… The statistics are for the 0.19 version
and don’t contain the compiler or the debugger.</p>
<p>mulle-objc is somewhat of an iceberg. I would guess that there is just
about the same amount of library code already written, which hasn’t been
released yet. For example MulleUI is coming along nicely and it, or parts of
it, may see a release this year.</p>
<h2 id="2022">2022</h2>
<p>Turning back to the present and the 0.20 release, here is another statistic.
The total number of repositories pushed to github by the mulle-objc 0.20
release was 87. Getting this accomplished typically takes a week, and it
wouldn’t be feasible without <a href="//github.com/mulle-nat/mulle-project">mulle-project</a>.</p>
<p>I still would like to see the time cut down significantly, but the
automation is already quite high. Most of the time spent during a release is
fixing bugs. So with my coders hat on, it’s not like completely wasted time in
terms of the development of the project.</p>
<h3 id="outlook">Outlook</h3>
<p>As I am currently jobless, I can spend much more time on mulle-objc. So I
would expect the pace of development to accelerate significantly. The long term
goal of mulle-objc is still cross-platform Application development. There is a
roadmap for the next five years, but I don’t want to make any promises, so
I keep the roadmap close to my heart and secret :)</p>
<h2 id="whats-new-">What’s new ?</h2>
<h3 id="project-structure">Project structure</h3>
<p>A look at a typical mulle-sde executable project as would be generated by
<code class="language-plaintext highlighter-rouge">mulle-sde init -m mulle-objc/objc-developer</code>, reveals:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>├── cmake
│ ├── README.md
│ ├── reflect
│ │ ├── _Dependencies.cmake
│ │ ├── _Headers.cmake
│ │ ├── _Libraries.cmake
│ │ ├── README.md
│ │ ├── _Resources.cmake
│ │ └── _Sources.cmake
│ └── share
│ ├── AAMSupportObjC.cmake
│ ├── README.md
│ ├── ...
│ └── UnwantedWarningsC.cmake
├── CMakeLists.txt
├── README.md
└── src
├── demo-version.h
├── generic
│ ├── import.h
│ ├── import-private.h
│ ├── include.h
│ ├── include-private.h
│ └── README.md
├── main.m
└── reflect
├── _demo-export.h
├── _demo-import.h
├── _demo-import-private.h
├── _demo-include.h
├── _demo-include-private.h
├── _demo-include-public.h
├── _demo-provide.h
├── objc-loader.inc
└── README.md
</code></pre></div></div>
<ol>
<li>There is now a <code class="language-plaintext highlighter-rouge">_Resources.cmake</code> in <code class="language-plaintext highlighter-rouge">cmake/reflect</code>. Images, text files and sound files with common file extension like jpg or wav will be automatically categorized as a resource. Of course the list of file extensions is extensible with mulle-match patternfiles.</li>
<li>There are a lot of little README.md files all over the place, that explain, what the directory should contain and what each file does. In an ideal world each file or directory in a mulle-objc project should be self-documenting or leading to documentation. These are steps in this direction.</li>
<li>The “generic” headers like <code class="language-plaintext highlighter-rouge">import.h</code> are now in their own directory “generic”. This has advantages for header motility. I should/will be writing a separate article on this in my generic header article series.</li>
</ol>
<p>If you look inside the generated files, like in this instance
<code class="language-plaintext highlighter-rouge">cmake/share/UnwantedWarnings.cmake</code>, you will find a footer like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># extension : mulle-c/c-cmake
# directory : project/all
# template : .../UnwantedWarningsC.cmake
# Suppress this comment with `export MULLE_SDE_GENERATE_FILE_COMMENTS=NO`
</code></pre></div></div>
<p>This will tell you which extension place this file there during installation,
which should make extsnsion a little easier to manager. Also you can inherit
mulle-sde extensions with your own extensions. So the file comments should make
customizing mulle-sde for your purposes a lot easier.</p>
<p>I have also tried to improve the usage texts for many of the mulle-sde
commands.</p>
<h3 id="framework-support">Framework support</h3>
<p>The new CMake templates now support the building of libraries as Frameworks
on the macos/darwin platform.</p>
<h3 id="objective-c-core-types">Objective-C core types</h3>
<p>I abandoned <code class="language-plaintext highlighter-rouge">mulle_utf8_t</code> as the UTF8 character type and went back
to <code class="language-plaintext highlighter-rouge">char</code>. This makes C string methods easier to interface with C. Also
<code class="language-plaintext highlighter-rouge">char *</code> is UTF8 in mulle-objc by default, so it just seemed right.</p>
<p><code class="language-plaintext highlighter-rouge">id</code> is pointer to an object. <code class="language-plaintext highlighter-rouge">void *</code> is a pointer to anything. <code class="language-plaintext highlighter-rouge">NSInteger</code>
is supposed to be able to be big enough to hold a <code class="language-plaintext highlighter-rouge">void *</code>. That would be
<code class="language-plaintext highlighter-rouge">intptr_t</code>. Now the problem is that for <code class="language-plaintext highlighter-rouge">printf</code> there is not modifier to
print <code class="language-plaintext highlighter-rouge">intptr_t</code>. But there is for <code class="language-plaintext highlighter-rouge">ptrdiff_t</code>. I hereby declare that
<code class="language-plaintext highlighter-rouge">sizeof(NSInteger) == sizeof( inptr_t) == sizeof( ptrdiff_t)</code> holds true
for mulle-objc. That way one van use <code class="language-plaintext highlighter-rouge">"%td"</code> to print <code class="language-plaintext highlighter-rouge">NSInteger</code>.
It’s nice to own the language :)</p>
<p>Together with <a href="//github.com/mulle-core/mulle-fprintf">mulle-fprintf</a> it’s now
possible to write:</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">mulle_printf</span><span class="p">(</span> <span class="s">"last: %@ length: %td</span><span class="se">\n</span><span class="s">, [array lastObject], [array count]);</span><span class="err">
</span></code></pre></div></div>
<p>Notice that the format string is a C-string. But an object gets formatted with
<code class="language-plaintext highlighter-rouge">"%@"</code> and there is no need for casting <code class="language-plaintext highlighter-rouge">NSUInteger</code>.</p>
<h3 id="tools">Tools</h3>
<p>As my old company Codeon is no longer, I moved the compiler and the debugger
into a new home. They are now in <a href="//github.com/mulle-cc">mulle-cc</a>. The
big change is noticable for homebrew users, as the old tap “Codeon-GmbH” won’t
give them the new compiler. This should do the trick:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew untap Codeon-GmbH/software
brew tap mulle-cc/software
</code></pre></div></div>
<p>There have been a ton of changes with regards to mulle-sde. It’s too much to
cover in the release notes. Let me drop a few buzzwords:</p>
<ul>
<li>macros for loops</li>
<li>improved include system</li>
<li>fake namespacing</li>
<li>precompiled headers</li>
</ul>
<p>I will probably write a few articles about all the changes.</p>
<p>All tools have been improved in terms of clarity of the documentation and
execution speed. Also there are less bugs…</p>
<p>So if something is unclear or you find a bug somewhere, by all means hit that
“issue” button on some project and let me know.</p>
<p>Also last but not least, mulle-objc is now sponsored by <a href="//jetbrains.com">Jetbrains</a>.
Thanks for the support.</p>
<p>A happy 2022 to everybody, it’s gonna be a good year for Objective-C.</p>
<h2 id="release-notes">Release Notes</h2>
<p>This is the list of release notes of the various projects. Enjoy the read :)</p>
<h3 id="mulleweb">MulleWeb</h3>
<p>There were a few fixes but no substantial changes in MulleWeb,</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.19 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/MulleWeb/MulleBase64/RELEASENOTES.md">MulleBase64</a></td>
<td>0.0.2</td>
<td>0.0.1</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleZlib/RELEASENOTES.md">MulleZlib</a></td>
<td>0.15.6</td>
<td>0.15.5</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleCurl/RELEASENOTES.md">MulleCurl</a></td>
<td>0.17.5</td>
<td>0.17.4</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleHoedown/RELEASENOTES.md">MulleHoedown</a></td>
<td>0.2.4</td>
<td>0.2.3</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleObjCJSMNFoundation/RELEASENOTES.md">MulleObjCJSMNFoundation</a></td>
<td>0.18.2</td>
<td>0.18.1</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleObjCHTTPFoundation/RELEASENOTES.md">MulleObjCHTTPFoundation</a></td>
<td>0.18.2</td>
<td>0.18.1</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleObjCInetFoundation/RELEASENOTES.md">MulleObjCInetFoundation</a></td>
<td>0.18.2</td>
<td>0.18.1</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleCivetWeb/RELEASENOTES.md">MulleCivetWeb</a></td>
<td>0.17.6</td>
<td>0.17.5</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleWebClient/RELEASENOTES.md">MulleWebClient</a></td>
<td>0.0.6</td>
<td>0.0.5</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleWebServer/RELEASENOTES.md">MulleWebServer</a></td>
<td>0.0.6</td>
<td>0.0.5</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleScion/RELEASENOTES.md">MulleScion</a></td>
<td>1859.1.8</td>
<td>1859.1.7</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleScionHTMLPreprocessor/RELEASENOTES.md">MulleScionHTMLPreprocessor</a></td>
<td>0.2.4</td>
<td>0.2.3</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/mulle-scion/RELEASENOTES.md">mulle-scion</a></td>
<td>1859.1.7</td>
<td>1859.1.6</td>
</tr>
</tbody>
</table>
<h3 id="mullefoundation">MulleFoundation</h3>
<p>The most interesting changes are found in <strong>MulleObjCPlistFoundation</strong>, <strong>MulleObjCOSFoundation</strong>, <strong>MulleObjCValueFoundation</strong>.
Overall not a lot has changed here.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.19 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCArchiverFoundation/RELEASENOTES.md">MulleObjCArchiverFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCCalendarFoundation/RELEASENOTES.md">MulleObjCCalendarFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCContainerFoundation/RELEASENOTES.md">MulleObjCContainerFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCExpatFoundation/RELEASENOTES.md">MulleObjCExpatFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCInetOSFoundation/RELEASENOTES.md">MulleObjCInetOSFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCKVCFoundation/RELEASENOTES.md">MulleObjCKVCFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCLockFoundation/RELEASENOTES.md">MulleObjCLockFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCMathFoundation/RELEASENOTES.md">MulleObjCMathFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCOSFoundation/RELEASENOTES.md">MulleObjCOSFoundation</a></td>
<td>0.20.0</td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCPlistFoundation/RELEASENOTES.md">MulleObjCPlistFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCStandardFoundation/RELEASENOTES.md">MulleObjCStandardFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCUnicodeFoundation/RELEASENOTES.md">MulleObjCUnicodeFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCValueFoundation/RELEASENOTES.md">MulleObjCValueFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleFoundation/RELEASENOTES.md">MulleFoundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/Foundation/RELEASENOTES.md">Foundation</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/objc-compat/RELEASENOTES.md">objc-compat</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/mulle-bunchobjects/RELEASENOTES.md">mulle-bunchobjects</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/mulle-testgen/RELEASENOTES.md">mulle-testgen</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCStandardFoundation-startup/RELEASENOTES.md">MulleObjCStandardFoundation-startup</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleFoundation-startup/RELEASENOTES.md">MulleFoundation-startup</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/Foundation-startup/RELEASENOTES.md">Foundation-startup</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/mulle-foundation-developer/RELEASENOTES.md">mulle-foundation-developer</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/foundation-developer/RELEASENOTES.md">foundation-developer</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-objc">mulle-objc</h3>
<p><strong>mulle-objc-runtime</strong> has spawned <strong>mulle-objc-debug</strong>. This reduces the compiled size
of mulle-objc-runtime significantly. mulle-objc-runtime and its internal
structures have seen an overhaul.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.19 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-runtime/RELEASENOTES.md">mulle-objc-runtime</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-runtime-startup/RELEASENOTES.md">mulle-objc-runtime-startup</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-list/RELEASENOTES.md">mulle-objc-list</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-debug/RELEASENOTES.md">mulle-objc-debug</a></td>
<td>0.20.0</td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/MulleObjC/RELEASENOTES.md">MulleObjC</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/MulleObjC-startup/RELEASENOTES.md">MulleObjC-startup</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-compat/RELEASENOTES.md">mulle-objc-compat</a></td>
<td>0.20.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-developer/RELEASENOTES.md">mulle-objc-developer</a></td>
<td>0.22.0</td>
<td>0.21.0</td>
</tr>
</tbody>
</table>
<blockquote>
<p>At the moment all mulle-objc and MulleFoundation projects still have a synced
version number. This was supposed to change, but hasn’t except for
mulle-objc-developer…</p>
</blockquote>
<h3 id="mulle-cc">mulle-cc</h3>
<p><strong>mulle-cc</strong> is a new github org, that holds the <strong>mulle-clang</strong> compiler
and the <strong>mulle-gdb</strong> debugger. A Windows (Visual Studio) download is
available for the compiler for the first time. The mulle-lldb debugger has
been dropped.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.19 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/mulle-cc/mulle-clang-project/releases/tag/13.0.0.1">mulle-clang-project</a></td>
<td>13.0.0.1</td>
<td>12.0.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-cc/mulle-gdb/releases/tag/11.1.0.0">mulle-gdb</a></td>
<td>11.1.0.0</td>
<td>10.1.0.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-core">mulle-core</h3>
<p><strong>mulle-fprintf</strong> interfaces <strong>mulle-sprintf</strong> with <code class="language-plaintext highlighter-rouge"><stdio.h></code>. It’s the new
default printf for mulle-objc (say bye bye to NSLog). There have been function
name and signature changes in mulle-sprintf.</p>
<p><strong>mulle-time</strong> is new. It provides an interface to the OS clock.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.19 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/mulle-core/mulle-atexit/RELEASENOTES.md">mulle-atexit</a></td>
<td>0.0.10</td>
<td>0.0.9</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-atinit/RELEASENOTES.md">mulle-atinit</a></td>
<td>0.0.7</td>
<td>0.0.6</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-dlfcn/RELEASENOTES.md">mulle-dlfcn</a></td>
<td>0.0.8</td>
<td>0.0.7</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-fprintf/RELEASENOTES.md">mulle-fprintf</a></td>
<td>0.0.1</td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-mmap/RELEASENOTES.md">mulle-mmap</a></td>
<td>0.1.1</td>
<td>0.1.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-sprintf/RELEASENOTES.md">mulle-sprintf</a></td>
<td>2.0.0</td>
<td>1.0.20</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-stacktrace/RELEASENOTES.md">mulle-stacktrace</a></td>
<td>0.2.4</td>
<td>0.2.3</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-testallocator/RELEASENOTES.md">mulle-testallocator</a></td>
<td>4.2.4</td>
<td>4.2.3</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-time/RELEASENOTES.md">mulle-time</a></td>
<td>0.1.0</td>
<td>0.0.1</td>
</tr>
</tbody>
</table>
<h3 id="mulle-concurrent">mulle-concurrent</h3>
<p>There is a new project <strong>mulle-mutififo</strong>, which can be useful for fixed size
pipes between multiple threads.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.19 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-aba/RELEASENOTES.md">mulle-aba</a></td>
<td>2.0.22</td>
<td>2.0.21</td>
</tr>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-concurrent/RELEASENOTES.md">mulle-concurrent</a></td>
<td>2.2.11</td>
<td>2.2.10</td>
</tr>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-fifo/RELEASENOTES.md">mulle-fifo</a></td>
<td>0.0.2</td>
<td>0.0.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-multififo/RELEASENOTES.md">mulle-multififo</a></td>
<td>0.0.1</td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-thread/RELEASENOTES.md">mulle-thread</a></td>
<td>4.4.0</td>
<td>4.3.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-c">mulle-c</h3>
<p><strong>mulle-container</strong> has gotten a major bump as I consolidated and
orthogonalized the API over the various data structures. <strong>mulle-utf</strong> can
now do some character set conversions.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.19 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/mulle-c/mulle-c11/RELEASENOTES.md">mulle-c11</a></td>
<td>4.1.2</td>
<td>4.1.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-allocator/RELEASENOTES.md">mulle-allocator</a></td>
<td>4.2.4</td>
<td>4.2.3</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-data/RELEASENOTES.md">mulle-data</a></td>
<td>0.0.3</td>
<td>0.0.2</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-buffer/RELEASENOTES.md">mulle-buffer</a></td>
<td>3.1.0</td>
<td>3.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-container/RELEASENOTES.md">mulle-container</a></td>
<td>6.0.0</td>
<td>5.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-http/RELEASENOTES.md">mulle-http</a></td>
<td>0.1.3</td>
<td>0.1.2</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-unicode/RELEASENOTES.md">mulle-unicode</a></td>
<td>2.4.3</td>
<td>2.4.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-url/RELEASENOTES.md">mulle-url</a></td>
<td>2.3.3</td>
<td>2.3.2</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-utf/RELEASENOTES.md">mulle-utf</a></td>
<td>3.1.0</td>
<td>3.0.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-vararg/RELEASENOTES.md">mulle-vararg</a></td>
<td>1.1.2</td>
<td>1.1.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-c-developer/RELEASENOTES.md">mulle-c-developer</a></td>
<td>0.14.0</td>
<td>0.13.2</td>
</tr>
</tbody>
</table>
<h3 id="mulle-sde">mulle-sde</h3>
<p>Because every function in each <strong>mulle-sde</strong> project has been renamed, the
version needed to be bumped a major version I felt. In fairness the project is
getting dangerously close to a real 1.0. I still feel I need to have a more
beginner friendly way of creating sourcetrees in order to “market” this a bit
more aggressively. But maybe soon…</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.19 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/mulle-sde/mulle-craft/RELEASENOTES.md">mulle-craft</a></td>
<td>1.0.0</td>
<td>0.19.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-dispense/RELEASENOTES.md">mulle-dispense</a></td>
<td>3.0.0</td>
<td>2.0.2</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-domain/RELEASENOTES.md">mulle-domain</a></td>
<td>1.0.0</td>
<td>0.0.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-env/RELEASENOTES.md">mulle-env</a></td>
<td>4.0.1</td>
<td>3.4.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-fetch/RELEASENOTES.md">mulle-fetch</a></td>
<td>3.0.0</td>
<td>2.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-make/RELEASENOTES.md">mulle-make</a></td>
<td>1.0.0</td>
<td>0.16.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-match/RELEASENOTES.md">mulle-match</a></td>
<td>1.0.0</td>
<td>0.8.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-monitor/RELEASENOTES.md">mulle-monitor</a></td>
<td>1.0.0</td>
<td>0.9.2</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-platform/RELEASENOTES.md">mulle-platform</a></td>
<td>1.0.0</td>
<td>0.5.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-sde/RELEASENOTES.md">mulle-sde</a></td>
<td>1.0.1</td>
<td>0.47.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-semver/RELEASENOTES.md">mulle-semver</a></td>
<td>1.0.0</td>
<td>0.0.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-sourcetree/RELEASENOTES.md">mulle-sourcetree</a></td>
<td>1.0.0</td>
<td>0.25.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-template/RELEASENOTES.md">mulle-template</a></td>
<td>1.0.0</td>
<td>0.0.4</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-test/RELEASENOTES.md">mulle-test</a></td>
<td>6.0.0</td>
<td>5.3.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-sde-developer/RELEASENOTES.md">mulle-sde-developer</a></td>
<td>0.23.0</td>
<td>0.22.0</td>
</tr>
</tbody>
</table>
<h3 id="mulle-nat">mulle-nat</h3>
<p><strong>mulle-bashfunction</strong> has lost a lot of unused API. The way scripts should
load mulle-bashfunctions has also changed. Last but not least <strong>mulle-bashfunctions-env</strong>
has been renamed to <strong>mulle-bashfunction</strong></p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.19 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/srcM/mulle-bashfunctions/RELEASENOTES.md">mulle-bashfunctions</a></td>
<td>4.0.0</td>
<td>3.4.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-nat/mulle-project/RELEASENOTES.md">mulle-project</a></td>
<td>3.0.0</td>
<td>2.4.0</td>
</tr>
</tbody>
</table>
https://www.mulle-kybernetik.com/weblog/2021/print_brother_linux.html2021-09-11T00:00:00+02:002021-09-11T00:00:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p>My <a href="https://www.brother-usa.com/products/hl4150cdn">Brother HL4150 CDN</a>
is a network connected, PostScript capable Laserprinter.
How hard can it be to get a print out ?</p>
<p>Well… Unfortunately the LPR/CUPS drivers are eleven years old. So the install
packages are 32 bit only. Without any drivers installed, the printer chokes
on any input with the cryptic error message:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Unrecoverable error: rangecheck in .putdeviceprops
Operand stack:
true
</code></pre></div></div>
<p>I have to admit, the last time I had to print something on the old machine,
I actually spent too much time, installing i386 comaptibility libraries,
building the open-sourced driver and searching around for compatible PPDs.</p>
<p>I did not want to do this again on the new machine.</p>
<p>Luckily printing is actually fairly simple. You connect to a socket, send over
your file and close the socket. As the printer maintains its own printing
queue. There is no need for me to buffer anything on the client.</p>
<p>So I use <code class="language-plaintext highlighter-rouge">pdftops</code> to convert the PDF to PostScript. And then I send the file
to the printer with <code class="language-plaintext highlighter-rouge">netcat</code>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pdftops foo.pdf foo.ps
netcat -N brother.local 9100 < foo.ps
</code></pre></div></div>
<p>And wrrrrrrrm, I get a nice printout.</p>
<hr />
<p>I made myself a shell script, which cleans up the temporary
PostScript file:</p>
<p><a href="https://gist.github.com/mulle-nat/099fb09380cf25744dc7b9ecf09a0e37">mulle-pdf</a></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="c">#</span>
<span class="c"># Print PDF to a PostScript capable network printer</span>
<span class="c">#</span>
<span class="nv">PRINTER</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">PRINTER</span><span class="k">:-</span><span class="nv">brother</span><span class="p">.local</span><span class="k">}</span><span class="s2">"</span>
<span class="nv">PRINTER_PORT</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">PRINTER_PORT</span><span class="k">:-</span><span class="nv">9100</span><span class="k">}</span><span class="s2">"</span>
<span class="k">if</span> <span class="o">[</span> <span class="nv">$# </span><span class="nt">-eq</span> 0 <span class="nt">-o</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">=</span> <span class="s1">'-h'</span> <span class="nt">-o</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"--help"</span> <span class="o">]</span>
<span class="k">then
</span><span class="nb">cat</span> <span class="o"><<</span><span class="no">EOF</span><span class="sh"> >&2
Usage:
mulle-printpdf [options] <pdffile>
Print a PDF file on a PostScript capable network printer.
Options will be passed to pdftops(1).
This script uses an **unencrypted** network connection!
Environment:
PRINTER : Printer network address (brother.local)
PRINTER_PORT : Printer port (9100)
Dependencies:
uuidgen (apt:uuid-runtime), netcat, pdftops
</span><span class="no">EOF
</span> <span class="nb">exit </span>1
<span class="k">fi</span>
<span class="c">#</span>
<span class="c"># pdftops works better than pdf2ps for me, as my brother laserprinter</span>
<span class="c"># croaks on pdf2ps output</span>
<span class="c">#</span>
<span class="nv">uuid</span><span class="o">=</span><span class="s2">"</span><span class="sb">`</span>uuidgen<span class="sb">`</span><span class="s2">"</span> <span class="c"># no problem if not installed</span>
<span class="nv">psfile</span><span class="o">=</span><span class="s2">"/tmp/printer-</span><span class="k">${</span><span class="nv">uuid</span><span class="k">:-${</span><span class="nv">LOGNAME</span><span class="k">}}</span><span class="s2">.ps"</span>
finish<span class="o">()</span>
<span class="o">{</span>
<span class="nb">rm</span> <span class="nt">-f</span> <span class="s2">"</span><span class="k">${</span><span class="nv">psfile</span><span class="k">}</span><span class="s2">"</span> 2> /dev/null
<span class="o">}</span>
<span class="nb">trap </span>finish EXIT
pdftops <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">psfile</span><span class="k">}</span><span class="s2">"</span> <span class="o">||</span> <span class="nb">exit </span>1
<span class="c">#</span>
<span class="c"># tell netcat to close after sending file with -N</span>
<span class="c">#</span>
netcat <span class="nt">-N</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PRINTER</span><span class="k">:-</span><span class="nv">printer</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PRINTER_PORT</span><span class="k">}</span><span class="s2">"</span> < <span class="s2">"</span><span class="k">${</span><span class="nv">psfile</span><span class="k">}</span><span class="s2">"</span> <span class="o">||</span> <span class="nb">exit </span>1
</code></pre></div></div>
https://www.mulle-kybernetik.com/weblog/2021/new_killer_linux_box3.html2021-08-10T23:30:00+02:002021-08-10T23:30:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p>Continued from <a href="/weblog/2021/new_killer_linux_box2.html">Part 2</a></p>
<p>Lots of boxes arrived yesterday. One aspect, that’s very much in favor of
<em>not</em> building your own system is the large amount of trash that buying
everything in discrete parts produces. The packaging by and large is
pretty nifty, but I used to care about this more. Nowadays I would be happier
with much less packaging.</p>
<p>PC assembly has gotten even easier over the years. Especially the placement
of the CPU is now a cinch. During assembly I remembered, why I prefer big
cases.</p>
<p>For one, I couldn’t mount the liquid cooler at the top. There was just no
space with the mainboard filling up the whole cavity. And then connecting
everything up in a compact case is very cramped.</p>
<h2 id="quick-first-impressions">Quick first impressions</h2>
<table>
<thead>
<tr>
<th>Component</th>
<th>Manufacturer/Model</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mainboard</td>
<td>Asrock TRX40 TAICHI TRX40</td>
</tr>
<tr>
<td>CPU</td>
<td>AMD Ryzen Threadripper 3970X 3.7GHz</td>
</tr>
<tr>
<td>Cooler</td>
<td>XILENCE LiQuRizer LQ240</td>
</tr>
<tr>
<td>RAM</td>
<td>128 GB G-Skill D4128GB 3200-16 RipJaws V K4 16-18-18-38</td>
</tr>
<tr>
<td>GFX</td>
<td>MSI GeForce RTX 3060 Ti 8GB</td>
</tr>
<tr>
<td>SSDs</td>
<td>SSD 2TB Western Digital SN850 NVMe Black PCIe 4.0</td>
</tr>
<tr>
<td>Power</td>
<td>Corsair RM750 750W</td>
</tr>
<tr>
<td>Case</td>
<td>Fractal D. Define 7 Compact</td>
</tr>
</tbody>
</table>
<p>The mainboard looks a little tacky, but feature wise its very nice. I
especially like that I got an extension board, with which I can install 4
more M.2 SSDs.</p>
<p>The CPU packaging was pretty much over the top. Like I purchased a Rolex watch
or something. But installation has been super simple and the momentum
screwdriver for fastening the CPU in the socket made it even more idiot-proof.
Cool.</p>
<p>From the waterpump of the cooler I don’t hear any noises and the fans can be
made to run quiet, if the system is idle. The system does rev up when its
working though. A real positive side effect of water cooling, is that there
is more room on the mainboard, where you don’t have to worry about a
massive air-cooler and its placement and possible entanglement with other
cables.</p>
<p>The RAM worked. :)</p>
<p>The MSI is a huge card with three fans. It just barely fit into the compact
case. It’s the loudest component when starting up. Maybe the two fan variant
is a better buy ?</p>
<p>The SSD were easy to install and they worked. But I underestimated how much
stuff I have. 6 TB would have been better. One disk has 1TB free the other
0.5 TB.</p>
<p>The only component I might replace is the PSU (but I probably won’t). Its fan
noise could be better, though it is quiet. The PSU fan is supposed to stop,
when its idle, but I guess this system never lets it idle.</p>
<p>The case is just as good as my Phanteks in every respect. Very, very good.
Out of sheer luck, the Define 7 Compact has a USB-C front plug and the
motherboard has a spare USB-C port. I didn’t remember to check the case for
compatibility, but everything fits perfectly.</p>
<h2 id="quick-review">Quick Review</h2>
<p>Once I got the fans dialed in, the new box is much quieter than the old one.</p>
<p>Initially the Ubuntu 21.4 install set up the “Nouveau” driver and put me into
Wayland. Things <em>appeared</em> fairly stable, but I am sceptical. The graphic
card fans kept running at 100% though and were unpleasantly noisy. When
I changed to the proprietary NVIDIA driver, the fans shut off. A quick check
with OBS in Wayland, showed that I couldn’t capture “Firefox” or “Terminal”
just like before. So no dice… Back to X11 and the proprietary driver.</p>
<blockquote>
<h4 id="memo">MEMO</h4>
<p>In the BIOS don’t set the fan connctor, that is feeding the watercooler
pump to “silent”. This seemingly turns it off. I saw temperatures climbing
rapidly and had to shut off the machine before it fried itself.</p>
</blockquote>
<p>Boot times are much better now. After the BIOS is through, the machine boots
into the desktop in like three seconds. Unfortunately the BIOS boot process is
still fairly length. There is an <a href="https://www.tenforums.com/tutorials/21284-enable-disable-fast-boot-uefi-firmware-settings-windows.html">ultra fast</a> BIOS boot
option, but then you can’t get back into the BIOS. I might try this some time
as there is a BIOS CMOS reset button on the board…</p>
<blockquote>
<h4 id="old-box-notices-new-box-and-behaves-better">Old box notices new box and behaves better</h4>
<p>Ironically, once I moved the <em>old box</em> from Display Port to HDMI and removed
the DVI connection, the old box reboot time was drastically
improved. That makes no sense to me, but whatever.</p>
</blockquote>
<p>The new box can be put to sleep and brought back. That’s also fairly huge,
though not as huge as it would have been, if boot times were still abysmal.</p>
<h2 id="benchmarks">Benchmarks</h2>
<p>Geekbench 5 results. Now I knew these numbers before I bought the new box.
I looked mostly at the “clang” result to estimate the gain to be had:</p>
<h3 id="old-box">Old box</h3>
<p>Weird differences in Geekbench, I combined two best results of
<a href="https://browser.geekbench.com/v5/cpu/9180994">Geekbench5 Run 1</a> and
<a href="https://browser.geekbench.com/v5/cpu/9181009">Geekbench5 Run 2</a></p>
<table>
<thead>
<tr>
<th>Single-Core Score</th>
<th>Multi-Core Score</th>
<th>Clang Multi-Core Score</th>
</tr>
</thead>
<tbody>
<tr>
<td>755</td>
<td>12235</td>
<td>16459</td>
</tr>
</tbody>
</table>
<h3 id="new-box">New box</h3>
<p>I only did one run of <a href="https://browser.geekbench.com/v5/cpu/9258621">Geekbench 5</a>
as the scores looked sensible:</p>
<table>
<thead>
<tr>
<th>Single-Core Score</th>
<th>Multi-Core Score</th>
<th>Clang Multi-Core Score</th>
</tr>
</thead>
<tbody>
<tr>
<td>1310</td>
<td>24405</td>
<td>36894</td>
</tr>
</tbody>
</table>
<h3 id="massive-build-of-mullefoundation">Massive build of MulleFoundation</h3>
<p>First the benchmark that vindicates the purchase. Here I rebuilt all
projects of the <a href="https://mullefoundation.github.io/">MulleFoundation</a>.
That’s something I would do quite often during a mulle-objc release. A
semi-annual event…</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cat <<EOF > REPOS
MulleObjCArchiverFoundation
MulleObjCCalendarFoundation
MulleObjCContainerFoundation
MulleObjCExpatFoundation
MulleObjCInetOSFoundation
MulleObjCKVCFoundation
MulleObjCLockFoundation
MulleObjCMathFoundation
MulleObjCOSFoundation
MulleObjCPlistFoundation
MulleObjCStandardFoundation
MulleObjCUnicodeFoundation
MulleObjCValueFoundation
Foundation
MulleFoundation
objc-compat
mulle-bunchobjects
mulle-testgen
MulleObjCStandardFoundation-startup
Foundation-startup
MulleFoundation-startup
EOF
mulle-project-all -p -m mulle-sde clean all
mulle-project-all -p -m mulle-sde craft
mulle-project-all -p -m mulle-sde clean all
time mulle-project-all -p -m mulle-sde craft
</code></pre></div></div>
<blockquote>
<p>I am doing this twice to have the caches filled. It’s not <em>much</em> of
a difference though.</p>
</blockquote>
<p>This builds all projects in parallel, and as each project is built with ninja
all files are built in parallel (up to load)</p>
<h3 id="old-box-1">Old box</h3>
<p>These are two Intel Xeon E5-2660 v3 @ 2.60GHz CPUs with 10 cores each:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>real 1m25,387s
user 40m6,258s
sys 5m12,614s
</code></pre></div></div>
<p>If we add user and sys together and divide by real, I believe we have a
measurement of the parallelism:</p>
<table>
<thead>
<tr>
<th>Time</th>
<th>Seconds</th>
</tr>
</thead>
<tbody>
<tr>
<td>real</td>
<td>85</td>
</tr>
<tr>
<td>user</td>
<td>2406</td>
</tr>
<tr>
<td>sys</td>
<td>312</td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
<tr>
<td>parallelism</td>
<td><strong>31</strong></td>
</tr>
</tbody>
</table>
<p>A speedup of 31 with 20 cores and 40 hyperthreads sounds reasonable.</p>
<h4 id="new-box-1">New box</h4>
<p>One AMD Ryzen Threadripper 3970X 32-Core Processor:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>real 0m44,872s
user 28m35,786s
sys 5m23,769s
</code></pre></div></div>
<p>So that’s approximately two times faster than the old box.</p>
<table>
<thead>
<tr>
<th>Time</th>
<th>Seconds</th>
</tr>
</thead>
<tbody>
<tr>
<td>real</td>
<td>45</td>
</tr>
<tr>
<td>user</td>
<td>1716</td>
</tr>
<tr>
<td>sys</td>
<td>323</td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
<tr>
<td>parallelism</td>
<td><strong>45</strong></td>
</tr>
</tbody>
</table>
<p>That’s <strong>45</strong> with 32 cores and 64 hyperthreads. It might be a case of
slightly diminishing returns with the more cores available, but it still
scales fairly nicely.</p>
<h3 id="small-build-of-mullefoundation">Small build of MulleFoundation</h3>
<p>The second benchmark casts some doubt over the purchase though. Only one
project is being rebuilt. That can happen quite often during daily
development:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd MulleFoundation/Foundation
mulle-sde clean all
mulle-sde craft
mulle-sde clean all
time mulle-sde craft
</code></pre></div></div>
<h3 id="old-box-2">Old box</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>real 0m36,941s
user 3m40,608s
sys 0m29,265s
</code></pre></div></div>
<table>
<thead>
<tr>
<th>Time</th>
<th>Seconds</th>
</tr>
</thead>
<tbody>
<tr>
<td>real</td>
<td>40</td>
</tr>
<tr>
<td>user</td>
<td>221</td>
</tr>
<tr>
<td>sys</td>
<td>29</td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
<tr>
<td>parallelism</td>
<td><strong>6</strong></td>
</tr>
</tbody>
</table>
<p>This may indicate that a six core machine with a higher clock rate might be
doing better.</p>
<h3 id="new-box-2">New box</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>real 0m22,185s
user 1m57,616s
sys 0m21,850s
</code></pre></div></div>
<p>That’s only like 1.5 times faster, not 2 as expected.</p>
<blockquote>
<h4 id="memo-1">MEMO</h4>
<p>Initially the time i got was 26s. I had forgotten to install <a href="https://ninja-build.org">ninja</a>.
<code class="language-plaintext highlighter-rouge">ninja</code> makes a huge difference.</p>
</blockquote>
<table>
<thead>
<tr>
<th>Time</th>
<th>Seconds</th>
</tr>
</thead>
<tbody>
<tr>
<td>real</td>
<td>22</td>
</tr>
<tr>
<td>user</td>
<td>118</td>
</tr>
<tr>
<td>sys</td>
<td>22</td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
<tr>
<td>parallelism</td>
<td><strong>6</strong></td>
</tr>
</tbody>
</table>
<p>Parallelism ist just about the same as the old box.
The reason why this isn’t more, is because <a href="//mulle-objc.github.io">mulle-objc</a>
consists of many small projects, that just can’t saturate the available cores.
Six is likely close to the average number of files to build in my projects.
Throwing more cores on this problem won’t make it faster.</p>
<h2 id="power-consumption">Power consumption</h2>
<p>The new box isn’t exactly <em>green</em> though…</p>
<h3 id="old-box-3">Old box</h3>
<table>
<thead>
<tr>
<th>Power usage</th>
<th>State</th>
</tr>
</thead>
<tbody>
<tr>
<td>70W</td>
<td>Idle, Ubuntu Login Screen</td>
</tr>
</tbody>
</table>
<h3 id="new-box-3">New box</h3>
<table>
<thead>
<tr>
<th>Power usage</th>
<th>State</th>
</tr>
</thead>
<tbody>
<tr>
<td>5W</td>
<td>Sleep</td>
</tr>
<tr>
<td>85W</td>
<td>Idle, Ubuntu Login Screen</td>
</tr>
<tr>
<td>150W</td>
<td>Desktop, editing something in Sublime Text</td>
</tr>
</tbody>
</table>
<h2 id="conclusion">Conclusion</h2>
<p><img src="/weblog/images/old-new-box.jpg" alt="Old and New Box" /></p>
<p>There are some very nice quality of life improvements that the new box
brings. It is in all respects a better machine than the old box. But the old
box isn’t yet obsolete in 2021.</p>
<p>I didn’t mind the somewhat louder old box, tough the relative quietness of the
new one is appreciated, when it is quiet. As the workload increases the fans
rev up and that may eventually get on my nerves. I might have to tweak this
some more.</p>
<p>I frankly expected more improvement in development turn-around. For day-to-day
development eight cores are probably sufficient.</p>
<p>It might be necessary to restructure my projects, so that more files are
compiled during a cmake/ninja invocation to improve parallelism.
Project composition is currently done at the link level with mostly static
libraries. Maybe the composition should be at the source file level at least
as an option.</p>
<p>So with hindsight, I probably could have waited a bit longer with the
purchase of a new work horse.</p>
https://www.mulle-kybernetik.com/weblog/2021/new_killer_linux_box2.html2021-08-09T11:00:00+02:002021-08-09T11:00:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p>Continued from <a href="/weblog/2021/new_killer_linux_box.html">Part1</a></p>
<h2 id="specs-of-the-new-box">Specs of the new Box</h2>
<p>I used the <a href="https://www.alternate.de/pcbuilder.xhtml#!/components">Alternate PC Konfigurator</a>
in conjunction with a few online reviews to figure out what to order. I don’t
want to overclock and I don’t need RGB LEDs, so keep this in mind.</p>
<table>
<thead>
<tr>
<th>Component</th>
<th>Manufacturer/Model</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mainboard</td>
<td>Asrock TRX40 TAICHI TRX40</td>
</tr>
<tr>
<td>CPU</td>
<td>AMD Ryzen Threadripper 3970X 3.7GHz</td>
</tr>
<tr>
<td>Cooler</td>
<td>XILENCE LiQuRizer LQ240</td>
</tr>
<tr>
<td>RAM</td>
<td>128 GB G-Skill D4128GB 3200-16 RipJaws V K4 16-18-18-38</td>
</tr>
<tr>
<td>GFX</td>
<td>MSI GeForce RTX 3060 Ti 8GB</td>
</tr>
<tr>
<td>SSD</td>
<td>SSD 2TB Western Digital SN850 NVMe Black PCIe 4.0</td>
</tr>
<tr>
<td>Power</td>
<td>Corsair RM750 750W</td>
</tr>
<tr>
<td>Case</td>
<td>Fractal D. Define 7 Compact</td>
</tr>
</tbody>
</table>
<p>I picked the mainboard, because it was available at the time and it had
WIFI on board. The ASROCK boards of very old, were pretty much cheap trash. But
ASROCK has become fairly reputable and the price isn’t really cheap. So fingers
crossed here. (EUR 500) <a href="https://www.tomshardware.com/reviews/asrock-trx40-taichi-motherboard">Toms</a></p>
<p>The mainboard is there to carry the CPU, which is the whole reason for the new
box. The AMD 3970X has 32 cores, 12 more than my current box with two CPUs.
Going dual CPU would have made the box too expensive and I think I will prefer
the single-CPU experience again (see last article). It has also got a higher
base frequency than my Xeons and then there are the generational improvements.
At EUR 2000 it is less than what I paid for the two Xeons. <a href="https://browser.geekbench.com/v5/cpu/9168426">Geekbench</a></p>
<p>The XILENCE cooler is a shot in the dark. I have another water-cooled PC and I was
severly disappointed by it, because it was noisier than a regular air cooled
system. There wasn’t much air cooling choice though and the AMD runs really hot,
so I picked the cheapest water cooler that tested well. EUR 60. <a href="https://www.igorslab.de/en/liqurizer-lq240rgb-in-test-what-can-the-newcomer-from-germany/">Igors</a></p>
<p>Again for the RAM I made some cursory glances at reviews, and the G-SKILL
package for the price gave me the best “feeling” with decent latencies. But
there weren’t many other options for 128 GB kits. Now my old machine has 64 KB,
which it needed but never exceeded. I went for 128 GB as it fit the budget and
twice the memory seems like a good idea for the long term. EUR 550 <a href="https://www.tweaktown.com/reviews/9660/skill-ripjaws-ddr4-3200-64gb-dual-channel-memory-kit/index.html#Final-Thoughts">Tweaktown</a></p>
<p>Initially I wanted to go again with the Samsung SSDs but the Western Digital
just benchmarked extremely well. It is supposedly twice as fast as my old M.2
SSDs. Again SSD speed is not really much of my concern, but I need to get
something and why not. 4TB should be fine for the start. (I have quite a few
VMs) EUR 750 <a href="https://www.tomshardware.com/reviews/wd-black-sn850-m-2-nvme-ssd-review">Toms</a></p>
<p>The graphics card had to be another NVIDIA, for all the reasons explained
in the previous part. Now the card prices are really stupid this year, so
for budgetary reasons, the 3060 TI is the best I wanted to do. I might swap
this card out with another from my gaming PC though for the actual build.
Since I don’t really need the power in the desktop. The MSI was reviewes as
being very quiet, so this was the deciding factor in favor of picking it over
a cheaper alternative. EUR 700</p>
<p>Do I really need another case ? Probably yes :) The Fractal is supposed
to be fairly quiet, so lets give this one a try. I might dislike that it is a
compact case. I have come to prefer large cases. But I didn’t find anything
on Alternate that I liked. And its in white. Unfortunately they don’t sell
Phanteks. I would have liked to buy another Phantek. EUR 80 (https://www.techpowerup.com/review/fractal-design-define-7-compact-atx-chassis/6.html)</p>
<p>The PSU is a Corsair RM750 as ordered. I just noticed, that I made a mistake.
So I’ll probably exchange it. I wanted the RM750X but ordered the RM750. The
RM750x is the same price, but supposedly has a better fan. And it can be
gotten in white. EUR 120. <a href="https://www.tomshardware.com/reviews/corsair-rm750-power-supply,6172.html">Toms</a></p>
<h2 id="but-why-not-a-mac-pro-">But… why not a Mac Pro ?</h2>
<p>One of the earliest posts in this blog, now almost 20(!) years ago, was me
buying a <a href="https://www.mulle-kybernetik.com/weblog/2003/g5_immer_mu_man_schimpfen_bis.html">PowerMac G5</a>. That was a great machine, that served me well a long time. It’s still sitting
in a corner here and gets a yearly boot. :)</p>
<p>Let’s see what I would have to pay to get an equally powerful Mac Pro
today. So the base price for a <a href="https://www.apple.com/de/shop/buy-mac/mac-pro/tower">Mac Pro Tower</a> is EUR 6500. That’s already EUR 1500 over budget. But let’s compare.</p>
<table>
<thead>
<tr>
<th>Linux Box 2</th>
<th>Mac Pro Tower</th>
</tr>
</thead>
<tbody>
<tr>
<td>32 Cores/3.4Ghz</td>
<td>8 Cores/3.5 Ghz</td>
</tr>
<tr>
<td>128 GB RAM</td>
<td>32 GB RAM</td>
</tr>
<tr>
<td>GeForce 3060 TI</td>
<td>Radeon Pro 580X</td>
</tr>
<tr>
<td>4TB SSD</td>
<td>256 GB SSD</td>
</tr>
</tbody>
</table>
<p>This is laughable. The new Mac Pro Tower is half the machine that my old box
was, and is more expensive than the new box!
If I were to spec it out, so that it comes closer,
to what the new box delivers I would have to spend:</p>
<ul>
<li>EUR 8000 to upgrade the CPU to a 2,5 GHz 28‑Core Xeon, which is still slower than the 3970X</li>
<li>EUR 1000 to upgrade to 96 GB (there is no 128 GB option strangely)</li>
<li>EUR 700 to upgrade to a Radeon Pro W5700X</li>
<li>EUR 1600 to get 4TB of SSD memory</li>
</ul>
<p>That all comes out to EUR 18000 for an arguably worse machine. Eighteen
thousand euros for less. The Mac Pro has become the desktop equivalent of
a <a href="https://en.wikipedia.org/wiki/Vertu">Vertu phone</a>. Yesterdays technology
for the tasteless rich.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I hope to end up with a system, that’s faster and quieter than
my old box. The build times have to be at least twice as fast as the current
system, otherwise I am going to be very disappointed. I am also hoping that
all the boot woes of old resolve themselves magically…</p>
<p>Continue to <a href="/weblog/2021/new_killer_linux_box3.html">Part 3</a></p>
https://www.mulle-kybernetik.com/weblog/2021/new_killer_linux_box.html2021-08-09T01:05:00+02:002021-08-09T01:05:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p>The first <a href="https://www.mulle-kybernetik.com/weblog/2015/building_a_killer_unix_box.html">Killer box for Linux development</a> is now already six years old.
It has by and large been a success. It indeed facilitated my migration from
OS X to Linux and it is now my main machine on which I write code.</p>
<p>It is still serving me well, but looking at the latest Geekbench 5 benchmarks
of machines in the same price range of around €5000, I noticed, that I should
be able to get more than two times faster build times now.
And two times faster build times, that’s where I know: <em>It’s time to buy a new
work horse.</em></p>
<h2 id="review-of-the-old-box">Review of the old Box</h2>
<p>What has been good and not so good about the old box ?</p>
<p><img src="https://www.mulle-kybernetik.com/weblog/images/killer-box.jpg" alt="" /></p>
<table>
<thead>
<tr>
<th>Component</th>
<th>Manufacturer/Model</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mainboard</td>
<td>Supermicro X10DAI</td>
</tr>
<tr>
<td>CPU</td>
<td>2x Xeon E5 2660 V3</td>
</tr>
<tr>
<td>Cooler</td>
<td>2x Enermax ETS-T40 Cooler</td>
</tr>
<tr>
<td>RAM</td>
<td>32GB Samsung DDR4-2133 M393A1G40DB0-CPB</td>
</tr>
<tr>
<td> </td>
<td>32GB Kingston DDR4-2133 9965604-026.D00G</td>
</tr>
<tr>
<td>GFX</td>
<td>GeForce GTX 970</td>
</tr>
<tr>
<td>SSD</td>
<td>2xSamsung XP941 Series SSD, PCIe M.2 Typ 2280</td>
</tr>
<tr>
<td> </td>
<td>and some other Samsung ATA SSDs</td>
</tr>
<tr>
<td>Power</td>
<td>Be Quiet Dark Power 650 W</td>
</tr>
<tr>
<td>Case</td>
<td>Phanteks Enthoo Pro</td>
</tr>
</tbody>
</table>
<h2 id="mainboard--cpus--ram--coolers">Mainboard / CPUs / RAM / Coolers</h2>
<p>Initially I didn’t know, that a dual board is like a siamese twin.
Each CPU has its own BIOS, so you see a BIOS menu twice during boot.
And each CPU has its own RAM, that isn’t shared with the other CPU.
In a single-CPU configuration I could very well have lived with the 32GB I
bought initially .</p>
<p>But a <a href="llvm.org">llvm</a> build taught me, that each CPU only has access to 16GB.
That was not enough to link <code class="language-plaintext highlighter-rouge">clang</code> with debug symbols and without swapping.
So I had to double the size of memory.</p>
<p>Everything is stable. I can’t remember ever having something like a kernel
panic in the last years. The hardware is super solid. My Mac Mini at work
is comparably flakey, with a few kernel panics per year.</p>
<p>While the machine makes a little bit of fan noise, the only time I notice it,
is when writing an article about the machine and I wonder, if there is fan
noise. But I am not overly sensitive, if the noise is quiet enough I just
overhear it.</p>
<h3 id="hardware--os-problems">Hardware / OS problems</h3>
<p>With Windows the system boots up fine and does so relatively speedily.
That is after the BIOS is through. The dual BIOS boot process is fairly slow
(˜15s ?).
Sleep works, reboot works, everything just works like you’d like it.</p>
<p>The Linux boot though is really slow. I never figured out why. It may have
something to do with the SuperMicro board. I feel that it has
gotten better, with the last kernel releases. But I haven’t timed it.</p>
<p>Sleep in Linux doesn’t work. Also a reboot cycle in Linux is painfully
slow (think minutes). These are all problems I tried to fix, but where I gave
up after some reasonable effort. Again none of these problems appear with
Windows, so I don’t blame the hardware.</p>
<h2 id="ssd">SSD</h2>
<p>The boot SATA SSD (Samsung 850 Pro 128GB) is the only SSD I’ve had so far,
which actually went bad. That was kinda tricky to diagnose and I had a few days
of downtime. The M.2 SSDs showed no problems.</p>
<p>While the M.2 SSD benchmarks well, I really didn’t notice much benefits in
build times over the regular SATA SSDs. If M.2s were much more expensive, I’d
buy SATA, no problem.</p>
<h2 id="gfx">GFX</h2>
<p>Graphics cards are the Achilles heel of Linux Desktop. I started out with
a AMD Radeon card, but that turned out to be unusable for Linux. So I bought
an NVIDIA card. The GeForce 970X and the proprietary (close-source) drivers
give me no trouble. Everything is looking fine and running smooth on the
desktop, once I am up and running.</p>
<blockquote>
<h4 id="wayland-">Wayland 😭</h4>
<p>Sadly six (!) years later <a href="https://metaredux.com/posts/2021/07/31/back-to-linux.html">nothing seems to have changed</a> in Linuxland and the AMD drivers and the open drivers
are still unusable.</p>
<p>This is actually a big deal for me. When I upgraded to Ubuntu 21.4, I found
out that <a href="https://wayland.freedesktop.org/">Wayland</a>, which is going to
replace X11, doesn’t work with the proprietary Nvidia driver.
You have to use the open “Nouveau” driver.</p>
<p>I have been waiting six years for Wayland. But I can’t use it.
It’s six years later and the open driver still locks up my system! What
does this say about future progress, if the driver is still in this state?</p>
<p>So Linux Desktop is going in the Wayland direction. Nvidia propietary drivers
aren’t supported by Wayland and I am not so sure, that Nvidia cares deeply
about Linux. “Nouveau” and AMD aren’t working. That’s a cul de sac,
if you ask me.</p>
<p>Which means, that I very well might have to leave the platform again to…
Windows ?</p>
</blockquote>
<h3 id="display-problems">Display problems</h3>
<p>I have my main monitor connected via Display Port and the secondary monitor
via DVI. When I boot into Windows, it works every time.</p>
<p>On Linux though, there is a 50:50 chance, that my main monitor is not detected.
The only fix is to reboot, then the main monitor is <em>always</em> detected. I then
never have to reboot a second time. Maddeningly the reboot is super slow (s.a.)
This is main pain point of the whole setup.</p>
<h2 id="case--power">Case / Power</h2>
<p>I gave Be Quiet a bad rap in the original article, but this particular PSU has
performed well and continues to do so. No complaints. The case still looks
good. I rarely have reason to open the machine, except for adding a SSD
or so.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The old box was and still is a great system for Windows. It is a good system
for Linux, though hopefully the next one will be better. I have high hopes for
improved boot times and getting proper sleep support would be nice as well.</p>
<p>To compare the current system in terms of build performance, the old
box seems to be about equivalent to a AMD Ryzen 7 5800X with 8 Cores and
3.8 Ghz or twice as fast as a M1 Macbook.</p>
<p>So I need something beefier than that, otherwise I could just stay with what
I have.</p>
<p>Continue to <a href="/weblog/2021/new_killer_linux_box2.html">Part 2</a></p>
https://www.mulle-kybernetik.com/weblog/2021/mulle_objc_0_19_release.html2021-07-24T19:00:00+02:002021-07-24T19:00:00+02:00Nat!https://www.mulle-kybernetik.com/weblog<p><a href="//mulle-objc.github.io"><img src="/weblog/images/35923740.png" alt="logo" /></a></p>
<p>The mulle-clang compiler has been pushed to version 12, following the llvm
project. Again, like last release, there should not be a noticable difference
between this mulle-clang and the last one.</p>
<p>There have been a lot of API changes, especially in mulle-container, but
also in mulle-objc-runtime, which you will notice when recompiling.</p>
<p>If you are new to the project, then head on over to <a href="//mulle-objc.github.io">mulle-objc</a>
and <a href="//mulle-objc.github.io">foundation-developer</a> for installation
instructions and help.</p>
<h2 id="release-notes">Release Notes</h2>
<p>Instead of rehashing what’s in the release notes, I just
point out the release notes, which contain interesting changes.</p>
<blockquote>
<p>Remember: Projects with a zero version major (0.x.y) are still alpha and
can have breaking changes with each minor change.</p>
</blockquote>
<h3 id="mulle-c">mulle-c</h3>
<p><strong>mulle-buffer</strong> and <strong>mulle-container</strong> got API breaking changes, so a major bump
in the version was needed. Parts of <strong>mulle-container</strong> have been moved to
<strong>mulle-data</strong>, but also some function signatures changed. And most importantly
there is an effort to make enumerators across the various data structures more
uniform:</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.18 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/mulle-c/mulle-c11/blob/release/RELEASENOTES.md">mulle-c11</a></td>
<td>4.1.1</td>
<td>4.1.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-allocator/blob/release/RELEASENOTES.md">mulle-allocator</a></td>
<td>4.2.3</td>
<td>4.2.2</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-data/blob/release/RELEASENOTES.md">mulle-data</a></td>
<td>0.0.2</td>
<td>0.0.1</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-buffer/blob/release/RELEASENOTES.md">mulle-buffer</a></strong></td>
<td><strong>3.0.0</strong></td>
<td>2.2.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-c/mulle-container/blob/release/RELEASENOTES.md">mulle-container</a></strong></td>
<td><strong>5.0.0</strong></td>
<td>4.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-http/blob/release/RELEASENOTES.md">mulle-http</a></td>
<td>0.1.2</td>
<td>0.1.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-unicode/blob/release/RELEASENOTES.md">mulle-unicode</a></td>
<td>2.4.1</td>
<td>2.4.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-url/blob/release/RELEASENOTES.md">mulle-url</a></td>
<td>2.3.2</td>
<td>2.3.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-utf/blob/release/RELEASENOTES.md">mulle-utf</a></td>
<td>3.0.1</td>
<td>3.0.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-vararg/blob/release/RELEASENOTES.md">mulle-vararg</a></td>
<td>1.1.1</td>
<td>1.1.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-c/mulle-c-developer/blob/release/RELEASENOTES.md">mulle-c-developer</a></td>
<td>0.13.2</td>
<td>0.13.1</td>
</tr>
</tbody>
</table>
<h3 id="mulle-concurrent">mulle-concurrent</h3>
<p>There is a new small project <strong>mulle-fifo</strong>, which can be useful for fixed size
pipes between two threads. <strong>mulle-thread</strong> has a change in the return value
of <code class="language-plaintext highlighter-rouge">mulle_thread_create</code>:</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.18 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-concurrent/mulle-thread/blob/release/RELEASENOTES.md">mulle-thread</a></strong></td>
<td><strong>4.3.0</strong></td>
<td>4.2.3</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-concurrent/mulle-fifo/blob/release/RELEASENOTES.md">mulle-fifo</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-aba/blob/release/RELEASENOTES.md">mulle-aba</a></td>
<td>2.0.21</td>
<td>2.0.20</td>
</tr>
<tr>
<td><a href="//github.com/mulle-concurrent/mulle-concurrent/blob/release/RELEASENOTES.md">mulle-concurrent</a></td>
<td>2.2.10</td>
<td>2.2.9</td>
</tr>
</tbody>
</table>
<h3 id="mulle-core">mulle-core</h3>
<p><strong>mulle-mmap</strong> gained some functionality, but the major change is the
addition of <strong>mulle-time</strong>:</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.18 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="//github.com/mulle-core/mulle-dlfcn/blob/release/RELEASENOTES.md">mulle-dlfcn</a></td>
<td>0.0.7</td>
<td>0.0.6</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-atexit/blob/release/RELEASENOTES.md">mulle-atexit</a></td>
<td>0.0.9</td>
<td>0.0.8</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-atinit/blob/release/RELEASENOTES.md">mulle-atinit</a></td>
<td>0.0.6</td>
<td>0.0.5</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-core/mulle-mmap/blob/release/RELEASENOTES.md">mulle-mmap</a></strong></td>
<td><strong>0.1.0</strong></td>
<td>0.0.2</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-sprintf/blob/release/RELEASENOTES.md">mulle-sprintf</a></td>
<td>1.0.20</td>
<td>1.0.19</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-stacktrace/blob/release/RELEASENOTES.md">mulle-stacktrace</a></td>
<td>0.2.3</td>
<td>0.2.2</td>
</tr>
<tr>
<td><a href="//github.com/mulle-core/mulle-testallocator/blob/release/RELEASENOTES.md">mulle-testallocator</a></td>
<td>4.2.3</td>
<td>4.2.2</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-core/mulle-time/blob/release/RELEASENOTES.md">mulle-time</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
</tbody>
</table>
<h3 id="mulle-objc">mulle-objc</h3>
<p><strong>mulle-objc-runtime</strong> and <strong>MulleObjC</strong> have interesting changes, the other
projects are pretty much as before. Major changes are the split-off of
<strong>MulleObjCLockFoundation</strong> from MulleObjC. There have been major
changes and breaking changes to the way -copy and -mutableCopy: are
implemented:</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.18 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-objc/mulle-objc-runtime/blob/release/RELEASENOTES.md">mulle-objc-runtime</a></strong></td>
<td><strong>0.19.0</strong></td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-list/blob/release/RELEASENOTES.md">mulle-objc-list</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-compat/blob/release/RELEASENOTES.md">mulle-objc-compat</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-objc/MulleObjC/blob/release/RELEASENOTES.md">MulleObjC</a></strong></td>
<td><strong>0.19.0</strong></td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/MulleObjC-startup/blob/release/RELEASENOTES.md">MulleObjC-startup</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-runtime-startup/blob/release/RELEASENOTES.md">mulle-objc-runtime-startup</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-objc/mulle-objc-developer/blob/release/RELEASENOTES.md">mulle-objc-developer</a></td>
<td>0.21.0</td>
<td>0.19.0</td>
</tr>
</tbody>
</table>
<blockquote>
<p>At the moment all mulle-objc and MulleFoundation projects still have a synced
version number. This was supposed to change, but hasn’t except for
mulle-objc-developer…</p>
</blockquote>
<h3 id="mullefoundation">MulleFoundation</h3>
<p>New repositories <strong>mulle-bunchobjects</strong>, <strong>MulleObjCArchiverFoundation</strong>,
<strong>MulleObjCLockFoundation</strong>, <strong>MulleObjCPlistFoundation</strong> appeared! Mostly
for reorganization of existing content.
Otherwise a few methods have been added here and there. And there are a few
breaking changes because of method renamings, that happened for consistency.</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.18 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleObjCArchiverFoundation/blob/release/RELEASENOTES.md">MulleObjCArchiverFoundation</a></strong></td>
<td><strong>0.19.0</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCCalendarFoundation/blob/release/RELEASENOTES.md">MulleObjCCalendarFoundation</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCContainerFoundation/blob/release/RELEASENOTES.md">MulleObjCContainerFoundation</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCExpatFoundation/blob/release/RELEASENOTES.md">MulleObjCExpatFoundation</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCInetOSFoundation/blob/release/RELEASENOTES.md">MulleObjCInetOSFoundation</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCKVCFoundation/blob/release/RELEASENOTES.md">MulleObjCKVCFoundation</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleObjCLockFoundation/blob/release/RELEASENOTES.md">MulleObjCLockFoundation</a></strong></td>
<td><strong>0.19.0</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCMathFoundation/blob/release/RELEASENOTES.md">MulleObjCMathFoundation</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCOSFoundation/blob/release/RELEASENOTES.md">MulleObjCOSFoundation</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleObjCPlistFoundation/blob/release/RELEASENOTES.md">MulleObjCPlistFoundation</a></strong></td>
<td><strong>0.19.0</strong></td>
<td>-</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleObjCStandardFoundation/blob/release/RELEASENOTES.md">MulleObjCStandardFoundation</a></strong></td>
<td><strong>0.19.0</strong></td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCUnicodeFoundation/blob/release/RELEASENOTES.md">MulleObjCUnicodeFoundation</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/MulleObjCValueFoundation/blob/release/RELEASENOTES.md">MulleObjCValueFoundation</a></strong></td>
<td><strong>0.19.0</strong></td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleFoundation/blob/release/RELEASENOTES.md">MulleFoundation</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/Foundation/blob/release/RELEASENOTES.md">Foundation</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td><a href="//github.com/MulleFoundation/objc-compat/blob/release/RELEASENOTES.md">objc-compat</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/MulleFoundation/mulle-bunchobjects/blob/release/RELEASENOTES.md">mulle-bunchobjects</a></strong></td>
<td><strong>0.19.0</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/mulle-testgen/blob/release/RELEASENOTES.md">mulle-testgen</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/MulleObjCStandardFoundation-startup/blob/release/RELEASENOTES.md">MulleObjCStandardFoundation-startup</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/Foundation-startup/blob/release/RELEASENOTES.md">Foundation-startup</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/mulle-foundation-developer/blob/release/RELEASENOTES.md">mulle-foundation-developer</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleFoundation/foundation-developer/blob/release/RELEASENOTES.md">foundation-developer</a></td>
<td>0.19.0</td>
<td>0.18.0</td>
</tr>
</tbody>
</table>
<h3 id="mulleweb">MulleWeb</h3>
<p>There is a new project <strong>MulleBase64</strong>, otherwise MulleWeb was only updated
to work with the changes in MulleFoundation:</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.18 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/MulleWeb/MulleBase64/blob/release/RELEASENOTES.md">MulleBase64</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleCivetWeb/blob/release/RELEASENOTES.md">MulleCivetWeb</a></td>
<td>0.17.5</td>
<td>0.17.3</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleCurl/blob/release/RELEASENOTES.md">MulleCurl</a></td>
<td>0.17.4</td>
<td>0.17.3</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleHoedown/blob/release/RELEASENOTES.md">MulleHoedown</a></td>
<td>0.2.3</td>
<td>0.2.2</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleZlib/blob/release/RELEASENOTES.md">MulleZlib</a></td>
<td>0.15.5</td>
<td>0.15.4</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleObjCHTTPFoundation/blob/release/RELEASENOTES.md">MulleObjCHTTPFoundation</a></td>
<td>0.18.1</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleObjCInetFoundation/blob/release/RELEASENOTES.md">MulleObjCInetFoundation</a></td>
<td>0.18.1</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleObjCJSMNFoundation/blob/release/RELEASENOTES.md">MulleObjCJSMNFoundation</a></td>
<td>0.18.1</td>
<td>0.18.0</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleWebClient/blob/release/RELEASENOTES.md">MulleWebClient</a></td>
<td>0.0.5</td>
<td>0.0.4</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleWebServer/blob/release/RELEASENOTES.md">MulleWebServer</a></td>
<td>0.0.5</td>
<td>0.0.4</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td><a href="//github.com/MulleWeb/MulleScion/blob/release/RELEASENOTES.md">MulleScion</a></td>
<td>1859.1.7</td>
<td>1859.1.6</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/MulleScionHTMLPreprocessor/blob/release/RELEASENOTES.md">MulleScionHTMLPreprocessor</a></td>
<td>0.2.3</td>
<td>0.2.2</td>
</tr>
<tr>
<td><a href="//github.com/MulleWeb/mulle-scion/blob/release/RELEASENOTES.md">mulle-scion</a></td>
<td>1859.1.6</td>
<td>1859.1.5</td>
</tr>
</tbody>
</table>
<h3 id="mulle-sde">mulle-sde</h3>
<p><strong>mulle-sde</strong> gained two new projects, <strong>mulle-semver</strong> and <strong>mulle-domain</strong>.
Most projects have seen massive improvements:</p>
<table>
<thead>
<tr>
<th>RELEASENOTES</th>
<th>Version</th>
<th>mulle-objc 0.18 version</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-craft/blob/release/RELEASENOTES.md">mulle-craft</a></strong></td>
<td><strong>0.19.0</strong></td>
<td>0.18.3</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-dispense/blob/release/RELEASENOTES.md">mulle-dispense</a></td>
<td>2.0.2</td>
<td>2.0.1</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-domain/blob/release/RELEASENOTES.md">mulle-domain</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-env/blob/release/RELEASENOTES.md">mulle-env</a></strong></td>
<td><strong>3.4.0</strong></td>
<td>3.3.2</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-fetch/blob/release/RELEASENOTES.md">mulle-fetch</a></strong></td>
<td><strong>2.0.0</strong></td>
<td>1.8.3</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-make/blob/release/RELEASENOTES.md">mulle-make</a></strong></td>
<td><strong>0.16.0</strong></td>
<td>0.15.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-match/blob/release/RELEASENOTES.md">mulle-match</a></strong></td>
<td><strong>0.8.0</strong></td>
<td>0.7.0</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-monitor/blob/release/RELEASENOTES.md">mulle-monitor</a></td>
<td>0.9.2</td>
<td>0.9.1</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-platform/blob/release/RELEASENOTES.md">mulle-platform</a></strong></td>
<td><strong>0.5.0</strong></td>
<td>0.4.2</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-nat/mulle-project/blob/release/RELEASENOTES.md">mulle-project</a></strong></td>
<td><strong>2.4.0</strong></td>
<td>2.3.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-sde/blob/release/RELEASENOTES.md">mulle-sde</a></strong></td>
<td><strong>0.47.0</strong></td>
<td>0.44.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-semver/blob/release/RELEASENOTES.md">mulle-semver</a></strong></td>
<td><strong>0.0.1</strong></td>
<td>-</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-sourcetree/blob/release/RELEASENOTES.md">mulle-sourcetree</a></strong></td>
<td><strong>0.25.0</strong></td>
<td>0.23.1</td>
</tr>
<tr>
<td><a href="//github.com/mulle-sde/mulle-template/blob/release/RELEASENOTES.md">mulle-template</a></td>
<td>0.0.4</td>
<td>0.0.2</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-test/blob/release/RELEASENOTES.md">mulle-test</a></strong></td>
<td>5.3.3</td>
<td>5.3.0</td>
</tr>
<tr>
<td><strong><a href="//github.com/mulle-sde/mulle-sde-developer/blob/release/RELEASENOTES.md">mulle-sde-developer</a></strong></td>
<td><strong>0.22.0</strong></td>
<td>0.21.0</td>
</tr>
</tbody>
</table>