<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Milen&#39;s Writings</title>
    <link>https://milen.me/writings/</link>
    <description>Milen&#39;s Writings</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-gb</language>
    <lastBuildDate>Fri, 30 May 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://milen.me/writings/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>AppKit State Restoration on macOS 14 Sonoma</title>
      <link>https://milen.me/writings/appkit-state-restoration-macos-14-sonoma/</link>
      <pubDate>Wed, 20 Dec 2023 00:00:00 +0000</pubDate>
      
      <guid>https://milen.me/writings/appkit-state-restoration-macos-14-sonoma/</guid>
      <description>&lt;h2 id=&#34;appkit-state-restoration-on-macos-14-sonoma&#34;&gt;AppKit State Restoration on macOS 14 Sonoma&lt;/h2&gt;
&lt;p&gt;AppKit state restoration behaviour changed on macOS 14 Sonoma in a subtle way
that can lead to apps not restoring their state correctly. The change can lead
to &lt;em&gt;silent&lt;/em&gt; breakages which can be hard to debug.&lt;/p&gt;
&lt;h3 id=&#34;behavioural-changes&#34;&gt;Behavioural Changes&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&#34;https://developer.apple.com/documentation/macos-release-notes/appkit-release-notes-for-macos-14&#34;&gt;AppKit release notes for macOS 14&lt;/a&gt; state:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Secure coding is automatically enabled for restorable state for applications
linked on the macOS 14.0 SDK&lt;/strong&gt;. Applications that target prior versions of macOS
should implement &lt;code&gt;NSApplicationDelegate.applicationSupportsSecureRestorableState()&lt;/code&gt;
to return true so it’s enabled on all supported OS versions.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;As usual, the behavioural changes only apply to apps that have been linked against
the latest SDK to preserve backwards compatibility for existing apps.&lt;/p&gt;
&lt;h3 id=&#34;consequences&#34;&gt;Consequences&lt;/h3&gt;
&lt;p&gt;The relase notes don&amp;rsquo;t make it immediately clear what the &lt;em&gt;consequences&lt;/em&gt; of
automatically enabling secure coding might be. In practice, it means that secure
coding violations can now occur. The response to such violations depends on the
value of &lt;code&gt;NSCoder&lt;/code&gt;&amp;rsquo;s &lt;code&gt;decodingFailurePolicy&lt;/code&gt; property which can be one of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;NSDecodingFailurePolicyRaiseException&lt;/code&gt;: A failure policy that directs the coder to raise an exception.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NSDecodingFailurePolicySetErrorAndReturn&lt;/code&gt;: A failure policy that directs the coder to capture the failure as an error object.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;decodingFailurePolicy&lt;/code&gt; property is readonly, thus outside of our control as
the &lt;code&gt;NSCoder&lt;/code&gt; object is created by the framework. AppKit uses
&lt;code&gt;NSDecodingFailurePolicySetErrorAndReturn&lt;/code&gt; as the default policy for state restoration.&lt;/p&gt;
&lt;p&gt;The documentation states:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;On decode failure, the &lt;code&gt;NSCoder&lt;/code&gt; will capture the failure as an &lt;code&gt;NSError&lt;/code&gt;,
and prevent further decodes (by returning &lt;code&gt;0&lt;/code&gt; / &lt;code&gt;nil&lt;/code&gt; equivalent as appropriate).&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Thus, after a secure coding violation, subsequent decoding operations would &lt;em&gt;silently&lt;/em&gt; fail.&lt;/p&gt;
&lt;h3 id=&#34;secure-coding-violations&#34;&gt;Secure Coding Violations&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;NSSecureCoding&lt;/code&gt; docs demonstrate the canonical secure coding violation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Historically, many classes decoded instances of themselves like this:&lt;/p&gt;&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-objc&#34; data-lang=&#34;objc&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00a8c8&#34;&gt;id&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;obj&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;decoder&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;decodeObjectForKey&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#d88200&#34;&gt;@&amp;#34;myKey&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00a8c8&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;obj&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;isKindOfClass&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;:[&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;MyClass&lt;/span&gt; &lt;span style=&#34;color:#00a8c8&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;]])&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;{&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;/* ...fail... */&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This technique is potentially unsafe because by the time you can verify
the class type, the object has already been constructed, and if this is part
of a collection class, potentially inserted into an object graph.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So any usages of &lt;code&gt;-[NSCoder decodeObjectForKey:]&lt;/code&gt; would trigger a secure coding
violation. The docs for &lt;code&gt;NSCoder.decodingFailurePolicy&lt;/code&gt; provide further examples:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A decode call can fail for the following reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&amp;hellip;snip&amp;hellip;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A secure coding violation occurs. This happens when you attempt to decode an
object that doesn’t conform to &lt;code&gt;NSSecureCoding&lt;/code&gt;. This also happens when the
encoded type doesn’t match any of the types passed to &lt;code&gt;decodeObject(of:forKey:)&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;how-to-debug&#34;&gt;How to Debug&lt;/h2&gt;
&lt;p&gt;Violations can now arise in any &lt;code&gt;-restoreStateWithCoder:&lt;/code&gt; implementations,
so they need to be audited.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check for any usages of &lt;code&gt;-[NSCoder decodeObjectForKey:]&lt;/code&gt;.
&lt;ul&gt;
&lt;li&gt;Replace with the appropriate secure variants.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;At the end of &lt;code&gt;-restoreStateWithCoder:&lt;/code&gt;, check the value of &lt;code&gt;NSCoder.error&lt;/code&gt; property.
&lt;ul&gt;
&lt;li&gt;If it&amp;rsquo;s non-&lt;code&gt;nil&lt;/code&gt;, an error must have occurred earlier.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.apple.com/documentation/macos-release-notes/appkit-release-notes-for-macos-14&#34;&gt;AppKit Release Notes for macOS 14&lt;/a&gt; (&lt;a href=&#34;https://archive.ph/kEjJ6&#34;&gt;archive&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.apple.com/documentation/foundation/nssecurecoding?language=objc&#34;&gt;&lt;code&gt;NSSecureCoding&lt;/code&gt;&lt;/a&gt; (&lt;a href=&#34;https://archive.ph/AAXYR&#34;&gt;archive&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.apple.com/documentation/foundation/nscoder?language=objc&#34;&gt;&lt;code&gt;NSCoder&lt;/code&gt;&lt;/a&gt; (&lt;a href=&#34;https://archive.ph/uvODR&#34;&gt;archive&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.apple.com/documentation/foundation/nscoder/1642984-decodingfailurepolicy?language=objc&#34;&gt;&lt;code&gt;NSCoder.decodingFailurePolicy&lt;/code&gt;&lt;/a&gt; (&lt;a href=&#34;https://archive.ph/6XOfT&#34;&gt;archive&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;&#34;&gt;&lt;code&gt;NSDecodingFailurePolicy&lt;/code&gt;&lt;/a&gt; (&lt;a href=&#34;https://archive.ph/dXIgo&#34;&gt;archive&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Premature Optimization: Universally Misunderstood</title>
      <link>https://milen.me/writings/premature-optimization-universally-misunderstood/</link>
      <pubDate>Mon, 28 Aug 2023 00:00:00 +0000</pubDate>
      
      <guid>https://milen.me/writings/premature-optimization-universally-misunderstood/</guid>
      <description>&lt;h2 id=&#34;premature-optimization&#34;&gt;&amp;ldquo;Premature Optimization&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;You might have come across the famous software optimisation quote popularised by Donald Knuth:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Premature optimization is the root of all evil.&lt;/p&gt;
