<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>mozilla — farre's blog</title>
    <link>https://blog.farre.se/tags/mozilla</link>
    <description>Posts tagged "mozilla" on farre's blog</description>
    <atom:link href="https://blog.farre.se/rss/mozilla.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Session History Diagrams in Firefox DevTools</title>
      <link>https://blog.farre.se/posts/2026/06/01/session-history-diagrams/</link>
      <guid isPermaLink="true">https://blog.farre.se/posts/2026/06/01/session-history-diagrams/</guid>
      <pubDate>Mon, 01 Jun 2026 00:00:00 +0000</pubDate>
      <description>&lt;p&gt;I’ve spent a lot of time at Mozilla working on session history, the machinery that keeps track of where you’ve been so the back and forward buttons do something sensible. It’s one of those parts of the browser that sounds simple from the outside and turns out to be anything but. Once you add iframes, nested iframes, and the subtle rules about when a navigation creates a new entry versus replacing the current one, the state you’re reasoning about gets large and hard to hold in your head.&lt;/p&gt;

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

&lt;p&gt;For years my main tool for understanding that state was reading code and printing things to a log. That works, but it’s slow, and it never quite shows you the shape of the thing. So I built a way to &lt;em&gt;see&lt;/em&gt; it: a new DevTools panel in Firefox Nightly called Session History Diagrams.&lt;/p&gt;

&lt;h2 id=&quot;enabling-it&quot;&gt;Enabling it&lt;/h2&gt;

&lt;p&gt;The panel is available in Firefox today, behind a pref. It’s been there in some form since Firefox 150, growing more stable with each release. To turn it on, set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devtools.application.sessionHistory.enabled&lt;/code&gt; to true in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;about:config&lt;/code&gt;, then reload DevTools. The new panel lives under the Application tab, next to Service Workers and Manifest, and it draws the browser’s session history as a diagram that updates as you navigate.&lt;/p&gt;

&lt;p&gt;Since Firefox 153 it also works over remote debugging. Connect to a device from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;about:debugging&lt;/code&gt; and you can watch the session history of a page running on Android, the same as you would on the desktop.&lt;/p&gt;

&lt;h2 id=&quot;jake-diagrams&quot;&gt;Jake diagrams&lt;/h2&gt;

&lt;p&gt;I didn’t invent the idea of drawing this. The HTML spec already has a notation for it, called a &lt;a href=&quot;https://html.spec.whatwg.org/#jake-diagrams&quot;&gt;Jake diagram&lt;/a&gt; after &lt;a href=&quot;https://jakearchibald.com/&quot;&gt;Jake Archibald&lt;/a&gt;, and that’s where I started. It’s a tabular notation where columns represent steps in session history, and rows represent navigables (the top-level browsing context plus any iframes). Background colors identify documents, a fresh color marking a new document loaded in that navigable, and the current step is shown in bold. It’s a genuinely useful way to capture multi-navigable interactions that are otherwise hard to describe in prose.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/2026-06-01-session-history-diagrams/jake-diagram.png&quot; alt=&quot;A Jake diagram showing a top-level navigable and two iframes across five history steps&quot; /&gt;&lt;/p&gt;

&lt;p&gt;These diagrams don’t have to be drawn by hand. &lt;a href=&quot;https://github.com/domenic&quot;&gt;Domenic Denicola&lt;/a&gt;, one of the HTML spec editors, built a &lt;a href=&quot;https://github.com/domenic/jake-diagram-generator&quot;&gt;Jake diagram generator&lt;/a&gt; that turns a description of a navigation sequence into a rendered diagram. That’s where I first started playing with a more dynamic approach to the visualization. The thing I missed the most was being able to build a history up step by step rather than describe a finished sequence all at once. So I wrote &lt;a href=&quot;https://github.com/farre/web-tools/tree/master/rejake&quot;&gt;rejake&lt;/a&gt;, a small tool that draws diagrams in the same style&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;, but lets you construct the history one step at a time.&lt;/p&gt;

