<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>hobby — farre's blog</title>
    <link>https://blog.farre.se/tags/hobby</link>
    <description>Posts tagged "hobby" on farre's blog</description>
    <atom:link href="https://blog.farre.se/rss/hobby.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>How to make Firefox builds&lt;sup&gt;&lt;small&gt;1&lt;/small&gt;&lt;/sup&gt; 17% faster&lt;sup&gt;&lt;small&gt;2&lt;/small&gt;&lt;/sup&gt;</title>
      <link>https://blog.farre.se/posts/2026/04/10/caching-webidl-codegen/</link>
      <guid isPermaLink="true">https://blog.farre.se/posts/2026/04/10/caching-webidl-codegen/</guid>
      <pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate>
      <description>&lt;p&gt;In the &lt;a href=&quot;/posts/2026/04/09/buildcache-with-mach/&quot;&gt;previous post&lt;/a&gt;, I mentioned that buildcache has some unique properties compared to ccache and sccache. One of them is its &lt;a href=&quot;https://gitlab.com/bits-n-bites/buildcache/-/blob/master/doc/lua.md&quot;&gt;Lua plugin system&lt;/a&gt;, which lets you write custom wrappers for programs that aren’t compilers in the traditional sense. With &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=2027655&quot;&gt;Bug 2027655&lt;/a&gt; now merged, we can use this to cache Firefox’s WebIDL binding code generation.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h2 id=&quot;whats-the-webidl-step&quot;&gt;What’s the WebIDL step?&lt;/h2&gt;

&lt;p&gt;When you build Firefox, one of the earlier steps runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python3 -m mozbuild.action.webidl&lt;/code&gt; to generate C++ binding code from hundreds of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.webidl&lt;/code&gt; files. It produces thousands of output files: headers, cpp files, forward declarations, event implementations, and so on. The step isn’t terribly slow on its own, but it runs on every clobber build, and the output is entirely deterministic given the same inputs. That makes it a perfect candidate for caching.&lt;/p&gt;

&lt;p&gt;The problem was that the compiler cache was never passed to this step. Buildcache was only wrapping actual compiler invocations, not the Python codegen.&lt;/p&gt;

&lt;h2 id=&quot;the-change&quot;&gt;The change&lt;/h2&gt;