&lt;p&gt;– Sir Tony Hoare&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;It has been commonly interpreted as &amp;ldquo;don&amp;rsquo;t think about performance in the beginning,
you can fix any performance problem later&amp;rdquo;. This interpretation is &lt;em&gt;completely and categorically wrong&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&#34;original-quote&#34;&gt;Original Quote&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s very common for statements to lose their original meaning when context has
been stripped and that&amp;rsquo;s exactly what happened here.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We should forget about &lt;strong&gt;small efficiencies&lt;/strong&gt;, say about 97% of the time:
premature optimization is the root of all evil.&lt;/p&gt;
&lt;p&gt;– Sir Tony Hoare&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The short version is missing a crucial part: &amp;ldquo;&lt;em&gt;small efficiencies&lt;/em&gt;&amp;rdquo;.
At the time the quote was made, &amp;ldquo;&lt;em&gt;small efficiencies&lt;/em&gt;&amp;rdquo; referred to techniques
like minimising the number of instructions used.&lt;/p&gt;
&lt;p&gt;With the additional context, the quote takes on a significantly
different meaning: it&amp;rsquo;s making a statement only about &lt;em&gt;micro&lt;/em&gt;-optimisations
(&amp;quot;&lt;em&gt;small efficiencies&lt;/em&gt;&amp;quot;), &lt;em&gt;not&lt;/em&gt; about performance in general.&lt;/p&gt;
&lt;p&gt;Hoare was simply advising against &lt;em&gt;micro&lt;/em&gt;-optimisations without finding
the hotspots first: the &amp;ldquo;premature&amp;rdquo; part refers to lacking measurements.&lt;/p&gt;
&lt;h3 id=&#34;hotspot-optimisation-make-it-fast-later-fallacy&#34;&gt;Hotspot Optimisation: &amp;ldquo;Make it Fast Later&amp;rdquo; Fallacy&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s quite tempting to adopt a &amp;ldquo;ship now, make it fast later&amp;rdquo; approach.
While optimisations will improve performance, it won&amp;rsquo;t change the
&lt;em&gt;fundamental performance envelope&lt;/em&gt;. As tech stack fundamentals, access patterns,
data dependencies and data structures are baked into a design,
&lt;strong&gt;it&amp;rsquo;s not possible to hotspot your way out of a slow architecture&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Daniel Lemire perfectly explains this in &lt;a href=&#34;https://lemire.me/blog/2023/04/27/hotspot-performance-engineering-fails/&#34;&gt;Hotspot performance engineering fails&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Developers often believe that software performance follows a Pareto distribution:
80% of the running time is spent in 20% of the code. Using this model,
you can write most of your code without any care for performance and focus on
the narrow pieces of code that are performance sensitive.&lt;/p&gt;
&lt;p&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;Sadly, it does not work.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://archive.ph/kSsrH&#34;&gt;Charles Cook&lt;/a&gt; encounters this with a configuration client which performs
a lot &lt;a href=&#34;https://en.wikipedia.org/wiki/Distributed_Component_Object_Model&#34;&gt;DCOM&lt;/a&gt; calls:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Again, optimizing this code would require a lot of re-working: optimization
after a design has been implemented nearly always involves much more work than
incorporating it into the original design.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In &lt;a href=&#34;https://www.computerenhance.com/p/performance-excuses-debunked&#34;&gt;Performance Excuses Debunked&lt;/a&gt;,
Casey Muratori astutely notes that if optimising hotspots was the solution to
serious performance problems, software wouldn&amp;rsquo;t have to get rewritten to make
it faster:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If Facebook’s performance problems were concentrated into “hotspots”,
why did they have to completely rewrite entire codebases?
Why would they have to do a “ground up” rewrite of something if only a few
hotspots were causing the problem? Why didn’t they just rewrite the hotspots?
Why did they have to rewrite an entire compiler in a new language,
instead of just rewriting the hotspots in that language?
Why did they have to make their own compiler to speed up PHP and Hack,
instead of just identifying the hotspots in those codebases and
rewriting them in C for performance?&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;designing-for-performance&#34;&gt;Designing for Performance&lt;/h2&gt;
&lt;p&gt;If you want to have fast software, &lt;strong&gt;you must think about performance from day one&lt;/strong&gt;.
This includes thinking about the tech stack, the architecture, the data access
patterns, the data dependencies, the networking and how it all fits together.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://archive.ph/kSsrH&#34;&gt;Charles Cook&lt;/a&gt; summarises it quite well:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Its usually not worth spending a lot of time micro-optimizing code before its
obvious where the performance bottlenecks are. But, conversely,
&lt;strong&gt;when designing software at a system level, performance issues should always be
considered from the beginning&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A good software developer will do this automatically, having developed a feel for
where performance issues will cause problems. An inexperienced developer will
not bother, misguidedly believing that a bit of fine tuning at a later
stage will fix any problems.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;a-revised-quote&#34;&gt;A Revised Quote&lt;/h3&gt;
&lt;p&gt;When having conversations about performance in the future, I&amp;rsquo;ll be using a &lt;em&gt;revised&lt;/em&gt; version:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Premature &lt;strong&gt;micro&lt;/strong&gt;-optimization is the root of all evil.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And that one, I completely agree with.&lt;/p&gt;
&lt;h2 id=&#34;quote-origin-update&#34;&gt;Quote Origin (Update)&lt;/h2&gt;
&lt;p&gt;Alexander Keller reached out about the origin of the quote. It first appeared
in Donald Knuth&amp;rsquo;s &lt;a href=&#34;https://dl.acm.org/doi/10.1145/356635.356640&#34;&gt;Structured Programming with go to Statements&lt;/a&gt;
and Knuth later &lt;a href=&#34;https://groups.google.com/g/alt.quotations/c/FL9A03TGOqs/m/KNQ4cvqBwa8J?hl=en&#34;&gt;attributed the quote&lt;/a&gt; to Hoare:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Just FYI &amp;hellip; after giving Knuth credit for this for years, I recently
saw a place where Knuth attributes it to C.A.R. Hoare. From &amp;ldquo;The
Errors of TeX&amp;rdquo; on page 276 of &amp;ldquo;Literate Programming&amp;rdquo; to Hoare:&lt;/p&gt;
&lt;p&gt;&amp;ldquo;But I also knew, and forgot, Hoare&amp;rsquo;s dictum that premature
optimization is the root of all evil in programming.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Does anybody know where this statement first appeared?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Now, the &lt;em&gt;interesting&lt;/em&gt; part is that Hoare did not actually claim to be the &lt;a href=&#34;https://hans.gerwitz.com/2004/08/12/premature-optimization-is-the-root-of-all-evil.html&#34;&gt;author of the quote&lt;/a&gt;.
Both Bruce Eckel and Tony Hoare credit Edsger Dijkstra.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Dear Hans,&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m sorry I have no recollection how this quotation came about.?
I might have attributed it to Edsger Dijkstra.&lt;/p&gt;
&lt;p&gt;I think it would be fair for you assume it is common culture or folklore.&lt;/p&gt;
&lt;p&gt;Tony.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://ubiquity.acm.org/article.cfm?id=1513451&#34;&gt;The Fallacy of Premature Optimization&lt;/a&gt; (&lt;a href=&#34;https://archive.ph/VrAYw&#34;&gt;archive&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://archive.ph/kSsrH&#34;&gt;Cook Computing: Premature Optimization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.computerenhance.com/p/performance-excuses-debunked&#34;&gt;Performance Excuses Debunked&lt;/a&gt; (&lt;a href=&#34;https://archive.ph/VScEg&#34;&gt;archive&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://lemire.me/blog/2023/04/27/hotspot-performance-engineering-fails/&#34;&gt;Hotspot performance engineering fails&lt;/a&gt; (&lt;a href=&#34;https://archive.ph/7obY6&#34;&gt;archive&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.mikeash.com/pyblog/friday-qa-2016-04-15-performance-comparisons-of-common-operations-2016-edition.html&#34;&gt;Performance Comparisons of Common Operations, 2016 Edition&lt;/a&gt; (&lt;a href=&#34;https://archive.ph/86ewU&#34;&gt;archive&lt;/a&gt;)
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/mikeash/PerformanceTest&#34;&gt;mikeash/PerformanceTest (GitHub)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://dl.acm.org/doi/10.1145/356635.356640&#34;&gt;Structured Programming with go to Statements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://hans.gerwitz.com/2004/08/12/premature-optimization-is-the-root-of-all-evil.html&#34;&gt;Hans Gerwitz: Premature optimization is the root of all evil&lt;/a&gt; (&lt;a href=&#34;https://archive.ph/zIRU&#34;&gt;archive&lt;/a&gt;)
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://groups.google.com/g/alt.quotations/c/FL9A03TGOqs/m/KNQ4cvqBwa8J?hl=en&#34;&gt;literary programming and C++&lt;/a&gt; (&lt;a href=&#34;https://archive.ph/eTJ2n&#34;&gt;archive&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Exploring Windows XP on macOS ARM64</title>
      <link>https://milen.me/writings/exploring-windows-xp-on-macos-arm64/</link>
      <pubDate>Wed, 23 Aug 2023 00:00:00 +0000</pubDate>
      
      <guid>https://milen.me/writings/exploring-windows-xp-on-macos-arm64/</guid>
      <description>&lt;h2 id=&#34;windows-xp&#34;&gt;Windows XP&lt;/h2&gt;
&lt;div class=&#34;center-wrapper&#34;&gt;
  
  
    
  
  &lt;img src=&#34;https://r2-cloudflare.milen.me/images/writings/windows-xp-on-macos-arm64/setup_06.jpg&#34; loading=&#34;lazy&#34; /&gt;