&lt;p&gt;But rejake, like the spec’s diagrams and Domenic’s generator before it, was stuck with a limitation the spec itself admits to, that they only work with a single level of nesting. That was exactly my problem. Real pages nest iframes inside iframes, and the bugs I was chasing usually lived down in that deeper nesting, precisely where the diagram stops being able to help. And however I drew them, I was still typing the history out by hand. It’s a short step from there to wanting the diagram to draw itself from the browser’s actual session history instead&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;.&lt;/p&gt;

&lt;h2 id=&quot;firefox-session-history-diagrams&quot;&gt;Firefox Session History Diagrams&lt;/h2&gt;

&lt;p&gt;So the panel extends Jake diagrams to handle arbitrary nesting. Every column is a step in the session history. Every row is a frame, listed in pre-order from the frame tree: top-level document first, then its first iframe, then that iframe’s children, and so on. The current entry is highlighted in blue, and the diagram updates live as you navigate.&lt;/p&gt;

&lt;div style=&quot;overflow:hidden; display:inline-block; max-width:100%&quot;&gt;&lt;video src=&quot;/assets/videos/2026-06-01-session-history-diagrams/demo.mp4&quot; style=&quot;display:block; max-width:100%&quot; muted=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; playsinline=&quot;&quot;&gt;&lt;/video&gt;&lt;/div&gt;

&lt;p&gt;The recording above is an ordinary bit of browsing, a handful of pages visited one after another. The top row tracks the page you’re actually looking at, and the current position is the one in blue. The interesting part is everything underneath that top row.&lt;/p&gt;

&lt;p&gt;Some of those pages didn’t just load a single document. They pulled in nested frames of their own, and the diagram stacks those below the page that owns them. None of that is visible in the address bar or anywhere in the page chrome, and the frames come and go as you move through the history. Normally you’d have no way of knowing they were ever there. Here you can read straight off the diagram which frames a given step carried, when each one entered, and when it dropped away again.&lt;/p&gt;

&lt;h2 id=&quot;who-else-might-want-this&quot;&gt;Who else might want this&lt;/h2&gt;

&lt;p&gt;I built this for myself, working on Gecko’s session history internals, where being able to watch the diagram change while reproducing a bug turns opaque state into something I can point at. But it turns out I’m not the only one who hits this wall. Plenty of people working elsewhere in Gecko, anywhere near navigation, end up reasoning about the same state, and now we all share one picture of it.&lt;/p&gt;

&lt;p&gt;If you build single-page applications, or work with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/History_API&quot;&gt;History API&lt;/a&gt; or &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API&quot;&gt;Navigation API&lt;/a&gt;, you’ve probably run into the same kind of confusion from the other side. A push where you expected a replace, a missing history entry, an iframe that accumulated entries unexpectedly. These are hard to reason about without seeing the state directly, and that’s exactly what the diagram gives you.&lt;/p&gt;

&lt;p&gt;Session history isn’t a Firefox-specific problem either. Every engine implements the same part of the HTML spec, and Jake diagrams come from that shared spec. The panel only ever shows Firefox’s state, but the rules are the same everywhere, so if you work on another engine it can still be a useful reference for how one implementation behaves. It’s often the only practical way to surface an interoperability difference, which might be a bug in any of the engines, but stays hidden until you can actually see it.&lt;/p&gt;

&lt;h2 id=&quot;thanks&quot;&gt;Thanks&lt;/h2&gt;

&lt;p&gt;A big thanks to &lt;a href=&quot;https://github.com/nchevobbe&quot;&gt;Nicolas Chevobbe&lt;/a&gt;, whose assistance was invaluable in getting the DevTools integration right. The work, including what’s still to come, is tracked in &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=2015726&quot;&gt;Bug 2015726&lt;/a&gt;. There’s a fair bit still on that list, like marking whether a step was a push or a replace, surfacing back/forward cache state, tying the diagram into the Network and Inspector panels, and more, all heading toward fuller DevTools support for Navigation and Session History.&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;Which, naturally, meant re-implementing the whole of Session History along the way. &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;Getting nerd-sniped by &lt;a href=&quot;https://github.com/jnjaeschke/&quot;&gt;Jan Jaeschke&lt;/a&gt; definitely contributed as well. &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>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>