&lt;p&gt;The fix in &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=2027655&quot;&gt;Bug 2027655&lt;/a&gt; is small. In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dom/bindings/Makefile.in&lt;/code&gt;, we now conditionally pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(CCACHE)&lt;/code&gt; as a command wrapper to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;py_action&lt;/code&gt; call:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-make&quot; data-lang=&quot;make&quot;&gt;&lt;span class=&quot;nv&quot;&gt;WEBIDL_CCACHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ifdef&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;MOZ_USING_BUILDCACHE&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;WEBIDL_CCACHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;$(&lt;/span&gt;CCACHE&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;endif&lt;/span&gt;

&lt;span class=&quot;nl&quot;&gt;webidl.stub&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;$(codegen_dependencies)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;$(&lt;/span&gt;call py_action,webidl &lt;span class=&quot;p&quot;&gt;$(&lt;/span&gt;relativesrcdir&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;,&lt;span class=&quot;p&quot;&gt;$(&lt;/span&gt;srcdir&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;,,&lt;span class=&quot;p&quot;&gt;$(&lt;/span&gt;WEBIDL_CCACHE&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;@$(&lt;/span&gt;TOUCH&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;py_action&lt;/code&gt; macro in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/makefiles/functions.mk&lt;/code&gt; is what runs Python build actions. The ability to pass a command wrapper as a fourth argument was also introduced in this bug. When buildcache is configured as the compiler cache, this means the webidl action is invoked as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buildcache python3 -m mozbuild.action.webidl ...&lt;/code&gt; instead of just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python3 -m mozbuild.action.webidl ...&lt;/code&gt;. That’s all buildcache needs to intercept it.&lt;/p&gt;

&lt;p&gt;Note the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifdef MOZ_USING_BUILDCACHE&lt;/code&gt; guard. This is specific to buildcache because ccache and sccache don’t have a mechanism for caching arbitrary commands. Buildcache does, through its Lua wrappers.&lt;/p&gt;

&lt;h2 id=&quot;the-lua-wrapper&quot;&gt;The Lua wrapper&lt;/h2&gt;

&lt;p&gt;Buildcache’s Lua plugin system lets you write a script that tells it how to handle a program it doesn’t natively understand. The wrapper for WebIDL codegen, &lt;a href=&quot;https://github.com/farre/buildcache-wrappers/blob/main/mozilla/webidl.lua&quot;&gt;webidl.lua&lt;/a&gt;, needs to answer a few questions for buildcache:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Can I handle this command?&lt;/strong&gt; Match on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mozbuild.action.webidl&lt;/code&gt; in the argument list.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;What are the inputs?&lt;/strong&gt; All the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.webidl&lt;/code&gt; source files, plus the Python codegen scripts. These come from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file-lists.json&lt;/code&gt; (which &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mach&lt;/code&gt; generates) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;codegen.json&lt;/code&gt; (which tracks the Python dependencies from the previous run).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;What are the outputs?&lt;/strong&gt; All the generated binding headers, cpp files, event files, and the codegen state files. Again derived from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file-lists.json&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that information, buildcache can hash the inputs, check the cache, and either replay the cached outputs or run the real command and store the results.&lt;/p&gt;

&lt;p&gt;The wrapper uses buildcache’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;direct_mode&lt;/code&gt; capability, meaning it hashes input files directly rather than relying on preprocessed output. This is the right approach here since we’re not dealing with a C preprocessor but with a Python script that reads &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.webidl&lt;/code&gt; files.&lt;/p&gt;

&lt;h2 id=&quot;numbers&quot;&gt;Numbers&lt;/h2&gt;

&lt;p&gt;Here are build times for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./mach build&lt;/code&gt; on Linux, comparing compiler cachers. Each row shows a clobber build with an empty cache (cold), followed by a clobber build with a filled cache (warm):&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;tool&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;cold&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;warm&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;with plugin&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;none&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;5m35s&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;n/a&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;n/a&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;ccache&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;5m42s&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;3m21s&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;n/a&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;sccache&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;9m38s&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;2m49s&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;n/a&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;buildcache&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;5m43s&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1m27s&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1m12s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The “with plugin” column is buildcache with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;webidl.lua&lt;/code&gt; wrapper active. It shaves another 15 seconds&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, bringing the total down to 1m12s&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Not a revolutionary improvement on its own, but it demonstrates the mechanism. The WebIDL step is just the first Python action to get this treatment; there are other codegen steps in the build that could benefit from the same approach.&lt;/p&gt;

&lt;p&gt;More broadly, these numbers show buildcache pulling well ahead on warm builds. Going from a 5m35s clean build to a 1m12s cached rebuild is a nice improvement to the edit-compile-test cycle.&lt;/p&gt;

&lt;p&gt;These are single runs on one machine, not rigorous benchmarks, but the direction is clear enough.&lt;/p&gt;

&lt;h2 id=&quot;setting-it-up&quot;&gt;Setting it up&lt;/h2&gt;

&lt;p&gt;If you’re already using buildcache with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mach&lt;/code&gt;, the Makefile change is available when updating to today’s central. To enable the Lua wrapper, clone the &lt;a href=&quot;https://github.com/farre/buildcache-wrappers&quot;&gt;buildcache-wrappers&lt;/a&gt; repo and point buildcache at it via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lua_paths&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.buildcache/config.json&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lua_paths&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/path/to/buildcache-wrappers/mozilla&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;max_cache_size&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10737418240&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;max_local_entry_size&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2684354560&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Alternatively, you can set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BUILDCACHE_LUA_PATH&lt;/code&gt; environment variable. A convenient place to do that is in your mozconfig:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;mk_add_options &lt;span class=&quot;s2&quot;&gt;&quot;export BUILDCACHE_LUA_PATH=/path/to/buildcache-wrappers/mozilla/&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The large &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max_local_entry_size&lt;/code&gt; (2.5 GB) is needed because some Rust crates produce very large cache entries.&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s next&lt;/h2&gt;

&lt;p&gt;The Lua plugin system is the interesting part here. The WebIDL wrapper is a proof of concept, but the same technique applies to any deterministic build step that takes known inputs and produces known outputs. There are other codegen actions in the Firefox build that could get the same treatment, and I plan to explore those next.&lt;/p&gt;

&lt;h4 id=&quot;notes&quot;&gt;Notes&lt;/h4&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;For a clobber build with a warm cache &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;
      &lt;p&gt;On my machine &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

&lt;script src=&quot;https://utteranc.es/client.js&quot; repo=&quot;farre/blog&quot; issue-term=&quot;og:title&quot; label=&quot;comments&quot; theme=&quot;github-light&quot; crossorigin=&quot;anonymous&quot; async=&quot;&quot;&gt;
&lt;/script&gt;

</description>
    </item>
    
    <item>
      <title>BuildCache now works with mach</title>
      <link>https://blog.farre.se/posts/2026/04/09/buildcache-with-mach/</link>
      <guid isPermaLink="true">https://blog.farre.se/posts/2026/04/09/buildcache-with-mach/</guid>
      <pubDate>Thu, 09 Apr 2026 00:00:00 +0000</pubDate>
      <description>&lt;p&gt;I’m happy to announce that &lt;a href=&quot;https://gitlab.com/bits-n-bites/buildcache&quot;&gt;buildcache&lt;/a&gt; is now a first-class compiler cache in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mach&lt;/code&gt;. This has been a long time coming, and I’m excited to finally see it land.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;For those unfamiliar, &lt;a href=&quot;https://gitlab.com/bits-n-bites/buildcache&quot;&gt;buildcache&lt;/a&gt; is a compiler cache that can drastically cut down your rebuild times by caching compilation results. It’s similar to ccache, but even more so sccache, in that it supports C/C++ out of the box, as well as Rust. It has some nice unique properties of its own though, which we’ll look at more closely in following posts.&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;

&lt;p&gt;Setting it up is straightforward. Just add the following to your mozconfig:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;ac_add_options &lt;span class=&quot;nt&quot;&gt;--with-ccache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;buildcache&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then build as usual:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;./mach build&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;h2 id=&quot;give-it-a-try&quot;&gt;Give it a try&lt;/h2&gt;

&lt;p&gt;If you run into any issues, please file a bug and tag me. I’d love to hear how it works out for people, and any rough edges you might hit.&lt;/p&gt;

&lt;script src=&quot;https://utteranc.es/client.js&quot; repo=&quot;farre/blog&quot; issue-term=&quot;og:title&quot; label=&quot;comments&quot; theme=&quot;github-light&quot; crossorigin=&quot;anonymous&quot; async=&quot;&quot;&gt;
&lt;/script&gt;

</description>
    </item>
    
  </channel>
</rss>