&lt;/div&gt;
&lt;p&gt;I have a lot of fond memories of Windows XP and after feeling nostalgic, I was
happy to find out that it&amp;rsquo;s relatively easy to take it for a spin on an
ARM64 Mac using &lt;a href=&#34;https://www.qemu.org/&#34;&gt;QEMU&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I decided to install XP and an assortment of classic software one final time,
taking screenshots of the whole process. This included
&lt;a href=&#34;https://milen.me/software/windows-xp/programs/firefox/&#34;&gt;Firefox&lt;/a&gt;,
&lt;a href=&#34;https://milen.me/software/windows-xp/programs/winamp/&#34;&gt;Winamp&lt;/a&gt;,
&lt;a href=&#34;https://milen.me/software/windows-xp/programs/winzip/&#34;&gt;WinZip&lt;/a&gt;,
&lt;a href=&#34;https://milen.me/software/windows-xp/programs/mirc/&#34;&gt;mIRC&lt;/a&gt;,
&lt;a href=&#34;https://milen.me/software/windows-xp/programs/delphi/&#34;&gt;Borland Delphi&lt;/a&gt; and
&lt;a href=&#34;https://milen.me/software/windows-xp/programs/visual-cpp/&#34;&gt;Visual C++ 6.0&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;sidenote-serenityos&#34;&gt;Sidenote: SerenityOS&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;re feeling similarly nostalgic, definitely check out &lt;a href=&#34;https://serenityos.org/&#34;&gt;SerenityOS&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;SerenityOS is a love letter to &amp;rsquo;90s user interfaces with a custom Unix-like core.
It flatters with sincerity by stealing beautiful ideas from various other systems.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The same people also started the &lt;a href=&#34;https://www.ladybird.dev/&#34;&gt;Ladybird&lt;/a&gt; browser:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ladybird is an ongoing project to build an independent web browser from scratch.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://awesomekling.github.io/&#34;&gt;Andreas&lt;/a&gt; posts regular updates on YouTube which
are a great way to marvel at the project&amp;rsquo;s impressive progress (e.g.,
&lt;a href=&#34;https://www.youtube.com/watch?v=54wL_OgKU9I&#34;&gt;SerenityOS update (July 2023)&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id=&#34;installation&#34;&gt;Installation&lt;/h2&gt;
&lt;p&gt;The instructions are based on &lt;a href=&#34;https://tinyapps.org/blog/202105220715_m1_mac_emulate_x86.html&#34;&gt;Emulating Windows XP x86 under M1 Mac via UTM &amp;amp; QEMU&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download, install and launch &lt;a href=&#34;https://mac.getutm.app/&#34;&gt;UTM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Download the &lt;a href=&#34;https://mac.getutm.app/gallery/windows-xp&#34;&gt;Windows XP UTM template&lt;/a&gt;
&lt;ol&gt;
&lt;li&gt;Extract &lt;code&gt;windows-xp-x64-utm.zip&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Open the &lt;code&gt;Windows XP.utm&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Click on the &amp;ldquo;CD/DVD&amp;rdquo; dropdown button and mount your installation CD &lt;code&gt;.iso&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Start the VM&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;installing-additional-software&#34;&gt;Installing Additional Software&lt;/h3&gt;
&lt;p&gt;I couldn&amp;rsquo;t get directory sharing to work using
&lt;a href=&#34;https://docs.getutm.app/guest-support/windows/&#34;&gt;SPICE tools&lt;/a&gt;, so instead I ended
up creating an &lt;code&gt;.iso&lt;/code&gt; with Firefox and all additional software I needed.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download &lt;a href=&#34;https://ftp.mozilla.org/pub/firefox/releases/52.9.0esr/win32/en-US/&#34;&gt;Firefox 52.9.0 ESR&lt;/a&gt;
which is the latest version to run on Windows XP.&lt;/li&gt;
&lt;li&gt;Create a folder named &lt;code&gt;Software&lt;/code&gt; containing all the files you want to share.&lt;/li&gt;
&lt;li&gt;Open Disk Utility.&lt;/li&gt;
&lt;li&gt;From the menu bar, go to &lt;code&gt;File → New Image → Image from Folder...&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Software&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;For the &lt;code&gt;Image Format: &lt;/code&gt; dropdown, selected &lt;code&gt;DVD/CD master&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Save&lt;/code&gt;. You will now have a &lt;code&gt;Software.cdr&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;Software.iso&lt;/code&gt; from &lt;code&gt;Software.cdr&lt;/code&gt; in the Terminal.
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;hdiutil makehybrid -iso -joliet -o Software.iso Software.cdr&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Mount &lt;code&gt;Software.iso&lt;/code&gt; in UTM.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;gallery&#34;&gt;Gallery&lt;/h2&gt;
&lt;p&gt;Enjoy the screenshots below and check out the &lt;a href=&#34;https://milen.me/software/windows-xp&#34;&gt;extensive gallery&lt;/a&gt; for more.&lt;/p&gt;
&lt;p&gt;&lt;div class=&#34;center-wrapper&#34;&gt;
  
  
    
  
  &lt;img src=&#34;https://r2-cloudflare.milen.me/images/writings/windows-xp-on-macos-arm64/welcome_02_settings_resolution.jpg&#34; loading=&#34;lazy&#34; /&gt;
&lt;/div&gt;
&lt;div class=&#34;center-wrapper&#34;&gt;
  
  
    
  
  &lt;img src=&#34;https://r2-cloudflare.milen.me/images/writings/windows-xp-on-macos-arm64/firefox_10.jpg&#34; loading=&#34;lazy&#34; /&gt;
&lt;/div&gt;
&lt;div class=&#34;center-wrapper&#34;&gt;
  
  
    
  
  &lt;img src=&#34;https://r2-cloudflare.milen.me/images/writings/windows-xp-on-macos-arm64/visual_cpp_20.jpg&#34; loading=&#34;lazy&#34; /&gt;
&lt;/div&gt;
&lt;div class=&#34;center-wrapper&#34;&gt;
  
  
    
  
  &lt;img src=&#34;https://r2-cloudflare.milen.me/images/writings/windows-xp-on-macos-arm64/delphi_15.jpg&#34; loading=&#34;lazy&#34; /&gt;
&lt;/div&gt;
&lt;div class=&#34;center-wrapper&#34;&gt;
  
  
    
  
  &lt;img src=&#34;https://r2-cloudflare.milen.me/images/writings/windows-xp-on-macos-arm64/winamp_08.jpg&#34; loading=&#34;lazy&#34; /&gt;
&lt;/div&gt;
&lt;div class=&#34;center-wrapper&#34;&gt;
  
  
    
  
  &lt;img src=&#34;https://r2-cloudflare.milen.me/images/writings/windows-xp-on-macos-arm64/mirc_10.jpg&#34; loading=&#34;lazy&#34; /&gt;
&lt;/div&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>AppKit vs SwiftUI: Stable vs Shiny</title>
      <link>https://milen.me/writings/appkit-vs-swiftui-stable-vs-shiny/</link>
      <pubDate>Thu, 10 Aug 2023 00:00:00 +0000</pubDate>
      
      <guid>https://milen.me/writings/appkit-vs-swiftui-stable-vs-shiny/</guid>
      <description>&lt;h2 id=&#34;appkit-vs-swiftui-the-question&#34;&gt;AppKit vs SwiftUI: The Question&lt;/h2&gt;
&lt;p&gt;When writing a native macOS app, developers need to decide which UI framework
to write new code in. AppKit, whose origins date back to 30yrs+ ago, feels
like a dinosaur soon to be retired with the shiny SwiftUI waiting around
corner to take over.&lt;/p&gt;
&lt;h2 id=&#34;ghostty-vs-swiftui&#34;&gt;Ghostty vs SwiftUI&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://mitchellh.com/&#34;&gt;Mitchell Hashimoto&lt;/a&gt; has been working on a new
cross-platform terminal written in Zig and &lt;a href=&#34;https://mitchellh.com/writing/ghostty-devlog-002&#34;&gt;posted a update&lt;/a&gt;
on the project&amp;rsquo;s progress. Notably, he talked about implementing non-native
full-screen on macOS:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is a feature amongst other macOS terminal emulators that is commonly called &amp;ldquo;non-native fullscreen.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;It turns out implementing this was a doozy. On the surface, it really is very
simple: you programmatically modify some window attributes. If you Google around,
the code samples to make this behavior happen are a &lt;strong&gt;dozen lines or so&lt;/strong&gt;.
But the &lt;strong&gt;Ghostty PR was +802/-239&lt;/strong&gt;. What?&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve been following Ghostty, I very proudly talk about how Ghostty is
written in Zig but uses SwiftUI for macOS GUI. Ghostty was 100% SwiftUI (for the GUI):
the main entrypoint for Ghostty.app was a SwiftUI App object.
The issue is that &lt;strong&gt;non-native fullscreen requires subclassing NSWindow and
with SwiftUI you just can&amp;rsquo;t&lt;/strong&gt; (or, nobody has publicly figured out how).&lt;/p&gt;
&lt;p&gt;So, in order to make non-native fullscreen work, we had to &lt;strong&gt;rip out the
SwiftUI app&lt;/strong&gt; and window lifecycle management and &lt;strong&gt;rewrite it ourselves using
plain old AppKit&lt;/strong&gt;.
Note: we still use SwiftUI for the views, just not the window/app lifecycle management.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And it turns out that non-native fullscreen was not the only issue:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If we just wanted non-native fullscreen, this probably wouldn&amp;rsquo;t be worth it.
But, we already had some other &lt;strong&gt;bugs or missing features looming because of SwiftUI&lt;/strong&gt;
and this gives us a path to fix all of them, so we decided it was worth it.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So, usage of SwiftUI constrained the product to have bugs and missing features.
But &lt;em&gt;why&lt;/em&gt; are developers choosing to use SwiftUI if it&amp;rsquo;s the source of such problems?&lt;/p&gt;
&lt;p&gt;To answer that, let&amp;rsquo;s rewind back to June 2022.&lt;/p&gt;
&lt;h2 id=&#34;wwdc-2022-the-swiftui-vision&#34;&gt;WWDC 2022: The SwiftUI Vision&lt;/h2&gt;
&lt;p&gt;At the &lt;a href=&#34;https://developer.apple.com/videos/play/wwdc2022/102/&#34;&gt;Platforms State of the Union&lt;/a&gt; session
during WWDC 2022, Josh Shaffer set out the vision of the future platforms:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Objective-C language, AppKit and UIKit frameworks, and Interface Builder
have empowered generations of developers. These technologies were built for
each other and will continue to serve us  well for a long time to come.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But over time, new abstractions become necessary&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://milen.me/images/writings/appkit-vs-swiftui-stable-vs-shiny/wwdc2022-102_objc_appkit_ib.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;A bit later in the session, he clearly communicated the direction, so that
developers know where to invest:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The best way to build an app is with Swift and SwiftUI.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://milen.me/images/writings/appkit-vs-swiftui-stable-vs-shiny/wwdc2022-102_swift_swiftui.jpg&#34; alt=&#34;The best way to build an app is with Swift and SwiftUI&#34;&gt;&lt;/p&gt;
&lt;p&gt;Unfortunately, that might not be &lt;em&gt;quite&lt;/em&gt; true on macOS.&lt;/p&gt;
&lt;h3 id=&#34;vision-vs-reality&#34;&gt;Vision vs Reality&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s important to interpret WWDC statements within the context of the event.
Long-term vision statements must to be ambitious &amp;amp; unambigious and as a consequence,
they might not match up present-day reality. The evidence certainly supports the
hypothesis that SwiftUI is simply not a &lt;em&gt;complete&lt;/em&gt; replacement of AppKit &lt;em&gt;today&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;swiftui-a-thousand-papercuts&#34;&gt;SwiftUI: A Thousand Papercuts&lt;/h2&gt;
&lt;p&gt;The problems with SwiftUI have been
&lt;a href=&#34;https://mjtsai.com/blog/2023/05/30/swiftui-notes-before-wwdc-2023/&#34;&gt;thoroughly documented by Michael Tsai&lt;/a&gt;,
so I&amp;rsquo;ll include just a few examples.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://telemetrydeck.com/blog/ios-open-source/&#34;&gt;Daniel Jilg&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The app is also hard to develop because we seem to be fighting against a host of bugs and unclear behaviours in SwiftUI on macOS, even in the newest versions.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://twitter.com/phillipcaudell/status/1572125644749959168&#34;&gt;Phillip Caudell&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Another day, another serious SwiftUI bug: List on macOS won’t select its initial value if the binding has an optional value type. Works on iOS, fails on macOS.&lt;/p&gt;
&lt;p&gt;I am deeply regretting my decision to use SwiftUI for Big Mail 2.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://twitter.com/oskargroth/status/1688034005138956288&#34;&gt;Oskar Groth&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;No macOS SwiftUI component has let me down as much as List. Just when you think you’ve got it working well, there is always some tiny issue relating to reorder, DisclosureGroup expansion, highlight or layout.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://mastodon.social/@stroughtonsmith/110453960832638854&#34;&gt;Steve Troughton-Smith&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Every WWDC from here on in I’ll be looking at from the perspective of ‘can you make better apps with SwiftUI vs not-SwiftUI?’. The answer right now is ‘no’, not for the apps I want to build and the platforms I want to build them for, but boy would it sure be nice to say yes and start using more of AppKit in my cross-platform apps.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;macos-major-versions&#34;&gt;macOS Major Versions&lt;/h3&gt;
&lt;p&gt;It seems that SwiftUI is breaking behaviour across major version upgrades as well.
&lt;a href=&#34;https://mastodon.social/@stroughtonsmith&#34;&gt;Steve Troughton-Smith&lt;/a&gt; on &lt;a href=&#34;https://mjtsai.com/blog/2022/10/25/macos-13-0-ventura/&#34;&gt;macOS 13 Ventura breaking SwiftUI layouts&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Another version of macOS, another set of SwiftUI layout changes that break my UI in some way.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And sometimes, you get important &lt;a href=&#34;https://mjtsai.com/blog/2023/08/03/how-nshostingview-determines-its-sizing/&#34;&gt;bug fixes and features&lt;/a&gt;
which only available on the &lt;em&gt;latest&lt;/em&gt; version:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I couldn’t get my SwiftUI view to expand to fill up the entire superview that the &lt;code&gt;NSHostingView&lt;/code&gt; was being added to.&lt;/p&gt;
&lt;p&gt;But, there’s just one problem&amp;hellip; this property was introduced in macOS 13 and I’m still targeting macOS 12.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;swiftui-case-studies&#34;&gt;SwiftUI: Case Studies&lt;/h2&gt;
&lt;p&gt;Another set of data points are case studies of app rewrites and green
frield projects. The experiences paint a consistent picture:
SwiftUI accelerates parts of the workflow but it introduces non-trivial
friction in others.&lt;/p&gt;
&lt;h3 id=&#34;case-study-rewriting-remotion-in-swiftui&#34;&gt;Case Study: Rewriting Remotion in SwiftUI&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://mjtsai.com/blog/2023/05/30/rewriting-remotion-in-swiftui/&#34;&gt;Rewriting Remotion in SwiftUI&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Development is faster, the app is more stable, and new teammates are ramping up faster thanks to the simpler code base.&lt;/p&gt;
&lt;p&gt;We now think: If you are a macOS or iOS developer who hasn’t yet taken the plunge yet, now is a great time to start writing a new app using almost exclusively SwiftUI, and use its friends Combine and concurrency for the data flow.&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;While SwiftUI is all you’ll need for a &lt;strong&gt;basic Mac or iOS application&lt;/strong&gt;,
there are &lt;strong&gt;still quite a few gaps&lt;/strong&gt; that will require you to partially make
use of classic Cocoa views. In our code base, for example, we need some
access to NSEvents, text input, and tweaking the first responder that
just &lt;strong&gt;aren’t possible with pure SwiftUI&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;case-study-wallaroo-and-swiftui-on-macos&#34;&gt;Case Study: Wallaroo and SwiftUI on macOS&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://mjtsai.com/blog/2023/05/26/wallaroo-and-swiftui-on-macos/&#34;&gt;Wallaroo and SwiftUI on macOS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Counterintuitively, SwiftUI made hard things and easy things hard:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I was expecting stuff like that to take a lot of effort to get working on the Mac. Instead, it ran 100% out of the box with no modifications at all.&lt;/p&gt;
&lt;p&gt;And the stuff that I was expecting to be easy, like a settings view, buttons, and menu commands, turned out to be hard.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;And a very common experience for developers targeting both iOS and macOS:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You’re going to have platform-specific code. More than you realize: certainly more than I expected!&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;case-study-timingis&#34;&gt;Case Study: timing.is&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://mjtsai.com/blog/2023/01/27/swiftui-in-timing-app/&#34;&gt;SwiftUI in Timing.is App&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It took a few hours to fall in love with SwiftUI. So much so that we instantly decided to abandon a cross-platform codebase and go fully native on iOS.&lt;/p&gt;
&lt;p&gt;Despite the &lt;strong&gt;regular friction&lt;/strong&gt;, we still loved it. Because like any commitment, you must let the majority rule. It was fun at least 51% of the time. But let’s talk about the &amp;lt;= 49% that wasn’t.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;appkit-maturity--stability&#34;&gt;AppKit: Maturity &amp;amp; Stability&lt;/h2&gt;
&lt;p&gt;AppKit is over 30yrs old, dating back to the early beginnings of NeXTSTEP. That&amp;rsquo;s
a &lt;em&gt;very&lt;/em&gt; long time to accumulate a set of APIs which been refined over a large variety
of use cases. It&amp;rsquo;s almost certain that any particular feature can be implemented.
On the other hand, it&amp;rsquo;s simply not possible to replace 30yrs+ of accumulated APIs
in just 4yrs (SwiftUI was released in 2019).&lt;/p&gt;
&lt;p&gt;Because of its maturity, AppKit does not change often nor significantly: it provides
a stable foundation to build upon. Desktop OS innovation is quite slow as resources
are focused on mobile and spatial. In turn, this means lower likelihood of breaking
changes on each major release and more time to focus on &lt;em&gt;your product&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;swiftui-solving-a-harder-problem&#34;&gt;SwiftUI: Solving a Harder Problem&lt;/h2&gt;
&lt;p&gt;SwiftUI is tackling a &lt;em&gt;much&lt;/em&gt; harder problem along multiple dimensions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Declarative&lt;/strong&gt;: SwiftUI adopts a new declarative paradigm vs AppKit&amp;rsquo;s imperative.
This requires changes to how solutions are expressed and how the APIs are designed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cross-platform&lt;/strong&gt;: SwiftUI is designed to be cross-platform across Apple&amp;rsquo;s
OSes. Cross-vendor cross-platforms frameworks suffer from a variety of
problems but Apple has an advantage here: it controls the full stack across all
environments. Nevertheless, designing a UI framework that can scale all the way
from a watch to a Mac is a non-trivial undertaking.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;swiftui-a-rewrite&#34;&gt;SwiftUI: A Rewrite&lt;/h3&gt;
&lt;p&gt;SwiftUI can be thought of as a unifying rewrite of AppKit and UIKit, so the usual
rewriting caveats, risks and benefits apply.&lt;/p&gt;
&lt;p&gt;In this particular case, a major problem is that the developers doing
the rewrite are &lt;em&gt;not&lt;/em&gt; the same developers who created and evolved AppKit/UIKit.
This means that a lot of institutional knowledge and context has been lost and
would have to be re-invented.&lt;/p&gt;
&lt;h2 id=&#34;uikit-mac-catalyst&#34;&gt;UIKit (Mac Catalyst)&lt;/h2&gt;
&lt;p&gt;While using UIKit on macOS is another option, Apple sees UIKit the same way&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; as
AppKit: an API destined to be replaced by SwiftUI. This is not surprising
at all, given that UIKit is an ancestor of AppKit, so shares the imperative nature
and having been designed with Objective-C in mind.&lt;/p&gt;
&lt;h2 id=&#34;conclusion-swiftui-on-macos&#34;&gt;Conclusion: SwiftUI on macOS?&lt;/h2&gt;
&lt;p&gt;To make an informed decision about the UI framework, we need understand
what&amp;rsquo;s driving the decision, what are the use cases and the priorities.&lt;/p&gt;
&lt;p&gt;For example, if a product is being built, the question needs to be answered from
the perspective of what&amp;rsquo;s best for the customers. End-users do not care
about which framework was to solve their problems. Not fighting a brand
new framework will save a lot of time that can be spent focusing on the product itself.&lt;/p&gt;
&lt;p&gt;If you want to have some fun, play with a shiny new API or write an app in a new paradigm,
then SwifUI is the clear winner here. You will gain important skills that
you will be able to leverage in future.&lt;/p&gt;
&lt;h3 id=&#34;macos-apprentice-by-sarah-reichelt&#34;&gt;&amp;ldquo;macOS Apprentice&amp;rdquo; by Sarah Reichelt&lt;/h3&gt;
&lt;p&gt;In &lt;a href=&#34;https://troz.net/post/2023/macos_book_2/&#34;&gt;macOS Apprentice&lt;/a&gt;, Sarah tries to answer
the &lt;a href=&#34;https://www.kodeco.com/books/macos-apprentice/v1.0/chapters/18-using-swiftui-in-appkit&#34;&gt;same question&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To support old versions of macOS, use AppKit.&lt;/p&gt;
&lt;p&gt;For long-form text editing or for thousands of records, use AppKit.&lt;/p&gt;
&lt;p&gt;For existing AppKit apps, add SwiftUI gradually.&lt;/p&gt;
&lt;p&gt;For everything else, start with SwiftUI and include AppKit as needed.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;final-words&#34;&gt;Final Words&lt;/h3&gt;
&lt;p&gt;In summary, think &lt;em&gt;carefully&lt;/em&gt; about your use-cases and pick the framework that
allows you to deliver the user experience you desire.&lt;/p&gt;
&lt;p&gt;If you want to provide the &lt;em&gt;best user experience&lt;/em&gt;, you might have to leverage the
old and rusty AppKit, at least for a while longer. It&amp;rsquo;s always important to
prioritise &lt;em&gt;what&amp;rsquo;s best for your customers&lt;/em&gt; because in the long term,
that&amp;rsquo;s in your best interest as well.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;As evidenced by the slides from WWDC 2022.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>milen.me v2: Simpler, Faster</title>
      <link>https://milen.me/writings/milen-me-v2-faster-simpler/</link>
      <pubDate>Thu, 04 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://milen.me/writings/milen-me-v2-faster-simpler/</guid>
      <description>&lt;h2 id=&#34;why&#34;&gt;Why?&lt;/h2&gt;
&lt;p&gt;A lot has changed since I&amp;rsquo;ve last updated my website. The current design dates back
to July 2012, more than 10yrs ago. Not only has technology moved on since then,
what I value and how I approach engineering has changed as well.&lt;/p&gt;
&lt;p&gt;While &lt;a href=&#34;https://jekyllrb.com/&#34;&gt;Jekyll&lt;/a&gt; has served me well, it was time to move to
a more robust and actively maintained &lt;a href=&#34;https://gohugo.io&#34;&gt;ecosystem&lt;/a&gt; that will
hopefully last for another 10yrs.&lt;/p&gt;
&lt;h2 id=&#34;simplicity&#34;&gt;Simplicity&lt;/h2&gt;
&lt;p&gt;An overarching theme in my life for the past decade has been the
&lt;em&gt;pursuit of simplicity&lt;/em&gt; over complexity, especially when it comes to software.
I optimised the new design for maximum signal to noise:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simpler Design&lt;/strong&gt;: &lt;del&gt;header images&lt;/del&gt;, &lt;del&gt;custom web fonts&lt;/del&gt;.&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;strong&gt;Unimportant Content&lt;/strong&gt;&lt;/del&gt;: e.g., not reflective of my values or personality.&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;strong&gt;Google Analytics&lt;/strong&gt;&lt;/del&gt;: not needed, nor do I support the data collection.&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;&lt;/del&gt;: removed &lt;a href=&#34;https://github.com/lemonmade/bigfoot&#34;&gt;bigfoot.js&lt;/a&gt;
and its jQuery dependency.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most pages are now between 5-15KiB (excluding images)
and they load &lt;em&gt;instantly&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;history&#34;&gt;History&lt;/h2&gt;
&lt;p&gt;For archival purposes, I saved screenshots of the old design:
&lt;a href=&#34;https://milen.me/images/writings/milen-me-v2-simpler-faster/milen-me-v1-about.png&#34;&gt;About&lt;/a&gt;,
&lt;a href=&#34;https://milen.me/images/writings/milen-me-v2-simpler-faster/milen-me-v1-writings.png&#34;&gt;Writings&lt;/a&gt;,
&lt;a href=&#34;https://milen.me/images/writings/milen-me-v2-simpler-faster/milen-me-v1-software.png&#34;&gt;Software&lt;/a&gt;,
&lt;a href=&#34;https://milen.me/images/writings/milen-me-v2-simpler-faster/milen-me-v1-resume.png&#34;&gt;Resume&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Performance: Faster or Fast?</title>
      <link>https://milen.me/writings/performance-faster-or-fast/</link>
      <pubDate>Sat, 08 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://milen.me/writings/performance-faster-or-fast/</guid>
      <description>&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;When describing the performance of systems, always prefer terms which are unambigious.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &amp;ldquo;as fast as&amp;rdquo; speed ratios and &amp;ldquo;as long as&amp;rdquo; time ratios.&lt;/li&gt;
&lt;li&gt;Always use &amp;ldquo;faster&amp;rdquo; or &amp;ldquo;slower&amp;rdquo; when referring to speed, never time.&lt;/li&gt;
&lt;li&gt;When referring to time in percentage delta terms, explicitly state &amp;ldquo;time increased/decreased by X%&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Time ratio is &lt;code&gt;time_new / time_baseline&lt;/code&gt; and speed ratio is &lt;code&gt;time_baseline / time_new&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;performance-language--ambiguity&#34;&gt;Performance Language &amp;amp; Ambiguity&lt;/h2&gt;
&lt;p&gt;As part of my work on the &lt;a href=&#34;https://github.com/facebook/buck2&#34;&gt;Buck2 build system&lt;/a&gt;,
I deal with performance analyses on a daily basis. It&amp;rsquo;s extremely important to use
&lt;strong&gt;precise and consistent language&lt;/strong&gt; when describing the performance of systems,
so that there&amp;rsquo;s no ambiguity in its intepretation.&lt;/p&gt;
&lt;p&gt;For example, a statement like &lt;strong&gt;&amp;ldquo;X is 50% faster&amp;rdquo;&lt;/strong&gt; is ambiguous, as it can be interpreted in two ways:
the speed was increased by 50% or the time was decreased by 50% (those are not equivalent).
As another example, a statement like &lt;strong&gt;&amp;ldquo;X is 150% faster&amp;rdquo;&lt;/strong&gt; is also amgiguous because it can be interpreted
as either 50% or 150% faster.&lt;/p&gt;
&lt;h2 id=&#34;faster-vs-as-fast-as&#34;&gt;&amp;ldquo;Faster&amp;rdquo; vs &amp;ldquo;As Fast As&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;The terms faster/slower refer to the &lt;strong&gt;delta&lt;/strong&gt; of the measurement against a baseline.
Faster indicates a positive delta (i.e., 20% faster means +20% or +0.2x),
while slower indicates a negative delta (i.e., 20% slower means -20% or -0.2x).
For example, &lt;strong&gt;20% faster&lt;/strong&gt; means the new value is equal to &lt;strong&gt;120% of the baseline&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;As fast as&amp;rdquo; refers to the &lt;strong&gt;ratio&lt;/strong&gt; of the measurement against a baseline.
For example, &amp;ldquo;1.2x as fast as&amp;rdquo; means that it&amp;rsquo;s &amp;ldquo;0.2x faster&amp;rdquo; (i.e., 20%).
1.2x can also be expressed as 120% in percentage terms.
&amp;ldquo;As fast as&amp;rdquo; is also used when a measurement is lower as well,
e.g., &amp;ldquo;0.8x as fast as&amp;rdquo;(i.e., &amp;ldquo;80% as fast as&amp;rdquo; or &amp;ldquo;20% slower&amp;rdquo;).&lt;/p&gt;
&lt;h2 id=&#34;speed-vs-time&#34;&gt;Speed vs Time&lt;/h2&gt;
&lt;p&gt;Time and speed are &lt;strong&gt;inversely proportional&lt;/strong&gt;, so when time decreases, the speed increases. Crucially, a &lt;strong&gt;percentage time delta does not translate to the same percentage speed delta&lt;/strong&gt;. For example, it&amp;rsquo;s incorrect to say that a 50% time reduction is the same as being 50% faster.&lt;/p&gt;
&lt;p&gt;For example, if the time decreases by 50% (i.e., halved), that means the speed is twice as fast - i.e., &amp;ldquo;2x/200% as fast as&amp;rdquo; (or equivalently &amp;ldquo;100% &lt;em&gt;faster&lt;/em&gt;&amp;rdquo;&amp;quot;).&lt;/p&gt;
&lt;h2 id=&#34;calculating-time&#34;&gt;Calculating Time&lt;/h2&gt;
&lt;p&gt;Assume you have measured the baseline time (&lt;code&gt;time_baseline&lt;/code&gt;) and the new time (&lt;code&gt;time_new&lt;/code&gt;).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;time_ratio&lt;/code&gt; is defined as &lt;code&gt;time_new / time_baseline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;time_ratio_delta&lt;/code&gt; is defined as &lt;code&gt;time_ratio - 1.0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, with a &lt;code&gt;time_baseline&lt;/code&gt; of 100 seconds and &lt;code&gt;time_new&lt;/code&gt; of 80 seconds, the time ratio as 0.8x (&lt;code&gt;80/100&lt;/code&gt;), thus time ratio delta is -0.2x (&lt;code&gt;0.8 - 1.0&lt;/code&gt;). We could present this as &amp;ldquo;0.8x as long as&amp;rdquo;, &amp;ldquo;80% as long as&amp;rdquo; or &amp;ldquo;time decreased by 20%&amp;rdquo;.&lt;/p&gt;
&lt;h2 id=&#34;calculating-speed&#34;&gt;Calculating Speed&lt;/h2&gt;
&lt;p&gt;Assume you have measured the baseline time (&lt;code&gt;time_baseline&lt;/code&gt;) and the new time (&lt;code&gt;time_new&lt;/code&gt;).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;speed_ratio&lt;/code&gt; is defined as &lt;code&gt;time_baseline / time_new&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;speed_ratio_delta&lt;/code&gt; is defined as &lt;code&gt;speed_ratio - 1.0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, with a &lt;code&gt;time_baseline&lt;/code&gt; of 100 seconds and &lt;code&gt;time_new&lt;/code&gt; of 80 seconds, the speed ratio is 1.25x (&lt;code&gt;100/80&lt;/code&gt;), thus speed ratio delta is 0.25x (&lt;code&gt;1.25 - 1.0&lt;/code&gt;). We could present this as &amp;ldquo;1.25x as fast as&amp;rdquo;, &amp;ldquo;125% as fast as&amp;rdquo; or &amp;ldquo;25% faster&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Note that a 20% decrease in time (i.e., 20s out of 100s) led to a 25% increase in speed. This becomes clear when we show the derivation of &lt;code&gt;speed_ratio&lt;/code&gt; which is the inverse of the &lt;code&gt;time_ratio&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;speed_baseline = distance / time_baseline
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;speed_new = distance / time_new
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;speed_ratio = speed_new / speed_baseline
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;speed_new / speed_baseline = (distance / time_new) / (distance / time_baseline)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           = (distance / time_new) * (time_baseline / distance)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                           = time_baseline / time_new
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>macOS Network Metrics Using sysctl()</title>
      <link>https://milen.me/writings/macos-network-metrics-sysctl-net-rt-iflist2/</link>
      <pubDate>Sun, 05 Mar 2023 00:00:00 +0000</pubDate>
      
      <guid>https://milen.me/writings/macos-network-metrics-sysctl-net-rt-iflist2/</guid>
      <description>&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Getting accurate network metrics on macOS is possible using &lt;code&gt;sysctl()&lt;/code&gt; with &lt;code&gt;NET_RT_IFLIST2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Traffic metrics are exposed in units of 1KiB to prevent fingerprinting (only for 3rd party programs).&lt;/li&gt;
&lt;li&gt;As of macOS Ventura 13.2.1, there&amp;rsquo;s a kernel bug which truncates traffic values at the 4GiB mark.&lt;/li&gt;
&lt;li&gt;Using &lt;a href=&#34;https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/getifaddrs.3.html&#34;&gt;getifaddrs()&lt;/a&gt; only exposes 32bit fields, so it&amp;rsquo;s not a viable API on modern systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;updates&#34;&gt;&lt;em&gt;Updates&lt;/em&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://twitter.com/Mojo_66/status/1640984227326951424&#34;&gt;Mojo_66&lt;/a&gt; pointed out that &lt;code&gt;IFMIB_IFDATA&lt;/code&gt; does not suffer from truncation (&lt;a href=&#34;https://github.com/milend/macos-network-metrics/commit/ba188fc97140072c3378a519d115b88a90f61d4c&#34;&gt;code&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;network-metrics-on-macos&#34;&gt;Network Metrics on macOS&lt;/h2&gt;
&lt;p&gt;As part of my work on the &lt;a href=&#34;https://buck2.build&#34;&gt;Buck2 build system&lt;/a&gt;, I needed a way to observe the network throughput of the system. After some research, the conclusion was to use &lt;code&gt;sysctl()&lt;/code&gt; with &lt;code&gt;NET_RT_IFLIST2&lt;/code&gt;: this provided access to 64bit metrics&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; which do not suffer from overflowing that affects the 32bit fields of the older APIs.&lt;/p&gt;
&lt;p&gt;I wrote up a &lt;a href=&#34;https://github.com/milend/macos-network-metrics&#34;&gt;short sample program&lt;/a&gt; to quickly test the API. While the metrics for packets sent/received &lt;em&gt;exactly matched&lt;/em&gt; the ones from Activity Monitor, the traffic metrics did &lt;em&gt;not&lt;/em&gt;. I noticed two interesting behaviours:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The traffic metrics were always increasing in multiples of 1KiB.&lt;/li&gt;
&lt;li&gt;The traffic metrics did not match the ones from Activity Monitor.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I decided to file a &lt;a href=&#34;https://developer.apple.com/support/technical/&#34;&gt;TSI&lt;/a&gt; with Apple before digging any further. Thankfully, &lt;a href=&#34;https://mastodon.social/@justkwin@toot.community&#34;&gt;Quinn&lt;/a&gt; resolved the mystery of the inaccurate numbers.&lt;/p&gt;
&lt;h3 id=&#34;1kib-units&#34;&gt;1KiB Units&lt;/h3&gt;
&lt;p&gt;If you looked at the traffic metrics, they would only ever increase in multiples of 1KiB. The reason for the behaviour is that &lt;em&gt;the kernel applies batching to prevent malicious code from fingerprinting the system&lt;/em&gt;. This restriction applies &lt;em&gt;only&lt;/em&gt; to 3rd party programs (i.e., not codesigned by Apple).&lt;/p&gt;
&lt;p&gt;For example, if you were to copy the &lt;code&gt;netstat&lt;/code&gt; binary and re-sign it with an adhoc code signature, you will observe the batching while the Apple-signed binary works without any issues.&lt;/p&gt;
&lt;h3 id=&#34;inaccurate-numbers&#34;&gt;Inaccurate Numbers&lt;/h3&gt;
&lt;p&gt;Testing revealed that &lt;em&gt;sometimes&lt;/em&gt; the API returned inaccurate traffic metrics. Upon further investigation, it became clear that the API truncates and wraps around the traffic metrics at the 4GiB mark. Again, this only affects 3rd party programs.&lt;/p&gt;
&lt;p&gt;The behaviour is confirmed to be a bug in the kernel as of macOS Ventura 13.2.1 and it&amp;rsquo;s tracked as &lt;a href=&#34;rdar://106029568&#34;&gt;rdar://106029568&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;activity-monitor&#34;&gt;Activity Monitor&lt;/h3&gt;
&lt;p&gt;Activity Monitor and &lt;code&gt;nettop(1)&lt;/code&gt; use the &lt;em&gt;private&lt;/em&gt; &lt;code&gt;NetworkStatistics.framework&lt;/code&gt; to get network metrics. In the &lt;a href=&#34;http://newosxbook.com/ios/thebooks.html&#34;&gt;*OS Internals, Volume I book&lt;/a&gt;, there&amp;rsquo;s a &lt;a href=&#34;http://newosxbook.com/bonus/vol1ch16.html&#34;&gt;bonus chapter&lt;/a&gt; which covers the details of how the private API works.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://newosxbook.com/src.jl?tree=listings&amp;amp;file=netbottom.c&#34;&gt;Netbottom&lt;/a&gt; is a clone of &lt;code&gt;nettop(1)&lt;/code&gt; that shows how to use the private APIs.&lt;/p&gt;
&lt;h3 id=&#34;alternative-apis&#34;&gt;Alternative APIs&lt;/h3&gt;
&lt;p&gt;Unfortunately, there&amp;rsquo;s no alternative public API that can return 64bit metrics on macOS. Using &lt;a href=&#34;https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/getifaddrs.3.html&#34;&gt;getifaddrs()&lt;/a&gt; would only expose a &lt;code&gt;struct if_data&lt;/code&gt; which contains 32bit fields that overflow quickly on modern systems: it does &lt;em&gt;not&lt;/em&gt; expose &lt;code&gt;struct if_data64&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;rust-crates&#34;&gt;Rust Crates&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;re using Rust, the following crates use the &lt;code&gt;NET_RT_IFLIST2&lt;/code&gt; API to get metrics on macOS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.rs/sysinfo/latest/sysinfo/&#34;&gt;sysinfo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.rs/psutil/latest/psutil/&#34;&gt;psutil&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.rs/netstat2/latest/netstat2/&#34;&gt;netstat2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;update-31-mar-2023&#34;&gt;Update (31 Mar, 2023)&lt;/h2&gt;
&lt;p&gt;Many thanks to &lt;a href=&#34;https://twitter.com/Mojo_66/status/1640984227326951424&#34;&gt;Mojo_66&lt;/a&gt; who reached out to note that we can use an additional &lt;code&gt;sysctl()&lt;/code&gt; call to get 64bit network metrics which do &lt;em&gt;not&lt;/em&gt; suffer from trunctation. Accordingly, I&amp;rsquo;ve updated the &lt;a href=&#34;https://github.com/milend/macos-network-metrics/commit/ba188fc97140072c3378a519d115b88a90f61d4c&#34;&gt;sample code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Furthermore, using &lt;code&gt;IFMIB_IFDATA&lt;/code&gt; does &lt;em&gt;not&lt;/em&gt; result in 1KiB batching of the reported metrics. My assumption is that this is a bug and would be fixed in a future version of macOS to prevent fingerprinting.&lt;/p&gt;
&lt;h2 id=&#34;thanks&#34;&gt;Thanks&lt;/h2&gt;
&lt;p&gt;Many thanks to &lt;a href=&#34;https://mastodon.social/@justkwin@toot.community&#34;&gt;Quinn &amp;ldquo;The Eskimo!&amp;rdquo;&lt;/a&gt; for investigating and digging into the macOS kernel. Many thanks to &lt;a href=&#34;https://twitter.com/Mojo_66/status/1640984227326951424&#34;&gt;Mojo_66&lt;/a&gt; for pointing out a 64bit network metrics API which does not suffer from truncation.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;&lt;code&gt;struct if_data64&lt;/code&gt; from &lt;code&gt;if_var.h&lt;/code&gt;.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Auto Linking on iOS &amp; macOS</title>
      <link>https://milen.me/writings/auto-linking-on-ios-and-macos/</link>
      <pubDate>Fri, 04 Sep 2020 00:00:00 +0000</pubDate>
      
      <guid>https://milen.me/writings/auto-linking-on-ios-and-macos/</guid>
      <description>&lt;h3 id=&#34;auto-linking-explained&#34;&gt;Auto Linking Explained&lt;/h3&gt;
&lt;p&gt;When object files get linked at the final build stage, the linker needs to know which libraries to link against. For example, if you add &lt;code&gt;#import &amp;lt;AppKit/AppKit.h&amp;gt;&lt;/code&gt; to an implementation file, you need to also add &lt;code&gt;-framework AppKit&lt;/code&gt; to the linker flags.&lt;/p&gt;
&lt;p&gt;Auto Linking aims to remove the latter step, i.e., it aims to derive the library linker flags from the import statements in your code. Developers do not need to add any framework/library linker flags anymore, they can just start using any framework by importing&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h3 id=&#34;under-the-hood&#34;&gt;Under the Hood&lt;/h3&gt;
&lt;p&gt;Auto Linking works by inserting linker flags in object files. When the linker creates the final executable, it’s as if those linker flags were passed as arguments.&lt;/p&gt;
&lt;p&gt;The linker flags are stored as &lt;code&gt;LC_LINKER_OPTION&lt;/code&gt; load commands in object files. They can be printed using &lt;code&gt;otool -l file.o&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Load command 5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmd LC_LINKER_OPTION
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmdsize 32
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  count 2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  string #1 -framework
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  string #2 AppKit
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Load command 6
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmd LC_LINKER_OPTION
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmdsize 40
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  count 2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  string #1 -framework
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  string #2 QuartzCore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I recently &lt;a href=&#34;https://github.com/DeVaukz/MachO-Kit/pull/18&#34;&gt;added support&lt;/a&gt; for &lt;code&gt;LC_LINKER_OPTION&lt;/code&gt; to &lt;a href=&#34;https://github.com/DeVaukz/MachO-Explorer&#34;&gt;MachO-Explorer&lt;/a&gt;, so you can use it to inspect the linker flags visually as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://milen.me/images/writings/autolinking-ios-macos/macho-explorer-lc-linker-option@2x.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;frameworks-vs-dynamic-libraries&#34;&gt;Frameworks vs Dynamic Libraries&lt;/h4&gt;
&lt;p&gt;It’s important to note that Auto Linking can work with &lt;em&gt;any&lt;/em&gt; Clang module, not just frameworks. For example, &lt;code&gt;/usr/include/module.modulemap&lt;/code&gt; in the macOS SDK defines the &lt;code&gt;zlib&lt;/code&gt; module as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;module zlib [system] [extern_c] {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  header &amp;#34;zlib.h&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  export *
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  link &amp;#34;z&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The above means that if we were to use &lt;code&gt;#import &amp;lt;zlib.h&amp;gt;&lt;/code&gt; with modules turned on, &lt;code&gt;-lz&lt;/code&gt; will be automatically inserted by the linker. We can verify this using &lt;code&gt;otool -l&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Load command 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmd LC_LINKER_OPTION
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmdsize 16
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  count 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  string #1 -lz
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;controlling-auto-linking&#34;&gt;Controlling Auto Linking&lt;/h3&gt;
&lt;p&gt;Auto Linking is active &lt;em&gt;only&lt;/em&gt; when Clang modules are turned on. If you’re invoking Clang on the command line, this means passing &lt;code&gt;-fmodules&lt;/code&gt;. The Xcode setting is &lt;code&gt;CLANG_ENABLE_MODULES&lt;/code&gt; (&amp;ldquo;Enable Modules (C and Objective-C)&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;Auto Linking itself can be disabled even if modules are enabled. The Clang option is &lt;code&gt;-fno-autolink&lt;/code&gt; and the corresponding Xcode setting is &lt;code&gt;CLANG_MODULES_AUTOLINK&lt;/code&gt; (&amp;ldquo;Link Frameworks Automatically&amp;rdquo;).&lt;/p&gt;
&lt;h3 id=&#34;swift&#34;&gt;Swift&lt;/h3&gt;
&lt;p&gt;Swift extensively uses the Auto Linking mechanism to link against its runtime and overlay frameworks. For example, if you were to compile a simple Swift file using &lt;code&gt;swiftc -c file.swift&lt;/code&gt; and inspect it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Load command 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmd LC_LINKER_OPTION
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmdsize 32
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  count 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  string #1 -lswiftAppKit
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Load command 5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmd LC_LINKER_OPTION
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmdsize 24
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  count 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  string #1 -lswiftCore
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Load command 8
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmd LC_LINKER_OPTION
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmdsize 32
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  count 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  string #1 -lswiftDarwin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Load command 39
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmd LC_LINKER_OPTION
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmdsize 40
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  count 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  string #1 -lswiftSwiftOnoneSupport
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Load command 41
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmd LC_LINKER_OPTION
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmdsize 40
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  count 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  string #1 -lswiftCompatibility50
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Load command 42
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmd LC_LINKER_OPTION
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cmdsize 56
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  count 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  string #1 -lswiftCompatibilityDynamicReplacements
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://clang.llvm.org/docs/Modules.html&#34;&gt;Modules — Clang 12 documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://indiestack.com/2015/02/enable-clang-modules-disable-auto-linking/&#34;&gt;Enable Clang Modules, Disable Auto Linking | Indie Stack&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Either using &lt;code&gt;#import&lt;/code&gt; or &lt;code&gt;@import&lt;/code&gt; in Objective-C or &lt;code&gt;import&lt;/code&gt; in Swift.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Distributed Caching &amp; Compilation</title>
      <link>https://milen.me/writings/distributed-caching-and-compilation/</link>
      <pubDate>Sun, 07 Jun 2020 00:00:00 +0000</pubDate>
      
      <guid>https://milen.me/writings/distributed-caching-and-compilation/</guid>
      <description>&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Determinism plays an important role in the ability to cache output of compilation.&lt;/li&gt;
&lt;li&gt;Distributed caching and compilation enable quick iteration on large scale software.&lt;/li&gt;
&lt;li&gt;Computation of distributed cache keys is non-trivial as not all inputs are explicit.&lt;/li&gt;
&lt;li&gt;A suitable distributed cache fill strategy is required for consistently fast local builds.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;deterministic-builds&#34;&gt;Deterministic Builds&lt;/h2&gt;
&lt;p&gt;Deterministic builds can be &lt;a href=&#34;http://blog.llvm.org/2019/11/deterministic-builds-with-clang-and-lld.html&#34;&gt;defined&lt;/a&gt; as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A build is called &lt;em&gt;deterministic&lt;/em&gt; or &lt;em&gt;reproducible&lt;/em&gt; if running it twice produces exactly the same build outputs.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The LLVM project outlines several levels of determinism:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Basic&lt;/em&gt;: Doing a full build of the &lt;em&gt;same&lt;/em&gt; source code in the &lt;em&gt;same&lt;/em&gt; directory on the &lt;em&gt;same&lt;/em&gt; machine produces exactly the &lt;em&gt;same&lt;/em&gt; output every time.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Incremental&lt;/em&gt;: Like basic determinism, but the output binaries also do not change in &lt;em&gt;partial&lt;/em&gt; rebuilds.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Local&lt;/em&gt;: Like incremental basic determinism, but builds are also &lt;em&gt;independent&lt;/em&gt; of the name of the build directory. Builds of the &lt;em&gt;same&lt;/em&gt; source code on the &lt;em&gt;same&lt;/em&gt; machine produce exactly the same output every time, independent of the location of the source checkout directory or the build directory.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Universal&lt;/em&gt;: Like local but builds are also &lt;em&gt;independent&lt;/em&gt; of the machine the build runs on.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;large-scale-software&#34;&gt;Large Scale Software&lt;/h3&gt;
&lt;p&gt;Deterministic builds provide multiple benefits, mostly relating to security and caching. In this post, I cover how determinism enables the usage of techniques to speed up development of large scale software.&lt;/p&gt;
&lt;p&gt;As software gets increasingly more complex, building &lt;em&gt;all&lt;/em&gt; of the source code of a single program &lt;em&gt;locally&lt;/em&gt; becomes impractical, especially as we want to support a &lt;em&gt;fast&lt;/em&gt; development cycle&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. It’s too inefficient to expect engineers to wait hours to build the latest commit every morning.&lt;/p&gt;
&lt;p&gt;Large scale software can arise in different ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Self Contained Software&lt;/em&gt;: The software itself is a single, self-contained product but it&amp;rsquo;s very complex and large in scope. For example, Google Chrome and Photoshop would qualify as such software.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Vertical Systems&lt;/em&gt;: Even though an individual program might be small, the whole vertical system, including frameworks and system libraries, can be very large. Compiling whole vertical systems from source has multiple benefits&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; (e.g., quicker vertical signal, lower integration cost, faster system iteration cycle) but compile times can escalate due to the sheer size of code being compiled.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;distributed-caching&#34;&gt;Distributed Caching&lt;/h3&gt;
&lt;p&gt;One of the major benefits of having &lt;em&gt;universal deterministic builds&lt;/em&gt; is that our CI and local development machines will be producing exactly the same artifacts, which allows us to leverage a &lt;em&gt;distributed cache&lt;/em&gt;. This changes compile times from being a function of the total code size to being a function of the local code changes (i.e., &lt;code&gt;O(total code)&lt;/code&gt; to &lt;code&gt;O(local code changes)&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;If you checkout a commit which has been built and cached by CI, almost all local computation would be replaced by fetching the remote artifacts. So, rather than waiting hours to compile everything, you can have a build ready in a few minutes.&lt;/p&gt;
&lt;p&gt;Note that distributed caching is also known as remote caching.&lt;/p&gt;
&lt;h3 id=&#34;distributed-compilation&#34;&gt;Distributed Compilation&lt;/h3&gt;
&lt;p&gt;A problem that arises when working on large codebases is making local changes that require a large computational power. For example, imagine there’s a header file included almost &lt;em&gt;everywhere&lt;/em&gt; and you make a change to that file locally (e.g., &lt;code&gt;Logging.h&lt;/code&gt;). A distributed cache will not help because CI machines have not built that revision: the file contains unique local changes. It would be extremely inefficient to have engineers stuck waiting for hours.&lt;/p&gt;
&lt;p&gt;There’s another technique that can speedup development in such cases: distributed compilation. The technique is also known as remote execution as it&amp;rsquo;s not just limited to compilation but applies more generally to execution of tools as part of the build process.&lt;/p&gt;
&lt;p&gt;Distribution compilation is about leveraging the power of multiple machines to remotely execute compile commands. It’s like having 1000s of CPU cores instead of just a few dozen. Usually, build systems would schedule a certain amount of compilation jobs locally and distribute the rest over remote machines which will compile the input files and return the object files as a result.&lt;/p&gt;
&lt;p&gt;Both &lt;a href=&#34;https://buck.build&#34;&gt;Buck&lt;/a&gt; and &lt;a href=&#34;https://docs.bazel.build/versions/master/remote-execution.html&#34;&gt;Bazel&lt;/a&gt; support remote execution.&lt;/p&gt;
&lt;h2 id=&#34;distributed-caching-vs-distributed-compilation&#34;&gt;Distributed Caching vs Distributed Compilation&lt;/h2&gt;
&lt;p&gt;While both distributed caching and distributed compilation help speed up the development cycle of large scale software&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;, they are fundamentally different techniques. Distributed caching is about &lt;em&gt;storing&lt;/em&gt; the results of a computation and &lt;em&gt;retrieving&lt;/em&gt; those same results later from other machines. Distributed compilation is about &lt;em&gt;leveraging the power of multiple machines&lt;/em&gt; to perform a computation that&amp;rsquo;s too expensive to perform locally.&lt;/p&gt;
&lt;p&gt;The techniques are &lt;em&gt;orthogonal&lt;/em&gt; and &lt;em&gt;complementary&lt;/em&gt;. You could have a setup with just distributed caching or just distributed compilation (ideally both).&lt;/p&gt;
&lt;p&gt;In a pure distributed compilation setting, the total amount of work stays roughly the same&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt;, it’s just spread across multiple machines. The main benefit is that we have the ability to perform expensive computations quickly but we are still bound by the total computational capacity across the fleet.&lt;/p&gt;
&lt;p&gt;On the other hand, distributed caching is a classic example of the &lt;a href=&#34;https://en.wikipedia.org/wiki/Space%E2%80%93time_tradeoff&#34;&gt;space-time tradeoff&lt;/a&gt; applied across a network of machines. In the end, as long cache hit rates are sufficiently high, we save a lot of CPU time in exchange for paying a storage cost.&lt;/p&gt;
&lt;h2 id=&#34;cache-keys&#34;&gt;Cache Keys&lt;/h2&gt;
&lt;h3 id=&#34;determinism&#34;&gt;Determinism&lt;/h3&gt;
&lt;p&gt;Before we cover key computation, it’s important to note the requirements imposed by having a cache: cached command outputs &lt;em&gt;must&lt;/em&gt; be deterministic. This property allows us to safely compute results on one machine and reuse them on others.&lt;/p&gt;
&lt;p&gt;If cached commands are &lt;em&gt;not deterministic&lt;/em&gt;, then this would lead to a very strange property: the output of a build would depend on the cache hits. Such a system would be very fragile and hard to reason about.&lt;/p&gt;
&lt;h3 id=&#34;key-computation&#34;&gt;Key Computation&lt;/h3&gt;
&lt;p&gt;Caches are indexed by a key, so let’s explore how such keys could be computed. Assume we want to cache the results of a command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;compiler --some-option -L /some/directory --input-file /path/to/file @/path/to/argfile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we were to hash the command itself, it would certainly not be enough to guarantee the output would be deterministic because the contents of the referenced directories and files can affect the output. At a minimum, we must include the following as part of the key:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Referenced Files&lt;/em&gt;: Need to include the contents of input files. For response files (argfiles), this needs to happen recursively.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Directory Contents&lt;/em&gt;: Need to include the state of referenced directories. For example, such directories could be used for header resolution.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Command Parameters&lt;/em&gt;: All parameters must be included, in the order they appear.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Compiler Identity&lt;/em&gt;: The exact compiler being used (e.g., hash of the binary).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;compiler-identity&#34;&gt;Compiler Identity&lt;/h3&gt;
&lt;p&gt;The compiler identity needs to be included in the computation of the cache key because the output can vary between versions. For example, Clang 5 would most likely behave differently compared to Clang 11.&lt;/p&gt;
&lt;p&gt;Unfortunately, determining the identity of the compiler is not as easy as just hashing the binary. That’s because the binary might just be a shim that redirects to another binary or even a set of binaries.&lt;/p&gt;
&lt;p&gt;Furthermore, the shim might operate in a dynamic manner: e.g., redirect to different compiler versions depending on parameters, environmental variables or state of the filesystem. It’s practically impossible for the build system to guess how the compiler works internally.&lt;/p&gt;
&lt;h3 id=&#34;implicit-inputs&#34;&gt;Implicit Inputs&lt;/h3&gt;
&lt;p&gt;In addition to the visible inputs as part of the command, there are two other major sources of &lt;em&gt;implicit&lt;/em&gt; inputs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Environmental Variables&lt;/em&gt;: the compiler’s behaviour could be influenced by environment variables. Those variables might have been set at the shell level, build system level or somewhere else.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Filesystem&lt;/em&gt;: the compiler will inevitably end up using many more files than those explicitly specified. There might be implicit search paths, SDK paths and so on.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;non-existent-files&#34;&gt;Non-Existent Files&lt;/h3&gt;
&lt;p&gt;One tricky aspect that can be easily missed are files which &lt;em&gt;do not exist&lt;/em&gt; on the filesystem but which affect the output of the compiler. Imagine if the compiler had code like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00a8c8&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#00a8c8&#34;&gt;char&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;lib_path&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#d88200&#34;&gt;&amp;#34;/some/predefined/path.a&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00a8c8&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#75af00&#34;&gt;static_lib_exists_as&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;lib_path&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;))&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75af00&#34;&gt;append_to&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;linked_libraries&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;lib_path&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#111&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This means the compiler will generate different outputs depending on the existence of &lt;code&gt;/some/predefined/path.a&lt;/code&gt;. Consequently, the file &lt;em&gt;must&lt;/em&gt; always be part of the cache key, even when it does not exist.&lt;/p&gt;
&lt;h3 id=&#34;explicit-inputs&#34;&gt;Explicit Inputs&lt;/h3&gt;
&lt;p&gt;Ultimately, the compiler itself truly knows all the inputs that were used to determine the output. Ideally, compilers would provide a way to output that information in a structured way, so that build systems can utilise it.&lt;/p&gt;
&lt;p&gt;For example, &lt;a href=&#34;https://clang.llvm.org/docs/ClangCommandLineReference.html#dependency-file-generation&#34;&gt;Clang&lt;/a&gt; and GCC support dependency files which list all the user and system header files that were used. MSVC supports a similar &lt;a href=&#34;https://docs.microsoft.com/en-us/cpp/build/reference/showincludes-list-include-files?view=vs-2019&#34;&gt;&lt;code&gt;/showIncludes&lt;/code&gt;&lt;/a&gt; option.&lt;/p&gt;
&lt;h3 id=&#34;parameters-not-affecting-determinism&#34;&gt;Parameters Not Affecting Determinism&lt;/h3&gt;
&lt;p&gt;Sometimes, tools might support options which do not affect the output but change the internal operation of the tool (e.g., algorithms used, concurrency, etc).&lt;/p&gt;
&lt;p&gt;While it’s possible to add custom handling to exclude such parameters from cache key computation, it’s safer to assume that all parameters affect the input and have CI machines (or a subset) always build the exact same local development configuration.&lt;/p&gt;
&lt;h2 id=&#34;cache-availability&#34;&gt;Cache Availability&lt;/h2&gt;
&lt;p&gt;An important aspect of a distributed cache is its hit rate: it should be very high. Assuming no local changes, the hit rate would usually depend on the particular commit being checked out, the cache retention policy and cache fill strategy.&lt;/p&gt;
&lt;p&gt;For example, if the cache gets filled once an hour by a CI job, checking out the latest commit on master might result in a very slow build if a previous commit invalidated most artifacts&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a href=&#34;#fn:5&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;In terms of fill and retention strategies, several aspects require careful consideration. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does the cache get filled at every commit or only certain ones?&lt;/li&gt;
&lt;li&gt;Does the cache get filled before a commit gets pushed or afterwards?&lt;/li&gt;
&lt;li&gt;In a monorepo, for a specific commit, are caches for all targets filled or only a subset?&lt;/li&gt;
&lt;li&gt;Is the cache per-target or a global one?&lt;/li&gt;
&lt;li&gt;What eviction policy does the cache use? What’s the size of the cache?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are no right or wrong answers: all of the above aspects represent different tradeoffs and will have associated benefits and costs. It will be down to the constraints and requirements within a company/project to make the appropriate choices and deliver the desired experience.&lt;/p&gt;
&lt;h3 id=&#34;metrics&#34;&gt;Metrics&lt;/h3&gt;
&lt;p&gt;Ultimately, the distributed cache should have a high hit rate for as many builds as possible. For example, that might be aiming for p95 miss rate of 1%: i.e., 95% of all builds will experience a miss rate of 1% or lower (i.e., hit rate of 99% or higher).&lt;/p&gt;
&lt;p&gt;Adopting a data-driven approach in optimising the cache hit rate is an appropriate strategy.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://buck.build/concept/http_cache_api.html&#34;&gt;Buck: HTTP Cache API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.bazel.build/versions/master/remote-execution.html&#34;&gt;Bazel Remote Execution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.bazel.build/versions/master/remote-caching.html&#34;&gt;Bazel Remote Caching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://ziglang.org/download/0.4.0/release-notes.html#Build-Artifact-Caching&#34;&gt;Zig: Build Artifact Caching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://apenwarr.ca/log/20181113&#34;&gt;mtime comparison considered harmful&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://clang.llvm.org/docs/ClangCommandLineReference.html#dependency-file-generation&#34;&gt;Clang Dependency File Options&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.microsoft.com/en-us/cpp/build/reference/showincludes-list-include-files?view=vs-2019&#34;&gt;MSVC List Include Files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Space%E2%80%93time_tradeoff&#34;&gt;Wikipedia: Space-time Tradeoff&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Incremental build time is &lt;em&gt;incredibly&lt;/em&gt; important for developer productivity. The difference between a 2s and 30s incremental build is very significant due to a simple fact: the wait time is not long enough to be utilised productively but it&amp;rsquo;s long enough to add up to a large total. Doing 100 builds per day leads to 46 minutes of wasted time.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Building everything from source is a separate topic which will not be covered in detail here.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;Another reason for the importance of fast iteration time is that it directly affects the ability to write good software. When tackling a new problem space, the freedom to easily explore and quickly iterate on solutions becomes key to success.&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:4&#34;&gt;
&lt;p&gt;There will be some additional overhead associated with cost of distributing the compilation across multiple machines.&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:5&#34;&gt;
&lt;p&gt;E.g., changing a widely included header file.&amp;#160;&lt;a href=&#34;#fnref:5&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Apple&#39;s Linker &amp; Deterministic Builds</title>
      <link>https://milen.me/writings/apple-linker-ld64-deterministic-builds-oso-prefix/</link>
      <pubDate>Sun, 03 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://milen.me/writings/apple-linker-ld64-deterministic-builds-oso-prefix/</guid>
      <description>&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://blog.llvm.org/2019/11/deterministic-builds-with-clang-and-lld.html&#34;&gt;Universal deterministic builds&lt;/a&gt; require that all paths in artifacts &lt;em&gt;must&lt;/em&gt; be repo checkout independent.
On Apple platforms, the linker will insert &lt;em&gt;absolute&lt;/em&gt; paths to object files in executables.
In Xcode 11, Apple added a new linker option, &lt;code&gt;-oso_prefix&lt;/code&gt;, that can relativise OSO absolute paths.
Another source of non-determinism in object files are the OSO timestamp entries.&lt;/p&gt;
&lt;h2 id=&#34;deterministic-builds&#34;&gt;Deterministic Builds&lt;/h2&gt;
&lt;p&gt;One of the requirements for &lt;a href=&#34;http://blog.llvm.org/2019/11/deterministic-builds-with-clang-and-lld.html&#34;&gt;universal deterministic builds&lt;/a&gt; is that they are &lt;em&gt;independent&lt;/em&gt; of the source checkout path, on any machine. Consequently, there can be no absolute paths in the output artifacts.&lt;/p&gt;
&lt;h3 id=&#34;debug-info--paths&#34;&gt;Debug Info &amp;amp; Paths&lt;/h3&gt;
&lt;p&gt;When it comes to compiling C/Obj-C/C++ code for Apple platforms, absolute paths in executables can be found in debug info inserted by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Compiler&lt;/em&gt;: paths to source files.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Linker&lt;/em&gt;: paths to object files.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An obvious question arises: &lt;em&gt;why&lt;/em&gt; does the linker insert paths to object files?&lt;/p&gt;
&lt;h3 id=&#34;dwarf-on-macos&#34;&gt;DWARF on macOS&lt;/h3&gt;
&lt;p&gt;On Apple platforms, DWARF debugging data can reside in one of two places:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Object Files&lt;/em&gt;: The object file for each translation unit will contain DWARF debugging data. Executables will contain &lt;em&gt;absolute&lt;/em&gt; paths to the object files, so that the debugger can find the debugging info there.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;dSYM Bundles&lt;/em&gt;: An Apple bundle which contains the combined DWARF data from all the object files. The executable and its corresponding debug information are linked using an UUID.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;rationale&#34;&gt;Rationale&lt;/h3&gt;
&lt;p&gt;There’s very special reason why DWARF data is embedded in the object files rather than the compiled executables: much faster incremental builds. Such builds do not have to incur the cost of embedding the full debug info on every build, even if just a single object file changes.&lt;/p&gt;
&lt;p&gt;The obvious downside is that it makes debugging of executables depend on having access to the original object files. That’s why dSYM bundles exist which combine all the debugging info from the object files. &lt;a href=&#34;https://www.manpagez.com/man/1/dsymutil/&#34;&gt;dsymutil&lt;/a&gt; can be thought of a DWARF linker.&lt;/p&gt;
&lt;h2 id=&#34;avoiding-absolute-paths&#34;&gt;Avoiding Absolute Paths&lt;/h2&gt;
&lt;h3 id=&#34;compiler&#34;&gt;Compiler&lt;/h3&gt;
&lt;p&gt;Clang supports the &lt;code&gt;-fdebug-prefix-map&lt;/code&gt; flag which provides the ability to relativise absolute source paths in the debug info. For example, you can use it like so: &lt;code&gt;-fdebug-prefix-map=/Users/milen/repo=.&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;linker&#34;&gt;Linker&lt;/h3&gt;
&lt;p&gt;As explained earlier, Mach-O executables would store paths to the object files which contain debug data in &lt;a href=&#34;http://wiki.dwarfstd.org/index.php?title=Apple%27s_%22Lazy%22_DWARF_Scheme&#34;&gt;OSO entries&lt;/a&gt;. You can run the &lt;code&gt;nm&lt;/code&gt; command on a binary with debug information to inspect such entries:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nm -a out/main &lt;span style=&#34;color:#111&#34;&gt;|&lt;/span&gt; grep OSO
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;000000005eaede8d - &lt;span style=&#34;color:#ae81ff&#34;&gt;03&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0001&lt;/span&gt; OSO /Users/milen/repo/out/hello.o
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;000000005eaede8f - &lt;span style=&#34;color:#ae81ff&#34;&gt;03&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0001&lt;/span&gt; OSO /Users/milen/repo/out/main.o
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In 2019, as part of Xcode 11, Apple added a new linker option, &lt;code&gt;-oso_prefix&lt;/code&gt;, which can be used to relativise the OSO paths. For example, when linking using the Clang driver, we can pass:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;clang ... -Wl,-oso_prefix,/Users/milen/repo/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we then print the OSO entries, we will see that they have been relativised:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nm -a out/main &lt;span style=&#34;color:#111&#34;&gt;|&lt;/span&gt; grep OSO
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;000000005eaede8d - &lt;span style=&#34;color:#ae81ff&#34;&gt;03&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0001&lt;/span&gt; OSO out/hello.o
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;000000005eaede8f - &lt;span style=&#34;color:#ae81ff&#34;&gt;03&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0001&lt;/span&gt; OSO out/main.o
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I’d also recommend using &lt;a href=&#34;https://github.com/DeVaukz/MachO-Explorer&#34;&gt;MachO-Explorer&lt;/a&gt; to visually inspect the symbol tables.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://milen.me/images/writings/apple-linker-deterministic-builds/macho-main-oso-symbols@2x.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;oso-entries--timestamps&#34;&gt;OSO Entries &amp;amp; Timestamps&lt;/h2&gt;
&lt;p&gt;Another source of non-determinism in Mach-O executables are the timestamps associated with the OSO symtab entries. Those are used to determine if the object files are out of sync with the executables.&lt;/p&gt;
&lt;p&gt;For example, if the debugger determines that an object file is newer than the executable which points to it, that means the executable was not recompiled after recompiling the object file.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://github.com/facebook/buck/blob/dfd6587a6290c638bac2e5e8e1c0e1d25baf2632/src/com/facebook/buck/cxx/toolchain/objectfile/ObjectFileScrubbers.java#L103&#34;&gt;strategy&lt;/a&gt; used in Buck to guarantee deterministic executables is to &lt;em&gt;always&lt;/em&gt; set the modification dates of all object files / static libraries to a &lt;em&gt;predefined&lt;/em&gt; date. The tradeoff there is that it breaks the ability of the debugger to detect object file / executable synchronisation issues.&lt;/p&gt;
&lt;h3 id=&#34;libtool--ld64&#34;&gt;libtool &amp;amp; ld64&lt;/h3&gt;
&lt;p&gt;If you do not want to apply postprocessing yourself, you have a few options.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;libtool&lt;/code&gt; supports the &lt;code&gt;-D&lt;/code&gt; option which can be used to guarantee deterministic values. The documentation says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When building a static library, set archive contents&amp;rsquo; user ids, group ids, dates, and file modes to reasonable defaults. This allows libraries created with identical input to be identical to each other, regardless of time of day, user, group, umask, and other aspects of the environment.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In addition, both &lt;code&gt;libtool&lt;/code&gt; and &lt;code&gt;ld64&lt;/code&gt; support the &lt;code&gt;ZERO_AR_DATE&lt;/code&gt; environment variable to control the timestamps for the OSO entries (&lt;a href=&#34;https://github.com/apple-opensource/ld64/blob/fd3feabb0a1eb18ab5d7910f3c3a5eed99cef6ab/src/ld/Options.cpp#L4420&#34;&gt;ld64 code&lt;/a&gt;, &lt;a href=&#34;https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/misc/libtool.c#L365-L372&#34;&gt;libtool code&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id=&#34;debugging&#34;&gt;Debugging&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;lldb&lt;/code&gt; tries to resolve relative paths against the current working directory, so to make sure debugging works, we need to adjust the cwd and add a source mapping. This can be done either at the &lt;code&gt;lldb&lt;/code&gt; prompt or in a &lt;code&gt;lldbinit&lt;/code&gt; script.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#111&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;os&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#111&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;os&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;chdir&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#d88200&#34;&gt;&amp;#34;/Users/milen/repo&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#111&#34;&gt;settings&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;append&lt;/span&gt; &lt;span style=&#34;color:#111&#34;&gt;target&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;source&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;map&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;./&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;Users&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;milen&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#111&#34;&gt;repo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;buck&#34;&gt;Buck&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/facebook/buck&#34;&gt;Buck&lt;/a&gt;, which supports distributed caching, is used as a core build system at Facebook. As the addition of &lt;code&gt;-oso_prefix&lt;/code&gt; is relatively recent, how did Buck produce deterministic Mach-O executables until now?&lt;/p&gt;
&lt;p&gt;The answer is that Buck performs an optional &lt;a href=&#34;https://github.com/facebook/buck/blob/dfd6587a6290c638bac2e5e8e1c0e1d25baf2632/src/com/facebook/buck/cxx/toolchain/objectfile/Machos.java#L96&#34;&gt;post-processing step&lt;/a&gt; where all OSO entries are relativised, making the executables independent of the checkout path.&lt;/p&gt;
&lt;p&gt;While that’s a working solution, there are several downsides.&lt;/p&gt;
&lt;h3 id=&#34;performance&#34;&gt;Performance&lt;/h3&gt;
&lt;p&gt;Relativisation requires rewriting both the symbol table and the string table. For large binaries (e.g., 500MiB-1,500MiB), the combined size of the tables can be around 50% of the binaries and processing that much data can be slow.&lt;/p&gt;
&lt;p&gt;Note that while we can patch the symbol and string tables in-place, different machines will produce string tables of different length depending on the checkout path length. That’s why we need to rewrite the full strings and symbol tables, to ensure that executables are bit for bit equal.&lt;/p&gt;
&lt;p&gt;As relativisation is very performance sensitive, special attention needs to be paid to the code implementing it. For example, just an additional 1 &lt;em&gt;microsecond&lt;/em&gt; to process a symtab entry will result in ~5s slow down if we need to process 5 million entries (not unusual for binaries of this size).&lt;/p&gt;
&lt;p&gt;For example, you can see several optimisations (&lt;a href=&#34;https://github.com/facebook/buck/commit/61155c76c09362f8c92b5fdb3db6726637145936&#34;&gt;[1]&lt;/a&gt; &lt;a href=&#34;https://github.com/facebook/buck/commit/a2a37f2ccea4dffb80d80b4798da638ebf19c3c2&#34;&gt;[2]&lt;/a&gt; &lt;a href=&#34;https://github.com/facebook/buck/commit/fb5bc5dc96ba846fdcedebb25bc0e3709c777880&#34;&gt;[3]&lt;/a&gt; &lt;a href=&#34;https://github.com/facebook/buck/commit/8bebc58fc78c9983870498499fe429f9355b2bbd&#34;&gt;[4]&lt;/a&gt;) I made to improve the performance of the process in Buck.&lt;/p&gt;
&lt;h3 id=&#34;maintenance&#34;&gt;Maintenance&lt;/h3&gt;
&lt;p&gt;More code means higher maintenance cost. Furthermore, as we have to keep compatibility with Apple’s tooling, the code&amp;rsquo;s behaviour has to be verified against every major Xcode release.&lt;/p&gt;
&lt;h3 id=&#34;code-signing&#34;&gt;Code Signing&lt;/h3&gt;
&lt;p&gt;Relativisation, as a post-processing step, only works if the executables are not already code signed. As we mutate the binaries, this would in turn invalidate their signatures. While that’s not a problem if we are building from source, the approach would not work in all situations.&lt;/p&gt;
&lt;h2 id=&#34;acknowledgement&#34;&gt;Acknowledgement&lt;/h2&gt;
&lt;p&gt;Many thanks to &lt;a href=&#34;https://github.com/michaeleisel&#34;&gt;Michael Eisel&lt;/a&gt; for &lt;a href=&#34;https://github.com/michaeleisel/zld/issues/35&#34;&gt;finding&lt;/a&gt; the new &lt;code&gt;-oso_prefix&lt;/code&gt; option. Thanks to &lt;a href=&#34;https://twitter.com/bdash&#34;&gt;Mark Rowe&lt;/a&gt; for &lt;a href=&#34;https://twitter.com/bdash/status/1267932614943313920?s=20&#34;&gt;surfacing&lt;/a&gt; &lt;code&gt;libtool&lt;/code&gt;&amp;rsquo;s option.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://wiki.dwarfstd.org/index.php?title=Apple%27s_%22Lazy%22_DWARF_Scheme&#34;&gt;Apple’s &amp;ldquo;Lazy&amp;rdquo; DWARF Scheme&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://blog.llvm.org/2019/11/deterministic-builds-with-clang-and-lld.html&#34;&gt;Deterministic builds with Clang and lld&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/DeVaukz/MachO-Explorer&#34;&gt;MachO-Explorer: A graphical Mach-O viewer for macOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://reproducible-builds.org&#34;&gt;Reproducible Builds — a set of software development practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
