<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title type="text"><![CDATA[Keith Smiley]]></title>
  
  <updated>2026-03-29T12:27:48-07:00</updated>
  <id>https://smileykeith.com/</id>
  <link rel="alternate" type="text/html" hreflang="en" href="https://smileykeith.com" />

  
  <link rel="self" type="application/atom+xml" href="https://smileykeith.com/atom.xml" />
  
  <rights>Copyright (c) 2026 Keith Smiley</rights>
  <generator uri="http://jekyllrb.com/" version="1.0.3">Jekyll</generator>

  
    <entry>
      <title type="html"><![CDATA[Bazel rule extensions]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2025/10/31/bazel-rule-extensions/" />
      <id>https://smileykeith.com/2025/10/31/bazel-rule-extensions</id>
      <updated>2025-10-31T10:00:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>One of Bazel's best features is being able to easily write custom rules
specific to your project. This is great for many use cases, but when
what you really want is to enhance the behavior of existing rules,
historically your options have been limited. What you would often do is
wrap the existing rule in a macro, and add some number of custom rules
to try and achieve the desired effect. When really what you want is to
edit the existing rule, without having to re-implement all of its
functionality (or maintain a fork).</p>

<p>With Bazel 8.0, Googlers added a few new ways to extend existing rules
that can help with this use case. In this post we will look at the aptly
named <a href="https://docs.google.com/document/d/1p6z-shWf9sdqo_ep7dcjZCGvqN5r2jsPkJCqHHgfRp4/edit?tab=t.0#heading=h.5mcn15i0e1ch">rule extensions</a> feature and some practical use cases I have
found for it.</p>

<h1 id="basic-rule-extensions">Basic rule extensions</h1>

<p>Rule extensions allow you to inherit the behavior of an existing rule,
similar to class inheritance in object-oriented programming. Importantly
you can make a few modifications to augment the behavior of the rule to
your liking.</p>

<p>Let's say you have a rule that concatenates the given <code class="language-plaintext highlighter-rouge">srcs</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">_foo_impl</span><span class="p">(</span><span class="n">ctx</span><span class="p">):</span>
    <span class="n">output</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">actions</span><span class="p">.</span><span class="n">declare_file</span><span class="p">(</span><span class="s">"output.txt"</span><span class="p">)</span>
    <span class="n">ctx</span><span class="p">.</span><span class="n">actions</span><span class="p">.</span><span class="n">run_shell</span><span class="p">(</span>
        <span class="n">inputs</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">files</span><span class="p">.</span><span class="n">srcs</span><span class="p">,</span>
        <span class="n">outputs</span> <span class="o">=</span> <span class="p">[</span><span class="n">output</span><span class="p">],</span>
        <span class="n">command</span> <span class="o">=</span> <span class="s">"cat {} &gt; {}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="s">" "</span><span class="p">.</span><span class="n">join</span><span class="p">([</span><span class="n">src</span><span class="p">.</span><span class="n">path</span> <span class="k">for</span> <span class="n">src</span> <span class="ow">in</span> <span class="n">ctx</span><span class="p">.</span><span class="n">files</span><span class="p">.</span><span class="n">srcs</span><span class="p">]),</span> <span class="n">output</span><span class="p">.</span><span class="n">path</span><span class="p">),</span>
    <span class="p">)</span>
    <span class="k">return</span> <span class="p">[</span><span class="n">DefaultInfo</span><span class="p">(</span><span class="n">files</span> <span class="o">=</span> <span class="n">depset</span><span class="p">([</span><span class="n">output</span><span class="p">]))]</span>

<span class="n">foo</span> <span class="o">=</span> <span class="n">rule</span><span class="p">(</span>
    <span class="n">implementation</span> <span class="o">=</span> <span class="n">_foo_impl</span><span class="p">,</span>
    <span class="n">attrs</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"srcs"</span><span class="p">:</span> <span class="n">attr</span><span class="p">.</span><span class="n">label_list</span><span class="p">(</span><span class="n">allow_files</span> <span class="o">=</span> <span class="bp">True</span><span class="p">),</span>
    <span class="p">},</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Now let's assume in your project, you want the output file to be sorted.
If you own the original rule you could of course change <code class="language-plaintext highlighter-rouge">_foo_impl</code> to
handle that for you, but if you are relying on a more complex upstream
rule, you may not have that luxury. Here's how we can extend this to
post-process the file it produces:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">_bar_impl</span><span class="p">(</span><span class="n">ctx</span><span class="p">):</span>
    <span class="n">providers</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="nb">super</span><span class="p">()</span> <span class="c1"># Invoke 'foo' and get the providers
</span>    <span class="c1"># NOTE: This assumes there's always only the provider we want.
</span>    <span class="n">original_output</span> <span class="o">=</span> <span class="n">providers</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">files</span><span class="p">.</span><span class="n">to_list</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">new_output</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">actions</span><span class="p">.</span><span class="n">declare_file</span><span class="p">(</span><span class="s">"new_output.txt"</span><span class="p">)</span>
    <span class="n">ctx</span><span class="p">.</span><span class="n">actions</span><span class="p">.</span><span class="n">run_shell</span><span class="p">(</span>
        <span class="n">inputs</span> <span class="o">=</span> <span class="p">[</span><span class="n">original_output</span><span class="p">],</span>
        <span class="n">outputs</span> <span class="o">=</span> <span class="p">[</span><span class="n">new_output</span><span class="p">],</span>
        <span class="n">command</span> <span class="o">=</span> <span class="s">"sort {} &gt; {}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">original_output</span><span class="p">.</span><span class="n">path</span><span class="p">,</span> <span class="n">new_output</span><span class="p">.</span><span class="n">path</span><span class="p">),</span>
    <span class="p">)</span>

    <span class="k">return</span> <span class="p">[</span><span class="n">DefaultInfo</span><span class="p">(</span><span class="n">files</span> <span class="o">=</span> <span class="n">depset</span><span class="p">([</span><span class="n">new_output</span><span class="p">]))]</span>

<span class="n">bar</span> <span class="o">=</span> <span class="n">rule</span><span class="p">(</span>
    <span class="n">implementation</span> <span class="o">=</span> <span class="n">_bar_impl</span><span class="p">,</span>
    <span class="n">parent</span> <span class="o">=</span> <span class="n">foo</span><span class="p">,</span>  <span class="c1"># Inherit everything from 'foo'
</span><span class="p">)</span>
</code></pre></div></div>

<p>This example illustrates the core new features of rule extensions. First
we inherit everything from <code class="language-plaintext highlighter-rouge">foo</code> with:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">parent</span> <span class="o">=</span> <span class="n">foo</span><span class="p">,</span>
</code></pre></div></div>

<p>Then we invoke the original implementation with:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">providers</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="nb">super</span><span class="p">()</span>
</code></pre></div></div>

<p>At this point we have all of the original providers, and we can
post-process them however we want. In this example we choose to extract
what we need from them and return entirely different providers based on
our new action.</p>

<h1 id="manipulating-providers">Manipulating providers</h1>

<p>As well as creating new providers, you can also manipulate the returned
providers for your use case. Recently I wanted to unify how debug info
was returned for <code class="language-plaintext highlighter-rouge">cc_binary</code> targets across macOS and Linux.
Specifically I wanted to be able to create a <code class="language-plaintext highlighter-rouge">filegroup</code> target pointing
to a specific <code class="language-plaintext highlighter-rouge">output_group</code> that would work on both platforms, where by
default you would have to fetch these from different output locations on
different platforms.</p>

<p>I was able to achieve this with a rule extension of <code class="language-plaintext highlighter-rouge">cc_binary</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">load</span><span class="p">(</span><span class="s">"@cc_compatibility_proxy//:proxy.bzl"</span><span class="p">,</span> <span class="n">_upstream_cc_binary</span> <span class="o">=</span> <span class="s">"cc_binary"</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">_cc_binary_impl</span><span class="p">(</span><span class="n">ctx</span><span class="p">):</span>
    <span class="n">providers</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="nb">super</span><span class="p">()</span>

    <span class="n">output_group_info</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">debug_package_info</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="n">passthrough_providers</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">provider</span> <span class="ow">in</span> <span class="n">providers</span><span class="p">:</span>
        <span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">provider</span><span class="p">)</span> <span class="o">==</span> <span class="s">"OutputGroupInfo"</span><span class="p">:</span>
            <span class="n">output_group_info</span> <span class="o">=</span> <span class="n">provider</span>
        <span class="k">elif</span> <span class="nb">type</span><span class="p">(</span><span class="n">provider</span><span class="p">)</span> <span class="o">==</span> <span class="s">"struct"</span> <span class="ow">and</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">provider</span><span class="p">,</span> <span class="s">"unstripped_file"</span><span class="p">):</span>  <span class="c1"># NOTE: Will require an update when this provider moves to starlark
</span>            <span class="n">debug_package_info</span> <span class="o">=</span> <span class="n">provider</span>
            <span class="n">passthrough_providers</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">provider</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">passthrough_providers</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">provider</span><span class="p">)</span>

    <span class="k">if</span> <span class="ow">not</span> <span class="n">output_group_info</span><span class="p">:</span>
        <span class="n">fail</span><span class="p">(</span><span class="s">"No OutputGroupInfo provider found"</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">debug_package_info</span><span class="p">:</span>
        <span class="n">fail</span><span class="p">(</span><span class="s">"No DebugPackageInfo provider found"</span><span class="p">)</span>

    <span class="n">dsyms</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">output_group_info</span><span class="p">,</span> <span class="s">"dsyms"</span><span class="p">,</span> <span class="n">depset</span><span class="p">())</span>
    <span class="n">new_output_group_info</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">if</span> <span class="n">dsyms</span><span class="p">:</span>
        <span class="n">new_output_group_info</span><span class="p">[</span><span class="s">"debug_info"</span><span class="p">]</span> <span class="o">=</span> <span class="n">dsyms</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">new_output_group_info</span><span class="p">[</span><span class="s">"debug_info"</span><span class="p">]</span> <span class="o">=</span> <span class="n">depset</span><span class="p">([</span><span class="n">debug_package_info</span><span class="p">.</span><span class="n">unstripped_file</span><span class="p">])</span>

    <span class="k">for</span> <span class="n">group</span> <span class="ow">in</span> <span class="nb">dir</span><span class="p">(</span><span class="n">output_group_info</span><span class="p">):</span>
        <span class="n">new_output_group_info</span><span class="p">[</span><span class="n">group</span><span class="p">]</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">output_group_info</span><span class="p">,</span> <span class="n">group</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">passthrough_providers</span> <span class="o">+</span> <span class="p">[</span>
        <span class="n">OutputGroupInfo</span><span class="p">(</span><span class="o">**</span><span class="n">new_output_group_info</span><span class="p">),</span>
    <span class="p">]</span>

<span class="n">cc_binary</span> <span class="o">=</span> <span class="n">rule</span><span class="p">(</span>
    <span class="n">implementation</span> <span class="o">=</span> <span class="n">_cc_binary_impl</span><span class="p">,</span>
    <span class="n">parent</span> <span class="o">=</span> <span class="n">_upstream_cc_binary</span><span class="p">,</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Most of this implementation is about collecting and re-propagating the
providers that I don't care about. The methods for doing this today are
pretty tedious but hopefully that will <a href="https://github.com/bazelbuild/bazel/issues/26960">improve in the
future</a>.</p>

<p>The core logic once I collect the original providers is this:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">dsyms</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">output_group_info</span><span class="p">,</span> <span class="s">"dsyms"</span><span class="p">,</span> <span class="n">depset</span><span class="p">())</span>
    <span class="k">if</span> <span class="n">dsyms</span><span class="p">:</span>
        <span class="n">new_output_group_info</span><span class="p">[</span><span class="s">"debug_info"</span><span class="p">]</span> <span class="o">=</span> <span class="n">dsyms</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">new_output_group_info</span><span class="p">[</span><span class="s">"debug_info"</span><span class="p">]</span> <span class="o">=</span> <span class="n">depset</span><span class="p">([</span><span class="n">debug_package_info</span><span class="p">.</span><span class="n">unstripped_file</span><span class="p">])</span>
</code></pre></div></div>

<p>Here I decide that if the <code class="language-plaintext highlighter-rouge">OutputGroupInfo</code> provider from the upstream
<code class="language-plaintext highlighter-rouge">cc_binary</code> implementation includes <code class="language-plaintext highlighter-rouge">dsyms</code>, we propagate that,
otherwise we propagate the unstripped binary which contains all the
debug info for Linux. I am then able to create a single <code class="language-plaintext highlighter-rouge">filegroup</code> to
fetch whichever one is found:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">filegroup</span><span class="p">(</span>
    <span class="n">name</span> <span class="o">=</span> <span class="s">"some_binary.debug_info"</span><span class="p">,</span>
    <span class="n">srcs</span> <span class="o">=</span> <span class="p">[</span><span class="s">":some_binary"</span><span class="p">],</span>
    <span class="n">output_group</span> <span class="o">=</span> <span class="s">"debug_info"</span><span class="p">,</span>
<span class="p">)</span>
</code></pre></div></div>

<h1 id="extending-rules-with-transitions">Extending rules with transitions</h1>

<p>Let's look at another use case for extending rules. This time I want to
add a custom transition to an upstream rule. In our project we produce
python wheels that include native extensions. In order to distribute
these wheels we have to build them for each version of python we
support. <code class="language-plaintext highlighter-rouge">rules_python</code> has a <code class="language-plaintext highlighter-rouge">py_wheel</code> rule that creates the wheel for
us, but it does that targeting the "current python version" (this could
potentially be improved upstream). The current version depends on how
you setup python in your <code class="language-plaintext highlighter-rouge">MODULE.bazel</code> file, but we want to change it
when we build different targets so we can target multiple python
versions in a single build. Thankfully the way <code class="language-plaintext highlighter-rouge">rules_python</code> has
implemented version selection is with a flag that we can write a
transition for. To add a transition to an upstream rule, while otherwise
maintaining the original functionality, we can do this:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">py_wheel</span> <span class="o">=</span> <span class="n">rule</span><span class="p">(</span>
    <span class="n">implementation</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">ctx</span><span class="p">:</span> <span class="n">ctx</span><span class="p">.</span><span class="nb">super</span><span class="p">(),</span>
    <span class="n">parent</span> <span class="o">=</span> <span class="n">py_wheel_rule</span><span class="p">,</span>
    <span class="n">attrs</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"python_version"</span><span class="p">:</span> <span class="n">attr</span><span class="p">.</span><span class="n">string</span><span class="p">(),</span>
    <span class="p">},</span>
    <span class="n">cfg</span> <span class="o">=</span> <span class="n">python_version_transition</span><span class="p">,</span>
<span class="p">)</span>
</code></pre></div></div>

<p>This use case has a few interesting things to note. Since we don't want
to change the functionality or providers returned by this rule, we don't
even create an implementation function, instead opting to call super
directly in a lambda:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">implementation</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">ctx</span><span class="p">:</span> <span class="n">ctx</span><span class="p">.</span><span class="nb">super</span><span class="p">(),</span>
</code></pre></div></div>

<p>Then we add an attribute the rule didn't have before. This is then read
by our transition to decide which python version to use:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">attrs</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"python_version"</span><span class="p">:</span> <span class="n">attr</span><span class="p">.</span><span class="n">string</span><span class="p">(),</span>
    <span class="p">},</span>
</code></pre></div></div>

<p>If the original rule had an appropriate attribute we could use that
instead, but in this case we need to provide our own. Finally we add the
transition (the contents of which aren't necessary to understand for
this example, but can be found
<a href="https://github.com/modular/rules_mojo/blob/87f860f96f73ce624d51fe8e836978888e14344c/mojo/private/transitions.bzl">here</a>):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">cfg</span> <span class="o">=</span> <span class="n">python_version_transition</span><span class="p">,</span>
</code></pre></div></div>

<p>Now with our new <code class="language-plaintext highlighter-rouge">py_wheel</code> rule we can set:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">python_version</span> <span class="o">=</span> <span class="s">"3.13"</span><span class="p">,</span>
</code></pre></div></div>

<p>And all transitive dependencies will be built targeting the passed
version instead of the default version.</p>

<p>Previously to solve this same use case you potentially could have
created a custom rule that applied this transition, and made the
original <code class="language-plaintext highlighter-rouge">py_wheel</code> target depend on your custom rule's output, but that
is definitely more overhead to understand and maintain for the use cases
I commonly hit.</p>

<h1 id="applying-platform-specific-transitions">Applying platform specific transitions</h1>

<p>Another use case for adding a transition to an existing rule is for
platform specific builds. A longstanding frustration in the iOS
community is bare targets like <code class="language-plaintext highlighter-rouge">cc_library</code> or <code class="language-plaintext highlighter-rouge">swift_library</code>, don't
have any knowledge of the platform they are being built for, even if you
have written them in a way that only supports a single platform. This
means that if you tried to directly build a <code class="language-plaintext highlighter-rouge">swift_library</code> whose code
only supports iOS, you'll be greeted with a compiler error as Bazel
attempts to build it for macOS.</p>

<p>Developers have often worked around this by wrapping their libraries in
a macro that adds an underlying platform specific target, such as an
<code class="language-plaintext highlighter-rouge">ios_build_test</code>, that has the necessary platform transition. This adds
complexity and causes confusion for non-Bazel developers. It also adds
general overhead in your build when you're querying things or otherwise
inspecting what targets exist, as every library now has additional
underlying targets.</p>

<p>With rule extensions you can eliminate this overhead by applying the
Apple platform transition directly to <code class="language-plaintext highlighter-rouge">swift_library</code>:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">load</span><span class="p">(</span><span class="s">"@rules_apple//apple/internal:transition_support.bzl"</span><span class="p">,</span> <span class="s">"transition_support"</span><span class="p">)</span>
<span class="n">load</span><span class="p">(</span><span class="s">"@rules_swift//swift:swift.bzl"</span><span class="p">,</span> <span class="n">_upstream_swift_library</span> <span class="o">=</span> <span class="s">"swift_library"</span><span class="p">)</span>

<span class="n">swift_library</span> <span class="o">=</span> <span class="n">rule</span><span class="p">(</span>
    <span class="n">implementation</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">ctx</span><span class="p">:</span> <span class="n">ctx</span><span class="p">.</span><span class="nb">super</span><span class="p">(),</span>
    <span class="n">parent</span> <span class="o">=</span> <span class="n">_upstream_swift_library</span><span class="p">,</span>
    <span class="n">cfg</span> <span class="o">=</span> <span class="n">transition_support</span><span class="p">.</span><span class="n">apple_rule_transition</span><span class="p">,</span>
    <span class="n">attrs</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"platform_type"</span><span class="p">:</span> <span class="n">attr</span><span class="p">.</span><span class="n">string</span><span class="p">(</span><span class="n">default</span> <span class="o">=</span> <span class="s">"ios"</span><span class="p">),</span>
        <span class="c1"># TODO: Extract to a constant that matches your ios_application targets
</span>        <span class="s">"minimum_os_version"</span><span class="p">:</span> <span class="n">attr</span><span class="p">.</span><span class="n">string</span><span class="p">(</span><span class="n">default</span> <span class="o">=</span> <span class="s">"16.0"</span><span class="p">),</span>
    <span class="p">},</span>
<span class="p">)</span>
</code></pre></div></div>

<p>This requires a bit of knowledge about how the transition works,
specifically we have to add 2 attributes the transition relies on, but
that's something that could potentially be improved.</p>

<p>Once you have this extended rule, building your <code class="language-plaintext highlighter-rouge">swift_library</code> targets
correctly builds for iOS, and they are not rebuilt when building them
from a platform specific target's dependency tree.</p>

<p>If your <code class="language-plaintext highlighter-rouge">swift_library</code> target supports multiple platforms, you can
still use something like this while respecting inherited platform from
your top level targets. The easiest way I found to do this would be to
<code class="language-plaintext highlighter-rouge">select()</code> on the current platform in a macro, and provide the correct
values given that target platform:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">load</span><span class="p">(</span><span class="s">"@rules_apple//apple/internal:transition_support.bzl"</span><span class="p">,</span> <span class="s">"transition_support"</span><span class="p">)</span>
<span class="n">load</span><span class="p">(</span><span class="s">"@rules_swift//swift:swift.bzl"</span><span class="p">,</span> <span class="n">_upstream_swift_library</span> <span class="o">=</span> <span class="s">"swift_library"</span><span class="p">)</span>

<span class="n">swift_library</span> <span class="o">=</span> <span class="n">rule</span><span class="p">(</span>
    <span class="n">implementation</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">ctx</span><span class="p">:</span> <span class="n">ctx</span><span class="p">.</span><span class="nb">super</span><span class="p">(),</span>
    <span class="n">parent</span> <span class="o">=</span> <span class="n">_upstream_swift_library</span><span class="p">,</span>
    <span class="n">cfg</span> <span class="o">=</span> <span class="n">transition_support</span><span class="p">.</span><span class="n">apple_rule_transition</span><span class="p">,</span>
    <span class="n">attrs</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"platform_type"</span><span class="p">:</span> <span class="n">attr</span><span class="p">.</span><span class="n">string</span><span class="p">(</span><span class="n">mandatory</span> <span class="o">=</span> <span class="bp">True</span><span class="p">),</span>
        <span class="s">"minimum_os_version"</span><span class="p">:</span> <span class="n">attr</span><span class="p">.</span><span class="n">string</span><span class="p">(</span><span class="n">mandatory</span> <span class="o">=</span> <span class="bp">True</span><span class="p">),</span>
    <span class="p">},</span>
<span class="p">)</span>

<span class="k">def</span> <span class="nf">my_swift_library</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
    <span class="n">swift_library</span><span class="p">(</span>
        <span class="n">platform_type</span> <span class="o">=</span> <span class="n">select</span><span class="p">({</span>
            <span class="s">"@platforms//os:tvos"</span><span class="p">:</span> <span class="s">"tvos"</span><span class="p">,</span>
            <span class="s">"//conditions:default"</span><span class="p">:</span> <span class="s">"ios"</span><span class="p">,</span>
        <span class="p">}),</span>
        <span class="n">minimum_os_version</span> <span class="o">=</span> <span class="s">"16.0"</span><span class="p">,</span> <span class="c1"># NOTE: Could select() here too if necessary
</span>        <span class="o">**</span><span class="n">kwargs</span>
    <span class="p">)</span>
</code></pre></div></div>

<p>With this example building the library directly will default to iOS, but
if you have a top level tvOS target that depends on it, it will still
correctly compile for tvOS.</p>

<h1 id="adding-an-attribute-with-aspects">Adding an attribute with aspects</h1>

<p>Another powerful use case for this feature is to add aspects to
attributes on the rule. For example if you want a rule to collect custom
files from all of its dependencies and propagate those in its own
runfiles, you can override an attribute from the parent, adding your
custom aspect:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">bar</span> <span class="o">=</span> <span class="n">rule</span><span class="p">(</span>
    <span class="n">implementation</span> <span class="o">=</span> <span class="n">_bar_impl</span><span class="p">,</span>
    <span class="n">parent</span> <span class="o">=</span> <span class="n">foo</span><span class="p">,</span>
    <span class="n">attrs</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"deps"</span><span class="p">:</span> <span class="n">attr</span><span class="p">.</span><span class="n">label_list</span><span class="p">(</span><span class="n">aspects</span> <span class="o">=</span> <span class="p">[</span><span class="n">custom_aspect</span><span class="p">]),</span>
    <span class="p">},</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Then in <code class="language-plaintext highlighter-rouge">_bar_impl</code> you can do whatever processing you need to collect
the outputs of the aspect.</p>

<h1 id="other-notes">Other notes</h1>

<ul>
  <li>There are currently some limitations on what types of attributes can
be overridden, for example you cannot override a
<code class="language-plaintext highlighter-rouge">attr.label_keyed_string_dict</code> to add aspects. In some cases a quick
workaround is to add a new attribute instead, and wrap your usage in a
macro that populates the new attribute with the same value as the
original attribute.</li>
  <li>While it may look confusing at first, I think there's some value in
re-using the same rule name with your extended rules so <code class="language-plaintext highlighter-rouge">bazel query
kind(cc_binary, ...)</code> continues to work as before. This way developers
who are using queries like this don't have to know about this
extension.</li>
  <li>You have to set <code class="language-plaintext highlighter-rouge">parent</code> to another Bazel rule, not a macro. This is
sometimes difficult as many popular rulesets expose macros for their
rules to perform pre-processing on the passed attributes. I hope as
this feature becomes more widely used that pattern is reduced. In the
cases where I am using this today I found I could skip the custom
macro logic as long as I was careful about what it was trying to
accomplish.</li>
  <li>I would like to see how an approach like this could augment existing
rules to add outputs for actions that already exist. For example when
passing <code class="language-plaintext highlighter-rouge">-ftime-trace</code> to clang ideally we could create that output in
our rule implementation but use the existing action to create it, I
couldn't find a way to modify the <code class="language-plaintext highlighter-rouge">copts</code> to do that today. It might
be possible with a combination of this approach and a macro.</li>
</ul>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Understanding Apple Debug Info]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2025/09/21/understanding-apple-debug-info/" />
      <id>https://smileykeith.com/2025/09/21/understanding-apple-debug-info</id>
      <updated>2025-09-21T10:00:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>Apple platforms (macOS, iOS, etc), and specifically Mach-O binaries,
have a slightly different approach to debug info than ELF binaries for
Linux. If you are familiar with Xcode, you might have seen a few related
settings that control what is produced and wondered what the trade-offs
are. The goal of this post is to help you debug cases where these
differences lead to a degraded debugging experience in <code class="language-plaintext highlighter-rouge">lldb</code> so that
you can fix them.</p>

<p>If you have a particularly complex build, potentially managed by
Bazel<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> or another tool, especially if you are using distributed
builds, you are even more likely to hit issues.</p>

<p>Let's dive in to how the pieces fit together.</p>

<h1 id="a-brief-explanation-of-debug-info">A brief explanation of debug info</h1>

<p>Debug info is metadata produced by the compiler that is consumed by
debuggers (like <code class="language-plaintext highlighter-rouge">lldb</code>), profilers, and other tools. It is used to map
runtime information, like addresses, function arguments, and stack
traces, back to the source that was used to produce the binary. Without
this information debugging in <code class="language-plaintext highlighter-rouge">lldb</code> shows primarily raw instructions
and addresses, which is rarely acceptable for common debugging
workflows.</p>

<h1 id="inspecting-debug-info">Inspecting debug info</h1>

<p>When building for Apple platforms debug info isn't contained in the
final binary (this is the primary difference from the default Linux
workflows). Instead the binary contains references to the files where
<code class="language-plaintext highlighter-rouge">lldb</code> can find it (this is conceptually similar to if you use
<code class="language-plaintext highlighter-rouge">-gsplit-dwarf</code> on Linux).</p>

<p>Let's inspect some binaries to see what this really means. First we
create a small binary:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat </span>main.c
int main<span class="o">()</span> <span class="o">{</span>
  <span class="k">return </span>0<span class="p">;</span>
<span class="o">}</span>

<span class="nv">$ </span>clang main.c <span class="nt">-g</span> <span class="nt">-c</span> <span class="nt">-o</span> main.o
<span class="nv">$ </span>clang main.o <span class="nt">-o</span> main
</code></pre></div></div>

<p>If we attempt to inspect the debug info contained in <code class="language-plaintext highlighter-rouge">main</code>, we find
nothing:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dwarfdump main  <span class="c"># use llvm-dwarfdump if not on macOS</span>
main:   file format Mach-O arm64

.debug_info contents:
</code></pre></div></div>

<p>However when we debug this binary in <code class="language-plaintext highlighter-rouge">lldb</code>, you will correctly see the
source file and line number information:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>lldb <span class="nt">--</span> main
<span class="o">(</span>lldb<span class="o">)</span> target create <span class="s2">"main"</span>
Current executable <span class="nb">set </span>to <span class="s1">'/tmp/demo/main'</span> <span class="o">(</span>arm64<span class="o">)</span><span class="nb">.</span>
<span class="o">(</span>lldb<span class="o">)</span> b main
Breakpoint 1: where <span class="o">=</span> main<span class="sb">`</span>main + 12 at main.c:2:3, address <span class="o">=</span> 0x0000000100000334
<span class="o">(</span>lldb<span class="o">)</span> r
Process 33357 launched: <span class="s1">'/tmp/demo/main'</span> <span class="o">(</span>arm64<span class="o">)</span>
Process 33357 stopped
<span class="k">*</span> thread <span class="c">#1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1</span>
    frame <span class="c">#0: 0x0000000100000334 main`main at main.c:2:3</span>
   1    int main<span class="o">()</span> <span class="o">{</span>
-&gt; 2      <span class="k">return </span>0<span class="p">;</span>
   3    <span class="o">}</span>
Target 0: <span class="o">(</span>main<span class="o">)</span> stopped.
</code></pre></div></div>

<p>Let's trace back how <code class="language-plaintext highlighter-rouge">lldb</code> discovers the debug info. First the <code class="language-plaintext highlighter-rouge">main</code>
binary has a reference to the intermediate <code class="language-plaintext highlighter-rouge">main.o</code> object file. We can
see the references by looking for the <code class="language-plaintext highlighter-rouge">N_OSO</code> entries in the binary:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dsymutil <span class="nt">-s</span> main | <span class="nb">grep </span>N_OSO  <span class="c"># use llvm-dsymutil if not on macOS</span>
<span class="o">[</span>     3] 0000002e 66 <span class="o">(</span>N_OSO        <span class="o">)</span> 00     0001   0000000068d03443 <span class="s1">'/private/tmp/demo/main.o'</span>
</code></pre></div></div>

<p>Then <code class="language-plaintext highlighter-rouge">lldb</code> loads the debug info directly from the <code class="language-plaintext highlighter-rouge">main.o</code> object file.
We can see the debug info it contains:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dwarfdump main.o
0x0000000c: DW_TAG_compile_unit
              DW_AT_producer    <span class="o">(</span><span class="s2">"Apple clang version 17.0.0 (clang-1700.3.19.1)"</span><span class="o">)</span>
              DW_AT_language    <span class="o">(</span>DW_LANG_C11<span class="o">)</span>
              DW_AT_name        <span class="o">(</span><span class="s2">"main.c"</span><span class="o">)</span>
              DW_AT_LLVM_sysroot        <span class="o">(</span><span class="s2">"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"</span><span class="o">)</span>
              DW_AT_APPLE_sdk   <span class="o">(</span><span class="s2">"MacOSX.sdk"</span><span class="o">)</span>
              DW_AT_str_offsets_base    <span class="o">(</span>0x00000008<span class="o">)</span>
              DW_AT_stmt_list   <span class="o">(</span>0x00000000<span class="o">)</span>
              DW_AT_comp_dir    <span class="o">(</span><span class="s2">"/tmp/demo"</span><span class="o">)</span>
              DW_AT_low_pc      <span class="o">(</span>0x0000000000000000<span class="o">)</span>
              DW_AT_high_pc     <span class="o">(</span>0x0000000000000014<span class="o">)</span>
              DW_AT_addr_base   <span class="o">(</span>0x00000008<span class="o">)</span>

0x00000025:   DW_TAG_subprogram
                DW_AT_low_pc    <span class="o">(</span>0x0000000000000000<span class="o">)</span>
                DW_AT_high_pc   <span class="o">(</span>0x0000000000000014<span class="o">)</span>
                DW_AT_APPLE_omit_frame_ptr      <span class="o">(</span><span class="nb">true</span><span class="o">)</span>
                DW_AT_frame_base        <span class="o">(</span>DW_OP_reg31 WSP<span class="o">)</span>
                DW_AT_name      <span class="o">(</span><span class="s2">"main"</span><span class="o">)</span>
                DW_AT_decl_file <span class="o">(</span><span class="s2">"/tmp/demo/main.c"</span><span class="o">)</span>
                DW_AT_decl_line <span class="o">(</span>1<span class="o">)</span>
                DW_AT_type      <span class="o">(</span>0x00000034 <span class="s2">"int"</span><span class="o">)</span>
                DW_AT_external  <span class="o">(</span><span class="nb">true</span><span class="o">)</span>

0x00000034:   DW_TAG_base_type
                DW_AT_name      <span class="o">(</span><span class="s2">"int"</span><span class="o">)</span>
                DW_AT_encoding  <span class="o">(</span>DW_ATE_signed<span class="o">)</span>
                DW_AT_byte_size <span class="o">(</span>0x04<span class="o">)</span>

0x00000038:   NULL
</code></pre></div></div>

<p>We can also see the object file contains specific <code class="language-plaintext highlighter-rouge">__debug*</code> sections
when debug info is present that are absent in our main binary:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>size <span class="nt">-m</span> main  <span class="c"># use llvm-size if not on macOS</span>
Segment __PAGEZERO: 4294967296 <span class="o">(</span>zero fill<span class="o">)</span>
Segment __TEXT: 16384
        Section __text: 20
        Section __unwind_info: 88
        total 108
Segment __LINKEDIT: 16384
total 4295000064

<span class="nv">$ </span>size <span class="nt">-m</span> main.o
Segment : 628
  Section <span class="o">(</span>__TEXT, __text<span class="o">)</span>: 20
  Section <span class="o">(</span>__DWARF, __debug_abbrev<span class="o">)</span>: 65
  Section <span class="o">(</span>__DWARF, __debug_info<span class="o">)</span>: 57
  Section <span class="o">(</span>__DWARF, __debug_str_offs<span class="o">)</span>: 36
  Section <span class="o">(</span>__DWARF, __debug_str<span class="o">)</span>: 179
  Section <span class="o">(</span>__DWARF, __debug_addr<span class="o">)</span>: 16
  Section <span class="o">(</span>__DWARF, __debug_names<span class="o">)</span>: 112
  Section <span class="o">(</span>__LD, __compact_unwind<span class="o">)</span>: 32
  Section <span class="o">(</span>__DWARF, __debug_line<span class="o">)</span>: 91
  Section <span class="o">(</span>__DWARF, __debug_line_str<span class="o">)</span>: 17
  total 625
total 628
</code></pre></div></div>

<p>The same pattern holds when you depend on static libraries:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ar cr libmain.a main.o
$ ar t libmain.a
__.SYMDEF SORTED
main.o

$ clang -lmain -L. -o main
$ dsymutil -s main | grep N_OSO
[     3] 0000002e 66 (N_OSO        ) 00     0001   0000000068d03443 '/private/tmp/demo/./libmain.a(main.o)'
</code></pre></div></div>

<p>This is almost everything we need to know for where to find debug info,
so now let's try to understand where this can go wrong.</p>

<h1 id="common-issues">Common Issues</h1>

<h2 id="1-invalid-absolute-paths">1. Invalid absolute paths</h2>

<p>So far we've been looking at an isolated example outside of Bazel. If
you've debugged Bazel issues before you may have rightfully felt some
alarm bells go off when you saw the absolute paths in the debug info. In
Bazel, if we are not being careful, those absolute paths would point to
ephemeral locations that only exist during the Bazel link action, and
not exist during debug time. This can also be the case if you are using
distributed builds with other tools. For example if I break
<code class="language-plaintext highlighter-rouge">apple_support</code> today, this is what you would see:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>bazel build main <span class="nt">--compilation_mode</span><span class="o">=</span>dbg
...

<span class="nv">$ </span>dsymutil <span class="nt">-s</span> bazel-bin/main | <span class="nb">grep </span>N_OSO
<span class="o">[</span>     3] 0000008d 66 <span class="o">(</span>N_OSO        <span class="o">)</span> 00     0001   0000000000000000 <span class="s1">'/private/var/tmp/_bazel_ksmiley/14bc203d9bd4089231ecb78dafab0640/sandbox/darwin-sandbox/4/execroot/_main/bazel-out/darwin_arm64-dbg/bin/_objs/main/main.o'</span>
</code></pre></div></div>

<p>In this case you can see the <code class="language-plaintext highlighter-rouge">.../darwin-sandbox/4</code> directory is where
the final binary recorded the object file existed during link time.
While this was valid during the link action, these directories are wiped
after the build completes, so if you try to <code class="language-plaintext highlighter-rouge">dwarfdump</code> this file
afterwards, you will see it doesn't exist.</p>

<p>You could potentially workaround this issue with <code class="language-plaintext highlighter-rouge">--sandbox_debug</code>,
which will keep those directories around, but instead of doing that we
use the
<a href="https://keith.github.io/xcode-man-pages/ld.1.html#oso_prefix"><code class="language-plaintext highlighter-rouge">-oso_prefix</code></a>
linker argument. This argument tells the linker to strip the given
prefix from the final <code class="language-plaintext highlighter-rouge">N_OSO</code> entries, and replace it with <code class="language-plaintext highlighter-rouge">.</code> so that
it is relative to the current directory. With this applied, the <code class="language-plaintext highlighter-rouge">N_OSO</code>
entries look like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dsymutil <span class="nt">-s</span> bazel-bin/main | <span class="nb">grep </span>N_OSO
<span class="o">[</span>     3] 00000026 66 <span class="o">(</span>N_OSO        <span class="o">)</span> 00     0001   0000000068d039ba <span class="s1">'bazel-out/darwin_arm64-dbg/bin/_objs/main/main.o'</span>
</code></pre></div></div>

<p>This path now exists relative to the root of the repository, so as long
as you are launching <code class="language-plaintext highlighter-rouge">lldb</code> from the root of the repository, it will
correctly discover this object file.</p>

<p>Now that these paths are reproducible and valid, let's shift our
attention to the paths in the object file itself (again by breaking
<code class="language-plaintext highlighter-rouge">apple_support</code>):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dwarfdump bazel-out/darwin_arm64-dbg/bin/_objs/main/main.o | <span class="nb">grep</span> <span class="s1">'"/'</span>
  DW_AT_LLVM_sysroot        <span class="o">(</span><span class="s2">"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk"</span><span class="o">)</span>
  DW_AT_comp_dir    <span class="o">(</span><span class="s2">"/private/var/tmp/_bazel_ksmiley/14bc203d9bd4089231ecb78dafab0640/sandbox/darwin-sandbox/12/execroot/_main"</span><span class="o">)</span>
  DW_AT_decl_file <span class="o">(</span><span class="s2">"/private/var/tmp/_bazel_ksmiley/14bc203d9bd4089231ecb78dafab0640/sandbox/darwin-sandbox/12/execroot/_main/main.c"</span><span class="o">)</span>
</code></pre></div></div>

<p>Here we can see 3 different absolute paths which might affect our
ability to debug, and undoubtedly affects the reproducibility of our
build. In this case if I debug the binary, while it can find the debug
info, it cannot find the source files to display inline when it hits a
breakpoint:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>lldb bazel-bin/main
<span class="o">(</span>lldb<span class="o">)</span> target create <span class="s2">"bazel-bin/main"</span>
Current executable <span class="nb">set </span>to <span class="s1">'/tmp/demo/bazel-bin/main'</span> <span class="o">(</span>arm64<span class="o">)</span><span class="nb">.</span>
<span class="o">(</span>lldb<span class="o">)</span> b main
Breakpoint 1: 17 locations.
<span class="o">(</span>lldb<span class="o">)</span> b
Current breakpoints:
1: name <span class="o">=</span> <span class="s1">'main'</span>, locations <span class="o">=</span> 1
  1.1: where <span class="o">=</span> main<span class="sb">`</span>main + 12 at main.c:2:3, address <span class="o">=</span> main[0x0000000100000334], unresolved, hit count <span class="o">=</span> 0
<span class="o">(</span>lldb<span class="o">)</span> r
Process 38440 launched: <span class="s1">'/tmp/demo/bazel-bin/main'</span> <span class="o">(</span>arm64<span class="o">)</span>
1 location added to breakpoint 1
Process 38440 stopped
<span class="k">*</span> thread <span class="c">#1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1</span>
    frame <span class="c">#0: 0x00000001000013ec main`main at main.c:2:3</span>
Target 0: <span class="o">(</span>main<span class="o">)</span> stopped.
</code></pre></div></div>

<p>Notably <code class="language-plaintext highlighter-rouge">lldb</code> knows that the breakpoint is set at <code class="language-plaintext highlighter-rouge">main.c:2:3</code> which is
pulled from the debug info, even though the source file cannot be found
to be displayed. If it didn't have the debug info at all, that output
would be subtly different and instead show no specific file location:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) b
Current breakpoints:
1: name = 'main', locations = 1
  1.1: where = main`main, address = 0x0000000100000328, unresolved, hit count = 0
</code></pre></div></div>

<p>To solve this clang has a few different arguments, similar to
<code class="language-plaintext highlighter-rouge">-oso_prefix</code>, to rewrite the absolute paths in the object files. The
most modern and comprehensive version of this flag is
<code class="language-plaintext highlighter-rouge">-ffile-compilation-dir=.</code>. This flag remaps all known paths that can be
embedded in the binary (not only debug info, but also coverage info, and
<code class="language-plaintext highlighter-rouge">__FILE__</code> macros, which otherwise won't be covered in this post) to be
relative to the current directory.</p>

<p>Once we apply this flag, we see the source file paths are fixed:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dwarfdump bazel-out/darwin_arm64-dbg/bin/_objs/main/main.o | <span class="nb">grep</span> <span class="nt">-e</span> sysroot <span class="nt">-e</span> comp_dir <span class="nt">-e</span> decl_file
  DW_AT_LLVM_sysroot        <span class="o">(</span><span class="s2">"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk"</span><span class="o">)</span>
  DW_AT_comp_dir    <span class="o">(</span><span class="s2">"."</span><span class="o">)</span>
  DW_AT_decl_file <span class="o">(</span><span class="s2">"./main.c"</span><span class="o">)</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">DW_AT_LLVM_sysroot</code> path is still absolute, but this likely doesn't
affect debugging since <code class="language-plaintext highlighter-rouge">lldb</code> rediscovers the SDK if this path is
invalid. In this case for reproducibility in Bazel we still rewrite this
to:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DW_AT_LLVM_sysroot ("/PLACEHOLDER_DEVELOPER_DIR/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk")
</code></pre></div></div>

<p>By passing
<code class="language-plaintext highlighter-rouge">-fdebug-prefix-map=$DEVELOPER_DIR=/PLACEHOLDER_DEVELOPER_DIR</code>, which
works similar to <code class="language-plaintext highlighter-rouge">-ffile-compilation-dir</code> but instead affects a single
prefix, not only relative paths.</p>

<p>With these 3 flags applied, our final binary is debuggable, and
reproducible!</p>

<h2 id="2-missing-object-or-source-files">2. Missing object or source Files</h2>

<p>Since we're relying on <code class="language-plaintext highlighter-rouge">lldb</code> reading the <code class="language-plaintext highlighter-rouge">N_OSO</code> entries in our final
binary, tracing those back to the object files, and then finally tracing
those back to the source files, all of those files must exist on the
machine running <code class="language-plaintext highlighter-rouge">lldb</code> at the time you are debugging the binary.</p>

<p>For simple local builds, this should always be the case, but in Bazel
there are a few potential times where you would break this assumption.</p>

<h3 id="21-pulling-from-the-bazel-cache">2.1. Pulling from the Bazel cache</h3>

<p>If you are using a remote cache or remote execution in Bazel, along with
<code class="language-plaintext highlighter-rouge">--remote_download_toplevel</code> (also known as BwtB or Builds Without The
Bytes which is enabled by default in modern Bazel versions), then Bazel
will avoid downloading as much as possible to speed up build time.</p>

<p>In this case if you didn't compile something locally, the intermediate
files may never be downloaded to your local machine, and therefore might
be missing when you go to debug. This can be pretty subtle since if you
do make changes that result in a local recompile (specifically if you
are only using the remote cache, or are using dynamic mode with
remote execution), the intermediate object files will be present and
your ability to debug will appear to be flaky.</p>

<p>To fix this you can instruct Bazel to always download the intermediate
files with something like this in your <code class="language-plaintext highlighter-rouge">.bazelrc</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>build --enable_platform_specific_config
# Specify the relevant extensions, likely only needed on macOS
build:macos --remote_download_regex='.*\.(a|lo|o)$'
</code></pre></div></div>

<h3 id="22-bazel-external-symlink">2.2. Bazel <code class="language-plaintext highlighter-rouge">external</code> symlink</h3>

<p>Another subtle case in Bazel is that while all first party code is
valid with the flags above, third party code is compiled from a separate
directory (since it isn't in your source tree), and therefore requires
more paths to be available at debug time.</p>

<p>For example building a binary that depends on <code class="language-plaintext highlighter-rouge">boringssl</code> has valid
<code class="language-plaintext highlighter-rouge">N_OSO</code> paths:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dsymutil <span class="nt">-s</span> bazel-bin/third_party_dep | <span class="nb">grep </span>N_OSO | <span class="nb">tail</span> <span class="nt">-1</span>
<span class="o">[</span> 49167] 00062e25 66 <span class="o">(</span>N_OSO        <span class="o">)</span> 00     0001   0000000000000000 <span class="s1">'bazel-out/darwin_arm64-dbg/bin/external/boringssl+/libcrypto_cxx.a(mlkem.o)'</span>
</code></pre></div></div>

<p>But if you inspect that archive's debug info, you see the source file
paths are nested under a directory called <code class="language-plaintext highlighter-rouge">external</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dwarfdump bazel-out/darwin_arm64-dbg/bin/external/boringssl+/libcrypto_cxx.a | <span class="nb">grep </span>decl_file | <span class="nb">head</span> <span class="nt">-1</span>
  DW_AT_decl_file <span class="o">(</span><span class="s2">"./external/boringssl+/crypto/mlkem/mlkem.cc"</span><span class="o">)</span>
</code></pre></div></div>

<p>Unfortunately this path doesn't exist in the root of your repository
like <code class="language-plaintext highlighter-rouge">bazel-out</code> does. The common workaround to solve this is to create
a symlink in the root of your repository manually with:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">ln</span> <span class="nt">-s</span> bazel-out/../../../external <span class="nb">.</span>
</code></pre></div></div>

<p>Then you can validate that this path exists so that <code class="language-plaintext highlighter-rouge">lldb</code> can find it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">head</span> <span class="nt">-5</span> external/boringssl+/crypto/mlkem/mlkem.cc
/<span class="k">*</span> Copyright <span class="o">(</span>c<span class="o">)</span> 2024, Google Inc.
 <span class="k">*</span>
 <span class="k">*</span> Permission to use, copy, modify, and/or distribute this software <span class="k">for </span>any
 <span class="k">*</span> purpose with or without fee is hereby granted, provided that the above
 <span class="k">*</span> copyright notice and this permission notice appear <span class="k">in </span>all copies.
</code></pre></div></div>

<p>If you don't often debug third party code in your project you can choose
to skip this, but it is also required for producing valid
<code class="language-plaintext highlighter-rouge">compile_commands.json</code> files for IDEs, so you might want it for that
too. You should add this symlink to your <code class="language-plaintext highlighter-rouge">gitignore</code> after creating it.</p>

<h2 id="3-using-rules_cc-or-toolchains_llvm-over-apple_support">3. Using <code class="language-plaintext highlighter-rouge">rules_cc</code> or <code class="language-plaintext highlighter-rouge">toolchains_llvm</code> over <code class="language-plaintext highlighter-rouge">apple_support</code></h2>

<p>Currently only <code class="language-plaintext highlighter-rouge">apple_support</code> handles all the flags above for Apple
platforms. That isn't an inherent limitation of <code class="language-plaintext highlighter-rouge">rules_cc</code> or
<code class="language-plaintext highlighter-rouge">toolchains_llvm</code> (which uses the config from <code class="language-plaintext highlighter-rouge">rules_cc</code>), but would
require some work to implement. Because of this you have to make sure
that you are getting the correct CC toolchain when building for
everything to work correctly.</p>

<p>If you think Bazel isn't choosing the correct toolchain you can pass
<code class="language-plaintext highlighter-rouge">--toolchain_resolution_debug=".*"</code> while building and you should see a
line like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>INFO: ToolchainResolution: Target platform @@platforms//host:host: Selected execution platform @@platforms//host:host, type @@bazel_tools//tools/cpp:toolchain_type -&gt; toolchain @@apple_support++apple_cc_configure_extension+local_config_apple_cc//:cc-compiler-darwin_arm64
</code></pre></div></div>

<p>Where the important part is that the toolchain on the right hand side is
from <code class="language-plaintext highlighter-rouge">@apple_support</code>, if it instead looks like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>INFO: ToolchainResolution: Target platform @@platforms//host:host: Selected execution platform @@platforms//host:host, type @@bazel_tools//tools/cpp:toolchain_type -&gt; toolchain @@rules_cc++cc_configure_extension+local_config_cc//:cc-compiler-darwin_arm64
</code></pre></div></div>

<p>You are getting the toolchain from <code class="language-plaintext highlighter-rouge">@rules_cc</code>.</p>

<p>When using <code class="language-plaintext highlighter-rouge">bzlmod</code> you must be sure to have <code class="language-plaintext highlighter-rouge">apple_support</code> <em>above</em>
<code class="language-plaintext highlighter-rouge">rules_cc</code> in your <code class="language-plaintext highlighter-rouge">MODULE.bazel</code> file to ensure this happens (this
won't affect non-Apple platforms):</p>

<pre><code class="language-bzl"># Has to be above rules_cc
bazel_dep(name = "apple_support", version = "1.23.1")
bazel_dep(name = "rules_cc", version = "0.2.8")
</code></pre>

<h2 id="4-bazel-producing-no-debug-info">4. Bazel producing no debug info</h2>

<p>By default only the <code class="language-plaintext highlighter-rouge">dbg</code> compilation mode produces debug info.
Therefore you must make sure to pass <code class="language-plaintext highlighter-rouge">--compilation_mode=dbg</code> in order
to get debug info. Otherwise the final binary will have no <code class="language-plaintext highlighter-rouge">N_OSO</code>
entries at all, which you can verify with <code class="language-plaintext highlighter-rouge">dsymutil -s &lt;binary&gt;</code>.
Alternatively you could manually pass <code class="language-plaintext highlighter-rouge">--copt=-g</code> in your build.</p>

<h2 id="5-bazel-stripping-produced-debug-info">5. Bazel stripping produced debug info</h2>

<p>If you pass <code class="language-plaintext highlighter-rouge">--strip=always</code> or <code class="language-plaintext highlighter-rouge">--strip=sometimes</code> (the default) to
Bazel, it will strip the <code class="language-plaintext highlighter-rouge">N_OSO</code> references from the produced binary.
Therefore you must make sure you don't do this in the cases where you
want debug info. Bazel also attempts to warn you in this case:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>bazel build main <span class="nt">-c</span> fastbuild <span class="nt">--copt</span><span class="o">=</span><span class="nt">-g</span>
WARNING: Stripping enabled, but <span class="s1">'--copt=-g'</span> <span class="o">(</span>or <span class="nt">--per_file_copt</span><span class="o">=</span>...@-g<span class="o">)</span> specified. Debug information will be generated and <span class="k">then </span>stripped away. This is probably not what you want! Use <span class="s1">'-c dbg'</span> <span class="k">for </span>debug mode, or use <span class="s1">'--strip=never'</span> to disable stripping
...
</code></pre></div></div>

<h2 id="6-debugging-bazel-exec-tools">6. Debugging Bazel exec tools</h2>

<p>By default Bazel doesn't produce debug info for tools built in the exec
configuration (because the default exec compilation mode is <code class="language-plaintext highlighter-rouge">opt</code>).
Instead of debugging a tool built for the exec configuration, you should
likely rebuild it directly (for the target configuration) and debug that
instead. This issue most commonly happens if you are debugging a bazel
rule and copy a command from Bazel's <code class="language-plaintext highlighter-rouge">--subcommands</code> output. You could
also pass <code class="language-plaintext highlighter-rouge">--host_copt=-g</code> if building the target directly doesn't work
for you.</p>

<h2 id="7-temporary-files">7. Temporary files</h2>

<p>If you use a single <code class="language-plaintext highlighter-rouge">clang</code> command to both compile and link a binary,
the intermediate object files referenced by the <code class="language-plaintext highlighter-rouge">N_OSO</code> entries are
temporary and deleted after the build:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>clang main.c <span class="nt">-o</span> main <span class="nt">-g</span>
<span class="nv">$ </span>dsymutil <span class="nt">-s</span> main | <span class="nb">grep </span>N_OSO
<span class="o">[</span>     3] 0000002e 66 <span class="o">(</span>N_OSO        <span class="o">)</span> 00     0001   0000000068d059de <span class="s1">'/var/folders/68/rn88yggx76bdgkj66fnxc1hc0000gn/T/main-637516.o'</span>
</code></pre></div></div>

<p>In this case you should likely split these commands apart to ensure the
object files are kept around. Alternatively as a quick workaround you
can pass <code class="language-plaintext highlighter-rouge">-save-temps</code> to <code class="language-plaintext highlighter-rouge">clang</code> to keep the intermediate object file
around.</p>

<h1 id="using-dsyms">Using <code class="language-plaintext highlighter-rouge">dSYM</code>s</h1>

<p>An entirely separate option for how to produce debug info for Apple
platforms is to use <code class="language-plaintext highlighter-rouge">dSYM</code> bundles. A <code class="language-plaintext highlighter-rouge">dSYM</code> is created from a linked
binary and accumulates the debug info for all transitive dependencies,
so you don't have to keep the other intermediate files around.</p>

<p>All of the same concerns stated above around absolute paths still apply,
but this can potentially simplify downloading intermediates or
redistributing debug info alongside a binary.</p>

<p>You can produce a <code class="language-plaintext highlighter-rouge">dSYM</code> from a binary with:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dsymutil <span class="nt">-o</span> main.dSYM main
</code></pre></div></div>

<p>If you are missing intermediates when creating a <code class="language-plaintext highlighter-rouge">dSYM</code>, you will see
relevant warnings (I would love a <code class="language-plaintext highlighter-rouge">dsymutil</code> flag to be able to make
this an error):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ dsymutil -o main.dSYM main
warning: (arm64) /private/tmp/demo/main.o unable to open object file: No such file or directory
warning: no debug symbols in executable (-arch arm64)
</code></pre></div></div>

<p>After you have the <code class="language-plaintext highlighter-rouge">dSYM</code> you can delete the intermediate files and
still debug as expected.</p>

<p>Bazel can automatically produce <code class="language-plaintext highlighter-rouge">dSYM</code>s for you if you pass
<code class="language-plaintext highlighter-rouge">--apple_generate_dsym --output_groups=+dsyms</code> when building (with any
<code class="language-plaintext highlighter-rouge">--compilation_mode</code>, but the <code class="language-plaintext highlighter-rouge">--strip</code> concerns above still apply).
Previously this wasn't supported for <code class="language-plaintext highlighter-rouge">cc_binary</code> targets but that has
recently been added in Bazel.</p>

<p>In Bazel, after producing the <code class="language-plaintext highlighter-rouge">dSYM</code>, the <code class="language-plaintext highlighter-rouge">N_OSO</code> entries in the binary
are stripped, and all debug info can be inspected in the <code class="language-plaintext highlighter-rouge">dSYM</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>bazel build main <span class="nt">--apple_generate_dsym</span> <span class="nt">--output_groups</span><span class="o">=</span>+dsyms
...
  bazel-bin/main
  bazel-bin/main.dSYM

<span class="nv">$ </span>dsymutil <span class="nt">-s</span> bazel-bin/main | <span class="nb">grep </span>N_OSO  <span class="c"># No output because they were stripped</span>
<span class="nv">$ </span>dwarfdump bazel-bin/main.dSYM
bazel-bin/main.dSYM/Contents/Resources/DWARF/main:      file format Mach-O arm64

.debug_info contents:
0x00000000: Compile Unit: length <span class="o">=</span> 0x00000035, format <span class="o">=</span> DWARF32, version <span class="o">=</span> 0x0005, unit_type <span class="o">=</span> DW_UT_compile, abbr_offset <span class="o">=</span> 0x0000, addr_size <span class="o">=</span> 0x08 <span class="o">(</span>next unit at 0x00000039<span class="o">)</span>
...
</code></pre></div></div>

<p>Since there are no <code class="language-plaintext highlighter-rouge">N_OSO</code> entries in the binary, <code class="language-plaintext highlighter-rouge">lldb</code> uses a
different mechanism to discover the <code class="language-plaintext highlighter-rouge">dSYM</code>. This is done by looking up
the UUID of the binary, and then looking for a matching <code class="language-plaintext highlighter-rouge">dSYM</code>. You can
inspect the UUIDs to make sure you have the appropriate <code class="language-plaintext highlighter-rouge">dSYM</code> locally:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dwarfdump <span class="nt">-u</span> bazel-bin/main bazel-bin/main.dSYM
UUID: 152D8F32-5A7E-31DF-9BF2-2F48E32450AE <span class="o">(</span>arm64<span class="o">)</span> bazel-bin/main
UUID: 152D8F32-5A7E-31DF-9BF2-2F48E32450AE <span class="o">(</span>arm64<span class="o">)</span> bazel-bin/main.dSYM/Contents/Resources/DWARF/main
</code></pre></div></div>

<p>If <code class="language-plaintext highlighter-rouge">lldb</code> isn't correctly discovering the <code class="language-plaintext highlighter-rouge">dSYM</code>, you can manually add
it with:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>lldb<span class="o">)</span> target symbols add bazel-bin/main.dSYM
</code></pre></div></div>

<p>You can also verify that <code class="language-plaintext highlighter-rouge">lldb</code> is correctly discovering the <code class="language-plaintext highlighter-rouge">dSYM</code> by
enabling the host logging by adding this to your <code class="language-plaintext highlighter-rouge">~/.lldbinit</code> file:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>log enable -f /tmp/host.log lldb host
</code></pre></div></div>

<p>After running <code class="language-plaintext highlighter-rouge">lldb</code> you will see that log should be populated with a
line like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ head -1 /tmp/host.log
dSYM with matching UUID &amp; arch found at /tmp/demo/bazel-bin/main.dSYM/Contents/Resources/DWARF/main
</code></pre></div></div>

<p>If the <code class="language-plaintext highlighter-rouge">dSYM</code> isn't found, there will be no related log entry. If you are
using other macOS tools like <code class="language-plaintext highlighter-rouge">Instruments.app</code> and it cannot find the
<code class="language-plaintext highlighter-rouge">dSYM</code>, you should try using the <code class="language-plaintext highlighter-rouge">symbolscache</code> CLI to manually add it.
See <code class="language-plaintext highlighter-rouge">symbolscache --help</code> for usage.</p>

<p>Generally the downside of using <code class="language-plaintext highlighter-rouge">dSYM</code>s is the (potentially small)
amount of time it takes to produce them, especially if you don't end up
debugging the binary. On the other hand if you use a crash reporting
service, you likely need to produce them anyway to symbolicate crash
reports, so it might be worth unifying the debug workflows.</p>

<p>Another benefit of using <code class="language-plaintext highlighter-rouge">dSYMs</code> is that you can fully <code class="language-plaintext highlighter-rouge">strip</code> the final
binary and still debug it. This is useful for making sure to produce the
smallest possible binary for release and test the same artifacts so you
know that the stripped binary works correctly (primarily risky if you are
using aggressive <code class="language-plaintext highlighter-rouge">strip</code> flags to heavily optimize binary size).</p>

<p>In Bazel, because producing the <code class="language-plaintext highlighter-rouge">dSYM</code> requires all the intermediate
files from the entire transitive dependency tree of the binary, it must
be done at the same time as the linking of the binary. This is done with
custom logic in <code class="language-plaintext highlighter-rouge">apple_support</code>'s CC toolchain, which doesn't exist in
<code class="language-plaintext highlighter-rouge">rules_cc</code>. You cannot run <code class="language-plaintext highlighter-rouge">dsymutil</code> manually on a Bazel binary unless
you have also downloaded all the intermediate files.</p>

<h1 id="other-considerations">Other considerations</h1>

<h2 id="launching-lldb-from-other-directories">Launching <code class="language-plaintext highlighter-rouge">lldb</code> from other directories</h2>

<p>Many of the recommendations above are about using relative paths for the
files that <code class="language-plaintext highlighter-rouge">lldb</code> is attempting to load. This works great as long as you
always launch <code class="language-plaintext highlighter-rouge">lldb</code> from the same directory (usually the root of your
repository). In some cases you might want to launch <code class="language-plaintext highlighter-rouge">lldb</code> from another
directory (especially in Bazel where runfiles discovery might require a
different PWD). In this case you can set the processes' working
directory separately from <code class="language-plaintext highlighter-rouge">lldb</code>'s working directory by still launching
<code class="language-plaintext highlighter-rouge">lldb</code> from the root of the repository (so that the relative debug info
paths are still valid), but then passing the other directory as a
setting:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>lldb <span class="nt">--one-line-before-file</span> <span class="s1">'settings set target.launch-working-dir some/other/directory'</span> <span class="nt">--</span> main
</code></pre></div></div>

<p>Or once you've launched <code class="language-plaintext highlighter-rouge">lldb</code> itself:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) platform settings -w some/other/directory
</code></pre></div></div>

<h2 id="partially-missing-debug-info">Partially missing debug info</h2>

<p>If you are linking pre-built artifacts, such as 3rd party static
libraries, it's possible they are built without debug info. In this case
if you are debugging and stop in their code, you might see no useful
information. You can use the commands outlined above to try and
understand if they have any information at all, but it is common
that closed source libraries ship stripped of debug info. If their
code is open source it's possible that you could pull it locally in
order to debug it, even while using the prebuilt artifact (if you
attempt this make sure to use the same sha the prebuilt artifact is
built from).</p>

<h2 id="lldb-source-maps"><code class="language-plaintext highlighter-rouge">lldb</code> source maps</h2>

<p>If you have prebuilt artifacts as outlined above which have absolute
paths in their debug info that you cannot change, you can attempt to use
<code class="language-plaintext highlighter-rouge">lldb</code>'s source mapping feature to rewrite those paths at debug time to
something that exists locally. In that case once you've identified the
path you need to rewrite by inspecting the binary with <code class="language-plaintext highlighter-rouge">dwarfdump</code>, you
can add a mapping in <code class="language-plaintext highlighter-rouge">lldb</code> like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) settings set target.source-map /absolute/path/in/binary /local/path/on/machine
</code></pre></div></div>

<p>You can have multiple of these paths which can be useful if you have
multiple prebuilt artifacts with different debug locations:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) settings set target.source-map /absolute/path/in/binary1 /local/path/on/machine1
(lldb) settings append target.source-map /absolute/path/in/binary2 /local/path/on/machine2
</code></pre></div></div>

<p>You could also use this instead of the <code class="language-plaintext highlighter-rouge">clang</code> flags, or the <code class="language-plaintext highlighter-rouge">external</code>
symlink mentioned above, but often this is harder to manage since the
paths could differ between different developer's machines and requires
injecting <code class="language-plaintext highlighter-rouge">lldb</code> settings.</p>

<p>Note that <code class="language-plaintext highlighter-rouge">-oso_prefix</code> is not affected by these mappings, and there can
only be 1 of them. If this is an issue for you, you could potentially
create your own symlink tree to solve this. I imagine it would also be
possible to rewrite the <code class="language-plaintext highlighter-rouge">N_OSO</code> entries in the binary but I don't think
any of the related tools do this today.</p>

<h2 id="shared-libraries">Shared libraries</h2>

<p>If you are debugging a binary that depends on shared libraries
(<code class="language-plaintext highlighter-rouge">dylib</code>s), the same principles that apply to the main binary apply to
every individual shared library. The shared libraries will have their
own <code class="language-plaintext highlighter-rouge">N_OSO</code> entries that must be valid, and their own <code class="language-plaintext highlighter-rouge">dSYMs</code> (if you go
that route). In this case the main binary won't have <code class="language-plaintext highlighter-rouge">N_OSO</code> entries for
the shared libraries (or their transitive dependencies), but instead
<code class="language-plaintext highlighter-rouge">lldb</code> will discover them as they are loaded by <code class="language-plaintext highlighter-rouge">dyld</code>.</p>

<p>In <code class="language-plaintext highlighter-rouge">lldb</code> you can see all of the loaded shared libraries with <code class="language-plaintext highlighter-rouge">image
list</code> (the first field is the same UUID discussed above):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) image list
[  0] 503338F7-DF39-3284-9927-7BF3F9E07F94 0x0000000100000000 /tmp/demo/main
[  1] 072C7C60-2B6C-3E07-AA56-FD1750EADC2F 0x000000018e6cf000 /usr/lib/libSystem.B.dylib
[  2] 25DE2C92-147C-3AC7-91BA-51B8F7EDC72B 0x0000000000000000 libfoo.dylib
[  3] 3247E185-CED2-36FF-9E29-47A77C23E004 0x00000001800cc000 /usr/lib/dyld
</code></pre></div></div>

<h2 id="symlinks-versus-actual-paths">Symlinks versus actual paths</h2>

<p>In the past there have been multiple bugs around how <code class="language-plaintext highlighter-rouge">-oso_prefix</code> and
<code class="language-plaintext highlighter-rouge">-ffile-compilation-dir</code> handle symlinks in paths. If your build heavily
uses symlinks, as Bazel does, and you're getting unexpected absolute
path behavior, this is something to consider. The typical bugs arise
from whether the path is remapped before or after the symlinks are
resolved.</p>

<h1 id="thanks-for-reading">Thanks for reading!</h1>

<p>Hopefully all this information helps you the next time you need to debug
something related to debug info!</p>

<p>If you have any questions or feedback please reach out to me on the
Bazel slack or Mastodon. Feel free to cc me on any relevant Bazel
issues.</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Using a modern version of Bazel and the
<a href="https://github.com/bazelbuild/apple_support"><code class="language-plaintext highlighter-rouge">apple_support</code></a> CC
toolchain handles all of this for you <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Finding unused targets with bazel]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2025/03/24/unused-bazel-targets/" />
      <id>https://smileykeith.com/2025/03/24/unused-bazel-targets</id>
      <updated>2025-03-24T11:00:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>Once you've fully migrated a codebase to bazel, one of the many
advantages is that you can easily inspect your build graph using <code class="language-plaintext highlighter-rouge">bazel
query</code>.</p>

<p>One of the many things you can do with queries is write scripts to
enforce coding standards, or in today's example, find unused targets
that can lead to discovering unused code.</p>

<p>The simplest version of this query starts like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let all_targets = //... in
let top_level_targets = tests($all_targets) union kind("(.*_binary) rule", $all_targets) in
$all_targets - deps($top_level_targets)
</code></pre></div></div>

<p>This initial version discovers all your first party targets, and then
subtracts the dependencies of all "top level" targets, so that any
remaining targets are considered unused.</p>

<p>The idea of "top level" targets is better defined as: anything that you
consider to be important enough that its dependencies are used.
Depending on your codebase you might want to exclude test targets from
this so that targets only in the dependencies of test targets are
diagnosed as unused (this won't work if you have intentional <code class="language-plaintext highlighter-rouge">testonly</code>
dependencies, although you could special case those as shown below).</p>

<p>Once you have this initial query, you can start iterating in order to
handle in edge cases. For example it's likely that you have some targets
that aren't binaries or tests, but are considered used. There are 2
approaches I would recommend to handle this. First you can continue to
build out the <code class="language-plaintext highlighter-rouge">kind</code> filter:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kind("(.*_binary|platform|test_suite) rule", $all_targets)
</code></pre></div></div>

<p>The downside with this approach is it can get unwieldy quickly. I like
adding rules here that have many uses, but for other one off cases
another approach you can use is to expand the query to look for special
tags:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let all_targets = //... in
let top_level_targets = tests($all_targets) union kind("(.*_binary|platform|test_suite) rule", $all_targets) in
let allowed_unused = attr(tags, allow-unused, $all_targets) in
$all_targets - deps($top_level_targets) - $allowed_unused
</code></pre></div></div>

<p>Then you can add <code class="language-plaintext highlighter-rouge">tags = ["allow-unused"]</code> to whatever targets that you need.</p>

<p>Similarly if you want to special case targets that can be considered
"top level" you can use a tag for that case as well:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let all_targets = //... in
let top_level_targets = tests($all_targets) union kind("(.*_binary|platform|test_suite) rule", $all_targets) union attr(tags, top-level, $all_targets) in
let allowed_unused = attr(tags, allow-unused, $all_targets) in
$all_targets - deps($top_level_targets) - $allowed_unused
</code></pre></div></div>

<p>Now adding <code class="language-plaintext highlighter-rouge">tags = ["top-level"]</code> to various targets will ensure their
entire dependency tree is also considered used. This case is especially
useful for targets like CC toolchains which are used in your
<code class="language-plaintext highlighter-rouge">MODULE.bazel</code> but potentially don't appear as used by bazel query.</p>

<p>Hopefully this post was instructive and gives you some ideas for how you
can write more advanced queries for your use cases.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Bazel caching and compressed debug info]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2025/02/14/compressed-debug-info/" />
      <id>https://smileykeith.com/2025/02/14/compressed-debug-info</id>
      <updated>2025-02-14T10:00:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>One of bazel's most attractive features is the ability for it to
remotely cache artifacts to reduce unnecessary work for large builds.
Unfortunately users quickly discover this comes with non-trivial
financial and bandwidth implications.</p>

<p>There are many ways, of varying difficulty, to try and improve your
cache usage. From breaking unnecessary dependencies, to adding
larger local storage for CI workers, builds without the bytes, build
avoidance, etc.</p>

<p>For codebases with lots of C or C++ one of the potentially easiest wins
is to enable compressed debug information<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Let's look at an example from
our codebase.</p>

<p>Looking at the size of a non-trivial C++ binary built with <code class="language-plaintext highlighter-rouge">-g -O2</code>
(similar to cmake's <code class="language-plaintext highlighter-rouge">RelWithDebInfo</code> configuration), or binary clocks in
at ~530mbs:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% du -sh bin
536M    bin
</code></pre></div></div>

<p>To get a sense of what percentage of this binary is debug info, we can
use <code class="language-plaintext highlighter-rouge">llvm-objcopy</code> to strip the debug info entirely:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% llvm-objcopy --strip-debug bin strippedbin
% du -sh strippedbin
159M    strippedbin
</code></pre></div></div>

<p>This shows us that almost 70%(!!) of the binary size is taken up with
debug info. In release configurations we can eliminate this entirely
with bazel's <code class="language-plaintext highlighter-rouge">--strip</code> argument, but for developer builds, or other use
cases where you need debug info, we can still improve this.</p>

<p>If we use <code class="language-plaintext highlighter-rouge">llvm-objcopy</code> again, this time to compress the debug info, we
can immediately see our potential gains:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% llvm-objcopy --compress-debug-sections bin compressedbin
% du -sh compressedbin
290M    compressedbin
</code></pre></div></div>

<p>This shows us we can get an almost 50%(!!) improvement in binary size in
this example.</p>

<p>To enable this in bazel, assuming you're using a relatively recent
version of <code class="language-plaintext highlighter-rouge">gcc</code> or <code class="language-plaintext highlighter-rouge">clang</code>, you can add something like this to your
<code class="language-plaintext highlighter-rouge">.bazelrc</code><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>build --enable_platform_specific_config
build:linux --copt=-gz --host_copt=-gz
build:linux --linkopt=-gz --host_linkopt=-gz
</code></pre></div></div>

<p>In practice we saw cache reads drop by nearly 60% when we rolled out
this change.</p>

<p>Reducing binary size with this approach has a lot of benefits, but it's
even more pronounced when using bazel and closely monitoring your cache
download sizes.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p><a href="https://maskray.me/blog/2022-01-23-compressed-debug-sections">This post</a> goes into way more detail than I will <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Passing these flags on macOS likely doesn't have a downside, but they are ignored <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Printing rpaths with objdump]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2022/03/16/objdump-rpaths/" />
      <id>https://smileykeith.com/2022/03/16/objdump-rpaths</id>
      <updated>2022-03-16T10:00:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>MachO binaries contain load commands to indicate to dyld where it should
search for the libraries the binary depends on.</p>

<p>These paths are often useful to inspect when debugging why your binary
isn't discovering the libraries you'd expect.</p>

<p>Previously you could discover these with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% otool -l `xcrun -f swiftc` \
  | grep -A2 LC_RPATH \
  | grep "^\s*path" \
  | cut -d " " -f 11
@executable_path/../lib/swift/macosx
@executable_path/../lib/swift/macosx
</code></pre></div></div>

<p>This example is quite verbose and fragile for such a common action, so
recently I <a href="https://reviews.llvm.org/D100681">committed a change</a> to add an easier option with
LLVM's <code class="language-plaintext highlighter-rouge">objdump</code>. This change shipped with LLVM 13 or Xcode 13.3 on
macOS, allowing you to run:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% objdump --macho --rpaths `xcrun -f swiftc`
/Applications/Xcode-13.3.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc:
@executable_path/../lib/swift/macosx
</code></pre></div></div>

<p>This is much more succinct and memorable, but also has slightly
different output. This is because <code class="language-plaintext highlighter-rouge">objdump</code> automatically detects the
current machine's architecture, and only prints the rpaths for that
slice of the fat binary. It also outputs the path of the binary being
run on, which you can disable with <code class="language-plaintext highlighter-rouge">--no-leading-headers</code>.</p>

<p>Hopefully you find this as useful as I do!</p>

]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Debugging bazel actions]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2022/03/02/debugging-bazel-actions/" />
      <id>https://smileykeith.com/2022/03/02/debugging-bazel-actions</id>
      <updated>2022-03-02T18:00:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>When working on bazel build infrastructure, something I often need to do
is reproduce an action outside of bazel's infrastructure in order to
debug it further. This debugging often involves changing flags or
swapping out the tool itself for a custom built version.</p>

<p>In many cases updating your bazel configuration as normal should work
well enough. But sometimes when you're iterating on things that
invalidate a significant portion of your build it can be faster to work
on things outside of bazel first, and then update your bazel
configuration based on your discoveries. Another case where this is
useful is if you want to benchmark a specific action by running it many
times individually without the contention caused by bazel parallelizing
other actions.</p>

<p>Since bazel has a lot of infrastructure for keeping builds hermetic,
there are a few steps you need to take to <em>roughly</em> reproduce what bazel
is doing so your debugging is as close to what it runs as possible.</p>

<h1 id="1-build-and-disable-sandboxing">1. Build and disable sandboxing</h1>

<p>In order for bazel to setup your build environment (including your
downloaded dependencies), and leave it intact for you to muck around
with, you must run a normal build and also either disable sandboxing by
passing <code class="language-plaintext highlighter-rouge">--spawn_strategy=standalone</code>, or make it leave the sandbox base
around by passing <code class="language-plaintext highlighter-rouge">--sandbox_debug</code>.</p>

<h1 id="2-grab-your-actions-command-line">2. Grab your action's command line</h1>

<p>Once bazel has run and left its environment intact, you need to grab the
command line being run for the action you want to debug. I find that
passing bazel's <code class="language-plaintext highlighter-rouge">-s</code> flag (also known as <a href="https://bazel.build/reference/command-line-reference#flag--subcommands"><code class="language-plaintext highlighter-rouge">--subcommands</code></a>)
is the easiest way to do this. You just have to make sure that the
action you're interested in actually runs. There are a few different
ways you can force bazel to run an action:</p>

<ul>
  <li>Invalidate the inputs for the action. Unlike other build systems
<code class="language-plaintext highlighter-rouge">touch</code>ing input files isn't enough, I often add newlines or comments
to files to force actions to re-run.</li>
  <li>Change the flags for a command line. For some actions such as C++
compiles, or native binary linking, there are easy command line flags
you can pass to invalidate the actions, specifically things like
<code class="language-plaintext highlighter-rouge">--copt=-v</code> and <code class="language-plaintext highlighter-rouge">--linkopt=-v</code> respectively. Another useful thing to
know is bazel doesn't have any semantics around the contents of these
flags, so if you need to invalidate the action a second time, you can
often append the same option again, repeating it, to make bazel re-run
it. For example <code class="language-plaintext highlighter-rouge">--linkopt=-v --linkopt=-v</code>, often the underlying tool
won't care about the repetition. This works best when changing flags
will only invalidate a small number of actions so you don't have to
rebuild a ton of things before you get to the action you care about.</li>
  <li>Change flags on the specific target. If changing flags globally is too
invasive for your build, you can often edit the <code class="language-plaintext highlighter-rouge">copts</code> attribute of
the specific target you care about to invalid the action. Again
passing <code class="language-plaintext highlighter-rouge">-v</code> is often a useful way to get it to re-run without
changing semantics of the build. You can also do this with
<a href="https://bazel.build/reference/command-line-reference#flag--per_file_coptg"><code class="language-plaintext highlighter-rouge">--per_file_copt</code></a> so you don't have to change any BUILD
files. Thanks to Tom Rybka for pointing this out.</li>
  <li>Make it fail. Change the inputs or flags to something that is invalid,
then your bazel invocation will stop after hitting the action in
question.</li>
</ul>

<p>Once you have forcibly re-run your action with <code class="language-plaintext highlighter-rouge">-s</code>, you should see some
output like this (specifics will vary based on your platform and the
action you're debugging):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SUBCOMMAND: # //some_target:some_target [action 'Compiling Swift module //some_target:some_target', configuration: 725a049b28caa74d2a9605a6748b603bdab9e977931b2d02c0bb07b9a06575b2, execution platform: //:macos_x86_64]
(cd /private/var/tmp/_bazel_ksmiley/751b7cfc481e6eb168e92ffcfb919baa/execroot/someworkspace &amp;&amp; \
  exec env - \
    APPLE_SDK_PLATFORM=iPhoneSimulator \
    APPLE_SDK_VERSION_OVERRIDE=15.2 \
    XCODE_VERSION_OVERRIDE=13.2.1.13C100 \
  bazel-out/darwin_x86_64-opt-exec-8F99CFCD-ST-41e1ca5c471d/bin/external/build_bazel_rules_swift/tools/worker/universal_worker swiftc @bazel-out/ios-sim_arm64-min12.0-applebin_ios-ios_sim_arm64-fastbuild-ST-40c63e007684/bin/some_target/some_target.swiftmodule-0.params
</code></pre></div></div>

<h1 id="3-reproduce-bazels-environment-and-run">3. Reproduce bazel's environment and run</h1>

<p>Now that you have the environment and command line bazel ran, you can
roughly reproduce what it did in a few steps:</p>

<ol>
  <li>Switch to the directory it built in using the <code class="language-plaintext highlighter-rouge">cd</code> command it prints:
<code class="language-plaintext highlighter-rouge">cd /private/var/tmp/_bazel_ksmiley/751b7cfc481e6eb168e92ffcfb919baa/execroot/someworkspace</code></li>
  <li>
    <p>Reproduce the environment variables it sets for the action:</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> export APPLE_SDK_PLATFORM=iPhoneSimulator
 export APPLE_SDK_VERSION_OVERRIDE=15.2
 export XCODE_VERSION_OVERRIDE=13.2.1.13C100
</code></pre></div>    </div>
  </li>
  <li>
    <p>Run the command line:</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> bazel-out/darwin_x86_64-opt-exec-8F99CFCD-ST-41e1ca5c471d/bin/external/build_bazel_rules_swift/tools/worker/universal_worker \
   swiftc \
   @bazel-out/ios-sim_arm64-min12.0-applebin_ios-ios_sim_arm64-fastbuild-ST-40c63e007684/bin/some_target/some_target.swiftmodule-0.params
</code></pre></div>    </div>
  </li>
</ol>

<h1 id="gotchas">Gotchas</h1>

<p>At this point you're likely very close to reproducing what bazel was
running, but there are a few other things to keep in mind:</p>

<ul>
  <li>Bazel has some implicit environment variable manipulation in some
cases that you need to reproduce. For example for builds that rely on
Xcode on macOS, the Xcode discovery logic is done implicitly by bazel,
requiring you to approximate that logic yourself. To reproduce this
specific case you need to make sure to set <code class="language-plaintext highlighter-rouge">DEVELOPER_DIR</code> and
<code class="language-plaintext highlighter-rouge">SDKROOT</code> with something like<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export DEVELOPER_DIR=$(xcode-select -p)
export SDKROOT=$(xcrun --show-sdk-path --sdk iphonesimulator)
</code></pre></div></div>

<ul>
  <li>Some actions in bazel use a number of wrappers before getting down to
the actual command being run. In the example above <code class="language-plaintext highlighter-rouge">universal_worker</code>
is a pre-processor for the Swift command line. Sometimes you might
want to go a bit deeper in the stack. I often pass <code class="language-plaintext highlighter-rouge">-v</code> as an extra
argument to the wrapper invocation to get the final command line it
runs, and then iterate on that instead of the one bazel invokes.</li>
  <li>Other environment variables can impact behavior. Depending on how you
run bazel normally and what you're debugging, this might matter as the
action may have access the environment variables that it wouldn't have
inside of bazel. This often doesn't make a difference if your build is
hermetic, but is worth keeping in mind (especially for <code class="language-plaintext highlighter-rouge">PATH</code>).</li>
  <li>These steps may change over time. Ideally there would be a more
straightforward way to reproduce actions like this, potentially by
parsing bazel's execution log, but in the meantime this approach works
well in my experience.</li>
</ul>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>I use <a href="https://github.com/keith/dotfiles/blob/main/functions/set-bazel-env">a script</a> for this <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Auto linking with Mach-O binaries]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2022/02/23/lc-linker-option/" />
      <id>https://smileykeith.com/2022/02/23/lc-linker-option</id>
      <updated>2022-02-23T18:00:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>Auto linking is a feature that embeds information in your binaries' at
compile time which is then used at link time to automatically link your
dependencies. This allows you to reduce the duplication of flags between
the different phases of your (or your consumers') builds.</p>

<p>For example, with this Objective-C file:</p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include &lt;Foundation/Foundation.h&gt;
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">NSLog</span><span class="p">(</span><span class="s">@"Hello, World!"</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Compiled with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ clang -fmodules -c foo.m -o foo.o
</code></pre></div></div>

<p>You can then inspect the options added for use at link time:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ otool -l foo.o | grep LC_LINKER_OPTION -A3
     cmd LC_LINKER_OPTION
 cmdsize 40
   count 2
  string #1 -framework
  string #2 Foundation
...
</code></pre></div></div>

<p>Now when linking this binary you don't have to pass any extra flags to
the linker to make sure you link <code class="language-plaintext highlighter-rouge">Foundation</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ld foo.o -syslibroot `xcrun --show-sdk-path`
</code></pre></div></div>

<p>To compare, if you compile the binary without <code class="language-plaintext highlighter-rouge">-fmodules</code><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ clang -c foo.m -o foo.o
</code></pre></div></div>

<p>You don't get any <code class="language-plaintext highlighter-rouge">LC_LINKER_OPTION</code>s. Then when linking the binary with
the same command as before, it fails with these errors:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ld foo.o -syslibroot `xcrun --show-sdk-path`
Undefined symbols for architecture arm64:
  "_NSLog", referenced from:
      _main in foo.o
  "___CFConstantStringClassReference", referenced from:
      CFString in foo.o
ld: symbol(s) not found for architecture arm64
</code></pre></div></div>

<p>To make it succeed you must explicitly link <code class="language-plaintext highlighter-rouge">Foundation</code> through an
argument to your linker invocation:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ld foo.o -syslibroot `xcrun --show-sdk-path` -framework Foundation
</code></pre></div></div>

<p>Auto linking is also applied when using module maps that use the <code class="language-plaintext highlighter-rouge">link</code>
directive. For example with this module map file:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// module.modulemap
module foo {
 link "foo"
 link framework "Foundation"
}
</code></pre></div></div>

<p>That you include with in this source file:</p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@import</span> <span class="n">foo</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And compile (with an include path to the <code class="language-plaintext highlighter-rouge">module.modulemap</code> file):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ clang -fmodules -c foo.m -o foo.o -I.
</code></pre></div></div>

<p>The produced object depends on <code class="language-plaintext highlighter-rouge">foo</code> and <code class="language-plaintext highlighter-rouge">Foundation</code>. This can be
useful for handwriting module map files for prebuilt libraries, and for
quite a few other cases. You can read about this file format in <a href="https://clang.llvm.org/docs/Modules.html">the
docs</a>.</p>

<p>You can also see auto linking with Swift code:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">print</span><span class="p">(</span><span class="s">"Hello, World!"</span><span class="p">)</span>
</code></pre></div></div>

<p>Compiled with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ swiftc foo.swift -o foo.o -emit-object
</code></pre></div></div>

<p>You can see it requires the Swift standard libraries:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ otool -l foo.o | grep LC_LINKER_OPTION -A3
     cmd LC_LINKER_OPTION
 cmdsize 24
   count 1
  string #1 -lswiftCore
...
</code></pre></div></div>

<p>For Swift this is especially useful since there are some underlying
libraries like <code class="language-plaintext highlighter-rouge">libswiftSwiftOnoneSupport.dylib</code> that need to be linked,
but should be treated as implementation details that Swift developers
are never exposed to.</p>

<p>In general, this is more than you'll ever need to know about auto
linking. But there are some situations where you might want to force
binaries to include <code class="language-plaintext highlighter-rouge">LC_LINKER_OPTION</code>s when they don't automatically.
For example, if your build system builds without <code class="language-plaintext highlighter-rouge">-fmodules</code> (like bazel
and cmake by default) and for some reason you cannot enable it<sup id="fnref:1:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, or
when you're distributing a library and don't want your consumers to have
to worry about adding extra linker flags.</p>

<p>There are 3 different ways you can explicitly add <code class="language-plaintext highlighter-rouge">LC_LINKER_OPTION</code>s
during your builds. First you can pass a flag when compiling your
sources with clang:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ clang -c foo.m -o foo.o -Xclang --linker-option=-lfoo
</code></pre></div></div>

<p>Or with swiftc:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ swiftc foo.swift -o foo.o -emit-object -Xcc -Xclang -Xcc --linker-option=-lfoo
</code></pre></div></div>

<p>Specifically for libraries there's an even easier way to do this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ clang -c foo.m -o foo.o -Xclang --dependent-lib=foo
</code></pre></div></div>

<p>These work perfectly for libraries you depend on, but for frameworks you
need to pass multiple flags, and because of the space between them, it
doesn't seem like there is a way to pass this with the current clang
flags (although it seems reasonable to add support for this). Luckily
the second option supports spaces in options. Instead of passing a flag,
you can add an assembly directive to one of the source files you're
compiling with clang:</p>

<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include &lt;Foundation/Foundation.h&gt;
</span>
<span class="n">asm</span><span class="p">(</span><span class="s">".linker_option </span><span class="se">\"</span><span class="s">-lfoo</span><span class="se">\"</span><span class="s">"</span><span class="p">);</span>
<span class="n">asm</span><span class="p">(</span><span class="s">".linker_option </span><span class="se">\"</span><span class="s">-framework</span><span class="se">\"</span><span class="s">, </span><span class="se">\"</span><span class="s">Foundation</span><span class="se">\"</span><span class="s">"</span><span class="p">);</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">NSLog</span><span class="p">(</span><span class="s">@"Hello, World!"</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Compiling this results in a binary that automatically links <code class="language-plaintext highlighter-rouge">Foundation</code>
and <code class="language-plaintext highlighter-rouge">foo</code>.</p>

<p>To see a real world example where this was helpful, check out <a href="https://github.com/keith/StaticIndexStore/blob/a2158c3419a7591bb4a89283abe7a8183d9596d9/example.patch">this
change</a> for building a static library from a C++ library that
requires some dependencies, but doesn't build with <code class="language-plaintext highlighter-rouge">-fmodules</code>.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>You should try to enable modules if possible, this flag just shows
  the difference in behavior. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:1:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
  </ol>
</div>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Silencing iOS simulator log noise]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2021/11/16/simulator-log-spew/" />
      <id>https://smileykeith.com/2021/11/16/simulator-log-spew</id>
      <updated>2021-11-16T18:00:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>When Apple transitioned to using <code class="language-plaintext highlighter-rouge">os_log</code> for system logs it seemed they
also decided to open the floodgates for what logs were surfaced in our
apps.</p>

<p>This lead to a plethora of stackoverflow questions recommending you
disable <code class="language-plaintext highlighter-rouge">os_log</code> entirely by setting <code class="language-plaintext highlighter-rouge">OS_ACTIVITY_MODE=disable</code> in your
target's scheme. This is fine for some cases but might also silence some
actually useful logs, or your own logs if you want to use <code class="language-plaintext highlighter-rouge">os_log</code> for
its feature-set.</p>

<p><code class="language-plaintext highlighter-rouge">os_log</code> has a nice set of categorization for logs, for example when
viewing streaming logs in <code class="language-plaintext highlighter-rouge">Console.app</code> you can show the process,
subsystem, and category for each log.</p>

<p><img src="/images/console-categories.png" alt="Screenshot of Console.app UI" /></p>

<p>Perhaps ideally we could use an environment variable with more granular
filtering based on this categorization, but that would likely get
complex quickly. Instead we can update OS log's configuration to disable
some specific types of logs. Here are a few examples I found useful for
Lyft's iOS project:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xcrun simctl spawn booted log config --subsystem com.apple.CoreBluetooth --mode level:off
xcrun simctl spawn booted log config --subsystem com.apple.CoreTelephony --mode level:off
xcrun simctl spawn booted log config --subsystem com.apple.network --category boringssl --mode level:off
</code></pre></div></div>

<p>You can replace <code class="language-plaintext highlighter-rouge">booted</code> here with a specific iOS simulator UDID, which
can be found by running <code class="language-plaintext highlighter-rouge">xcrun simctl list devices</code>. Since this is
simulator specific, you will have to re-run whatever commands you decide
on when you create new simulators.</p>

<p>More options for the <code class="language-plaintext highlighter-rouge">log</code> command can be found with <a href="https://keith.github.io/xcode-man-pages/log.1.html"><code class="language-plaintext highlighter-rouge">man
log</code></a></p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Reproducible codesigning on Apple Silicon]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2021/10/05/codesign-m1/" />
      <id>https://smileykeith.com/2021/10/05/codesign-m1</id>
      <updated>2021-10-05T20:00:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>For people who expect <a href="https://reproducible-builds.org">reproducible builds</a>, Apple Silicon machines
provide an interesting challenge. Apple Silicon <a href="https://eclecticlight.co/2020/08/22/apple-silicon-macs-will-require-signed-code/">requires</a> <code class="language-plaintext highlighter-rouge">arm64</code>
binaries, including command line tools you build yourself, be
codesigned. This change is mostly transparent to developers, because
<a href="https://github.com/keith/ld64/blob/90cb020963ce62a84000b99362cbed36bd16adcd/src/ld/Options.cpp#L5683-L5685">Apple updated their linker</a> to automatically ad-hoc sign
binaries<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Unfortunately, if you're interested in producing binaries
that support both Intel Macs and Apple Silicon Macs, you likely want to
produce a <a href="https://en.wikipedia.org/wiki/Fat_binary"><code class="language-plaintext highlighter-rouge">fat binary</code></a>. When codesigning this binary you hit
some behavior that depends on your current machine's architecture.</p>

<h2 id="example">Example</h2>

<p>You can consistently produce the same result across multiple machines
when compiling a binary without signing it. Here's an example with a
simple C program:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ echo "int main() { return 0; }" &gt; main.c
$ clang main.c -Wl,-no_adhoc_codesign -arch arm64 -arch x86_64 -o main
$ shasum main
113033b3d9a247210b49a476bbfadb2e347846fe  main
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">shasum</code> of <code class="language-plaintext highlighter-rouge">main</code> should always be the same regardless of your host
machine<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. On Apple Silicon machines you can see this binary has the
same <code class="language-plaintext highlighter-rouge">sha1</code> even if you run <code class="language-plaintext highlighter-rouge">clang</code> under Rosetta 2<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ arch -x86_64 clang main.c -Wl,-no_adhoc_codesign -arch arm64 -arch x86_64 -o main
$ shasum main
113033b3d9a247210b49a476bbfadb2e347846fe  main
</code></pre></div></div>

<p>The issue is introduced when you <code class="language-plaintext highlighter-rouge">codesign</code> the binary on Apple Silicon
machines versus Intel machines. You can immediately see the
difference<sup id="fnref:3:1" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ codesign --force --sign - main
$ shasum main
84631e812bd480c306766ba03a728dd2565dd672  main
% arch -x86_64 codesign --force --sign - main
% shasum main
f631b6c0daf3ffd0bb5f65d19fa045acf447a72d  main
</code></pre></div></div>

<p>We get closer to identifying the problem when you compare the details of
these differences:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ codesign --force --sign - main
$ codesign -dvvv main &gt; arm.txt 2&gt;&amp;1
$ arch -x86_64 codesign --force --sign - main
$ codesign -dvvv main &gt; intel.txt 2&gt;&amp;1
$ diff -Nur intel.txt arm.txt
</code></pre></div></div>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gd">--- intel.txt   2021-10-05 21:26:32.731918710 -0700
</span><span class="gi">+++ arm.txt     2021-10-05 21:26:29.473702845 -0700
</span><span class="p">@@ -1,14 +1,14 @@</span>
 Executable=/private/tmp/main
<span class="gd">-Identifier=main-555549445413bc88d3b13dfa855a7f47cf229020
</span><span class="gi">+Identifier=main-55554944e4472179d9ec3bff92ff3f8e6d013184
</span> Format=Mach-O universal (x86_64 arm64)
 CodeDirectory v=20400 size=358 flags=0x2(adhoc) hashes=5+2 location=embedded
 Hash type=sha256 size=32
<span class="gd">-CandidateCDHash sha256=78fab81d4fef72ae942e3272ba2c9085e1893828
-CandidateCDHashFull sha256=78fab81d4fef72ae942e3272ba2c9085e1893828a77095ae517ab7d3b8229ad0
</span><span class="gi">+CandidateCDHash sha256=1a9fcc4eabde35d87326380f5eca6672a1c96f78
+CandidateCDHashFull sha256=1a9fcc4eabde35d87326380f5eca6672a1c96f78f252bbecec7f19ffdd56e420
</span> Hash choices=sha256
<span class="gd">-CMSDigest=78fab81d4fef72ae942e3272ba2c9085e1893828a77095ae517ab7d3b8229ad0
</span><span class="gi">+CMSDigest=1a9fcc4eabde35d87326380f5eca6672a1c96f78f252bbecec7f19ffdd56e420
</span> CMSDigestType=2
<span class="gd">-CDHash=78fab81d4fef72ae942e3272ba2c9085e1893828
</span><span class="gi">+CDHash=1a9fcc4eabde35d87326380f5eca6672a1c96f78
</span> Signature=adhoc
 Info.plist=not bound
 TeamIdentifier=not set
</code></pre></div></div>

<p>There are a few fields that differ here, but, given that most of them
seem to be hashes, it felt natural to focus on the <code class="language-plaintext highlighter-rouge">Identifier</code> field
(especially since that appears to contain our file name).</p>

<p>In the manual page for <a href="https://keith.github.io/xcode-man-pages/codesign.1.html#i,"><code class="language-plaintext highlighter-rouge">codesign(1)</code></a>, we see some useful
information related to <code class="language-plaintext highlighter-rouge">identifier</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-i, --identifier identifier
    During signing, explicitly specify the unique identifier string that is
    embedded in code signatures. If this option is omitted, the identifier
    is derived from either the Info.plist (if present), or the filename of
    the executable being signed, possibly modified by the --prefix option.
    It is a very bad idea to sign different programs with the same
    identifier.
</code></pre></div></div>

<p>This gives us further hints to what is going on. Specifically, since we
do not have an <code class="language-plaintext highlighter-rouge">Info.plist</code>, the logic must not be deriving the
identifier in that way. Given this information we can assume
<code class="language-plaintext highlighter-rouge">codesign</code> is falling back to some other value here, but it's still
not clear why it isn't reproducible across architectures.</p>

<p>Luckily, Apple's <a href="https://opensource.apple.com">open source</a> page has quite a few internal
libraries, which in this case covers our issue. Looking through the
<a href="https://opensource.apple.com/tarballs/Security"><code class="language-plaintext highlighter-rouge">Security</code> project's source code</a><sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>, we can immediately see
some useful information for this field:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@constant kSecCodeSignerIdentifier If present, a CFString that explicitly specifies
 the unique identifier string sealed into the code signature. If absent, the identifier
 is derived implicitly from the code being signed.
</code></pre></div></div>

<p>This begs the question: How is this information derived if there is no
explicit identifier? Tracing this constant through the code, we can see
it sets the <code class="language-plaintext highlighter-rouge">mIdentifier</code> field, which is otherwise only set through
this logic:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">identifier</span> <span class="o">=</span> <span class="n">rep</span><span class="o">-&gt;</span><span class="n">recommendedIdentifier</span><span class="p">(</span><span class="o">*</span><span class="n">this</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">identifier</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="sc">'.'</span><span class="p">)</span> <span class="o">==</span> <span class="n">string</span><span class="o">::</span><span class="n">npos</span><span class="p">)</span>
  <span class="n">identifier</span> <span class="o">=</span> <span class="n">state</span><span class="p">.</span><span class="n">mIdentifierPrefix</span> <span class="o">+</span> <span class="n">identifier</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">identifier</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="sc">'.'</span><span class="p">)</span> <span class="o">==</span> <span class="n">string</span><span class="o">::</span><span class="n">npos</span> <span class="o">&amp;&amp;</span> <span class="n">isAdhoc</span><span class="p">())</span>
  <span class="n">identifier</span> <span class="o">=</span> <span class="n">identifier</span> <span class="o">+</span> <span class="s">"-"</span> <span class="o">+</span> <span class="n">uniqueName</span><span class="p">();</span>
</code></pre></div></div>

<p>The prefix referenced here is from the <code class="language-plaintext highlighter-rouge">--prefix</code> field mentioned in the
<a href="https://keith.github.io/xcode-man-pages/codesign.1.html#i,"><code class="language-plaintext highlighter-rouge">codesign(1)</code></a> man page. Since we're also not passing that we can
ignore this logic and assume the <code class="language-plaintext highlighter-rouge">uniqueName()</code> logic is key here (which
also explains the <code class="language-plaintext highlighter-rouge">-</code> we see after our filename). As we trace this logic
through the codebase, we begin to see the core issue:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//</span>
<span class="c1">// Generate a unique string from our underlying DiskRep.</span>
<span class="c1">// We could get 90%+ of the uniquing benefit by just generating</span>
<span class="c1">// a random string here. Instead, we pick the (hex string encoding of)</span>
<span class="c1">// the source rep's unique identifier blob. For universal binaries,</span>
<span class="c1">// this is the canonical local architecture, which is a bit arbitrary.</span>
<span class="c1">// This provides us with a consistent unique string for all architectures</span>
<span class="c1">// of a fat binary, *and* (unlike a random string) is reproducible</span>
<span class="c1">// for identical inputs, even upon resigning.</span>
<span class="c1">//</span>
<span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">SecCodeSigner</span><span class="o">::</span><span class="n">Signer</span><span class="o">::</span><span class="n">uniqueName</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span>
  <span class="n">CFRef</span><span class="o">&lt;</span><span class="n">CFDataRef</span><span class="o">&gt;</span> <span class="n">identification</span> <span class="o">=</span> <span class="n">rep</span><span class="o">-&gt;</span><span class="n">identification</span><span class="p">();</span>
  <span class="p">...</span>
<span class="p">}</span>

<span class="c1">//</span>
<span class="c1">// We choose the binary identifier for a Mach-O binary as follows:</span>
<span class="c1">//  - If the Mach-O headers have a UUID command, use the UUID.</span>
<span class="c1">//  - Otherwise, use the SHA-1 hash of the (entire) load commands.</span>
<span class="c1">//</span>
<span class="n">CFDataRef</span> <span class="n">MachORep</span><span class="o">::</span><span class="n">identification</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">MachO</span><span class="o">&gt;</span> <span class="n">macho</span><span class="p">(</span><span class="n">mainExecutableImage</span><span class="p">()</span><span class="o">-&gt;</span><span class="n">architecture</span><span class="p">());</span>
  <span class="k">return</span> <span class="n">identificationFor</span><span class="p">(</span><span class="n">macho</span><span class="p">.</span><span class="n">get</span><span class="p">());</span>
<span class="p">}</span>

<span class="n">CFDataRef</span> <span class="n">MachORep</span><span class="o">::</span><span class="n">identificationFor</span><span class="p">(</span><span class="n">MachO</span> <span class="o">*</span><span class="n">macho</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// if there is a LC_UUID load command, use the UUID contained therein</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">const</span> <span class="n">load_command</span> <span class="o">*</span><span class="n">cmd</span> <span class="o">=</span> <span class="n">macho</span><span class="o">-&gt;</span><span class="n">findCommand</span><span class="p">(</span><span class="n">LC_UUID</span><span class="p">))</span> <span class="p">{</span>
    <span class="k">const</span> <span class="n">uuid_command</span> <span class="o">*</span><span class="n">uuidc</span> <span class="o">=</span> <span class="n">reinterpret_cast</span><span class="o">&lt;</span><span class="k">const</span> <span class="n">uuid_command</span> <span class="o">*&gt;</span><span class="p">(</span><span class="n">cmd</span><span class="p">);</span>
    <span class="c1">// uuidc-&gt;cmdsize should be sizeof(uuid_command), so if it is not,</span>
    <span class="c1">// something is wrong. Fail out.</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">macho</span><span class="o">-&gt;</span><span class="n">flip</span><span class="p">(</span><span class="n">uuidc</span><span class="o">-&gt;</span><span class="n">cmdsize</span><span class="p">)</span> <span class="o">!=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uuid_command</span><span class="p">))</span>
      <span class="n">MacOSError</span><span class="o">::</span><span class="n">throwMe</span><span class="p">(</span><span class="n">errSecCSSignatureInvalid</span><span class="p">);</span>
    <span class="kt">char</span> <span class="n">result</span><span class="p">[</span><span class="mi">4</span> <span class="o">+</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uuidc</span><span class="o">-&gt;</span><span class="n">uuid</span><span class="p">)];</span>
    <span class="n">memcpy</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="s">"UUID"</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
    <span class="n">memcpy</span><span class="p">(</span><span class="n">result</span><span class="o">+</span><span class="mi">4</span><span class="p">,</span> <span class="n">uuidc</span><span class="o">-&gt;</span><span class="n">uuid</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uuidc</span><span class="o">-&gt;</span><span class="n">uuid</span><span class="p">));</span>
    <span class="k">return</span> <span class="n">makeCFData</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">result</span><span class="p">));</span>
  <span class="p">}</span>

  <span class="c1">// otherwise, use the SHA-1 hash of the entire load command area (this is way, way obsolete)</span>
  <span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The gist of this logic is to fetch the UUID embedded in every binary
and use that to derive the identifier. The reason this isn't
reproducible across architectures is because the UUID is based on the
content of each binary, which differs across architectures. You can
print these UUIDs with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ dwarfdump -u main
UUID: 5413BC88-D3B1-3DFA-855A-7F47CF229020 (x86_64) main
UUID: E4472179-D9EC-3BFF-92FF-3F8E6D013184 (arm64) main
</code></pre></div></div>

<p>The fallback logic we see above relies on the contents of binary's load
commands which unfortunately also differs based on architecture.</p>

<h2 id="summary">Summary</h2>

<p>While this was a very informative deep dive into this logic, if you rely
on reproducible binaries and want to support Apple Silicon machines, you
need to do 2 things for binaries without <code class="language-plaintext highlighter-rouge">Info.plist</code> files:</p>

<ol>
  <li>Don't allow the linker to automatically sign your binaries by passing
<code class="language-plaintext highlighter-rouge">-no_adhoc_codesign</code></li>
  <li>Pass an explicit identifier when linking binaries with <code class="language-plaintext highlighter-rouge">--identifier</code>
to the <code class="language-plaintext highlighter-rouge">codesign</code> invocation</li>
</ol>

<p>I filed a radar about this behavior: FB9681559 (Make codesign for fat
binaries reproducible across architectures).</p>

<h2 id="updates">Updates</h2>

<ul>
  <li>After working around this codesign issue it was noted that the same
issue with random binary names also affects the UUID computation. To
workaround this you can pass <code class="language-plaintext highlighter-rouge">-Wl,-no_uuid</code> but note this has a
negative impact on LLDB attach times for binaries if you need to debug
them.</li>
  <li>I landed a <a href="https://reviews.llvm.org/rG17386cb4dc89afad62623b9bc08516b99b9c6df7">fix in
clang</a>
to make these temporary binary names reproducible. It will hopefully
be part of a future version of Apple's clang fork.</li>
</ul>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>This behavior can be disabled by passing <code class="language-plaintext highlighter-rouge">-no_adhoc_codesign</code> to
  link invocations. With <code class="language-plaintext highlighter-rouge">OTHER_LDFLAGS</code> in Xcode you need to pass
  <code class="language-plaintext highlighter-rouge">-Wl,-no_adhoc_codesign</code> since link invocations go through
  <code class="language-plaintext highlighter-rouge">clang</code>, not directly to <code class="language-plaintext highlighter-rouge">ld</code> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>As long as you're using the same version of <code class="language-plaintext highlighter-rouge">clang</code>. This example
  was built with Xcode 13.0 13A233 <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Luckily if you run this on an Intel machine, instead of through
  <code class="language-plaintext highlighter-rouge">arch</code>'s Intel emulation, you'll get the same results <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:3:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>I was looking at 59754.140.13 <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Switching Xcode versions without a password]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2021/08/12/xcode-select-sudoers/" />
      <id>https://smileykeith.com/2021/08/12/xcode-select-sudoers</id>
      <updated>2021-08-12T13:40:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>When switching between multiple Xcode versions one way to globally
update the version you want to use is by running
<a href="https://keith.github.io/xcode-man-pages/xcode-select.1.html"><code class="language-plaintext highlighter-rouge">xcode-select</code></a> like this:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>xcode-select <span class="nt">-s</span> /Applications/Xcode-12.5.1.app
</code></pre></div></div>

<p>Then, if you want to automatically accept Xcode's license, and install
any extra packages it requires (which should only be required the for
the first time you run a new version), you can run:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>xcodebuild <span class="nt">-runFirstLaunch</span>
</code></pre></div></div>

<p>This works fine locally, but when updating remote CI machines, entering
the password can be troublesome. Furthermore if you want to support
having CI machines automatically switch between Xcode versions when
testing upcoming changes, you may not have the opportunity to be
prompted at all. Lucky for us, the <a href="https://keith.github.io/xcode-man-pages/sudoers.5.html"><code class="language-plaintext highlighter-rouge">sudoers</code></a> file format,
which configures the <code class="language-plaintext highlighter-rouge">sudo</code> command, allows us to skip password entry
for specific commands with a bit of configuration.</p>

<p>The easiest way to edit this configuration is by running:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>visudo
</code></pre></div></div>

<p>This opens the default <code class="language-plaintext highlighter-rouge">/etc/sudoers</code> configuration file in vim. While
we could add our custom configuration here, we can also see the default
configuration that ships with macOS contains this line:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#includedir /private/etc/sudoers.d
</code></pre></div></div>

<p>This tells <code class="language-plaintext highlighter-rouge">sudo</code> to load all the files in <code class="language-plaintext highlighter-rouge">/etc/sudoers.d</code><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> as
configuration as well. Using this knowledge we can nicely separate our
custom configuration, making it easier to overwrite, or remove, in the
future. Separating our custom configuration also makes us less likely to
break the default configuration, potentially leading to major issues.</p>

<p>To setup our custom configuration we can run this command:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"%admin ALL=NOPASSWD: /usr/bin/xcode-select,/usr/bin/xcodebuild -runFirstLaunch"</span> | <span class="nb">sudo tee</span> /etc/sudoers.d/xcode
</code></pre></div></div>

<p>Let's break this down<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. The <code class="language-plaintext highlighter-rouge">%admin</code> component makes this
configuration apply to all users that are in the <code class="language-plaintext highlighter-rouge">admin</code> group<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>.
Using this group is probably good enough for this use case, but if you'd
like to restrict this more, you can change this to a your account's
specific username such as <code class="language-plaintext highlighter-rouge">ksmiley</code>.</p>

<p>The second component <code class="language-plaintext highlighter-rouge">ALL</code> makes this rule apply to all hosts, I'm not
sure in what context any host besides the current one would take these
rules into account, but <code class="language-plaintext highlighter-rouge">ALL</code> ignores that.</p>

<p>The third component <code class="language-plaintext highlighter-rouge">NOPASSWD</code> is the key piece of this functionality.
This enables us to run the following commands without being prompted for
our password.</p>

<p>The last component is the commands we want to allow to be run without a
password. There are 2 things to note here.</p>

<ol>
  <li>We specify just the <code class="language-plaintext highlighter-rouge">xcode-select</code> binary, using the absolute path.
This allows all subcommands handled by <code class="language-plaintext highlighter-rouge">xcode-select</code> to be run
without a password.</li>
  <li>The <code class="language-plaintext highlighter-rouge">xcodebuild</code> command also contains the one subcommand we want to
be able to run without a password. Limiting this is important because
otherwise you could run <code class="language-plaintext highlighter-rouge">sudo xcodebuild build</code> without a password,
which could execute malicious run scripts or do other terrible
things. With this argument specified any other invocation of <code class="language-plaintext highlighter-rouge">sudo
xcodebuild</code> will still require a password.</li>
</ol>

<p>Just like that we no longer have to enter our password when swapping
between Xcode versions.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p><code class="language-plaintext highlighter-rouge">/etc</code> is actually a symlink to <code class="language-plaintext highlighter-rouge">/private/etc</code>, which is why we
  can use them interchangeably in this case <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>For many more format examples checkout the <a href="https://keith.github.io/xcode-man-pages/sudoers.5.html">man page</a> <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>You can see what groups the current user is in by running
  <code class="language-plaintext highlighter-rouge">groups</code>, but for macOS all administrator accounts are part of
  this group. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Locking Xcode versions in bazel]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2021/03/08/locking-xcode-in-bazel/" />
      <id>https://smileykeith.com/2021/03/08/locking-xcode-in-bazel</id>
      <updated>2021-03-08T08:40:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>When using <a href="https://www.bazel.build">bazel</a> on a team, one of the
things you quickly want to do is stand up a <a href="https://docs.bazel.build/versions/master/remote-caching.html">remote
cache</a>.
This allows bazel to download build artifacts instead of spending CPU
cycles reproducing things that have already been built by someone else.</p>

<p>In order for bazel to guarantee that downloading the artifacts instead
of building them will produce the same results, it must ensure that all
the inputs of your build are the same as a previous build.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> For macOS
and iOS builds bazel's inputs include the version of Xcode you're using.
This means if developers on your team use different versions of Xcode,
they cannot share the same build cache.</p>

<p>Bazel discovers your currently installed Xcode versions by running
<a href="https://github.com/bazelbuild/bazel/blob/cd6724deb7dad05c477425fb09eff613659a5b3f/tools/osx/xcode_locator.m"><code class="language-plaintext highlighter-rouge">xcode_locator</code></a>,
and then
<a href="https://github.com/bazelbuild/bazel/blob/cd6724deb7dad05c477425fb09eff613659a5b3f/tools/osx/xcode_configure.bzl">generating</a>
a <code class="language-plaintext highlighter-rouge">BUILD</code> file that contains an entry for every version you currently
have installed. The result looks something like this:<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup></p>

<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">load</span><span class="p">(</span><span class="s">"@apple_support//xcode:xcode_config.bzl"</span><span class="p">,</span> <span class="s">"xcode_config"</span><span class="p">)</span>
<span class="n">load</span><span class="p">(</span><span class="s">"@apple_support//xcode:xcode_version.bzl"</span><span class="p">,</span> <span class="s">"xcode_version"</span><span class="p">)</span>

<span class="n">xcode_version</span><span class="p">(</span>
    <span class="n">name</span> <span class="o">=</span> <span class="s">"version12_4_0_12D4e"</span><span class="p">,</span>
    <span class="n">version</span> <span class="o">=</span> <span class="s">"12.4.0.12D4e"</span><span class="p">,</span>
    <span class="n">aliases</span> <span class="o">=</span> <span class="p">[</span><span class="s">"12.4.0"</span><span class="p">,</span> <span class="s">"12.4"</span><span class="p">,</span> <span class="s">"12.4.0.12D4e"</span><span class="p">],</span>
    <span class="n">default_ios_sdk_version</span> <span class="o">=</span> <span class="s">"14.4"</span><span class="p">,</span>
    <span class="n">default_tvos_sdk_version</span> <span class="o">=</span> <span class="s">"14.3"</span><span class="p">,</span>
    <span class="n">default_macos_sdk_version</span> <span class="o">=</span> <span class="s">"11.1"</span><span class="p">,</span>
    <span class="n">default_watchos_sdk_version</span> <span class="o">=</span> <span class="s">"7.2"</span><span class="p">,</span>
<span class="p">)</span>

<span class="n">xcode_version</span><span class="p">(</span>
    <span class="n">name</span> <span class="o">=</span> <span class="s">"version12_2_0_12B45b"</span><span class="p">,</span>
    <span class="n">version</span> <span class="o">=</span> <span class="s">"12.2.0.12B45b"</span><span class="p">,</span>
    <span class="n">aliases</span> <span class="o">=</span> <span class="p">[</span><span class="s">"12.2.0"</span><span class="p">,</span> <span class="s">"12"</span><span class="p">,</span> <span class="s">"12.2"</span><span class="p">,</span> <span class="s">"12.2.0.12B45b"</span><span class="p">],</span>
    <span class="n">default_ios_sdk_version</span> <span class="o">=</span> <span class="s">"14.2"</span><span class="p">,</span>
    <span class="n">default_tvos_sdk_version</span> <span class="o">=</span> <span class="s">"14.2"</span><span class="p">,</span>
    <span class="n">default_macos_sdk_version</span> <span class="o">=</span> <span class="s">"11.0"</span><span class="p">,</span>
    <span class="n">default_watchos_sdk_version</span> <span class="o">=</span> <span class="s">"7.1"</span><span class="p">,</span>
<span class="p">)</span>

<span class="n">xcode_config</span><span class="p">(</span>
    <span class="n">name</span> <span class="o">=</span> <span class="s">"host_xcodes"</span><span class="p">,</span>
    <span class="n">versions</span> <span class="o">=</span> <span class="p">[</span><span class="s">":version12_4_0_12D4e"</span><span class="p">,</span> <span class="s">":version12_2_0_12B45b"</span><span class="p">],</span>
    <span class="n">default</span> <span class="o">=</span> <span class="s">":version12_4_0_12D4e"</span><span class="p">,</span>
<span class="p">)</span>
</code></pre></div></div>

<p>To fetch the contents of this file on your machine you can run:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat </span>bazel-<span class="si">$(</span><span class="nb">basename</span> <span class="nv">$PWD</span><span class="si">)</span>/external/local_config_xcode/BUILD
</code></pre></div></div>

<p>In order to enforce developers use the same version, you can short
circuit bazel's Xcode discovery and instead reference a local
<a href="https://docs.bazel.build/versions/master/build-ref.html#targets">target</a>
that you provide.<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup></p>

<p>To do this, you can setup your target in the <code class="language-plaintext highlighter-rouge">BUILD</code> file at the root of
your project (or somewhere else if you'd prefer). Using the contents
from the example above, but only including the Xcode versions you want
to support, it will contain:</p>

<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">load</span><span class="p">(</span><span class="s">"@apple_support//xcode:xcode_config.bzl"</span><span class="p">,</span> <span class="s">"xcode_config"</span><span class="p">)</span>
<span class="n">load</span><span class="p">(</span><span class="s">"@apple_support//xcode:xcode_version.bzl"</span><span class="p">,</span> <span class="s">"xcode_version"</span><span class="p">)</span>

<span class="n">xcode_version</span><span class="p">(</span>
    <span class="n">name</span> <span class="o">=</span> <span class="s">"version12_4_0_12D4e"</span><span class="p">,</span>
    <span class="n">version</span> <span class="o">=</span> <span class="s">"12.4.0.12D4e"</span><span class="p">,</span>
    <span class="n">aliases</span> <span class="o">=</span> <span class="p">[</span><span class="s">"12.4.0"</span><span class="p">,</span> <span class="s">"12.4"</span><span class="p">,</span> <span class="s">"12.4.0.12D4e"</span><span class="p">],</span>
    <span class="n">default_ios_sdk_version</span> <span class="o">=</span> <span class="s">"14.4"</span><span class="p">,</span>
    <span class="n">default_tvos_sdk_version</span> <span class="o">=</span> <span class="s">"14.3"</span><span class="p">,</span>
    <span class="n">default_macos_sdk_version</span> <span class="o">=</span> <span class="s">"11.1"</span><span class="p">,</span>
    <span class="n">default_watchos_sdk_version</span> <span class="o">=</span> <span class="s">"7.2"</span><span class="p">,</span>
<span class="p">)</span>

<span class="n">xcode_config</span><span class="p">(</span>
    <span class="n">name</span> <span class="o">=</span> <span class="s">"host_xcodes"</span><span class="p">,</span>
    <span class="n">versions</span> <span class="o">=</span> <span class="p">[</span><span class="s">":version12_4_0_12D4e"</span><span class="p">],</span>
    <span class="n">default</span> <span class="o">=</span> <span class="s">":version12_4_0_12D4e"</span><span class="p">,</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Then you can add this to your
<a href="https://docs.bazel.build/versions/master/guide.html#bazelrc-the-bazel-configuration-file"><code class="language-plaintext highlighter-rouge">.bazelrc</code></a>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>build --xcode_version_config=//:host_xcodes
</code></pre></div></div>

<p>This way if a developer tries to build with a version of Xcode that is
not explicitly supported, they see this error:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ERROR: /.../BUILD.bazel:31:11: Compiling something failed: I/O exception during sandboxed execution: Running '/.../xcode-locator 12.4.0.12D4e' failed with code 1.
This most likely indicates that xcode version 12.4.0.12D4e is not available on the host machine.
</code></pre></div></div>

<p>This is a simple solution <em>if</em> you only want to support a single version
of Xcode at once. Often it's useful to support multiple Xcode versions
for testing, even if not all versions will get cache hits. In this
case, one solution is to include multiple versions in your <code class="language-plaintext highlighter-rouge">BUILD</code>
file:</p>

<div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">load</span><span class="p">(</span><span class="s">"@apple_support//xcode:xcode_config.bzl"</span><span class="p">,</span> <span class="s">"xcode_config"</span><span class="p">)</span>
<span class="n">load</span><span class="p">(</span><span class="s">"@apple_support//xcode:xcode_version.bzl"</span><span class="p">,</span> <span class="s">"xcode_version"</span><span class="p">)</span>

<span class="n">xcode_version</span><span class="p">(</span>
    <span class="n">name</span> <span class="o">=</span> <span class="s">"version12_4_0_12D4e"</span><span class="p">,</span>
    <span class="n">version</span> <span class="o">=</span> <span class="s">"12.4.0.12D4e"</span><span class="p">,</span>
    <span class="n">aliases</span> <span class="o">=</span> <span class="p">[</span><span class="s">"12D4e"</span><span class="p">],</span>
    <span class="n">default_ios_sdk_version</span> <span class="o">=</span> <span class="s">"14.4"</span><span class="p">,</span>
    <span class="n">default_tvos_sdk_version</span> <span class="o">=</span> <span class="s">"14.3"</span><span class="p">,</span>
    <span class="n">default_macos_sdk_version</span> <span class="o">=</span> <span class="s">"11.1"</span><span class="p">,</span>
    <span class="n">default_watchos_sdk_version</span> <span class="o">=</span> <span class="s">"7.2"</span><span class="p">,</span>
<span class="p">)</span>

<span class="n">xcode_version</span><span class="p">(</span>
    <span class="n">name</span> <span class="o">=</span> <span class="s">"version12_2_0_12B45b"</span><span class="p">,</span>
    <span class="n">version</span> <span class="o">=</span> <span class="s">"12.2.0.12B45b"</span><span class="p">,</span>
    <span class="n">aliases</span> <span class="o">=</span> <span class="p">[</span><span class="s">"12B45b"</span><span class="p">],</span>
    <span class="n">default_ios_sdk_version</span> <span class="o">=</span> <span class="s">"14.2"</span><span class="p">,</span>
    <span class="n">default_tvos_sdk_version</span> <span class="o">=</span> <span class="s">"14.2"</span><span class="p">,</span>
    <span class="n">default_macos_sdk_version</span> <span class="o">=</span> <span class="s">"11.0"</span><span class="p">,</span>
    <span class="n">default_watchos_sdk_version</span> <span class="o">=</span> <span class="s">"7.1"</span><span class="p">,</span>
<span class="p">)</span>

<span class="n">xcode_config</span><span class="p">(</span>
    <span class="n">name</span> <span class="o">=</span> <span class="s">"host_xcodes"</span><span class="p">,</span>
    <span class="n">versions</span> <span class="o">=</span> <span class="p">[</span><span class="s">":version12_4_0_12D4e"</span><span class="p">,</span> <span class="s">":version12_2_0_12B45b"</span><span class="p">],</span>
    <span class="n">default</span> <span class="o">=</span> <span class="s">":version12_4_0_12D4e"</span><span class="p">,</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Which is very similar to our first example, except we replaced the
<code class="language-plaintext highlighter-rouge">aliases</code> with the build numbers from each version. This is a perfect
unique value to differentiate between multiple versions during the Xcode
beta cycle. Now we can explicitly pass the
<a href="https://docs.bazel.build/versions/4.0.0/command-line-reference.html#flag--xcode_version"><code class="language-plaintext highlighter-rouge">xcode_version</code></a>
argument with the build number you want to use. You can retrieve the
build number from your current Xcode version like this:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% xcodebuild <span class="nt">-version</span> | <span class="nb">tail</span> <span class="nt">-1</span> | <span class="nb">cut</span> <span class="nt">-d</span> <span class="s2">" "</span> <span class="nt">-f3</span>
12D4e
</code></pre></div></div>

<p>Using this method you can support multiple versions of Xcode, while
still respecting the user's <code class="language-plaintext highlighter-rouge">xcode-select</code> value (or <code class="language-plaintext highlighter-rouge">DEVELOPER_DIR</code>
environment variable).</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Debugging remote cache misses is something you'll need to get used
  to doing. <a href="https://docs.bazel.build/versions/master/remote-caching-debug.html">This documentation</a>
  is very helpful. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>This example doesn't include the remote execution configuration,
  but it works similarity <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>This also improves repository setup time. Otherwise it increases
  with the number of Xcode versions you have installed. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Supporting relative paths: XCTest failures in Xcode]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2021/03/04/supporting-relative-paths/" />
      <id>https://smileykeith.com/2021/03/04/supporting-relative-paths</id>
      <updated>2021-03-04T20:48:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>If you build your iOS app with an alternate build system such as
<a href="https://www.bazel.build">bazel</a>, it's likely that you use relative
paths, instead of absolute paths, for compilation.</p>

<p>Specifically, when building swift code, Xcode calls the compiler with
something like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>swiftc [ARGS] /path/to/srcroot/path/to/file1.swift /path/to/srcroot/path/to/file2.swift
</code></pre></div></div>

<p>Where bazel will call the compiler with something like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>swiftc [ARGS] path/to/file1.swift path/to/file2.swift
</code></pre></div></div>

<p>Normally, this difference is inconsequential, both compilations will
result in a similar enough output. So, the question is: Why would you
pick one over the other? For bazel, the answer lies in its core feature
of "hermeticity". In bazel's case, being hermetic means that given the
same inputs you always produce the same outputs. This means that
regardless of what machine you're building on, or what directory your
source is cloned in, the results should be the same. Because, those
details aren't considered important inputs in the build.</p>

<p>Unfortunately, in a few places, Xcode relies on paths being absolute.
Today, we'll look at how Xcode reports test failures in the UI.
Specifically the underlying
<a href="https://developer.apple.com/documentation/xctest/xctissue"><code class="language-plaintext highlighter-rouge">XCTIssue</code></a>
that <code class="language-plaintext highlighter-rouge">XCTest</code> creates is expected to be instantiated with an absolute
path. This absolute path is populated from the <code class="language-plaintext highlighter-rouge">#filePath</code> (previously
<code class="language-plaintext highlighter-rouge">#file</code>) keyword which is <em>supposed</em> to reference the absolute path of
the current source file.</p>

<p>The first question is: How does the Swift compiler know what the
absolute path of the current file is? It's easy when Xcode passes an
absolute path to the compiler. But, what if you pass a relative path? In
this case, the Swift compiler uses the directory passed with the
<code class="language-plaintext highlighter-rouge">-working-directory</code> argument to make the path absolute. It turns out if
you don't pass this argument, the compiler has no choice but to use the
relative path. This means the <code class="language-plaintext highlighter-rouge">#filePath</code> keyword ends up translating to
a relative path, which means the <code class="language-plaintext highlighter-rouge">XCTIssue</code> is created with a relative
path.</p>

<p>With relative paths when you run your tests in Xcode and they
fail, clicking the failure in the issue navigator doesn't do anything.
But, it's supposed to jump you to the test case that failed (FB8451256,
FB8454623).</p>

<p>So, how do we fix this? Luckily, we know the core issue is <code class="language-plaintext highlighter-rouge">XCTIssue</code> is
created with a relative path. Since <code class="language-plaintext highlighter-rouge">XCTIssue</code> instances are created as
part of our process, we can
<a href="https://nshipster.com/method-swizzling">swizzle</a> it to fix this.</p>

<p>Looking at the underlying <a href="https://developer.apple.com/documentation/xctest/xctsourcecodelocation"><code class="language-plaintext highlighter-rouge">XCTSourceCodeLocation</code>
docs</a>,
we can see there are 2 initializers we're potentially interested in.
Setting some quick breakpoints we can see that <code class="language-plaintext highlighter-rouge">XCTIssue</code> goes through
the <code class="language-plaintext highlighter-rouge">init(fileURL:lineNumber:)</code> initializer. When we inspect the
argument it receives in the debugger, we can see it's the relative path
we passed to the compiler. Knowing this, we can surmise that by
swizzling the initializer, and make the argument an absolute path, we
can satisfy Xcode's requirement. So, what path do we use? Using Xcode's
scheme environment variables, we can pass Xcode's <code class="language-plaintext highlighter-rouge">SRCROOT</code> through an
environment variable named the same thing:</p>

<p><img src="/images/srcroot.png" alt="" /></p>

<p>Make sure to have the "Expand Variables Based On" dropdown set to some
target (FB8454879) or the <code class="language-plaintext highlighter-rouge">$(SRCROOT)</code> string will be passed through
literally.</p>

<p>Now, for the swizzling:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">Foundation</span>
<span class="kd">import</span> <span class="kt">XCTest</span>

<span class="c1">// NOTE: This path has to start with a / for fileURLWithPath to resolve it correctly as an absolute path</span>
<span class="kd">public</span> <span class="k">let</span> <span class="nv">kSourceRoot</span> <span class="o">=</span> <span class="kt">ProcessInfo</span><span class="o">.</span><span class="n">processInfo</span><span class="o">.</span><span class="n">environment</span><span class="p">[</span><span class="s">"SRCROOT"</span><span class="p">]</span><span class="o">!</span>

<span class="kd">private</span> <span class="kd">func</span> <span class="nf">remapFileURL</span><span class="p">(</span><span class="n">_</span> <span class="nv">fileURL</span><span class="p">:</span> <span class="kt">URL</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">URL</span> <span class="p">{</span>
    <span class="k">if</span> <span class="n">fileURL</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="nf">hasPrefix</span><span class="p">(</span><span class="n">kSourceRoot</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">fileURL</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="kt">URL</span><span class="p">(</span><span class="nv">fileURLWithPath</span><span class="p">:</span> <span class="s">"</span><span class="se">\(</span><span class="n">kSourceRoot</span><span class="se">)</span><span class="s">/</span><span class="se">\(</span><span class="n">fileURL</span><span class="o">.</span><span class="n">relativePath</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">private</span> <span class="kd">extension</span> <span class="kt">XCTSourceCodeLocation</span> <span class="p">{</span>
    <span class="kd">@objc</span>
    <span class="kd">convenience</span> <span class="nf">init</span><span class="p">(</span><span class="n">initWithRelativeFileURL</span> <span class="nv">relativeURL</span><span class="p">:</span> <span class="kt">URL</span><span class="p">,</span> <span class="nv">lineNumber</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// NOTE: This call is not recursive because of swizzling</span>
        <span class="k">self</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">initWithRelativeFileURL</span><span class="p">:</span> <span class="nf">remapFileURL</span><span class="p">(</span><span class="n">relativeURL</span><span class="p">),</span> <span class="nv">lineNumber</span><span class="p">:</span> <span class="n">lineNumber</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">swizzleXCTSourceCodeLocationIfNeeded</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// NOTE: Make sure our "Expand Variables Based On" is set correctly</span>
    <span class="k">if</span> <span class="n">kSourceRoot</span> <span class="o">==</span> <span class="s">"$(SRCROOT)"</span> <span class="p">{</span>
        <span class="nf">fatalError</span><span class="p">(</span><span class="s">"Got unsubstituted SRCROOT"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">let</span> <span class="nv">originalSelector</span> <span class="o">=</span> <span class="kd">#selector(</span><span class="nf">XCTSourceCodeLocation.init(fileURL:lineNumber:)</span><span class="kd">)</span>
    <span class="k">let</span> <span class="nv">swizzledSelector</span> <span class="o">=</span> <span class="kd">#selector(</span><span class="nf">XCTSourceCodeLocation.init(initWithRelativeFileURL:lineNumber:)</span><span class="kd">)</span>

    <span class="k">guard</span> <span class="k">let</span> <span class="nv">originalMethod</span> <span class="o">=</span> <span class="nf">class_getInstanceMethod</span><span class="p">(</span><span class="kt">XCTSourceCodeLocation</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="n">originalSelector</span><span class="p">),</span>
        <span class="k">let</span> <span class="nv">swizzledMethod</span> <span class="o">=</span> <span class="nf">class_getInstanceMethod</span><span class="p">(</span><span class="kt">XCTSourceCodeLocation</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="n">swizzledSelector</span><span class="p">)</span> <span class="k">else</span>
    <span class="p">{</span>
        <span class="nf">fatalError</span><span class="p">(</span><span class="s">"Failed to swizzle XCTSourceCodeLocation"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="nf">method_exchangeImplementations</span><span class="p">(</span><span class="n">originalMethod</span><span class="p">,</span> <span class="n">swizzledMethod</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With this implementation, you need to call
<code class="language-plaintext highlighter-rouge">swizzleXCTSourceCodeLocationIfNeeded()</code> somewhere. Using the
<a href="https://developer.apple.com/documentation/bundleresources/information_property_list/nsprincipalclass"><code class="language-plaintext highlighter-rouge">NSPrincipalClass</code> plist
key</a>,
we can define a class that is initialized as soon as your test bundle
starts to run. This key must be set in your test bundle's plist, not any
host apps you are using for the test bundle. For bazel this means on the
<code class="language-plaintext highlighter-rouge">infoplists</code> key of your <code class="language-plaintext highlighter-rouge">ios_unit_test</code> rule. We can define a small
class to call our swizzling code:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">ObjectiveC</span>

<span class="kd">final</span> <span class="kd">class</span> <span class="kt">UnitTestMain</span><span class="p">:</span> <span class="kt">NSObject</span> <span class="p">{</span>
    <span class="k">override</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>

        <span class="nf">swizzleXCTSourceCodeLocationIfNeeded</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Disclaimer: this relies on a lot of implementation details in Xcode
which might break in the future. You should avoid this if possible.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Editing rpaths for _InternalSwiftSyntaxParser]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2021/03/03/editing-rpaths/" />
      <id>https://smileykeith.com/2021/03/03/editing-rpaths</id>
      <updated>2021-03-03T19:48:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>One of the issues with shipping a tool that depends on
<a href="https://github.com/apple/swift-syntax">SwiftSyntax</a> is that it depends
on a dynamic library that is provided with Xcode called
<code class="language-plaintext highlighter-rouge">_InternalSwiftSyntaxParser</code>. This library provides some of Swift's
logic for how to parse Swift code. When you run a command line tool that
was built with a different version of Xcode than what you have installed
locally, you hit this issue:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;unknown&gt;:0:0: error: The loaded '_InternalSwiftSyntaxParser' library is from a toolchain that is not compatible with this version of SwiftSyntax
</code></pre></div></div>

<p>Ideally, this library would be statically linked to your executable (and
I'm hoping we can find <a href="https://github.com/apple/swift/pull/36151">a
solution</a> to this) so you
would no longer have to worry about this. In the meantime, we can work
around this issue by shipping the version of the library from Xcode
alongside your executable, and loading that instead. This will increase
your distribution archive's size, but make it easier to support multiple
versions of Xcode at once.</p>

<p>The key to this workaround relies on how
<a href="https://keith.github.io/xcode-man-pages/dyld.1.html"><code class="language-plaintext highlighter-rouge">dyld</code></a> works.
<code class="language-plaintext highlighter-rouge">dyld</code> is responsible for loading the dynamic libraries your binary
depends on. First, it's useful for you to see what libraries you depend
on with
<a href="https://keith.github.io/xcode-man-pages/llvm-otool.1.html"><code class="language-plaintext highlighter-rouge">otool</code></a>.
For example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% otool -L ./.build/debug/drstring-cli
./.build/debug/drstring-cli:
  ...
  /usr/lib/swift/libswiftObjectiveC.dylib (compatibility version 1.0.0, current version 1.0.0, weak)
  /usr/lib/swift/libswiftXPC.dylib (compatibility version 1.0.0, current version 1.1.0, weak)
  @rpath/lib_InternalSwiftSyntaxParser.dylib (compatibility version 1.0.0, current version 17013.0.0)
</code></pre></div></div>

<p>Here you can see many libraries are directly referenced with their
absolute paths while <code class="language-plaintext highlighter-rouge">lib_InternalSwiftSyntaxParser.dylib</code>, the library
we're specifically interested in, is referenced via a
<a href="https://en.wikipedia.org/wiki/Rpath"><code class="language-plaintext highlighter-rouge">rpath</code></a>. You can run this command
to see your binary's <code class="language-plaintext highlighter-rouge">rpaths</code> (yours may differ depending on your
absolute path to Xcode):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% otool -l ./.build/debug/drstring-cli \
  | grep -A2 LC_RPATH \
  | grep "^\s*path" | cut -d " " -f 11
@loader_path
/Applications/Xcode-12.4.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx
</code></pre></div></div>

<p>Here we can see that <code class="language-plaintext highlighter-rouge">dyld</code> is instructed to look in 2 directories to
find <code class="language-plaintext highlighter-rouge">lib_InternalSwiftSyntaxParser.dylib</code>. First, it looks in the
directory specified by <code class="language-plaintext highlighter-rouge">@loader_path</code>, which in our case is likely
irrelevant since it is the directory that contains our executable. Then,
it looks inside a directory within my absolute path to Xcode (which
isn't very portable), which we can see this includes the library we
expect (you'll have to change this path to your local Xcode path):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% ls /Applications/Xcode-12.4.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx
layouts-x86_64.yaml                  libswiftCompatibilityDynamicReplacements.a
lib_InternalSwiftSyntaxParser.dylib  libswiftRemoteMirror42.dylib
libswiftCompatibility50.a            libswiftRemoteMirrorLegacy.dylib
libswiftCompatibility51.a            prebuilt-modules
</code></pre></div></div>

<p>Given this information, our goal is to replace the default locations
<code class="language-plaintext highlighter-rouge">dyld</code> searches, and replace those with the directory we want. There are
a few ways we can do this, but first we need to decide what directory we
will ship the library in. Typically, the directory structure for a
command line tool that includes a dynamic library looks something
like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;prefix&gt;
├── bin
│   └── drstring-cli
└── lib
    └── lib_InternalSwiftSyntaxParser.dylib
</code></pre></div></div>

<p>We can use this for our example. First, we need to copy the library
from Xcode using (this might change with future Xcode releases):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cp "$(xcode-select -p)"/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/lib_InternalSwiftSyntaxParser.dylib .
</code></pre></div></div>

<p>Then, using
<a href="https://keith.github.io/xcode-man-pages/install_name_tool.1.html"><code class="language-plaintext highlighter-rouge">install_name_tool</code></a>,
we can edit the <code class="language-plaintext highlighter-rouge">rpaths</code> in our binary. In this case, since we only have
2 <code class="language-plaintext highlighter-rouge">rpaths</code>, and neither of them are what we want, lets delete them both
(you'll have to change the Xcode path for your local installation):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% install_name_tool \
  -delete_rpath @loader_path \
  -delete_rpath /Applications/Xcode-12.4.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \
  bin/drstring-cli
</code></pre></div></div>

<p>Now, when we run our binary, we see it crashes immediately because it
cannot find  the libraries it needs:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% bin/drstring-cli
dyld: Library not loaded: @rpath/lib_InternalSwiftSyntaxParser.dylib
  Referenced from: /Users/ksmiley/dev/DrString/bin/drstring-cli
  Reason: image not found
</code></pre></div></div>

<p>At this point we have 2 options. We can either launch our binary with
some special environment variables that <code class="language-plaintext highlighter-rouge">dyld</code> reads, or encode the
<code class="language-plaintext highlighter-rouge">rpath</code> we want into the binary. Since adding the <code class="language-plaintext highlighter-rouge">rpath</code> to the binary
is destructive, lets try the environment variable approach first as an
example. Using <code class="language-plaintext highlighter-rouge">DYLD_LIBRARY_PATH</code> we can instruct <code class="language-plaintext highlighter-rouge">dyld</code> to discover
the libraries we want:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% DYLD_LIBRARY_PATH=lib bin/drstring-cli
OVERVIEW: A Swift docstring linter, formatter, nitpicky assistant...
...
</code></pre></div></div>

<p>There is also <code class="language-plaintext highlighter-rouge">DYLD_FALLBACK_LIBRARY_PATH</code>, which unlike
<code class="language-plaintext highlighter-rouge">DYLD_LIBRARY_PATH</code>, has a default of <code class="language-plaintext highlighter-rouge">/usr/local/lib:/usr/lib</code>. This
means if your library doesn't exist in the binary's <code class="language-plaintext highlighter-rouge">rpaths</code>, but then
happens to be in <code class="language-plaintext highlighter-rouge">/usr/local/lib</code>, it will still run as expected. This
is useful to know, because <a href="https://brew.sh/">homebrew</a> installs
libraries to <code class="language-plaintext highlighter-rouge">/usr/local/lib</code> on Intel based Macs. This can be
surprising if you install an unrelated tool that depends on the same
library and then your binary discovers this unrelated installation when
you don't want it to. If you want to disable this fallback, you can set
the value to <code class="language-plaintext highlighter-rouge">/dev/null</code>. In our example, using
<code class="language-plaintext highlighter-rouge">DYLD_FALLBACK_LIBRARY_PATH</code> results in same behavior:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% DYLD_FALLBACK_LIBRARY_PATH=lib bin/drstring-cli
OVERVIEW: A Swift docstring linter, formatter, nitpicky assistant...
...
</code></pre></div></div>

<p>Instead of setting environment variables every time we run the binary,
we can edit our binary to instruct <code class="language-plaintext highlighter-rouge">dyld</code> to search the correct
directory. Again, we use <code class="language-plaintext highlighter-rouge">install_name_tool</code> for this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% install_name_tool -add_rpath @executable_path/../lib bin/drstring-cli
</code></pre></div></div>

<p>In this case we rely on a relative path, based on the executable's
current path, to find our library. Now, as long as we ship the library
alongside our binary, we can run it without setting any environment
variables.</p>

<p>To check that this worked as expected you can launch your executable
with <code class="language-plaintext highlighter-rouge">DYLD_PRINT_LIBRARIES</code> set and <code class="language-plaintext highlighter-rouge">grep</code> for the library:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% DYLD_PRINT_LIBRARIES=yesplz bin/drstring-cli 2&gt;&amp;1 | grep _Internal
dyld: loaded: &lt;A1954DF6-6F32-3A7C-A50E-0B7942D95F99&gt; /Users/ksmiley/dev/DrString/bin/../lib/lib_InternalSwiftSyntaxParser.dylib
</code></pre></div></div>

<p>To use this method with Swift Package Manager you'll have to run a
post-processing script that alters your <code class="language-plaintext highlighter-rouge">rpaths</code> using what we've
learned.</p>

<p>Overall this is more work than if we could produce a statically linked
binary, but it's better than having to force your users on to a specific
version of Xcode.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Cross compiling for Apple Silicon with Swift Package Manager]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2020/12/24/swiftpm-cross-compile/" />
      <id>https://smileykeith.com/2020/12/24/swiftpm-cross-compile</id>
      <updated>2020-12-24T14:32:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>If you distribute binaries for command line tools built with <a href="https://swift.org/package-manager">Swift
Package Manager</a>, you might have
previously built your distribution binary with:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% swift build <span class="nt">--configuration</span> release
</code></pre></div></div>

<p>If you inspect the binary, you can see it was built for the current
machine's architecture by default:</p>

<pre><code class="language-none">% file .build/release/package
.build/release/package: Mach-O 64-bit executable x86_64
</code></pre>

<p>Previously, this was sufficient since macOS only supported one
architecture. Now, in order to fully utilize the native performance of
Apple Silicon chips, we need to produce a <a href="https://en.wikipedia.org/wiki/Fat_binary">fat
binary</a> that contains a slice
for both <code class="language-plaintext highlighter-rouge">x86_64</code> and <code class="language-plaintext highlighter-rouge">arm64</code>.</p>

<p>Swift Package Manager has a few different ways to achieve this. The
easiest way, as far as I can tell, is to pass the hidden <code class="language-plaintext highlighter-rouge">--arch</code> flag
once for each architecture:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% swift build <span class="nt">--configuration</span> release <span class="nt">--arch</span> arm64 <span class="nt">--arch</span> x86_64
</code></pre></div></div>

<p>This goes through a <a href="https://github.com/apple/swift-package-manager/blob/ec407ac14738bf132b23441aa9435a919124eda6/Sources/XCBuildSupport/XcodeBuildSystem.swift">different code
path</a>
in Swift Package Manager, and utilizes Xcode's underlying XCBuild tool.
This results in the built binary being in a different path than usual.
Inspecting the new artifact, we can see we have a binary containing both
requested architectures:</p>

<pre><code class="language-none">% file .build/apple/Products/Release/package
.build/apple/Products/Release/package: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64:Mach-O 64-bit executable arm64]
.build/apple/Products/Release/package (for architecture x86_64):        Mach-O 64-bit executable x86_64
.build/apple/Products/Release/package (for architecture arm64): Mach-O 64-bit executable arm64
</code></pre>

<p>Another option is to build once for each architecture, and then combine
the binaries using
<a href="https://keith.github.io/xcode-man-pages/lipo.1.html"><code class="language-plaintext highlighter-rouge">lipo</code></a>. Unlike
the <code class="language-plaintext highlighter-rouge">--arch</code> option, this approach also works on Linux. Here's an
example:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% swift build <span class="nt">--configuration</span> release <span class="nt">--triple</span> arm64-apple-macosx
% swift build <span class="nt">--configuration</span> release <span class="nt">--triple</span> x86_64-apple-macosx
% lipo <span class="nt">-create</span> <span class="nt">-output</span> package .build/arm64-apple-macosx/release/package .build/x86_64-apple-macosx/release/package
</code></pre></div></div>

<p>Inspecting our final binary we can see it correctly has both
architectures:</p>

<pre><code class="language-none">% file package
package: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64:Mach-O 64-bit executable arm64]
package (for architecture x86_64):      Mach-O 64-bit executable x86_64
package (for architecture arm64):       Mach-O 64-bit executable arm64
</code></pre>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[LLDB Reproducers]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2020/09/29/lldb-reproducers/" />
      <id>https://smileykeith.com/2020/09/29/lldb-reproducers</id>
      <updated>2020-09-29T19:39:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>Swift developers love to complain about LLDB. While there are many
reasonable complaints, the important question is what can we do to make
it better.</p>

<p>Enter <a href="https://lldb.llvm.org/resources/reproducers.html">reproducers</a>. Reproducers provide a way to run LLDB
while also capturing information about your debugging session. With this
information you can <a href="https://feedbackassistant.apple.com">submit</a> a more useful bug report to Apple
with a reliable reproduction case.</p>

<h1 id="how">How?</h1>

<p>Although the steps to use reproducers are mostly straightforward,
launching LLDB from Xcode does not enable <code class="language-plaintext highlighter-rouge">--capture</code> mode (FB7878562).
This means if you want to provide a reproducer for an issue you've
experienced in a Xcode debugging session, you need to reproduce it
outside of Xcode instead.</p>

<p><strong>Update</strong>: the folks from PSPDFKit <a href="https://pspdfkit.com/blog/2020/an-introduction-to-lldb-reproducers">pointed
out</a> as
of Xcode 12 there is a private default for enabling capture mode for
debugging sessions launched from Xcode:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>defaults write com.apple.dt.Xcode IDEDebuggerEnableReproducerCapture -bool YES
</code></pre></div></div>

<p>Note: to provide enough information to reproduce your issue, LLDB
bundles all files the debugging session touched. This includes binaries
with debug info that you may consider sensitive. As <a href="https://twitter.com/johannesweiss/status/1311350073767362565">pointed out on
Twitter</a>
this will also contain anything in memory at the time of capture. Be
sure to verify what you're sharing with Apple before you <a href="https://feedbackassistant.apple.com">send
it</a>.</p>

<h2 id="cli--macos-app">CLI / macOS app</h2>

<p>If you're debugging a program on your Mac, there are a few steps:</p>

<ol>
  <li>Run the app in Xcode and stop it. This way you know the binary is up
to date.</li>
  <li>In Terminal.app navigate to your DerivedData directory (you can find
this by right clicking on your app in the "Products" section of
Xcode's project navigator, and clicking "Show in Finder").</li>
  <li>Run <code class="language-plaintext highlighter-rouge">lldb --capture /path/to/Your.app</code>.</li>
  <li>In the LLDB session run <code class="language-plaintext highlighter-rouge">process launch --stop-at-entry</code>.</li>
  <li>Now you're in a paused LLDB session. Here you can <a href="https://lldb.llvm.org/use/tutorial.html#setting-breakpoints">set the
breakpoints</a> you need to reproduce your issue. Often for
me this means breaking at a specific place, and running some version
of <code class="language-plaintext highlighter-rouge">po foo</code> that causes an issue.</li>
  <li>Once you're done reproducing the issue, run <code class="language-plaintext highlighter-rouge">reproducer generate</code> in
LLDB. This will print the path the information was written to.</li>
  <li>Verify the contents of the output directory doesn't include anything
you're not comfortable sharing with Apple, zip it, and <a href="https://feedbackassistant.apple.com">submit a
radar</a>!</li>
</ol>

<h2 id="ios-app-on-the-simulator">iOS app on the simulator</h2>

<p>Running apps directly from LLDB on the iOS simulator does <a href="https://forums.swift.org/t/using-lldb-with-ios-simulator-from-cli/33990/6">not
work</a>
the same way as running a macOS app. Because of this, the steps differ.</p>

<ol>
  <li>Run the app in Xcode and stop it. This way it's updated and installed
on the iOS simulator.</li>
  <li>Run <code class="language-plaintext highlighter-rouge">lldb --capture --wait-for --attach-name YOUR_APP_NAME</code>.</li>
  <li>Manually launch your app in the Simulator.</li>
  <li>Now you're in a paused LLDB session. Here you can <a href="https://lldb.llvm.org/use/tutorial.html#setting-breakpoints">set the
breakpoints</a> you need to reproduce your issue. Often for
me this means breaking at a specific place, and running some version
of <code class="language-plaintext highlighter-rouge">po foo</code> that causes an issue.</li>
  <li>Once you're done reproducing the issue, run <code class="language-plaintext highlighter-rouge">reproducer generate</code> in
LLDB. This will print the path the information was written to.</li>
  <li>Verify the contents of the output directory doesn't include anything
you're not comfortable sharing with Apple, zip it, and <a href="https://feedbackassistant.apple.com">submit a
radar</a>!</li>
</ol>

<h2 id="ios-app-on-device">iOS app on device</h2>

<p>Unfortunately, I haven't yet figured out the right incantation to launch
LLDB directly and attach to an on-device process. If anyone has a good
workflow for this please <a href="https://twitter.com/SmileyKeith">let me know</a>.</p>

<h1 id="tips">Tips</h1>

<ul>
  <li>Checkout the <a href="https://lldb.llvm.org/resources/reproducers.html">reproducers</a> page for more details.</li>
  <li><code class="language-plaintext highlighter-rouge">man lldb</code> has more usage info.</li>
  <li>Passing <code class="language-plaintext highlighter-rouge">--capture-path</code> to LLDB might be helpful.</li>
  <li>Run <code class="language-plaintext highlighter-rouge">reproducer status</code> to verify you're in capture mode.</li>
  <li>Run <code class="language-plaintext highlighter-rouge">help COMMAND [SUBCOMMAND]</code> in LLDB to get info on the commands
you're running.</li>
  <li>Run <code class="language-plaintext highlighter-rouge">help b</code> to see examples of how to set breakpoints from the
command line interface.</li>
  <li>See the <a href="https://lldb.llvm.org/use/tutorial.html#setting-breakpoints">LLDB tutorial</a> for more breakpoint examples.</li>
  <li>Try out <code class="language-plaintext highlighter-rouge">lldb --replay /path/to/your/reproducer</code> to see what Apple
will see.</li>
  <li>You can attach to XCTest processes by using <code class="language-plaintext highlighter-rouge">xctest</code> as your
<code class="language-plaintext highlighter-rouge">--attach-name</code> argument.</li>
</ul>

]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[NSProgress with Asynchronous Tasks]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2015/03/14/nsprogress-with-asynchronous-tasks/" />
      <id>https://smileykeith.com/2015/03/14/nsprogress-with-asynchronous-tasks</id>
      <updated>2015-03-14T18:57:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>After having a use for <code class="language-plaintext highlighter-rouge">NSProgress</code> I finally got a chance to dive in to
its API. I found it to be less than understandable. So I wrote about it
on the thoughtbot blog. You can find my post, <a href="https://robots.thoughtbot.com/asynchronous-nsprogress">NSProgress with
Asynchronous Tasks,
here</a>.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[ReactiveCocoa and Core Data]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2015/03/14/reactive-cocoa-and-core-data/" />
      <id>https://smileykeith.com/2015/03/14/reactive-cocoa-and-core-data</id>
      <updated>2015-03-14T18:53:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>After finally starting to use
<a href="https://github.com/ReactiveCocoa/ReactiveCocoa">ReactiveCocoa</a> I found
integrating with Core Data to be a sore spot. I wrote about improving this
integration on the thoughtbot blog
<a href="https://robots.thoughtbot.com/reactive-core-data">here</a>.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Writing Vim Syntax Plugins]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2015/03/14/writing-vim-syntax-plugins/" />
      <id>https://smileykeith.com/2015/03/14/writing-vim-syntax-plugins</id>
      <updated>2015-03-14T18:48:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>After <a href="http://www.smileykeith.com/2015/03/14/clojure/">writing about
Clojure</a> I wrote about
writing Vim syntax plugins. Check it out
<a href="https://robots.thoughtbot.com/writing-vim-syntax-plugins">here</a>.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Clojure]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2015/03/14/clojure/" />
      <id>https://smileykeith.com/2015/03/14/clojure</id>
      <updated>2015-03-14T18:27:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>I haven't had anything to write in a while. Mainly because, as I've been
working at <a href="https://thoughtbot.com">thoughtbot</a>, I've been writing
everything I can on <a href="https://robots.thoughtbot.com">the thoughtbot
blog</a>. I spent a little while writing
<a href="http://clojure.org">Clojure</a>, and out of that came a few posts.</p>

<p>Overall I wasn't a big fan of Clojure. I definitely lean more towards
functional languages <a href="https://www.haskell.org">with types</a> but I wanted
to link these articles here regardless.</p>

<ul>
  <li><a href="https://robots.thoughtbot.com/getting-started-with-liberator">Getting Started with Liberator</a></li>
  <li><a href="https://robots.thoughtbot.com/using-yesql-in-clojure">Using Yesql in Clojure</a></li>
  <li><a href="https://robots.thoughtbot.com/writing-clojure-in-vim">Writing Clojure in Vim</a></li>
</ul>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Vim TagBar with Objective-C]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2014/02/14/vim-tagbar-with-objective-c/" />
      <id>https://smileykeith.com/2014/02/14/vim-tagbar-with-objective-c</id>
      <updated>2014-02-14T13:53:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>When working with large files in Vim,
<a href="http://majutsushi.github.io/tagbar/">Tagbar</a> has become an invaluable
part of my workflow. It provides a succinct list of methods, modules,
variables and other language specific constructs. When I started trying
to spend more time in Vim writing Objective-C I was disappointed to see
that, out of the box, it was not supported.</p>

<p>Hopefully in the future it won't be difficult to set this up in Vim.
Currently ctags <a href="https://svn.code.sf.net/p/ctags/code/trunk/objc.c">already
has</a> built in support
for Objective-C. Unfortunately there hasn't been a release of ctags
since 2009. As recommended in the <a href="http://bastibe.de/2011-12-04-how-to-make-tagbar-work-with-objective-c.html">canonical how to
article</a>
you can attempt to use the trunk version of ctags and just define the
Tagbar settings. For me, this ended up producing a ton of
mis-categorized duplicates. I also opened and closed <a href="https://github.com/majutsushi/tagbar/issues/193">an
issue</a> on the Tagbar
Github repo hoping that Objective-C support will be added by default in
the future.</p>

<p>The only other resource I could find about this issue was <a href="https://gist.github.com/yamaya/5598909">this
gist</a>. It uses regex to define
Objective-C to ctags and then match it with Tagbar. I improved it a
little bit and came up with this. Put this file anywhere you want, you
will define its path in your vimrc.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--langdef=objc
--langmap=objc:.m..mm..h
--regex-objc=/\@interface[[:space:]]+([[:alnum:]_]+)/\1/i,interface/
--regex-objc=/\@implementation[[:space:]]+([[:alnum:]_]+)/\1/I,implementation/
--regex-objc=/\@protocol[[:space:]]+([[:alnum:]_]+)/\1/P,protocol/
--regex-objc=/\@property[[:space:]]+\([[:alnum:],[:space:]]+\)[[:space:]]+[[:alnum:]_]+[[:space:]]+\*?([[:alnum:]_]+)/\1/p,property/
--regex-objc=/([-+])[[:space:]]*\([[:alpha:]_][^)]*\)[[:space:]]*([[:alpha:]_][^:;{]+).*/\1\2/M,method definition/
--regex-objc=/^[^#@[:space:]][^=]*[[:space:]]([[:alpha:]_][[:alnum:]_]*)[[:space:]]*=/\1/c,constant/
--regex-objc=/^[[:space:]]*typedef[[:space:]][^;]+[[:space:]]([[:alpha:]_][[:alnum:]]*)[[:space:]]*;/\1/t,typedef/
--regex-objc=/^[[:space:]]*NS_ENUM\([[:alnum:]]+[[:space:]]*,[[:space:]]([[:alnum:]]+)\)/\1/e,enum/
--regex-objc=/^#pragma[[:space:]]+mark[[:space:]]+-?[[:space:]]+([[:alnum:][:space:]]+)/\1/g,pragma/
</code></pre></div></div>

<p>Then in your vimrc:</p>

<div class="language-vim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">g:tagbar_type_objc</span> <span class="p">=</span> <span class="p">{</span>
<span class="se">  \</span> <span class="s1">'ctagstype'</span><span class="p">:</span> <span class="s1">'objc'</span><span class="p">,</span>
<span class="se">  \</span> <span class="s1">'ctagsargs'</span><span class="p">:</span> <span class="p">[</span>
<span class="se">    \</span> <span class="s1">'-f'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'-'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'--excmd=pattern'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'--extra='</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'--format=2'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'--fields=nksaSmt'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'--options='</span> <span class="p">.</span> <span class="nb">expand</span><span class="p">(</span><span class="s1">'~/.vim/objctags'</span><span class="p">),</span>
<span class="se">    \</span> <span class="s1">'--objc-kinds=-N'</span><span class="p">,</span>
<span class="se">  \</span> <span class="p">],</span>
<span class="se">  \</span> <span class="s1">'sro'</span><span class="p">:</span> <span class="s1">' '</span><span class="p">,</span>
<span class="se">  \</span> <span class="s1">'kinds'</span><span class="p">:</span> <span class="p">[</span>
<span class="se">    \</span> <span class="s1">'c:constant'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'e:enum'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'t:typedef'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'i:interface'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'P:protocol'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'p:property'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'I:implementation'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'M:method'</span><span class="p">,</span>
<span class="se">    \</span> <span class="s1">'g:pragma'</span><span class="p">,</span>
<span class="se">  \</span> <span class="p">],</span>
<span class="se">\</span> <span class="p">}</span>
</code></pre></div></div>

<p>Replace the <code class="language-plaintext highlighter-rouge">~/.vim/objctags</code> with the path where you chose to put the
first file. Please let me know if you see any way that this could be
improved.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[IPSEC/L2TP VPN on a Raspberry Pi running Arch Linux]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2014/01/27/ipsec-l2tp-vpn-on-a-raspberry-pi-running-arch-linux/" />
      <id>https://smileykeith.com/2014/01/27/ipsec-l2tp-vpn-on-a-raspberry-pi-running-arch-linux</id>
      <updated>2014-01-27T20:49:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>After you buy a Raspberry Pi, or two, you need to figure out what to use them
for. While you'll get a ton of <em>interesting</em> ideas from Googling "uses for a
Raspberry Pi," I didn't particularly find them any more than a <a href="http://arstechnica.com/information-technology/2012/12/10-raspberry-pi-creations-that-show-how-amazing-the-tiny-pc-can-be/">thought
exercise</a>.
Making a VPN stood out as an actually useful configuration.</p>

<p>Originally when I got my (accidentally chosen) Model A, I spent a little
while going through <a href="http://willitscript.com/post/40357408648/using-your-pi-as-a-l2tp-vpn-server">this
guide</a>
using Raspbian. That seemed to work fine until I recently purchased a
Model B to replace it and couldn't reproduce the configuration. I
decided to write the steps that I was finally able to use to get a
functional VPN running on Arch Linux.</p>

<p>I started out by following <a href="https://raymii.org/s/tutorials/IPSEC_L2TP_vpn_on_a_Raspberry_Pi_with_Arch_Linux.html">this
guide</a>
hoping that it would get me a functioning VPN without too much work.  Most of
this setup will be based on that article with some tweaks for what I had to do
to make the settings stick.  Unfortunately while it worked after the setup the
configuration did not persist after restart.  For this configuration, like I
said earlier, I wanted to use the ARM version of Arch Linux rather than
Raspbian for the install. You can download the Raspberry Pi compatible Arch
image from their <a href="http://www.raspberrypi.org/downloads">downloads page</a>. I'm
not sure I would recommend Arch for people who haven't installed it before or
at least gotten through their <a href="https://wiki.archlinux.org/index.php/Beginners'_Guide">Beginners'
Guide</a>. The ARM Image,
like the normal image, doesn't come with a GUI, perfect for this use of the Pi.</p>

<p>I'm not going to bother with making sure this works before restarting, since
that doesn't seem like much of an issue with actual usage (although you can
just run the scripts we create and it should work fine). I wouldn't recommend
doing much configuration before doing this intial setup. I did this the first
time and after an hour of configuration my VPN did not work correctly, I ended
up nuking the work I had done and starting over.</p>

<p>Start by installing the necessary components:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pacman -Sy openswan xl2tpd ppp lsof python2
</code></pre></div></div>

<p>You need to do some configuration of the firewall and redirects:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">echo</span> <span class="s2">"net.ipv4.ip_forward = 1"</span> |  <span class="nb">tee</span> <span class="nt">-a</span> /etc/sysctl.conf
<span class="nb">echo</span> <span class="s2">"net.ipv4.conf.all.accept_redirects = 0"</span> |  <span class="nb">tee</span> <span class="nt">-a</span> /etc/sysctl.conf
<span class="nb">echo</span> <span class="s2">"net.ipv4.conf.all.send_redirects = 0"</span> |  <span class="nb">tee</span> <span class="nt">-a</span> /etc/sysctl.conf
<span class="nb">echo</span> <span class="s2">"net.ipv4.conf.default.rp_filter = 0"</span> |  <span class="nb">tee</span> <span class="nt">-a</span> /etc/sysctl.conf
<span class="nb">echo</span> <span class="s2">"net.ipv4.conf.default.accept_source_route = 0"</span> |  <span class="nb">tee</span> <span class="nt">-a</span> /etc/sysctl.conf
<span class="nb">echo</span> <span class="s2">"net.ipv4.conf.default.send_redirects = 0"</span> |  <span class="nb">tee</span> <span class="nt">-a</span> /etc/sysctl.conf
<span class="nb">echo</span> <span class="s2">"net.ipv4.icmp_ignore_bogus_error_responses = 1"</span> |  <span class="nb">tee</span> <span class="nt">-a</span> /etc/sysctl.conf</code></pre></figure>

<p>To make these settings persist we need to create a script that gets launched by
systemd each time we restart the system. As recommended in the original
article, and being a <a href="http://brew.sh/">Homebrew</a> user I created the script in
<code class="language-plaintext highlighter-rouge">/usr/local/bin/vpn-boot.sh</code>:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/usr/bin/bash</span>

iptables <span class="nt">--table</span> nat <span class="nt">--append</span> POSTROUTING <span class="nt">--jump</span> MASQUERADE

<span class="k">for </span>vpn <span class="k">in</span> /proc/sys/net/ipv4/conf/<span class="k">*</span><span class="p">;</span> <span class="k">do
    </span><span class="nb">echo </span>0 <span class="o">&gt;</span> <span class="nv">$vpn</span>/accept_redirects<span class="p">;</span>
    <span class="nb">echo </span>0 <span class="o">&gt;</span> <span class="nv">$vpn</span>/send_redirects<span class="p">;</span>
<span class="k">done

</span>sysctl <span class="nt">-p</span></code></pre></figure>

<p>There are a few things that differ here to the original article. First
the hashbang path was changed since the default $PATH on the ARM version
of Arch didn't include <code class="language-plaintext highlighter-rouge">/bin</code>. I would run <code class="language-plaintext highlighter-rouge">which -a bash</code> on your
install to make sure this works for you. This obviously doesn't have
to be changed, but I think it's better in the long run. I also added
<code class="language-plaintext highlighter-rouge">sysctl -p</code> since these settings didn't seemed to be applied otherwise.
Then you must make this script executable with something like:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">chmod</span> +x /usr/local/bin/vpn-boot.sh</code></pre></figure>

<p>Since Arch uses systemd to this script has to be launched by creating a
service to be ran through systemd. You can create this file in
<code class="language-plaintext highlighter-rouge">/etc/systemd/system/vpnboot.service</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit]
Description=VPN Settings at boot
After=netctl@eth0.service
Before=openswan.service xl2tpd.service

[Service]
ExecStart=/usr/local/bin/vpn-boot.sh

[Install]
WantedBy=multi-user.target
</code></pre></div></div>

<p>I added a few things here as well. I wanted to make sure that the boot command
would launch after the network settings had been established and before the
other VPN software was launched. I'm not sure how many of these changes would
be required for systemd to do what I wanted it to but the order really seemed
to matter for here. After you create this service enable it within systemd
with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl enable vpnboot.service
</code></pre></div></div>

<p>I also made some changes to <code class="language-plaintext highlighter-rouge">/etc/ipsec.conf</code> (note the comments in the
default file for some more info on these settings):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>config setup
  dumpdir=/var/run/pluto/
  nat_traversal=yes
  virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v6:fd00::/8,%v6:fe80::/10
  oe=off
  protostack=netkey
  plutoopts="--interface=eth0"

conn L2TP-PSK-noNAT
  authby=secret
  pfs=no
  auto=add
  keyingtries=3
  ikelifetime=8h
  keylife=1h
  type=transport
  # Your server's IP (I used my internal IP, assuming you're using NAT)
  left=172.16.1.1
  leftprotoport=17/1701
  right=%any
  rightprotoport=17/%any
  rightsubnetwithin=0.0.0.0/0
  dpddelay=10
  dpdtimeout=20
  dpdaction=clear
</code></pre></div></div>

<p>Then for the <code class="language-plaintext highlighter-rouge">/etc/ipsec.secrets</code> (use the same server IP address):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>%SameIP%  %any: PSK "super random key"
</code></pre></div></div>

<p>The make systemd start openswan on boot as well:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl enable openswan
</code></pre></div></div>

<p>I also edited the openswan service file in
<code class="language-plaintext highlighter-rouge">/etc/systemd/system/multi-user.target.wants/openswan.service</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit]
Description=Openswan daemon
After=netctl@eth0.service vpnboot.service
Before=xl2tpd.service

[Service]
Type=forking
ExecStart=/usr/lib/systemd/scripts/ipsec --start
ExecStop=/usr/lib/systemd/scripts/ipsec --stop
ExecReload=/usr/lib/systemd/scripts/ipsec --restart
Restart=always

[Install]
WantedBy=multi-user.target
</code></pre></div></div>

<p>As you can see I removed the original network dependency and added a new
dependency of <a href="https://wiki.archlinux.org/index.php/Netctl">netctl's</a>
default network interface (we haven't enabled this yet).</p>

<p>Next for <code class="language-plaintext highlighter-rouge">/etc/xl2tpd/xl2tpd.conf</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[global]
ipsec saref = yes
saref refinfo = 30

[lns default]
ip range = 172.16.1.70-172.16.1.89
local ip = 172.16.1.1
require authentication = yes
ppp debug = yes
pppoptfile = /etc/ppp/options.xl2tpd
length bit = yes
unix authentication = yes
</code></pre></div></div>

<p>Where <code class="language-plaintext highlighter-rouge">local ip</code> is the server's ip and the <code class="language-plaintext highlighter-rouge">ip range</code> is the range of
IP addresses you want to use for VPN clients. You need to enable this
service too with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl enable xl2tpd
</code></pre></div></div>

<p>I also edited the systemd file for xl2tpd at
<code class="language-plaintext highlighter-rouge">/etc/systemd/system/multi-user.target.wants/xl2tpd.service</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit]
Description=Level 2 Tunnel Protocol Daemon (L2TP)
After=syslog.target netctl@eth0.service openswan.service
Requires=openswan.service

[Service]
Type=simple
PIDFile=/run/xl2tpd/xl2tpd.pid
ExecStart=/usr/bin/xl2tpd -D
Restart=on-abort

[Install]
WantedBy=multi-user.target
</code></pre></div></div>

<p>The other guide also recommends creating the xl2tpd control folder with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir /var/run/xl2tpd/
</code></pre></div></div>

<p>Now we need to create/edit <code class="language-plaintext highlighter-rouge">/etc/ppp/options.xl2tpd</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ipcp-accept-local
ipcp-accept-remote
ms-dns 8.8.8.8
ms-dns 8.8.4.4
auth
mtu 1200
mru 1000
crtscts
hide-password
modem
name l2tpd
proxyarp
lcp-echo-interval 30
lcp-echo-failure 4
login
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">/etc/pam.d/ppp</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auth    required        pam_nologin.so
auth    required        pam_unix.so
account required        pam_unix.so
session required        pam_unix.so
</code></pre></div></div>

<p>And <code class="language-plaintext highlighter-rouge">/etc/ppp/pap-secrets</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>*       l2tpd           ""              *
</code></pre></div></div>

<p>If you'd like you can also restrict the users accounts that can access
the vpn. This way you can separate your login user from your VPN users
who can have much stronger passwords. You'd do that in your
<code class="language-plaintext highlighter-rouge">/etc/ppp/pap-secrets</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vpnuser   l2tpd         ""              *
</code></pre></div></div>

<p>To enable the startup of the default netctl eth0 interface you need to
run:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>netctl enable eth0
</code></pre></div></div>

<p>You'll probably want to disable any other netctl systemd functions that are
enabled by default. Check <code class="language-plaintext highlighter-rouge">/etc/systemd/system/mutli-user.target.wants</code> to for
other <code class="language-plaintext highlighter-rouge">netctl</code> profiles.</p>

<p>So at this point you should be able to enable VPN clients using the
super secret keys you enabled before and the username and passwords
you've created previously. You can create new users for specifically VPN
usage with something like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>useradd -s /sbin/nologin vpnuser
</code></pre></div></div>

<p>This disallows users from being able to be used for login which is probably
more secure for your VPN (although not required). For testing you can use the
root/root defualt user and a less secure key, although you should <em>definitely</em>
change these before allowing access to the outside world.</p>

<h3 id="troubleshooting">Troubleshooting</h3>

<p>Undoubtedly you'll have to deal with something that doesn't work exactly
how my setup works. The most useful things to seeing what was happening
were these:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>netstat -tulpan
systemctl status openswan
systemctl status xl2tpd
journalctl -f
</code></pre></div></div>

<p>You can glance at some of the other guides to see what should be going
on. You probably shouldn't see any red in the <code class="language-plaintext highlighter-rouge">openswan</code> status and you
should see ports open under <code class="language-plaintext highlighter-rouge">pluto</code> with netstat. You can check out the
<a href="http://linux.die.net/man/5/ipsec.conf">ipsec manpage</a> or the <a href="https://github.com/xelerance/Openswan/wiki/L2tp-ipsec-configuration-using-openswan-and-xl2tpd">openswan
wiki
page</a>
for a little more information on some of the settings. Also I used <a href="http://www.freedesktop.org/software/systemd/man/systemd.service.html">this
page</a>
for some more info on how systemd settings work. Please let me know if
there's anything here that could be done easier/better for this
configuration.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[iTerm theme based on the time of day]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/09/03/iterm-theme-based-on-the-time-of-day/" />
      <id>https://smileykeith.com/2013/09/03/iterm-theme-based-on-the-time-of-day</id>
      <updated>2013-09-03T10:56:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>One of the great things about Vim's textual configuration is it's
ability to contain logic based on outside factors. For the purpose of
this post I'm referring to the ability to set your colorscheme based on
the time of day with something like
<a href="https://github.com/keith/dotfiles/blob/a34e432b59e26225ebdb05737b30729b7ea670d9/vimrc#L102-L108">this</a>.</p>

<p>Having this functionality in Vim with the
<a href="http://ethanschoonover.com/solarized">Solarized</a> theme at night really
made me want this in <a href="http://www.iterm2.com/#/section/home">iTerm</a> as
well. Unfortunately iTerm's conifguration doesn't allow anything similar
to this. The closest you get is profiles which you can assign keyboard
shortcuts to for quickly opening windows with different colorschemes.
Luckily, thanks to this <a href="https://github.com/gnachman/iTerm2/pull/10">pull
request</a> two years ago from
<a href="https://twitter.com/junkiesxl">Piet Jaspers</a>, support was added for
scripting iTerm's entire colorscheme with AppleScript. Using these
AppleScript bindings I was able to create a
<a href="https://github.com/keith/dotfiles/blob/master/scripts/itermcolors.applescript">script</a>
that changes the entire colorscheme of iTerm based on the time of day
between Solarized light and dark. As you can see the
<a href="https://github.com/keith/dotfiles/blob/master/scripts/itermcolors.applescript#L27-L49">bulk</a>
of this script is just setting different color attributes based on the
theme you want. While you could do this conversion by hand to 65535
flavored RGB, I made a
tiny Objective-C app to automate the process which is <a href="https://github.com/keith/ColorConvert">on
Github</a>. You can download
the signed binary
<a href="https://github.com/keith/ColorConvert/releases/tag/1.0">here</a>.</p>

<p>Using this newly created AppleScript I then made a zsh
<a href="https://github.com/keith/dotfiles/blob/master/functions/colorize">function</a> so that I could call <code class="language-plaintext highlighter-rouge">colorize</code> from anywhere to update the color scheme of the current terminal.
I also chose to do this at the end of my <code class="language-plaintext highlighter-rouge">.zshrc</code> <a href="https://github.com/keith/dotfiles/blob/763e6f3f2bbcd93775c70e0d9ed9878ac99896a3/zshrc#L51">here</a>.
This way everytime I open a new session my theme is automatically set.</p>

<p>If you have any input on how I could optimize this let me know.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Global htaccess]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/08/14/global-htaccess/" />
      <id>https://smileykeith.com/2013/08/14/global-htaccess</id>
      <updated>2013-08-14T13:12:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>When starting a new web project one of the first things I do is download
the most up to date <a href="http://html5boilerplate.com/">HTML5 Boilerplate</a>.
It provides a great starting point for the HTML you need in a project.
It also comes with an extremely complete <a href="https://github.com/h5bp/html5-boilerplate/blob/master/.htaccess">.htaccess</a>
file. While this is very nice for a single site they recommend you do
something different for multiple sites at <a href="https://github.com/h5bp/html5-boilerplate/blob/21c614849afc5b518685b68d81d2b0c8f7971f0a/.htaccess#L4-L6">the very top</a>.</p>

<blockquote>
  <p>(!) Using .htaccess files slows down Apache, therefore, if you have access
to the main server config file (usually called httpd.conf), you should add
this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.</p>
</blockquote>

<p>This got me to their awesome collection of <a href="https://github.com/h5bp/server-configs">server configs</a>
which has their, and in many ways the communities, recommended settings
depending on your webserver. The <a href="https://github.com/h5bp/server-configs-apache">apache configs</a>
have the same <code class="language-plaintext highlighter-rouge">.htaccess</code> file so I decided to dig into how to do this.</p>

<p>They direct you to the <a href="http://httpd.apache.org/docs/current/howto/htaccess.html">apache article</a>
about using <code class="language-plaintext highlighter-rouge">.htaccess</code> files which has a similar comment about their
use.</p>

<blockquote>
  <p>You should avoid using .htaccess files completely if you have access to httpd main server config file
Using .htaccess files slows down your Apache http server. Any directive that you can include in a
.htaccess file is better set in a Directory block, as it will have the same effect with better performance.</p>
</blockquote>

<p>So I decided to set this up on my <a href="http://www.linode.com/?r=c190426bf1ff0f144b48997675bae8b32d339824">Linode VPS</a> which is running Ubuntu 10.04.
As stated in the original file comment they recommend using the
<code class="language-plaintext highlighter-rouge">httpd.conf</code> file for your custom configuration like this. But
<a href="http://stackoverflow.com/a/11687212/902968">apparently</a> that file could be
overwritten on updates of Apache which would be pretty annoying. Luckily
the default Apache config file (<code class="language-plaintext highlighter-rouge">apache2.conf</code> on 10.04) includes the
contents of the <code class="language-plaintext highlighter-rouge">conf.d</code> folder which is in the same location. By
creating a <code class="language-plaintext highlighter-rouge">foo.conf</code> file in that directory Apache should immediately
load its contents. As mentioned in the comment from the Apache site the
custom configuration needs to be wrapped in a <a href="http://httpd.apache.org/docs/current/mod/core.html#directory">Directory</a> block.
The block expects you to provide a path to the files you want to be
affected by the contained configuration. Since I wanted this to work for
all the sites being served by Apache I simply used <code class="language-plaintext highlighter-rouge">/srv/www/*/</code> which
includes my entire sites directory.</p>

<p>Besides the speed increased gained by using a global <code class="language-plaintext highlighter-rouge">.htaccess</code> file
this allows you to have much shorter custom files for site specific
configuration. For example only required configuration for one of my sites
was the <code class="language-plaintext highlighter-rouge">ErrorDocument</code>s. Now my <code class="language-plaintext highlighter-rouge">.htaccess</code> file went from 300+ lines
to</p>

<figure class="highlight"><pre><code class="language-apache" data-lang="apache"><span class="nc">ErrorDocument</span> 403 /403.php
<span class="nc">ErrorDocument</span> 404 /404.php
<span class="nc">ErrorDocument</span> 500 /500.php</code></pre></figure>

]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[OS X Crash Report Symbolication]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/08/09/os-x-crash-symbolication/" />
      <id>https://smileykeith.com/2013/08/09/os-x-crash-symbolication</id>
      <updated>2013-08-09T13:26:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>As you may know I write a small OS X called <a href="http://sailforapp.net/">Sail</a>. Over
the past few months that it has been available I've received a few crash reports
about an issue I wasn't able to reproduce. Today I decided I wanted to dive into
them and see if I could at least figure out the root of the issue and fix it
with my next release.</p>

<p>This lead me down the rabbit hole of
<a href="http://lldb.llvm.org/symbolication.html">symbolication</a>, something I personally
hadn't dealt with myself before (since Crashlytics does it for you). I was
hoping I would be able to find something around the internet about this,
unfortunately what I mostly came up with was a lot of iOS related answers that
didn't seem to work the same way and
<a href="http://developer.apple.com/tools/xcode/symbolizingcrashdumps.html">two</a>
<a href="http://developer.apple.com/library/mac/technotes/tn2004/tn2123.html">links</a> to
Apple documentation that have been removed. Other than the process for
symbolicating reports for OS X apps seems to be different than iOS apps which
there is plenty of documentation for (I'm not bitter). Daniel Jalkut has <a href="http://www.red-sweater.com/blog/439/crappy-crash-logs">a
post</a> about these but his
exact method didn't seem to work for me.</p>

<p>Here is what did work for me. For my first abridged crash report I had this</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Process:         Sail [35072]
Path:            /Applications/Sail.app/Contents/MacOS/Sail
Load Address:    0x106823000
Identifier:      com.keithsmiley.SailOSX
Version:         4 (1.2.0)
Code Type:       x86_64 (Native)
Parent Process:  launchd [207]

Date/Time:       2013-07-19 16:09:24.097 +0200
OS Version:      Mac OS X 10.8.4 (12E55)
Report Version:  8

Thread 0:
13  Accounts                        0x00007fff839fd1b1 -[ACAccountStore accountTypeWithAccountTypeIdentifier:] + 230
14  Sail                            0x00000001068308f7
15  Sail                            0x0000000106830798
16  Sail                            0x0000000106825249
17  CoreFoundation                  0x00007fff82465eda _CFXNotificationPost + 2554
18  Foundation                      0x00007fff8611b7b6 -[NSNotificationCenter postNotificationName:object:userInfo:] + 64
31  AppKit                          0x00007fff812cc1a3 -[NSApplication run] + 517
32  AppKit                          0x00007fff81270bd6 NSApplicationMain + 869
33  libdyld.dylib                   0x00007fff869d07e1 start + 0

Binary Images:
  0x106823000 - 0x106896fff  com.keithsmiley.SailOSX (1.2.0 - 4) &lt;D1F313B6-21F6-341B-8627-5480C5D1DB20&gt; /Applications/Sail.app/Contents/MacOS/Sail
</code></pre></div></div>

<p>Just glancing at this crash report it's not too difficult to understand a bit
about what was going on. A notification was sent, some methods were called in my
application and then <code class="language-plaintext highlighter-rouge">accountTypeWithAccountTypeIdentifier</code> was called. Based on
the small number of times I call that method I was quickly able to assume where
the issue was but I still wanted to see exactly what methods of mine were being
called first.</p>

<p>This brings me to <code class="language-plaintext highlighter-rouge">atos</code> the command line tool Apple provides to symbolicate
these reports. This is where my experience differs with most of what I found
online. My usage looked like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>atos -arch x86_64 -o Sail.app.dSYM/Contents/Resources/DWARF/Sail -l 0x106823000
</code></pre></div></div>

<p>This uses my dSYM file that was generated with the archive build I submitted to
the app store along with the knowledge that it was running on an x86_64
architecture and the most important part, for me, the load address.</p>

<p>To find my dSYM file that was generated when I did my archive build I simply
noted the version and build number from the crash report, went to the Archives
tab in the Xcode organizer, found the build with the same number, right clicked
and clicked "Show in Finder." This takes you directly to the <code class="language-plaintext highlighter-rouge">.xcarchive</code> file
on disk which you can right click and click "Show Package Contents." From there
I copied my dSYM to the desktop so I didn't overwrite anything unintentionally.</p>

<p>The load address is the starting memory address of your application. The tool
uses this address as an offset to find the correct methods in your symbols. In
the above crash report <code class="language-plaintext highlighter-rouge">Load Address</code> is a provided field. This was the only
report I saw that had that, typically I needed to look under the <code class="language-plaintext highlighter-rouge">Binary Images</code>
section for the address range of my application. In this example it was
<code class="language-plaintext highlighter-rouge">0x106823000 - 0x106896fff</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">atos</code> command then provides an interactive prompt where you can paste
addresses into the stdin and it will tell you the corresponding methods. Mine
looked like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0x00000001068308f7
-[KSAccountsPreferences dealloc] (in Sail) (KSAccountsPreferences.m:77)
0x0000000106830798
-[KSAccountsPreferences viewDidLoad] (in Sail) (KSAccountsPreferences.m:73)
0x0000000106825249
-[KSAppDelegate openAboutWindow:] (in Sail) (KSAppDelegate.m:59)
</code></pre></div></div>

<p>Here I can see that the notification that was being posted was probably an
<code class="language-plaintext highlighter-rouge">NSApplicationDidFinishLaunchingNotification</code> starting off some methods in my
app delegate. I then load the accounts preferences, which would make sense to
call the <code class="language-plaintext highlighter-rouge">ACAccountStore</code> method, but then <code class="language-plaintext highlighter-rouge">dealloc</code> is called. Seeing this was
an immediate red flag since <code class="language-plaintext highlighter-rouge">KSAccountsPreferences</code> should be retained since it
provides information about available accounts to the rest of the application.</p>

<p>I had another crash report from a different issue that was a little bit harder
to parse without symbolicating the methods.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Process:         Sail [47027]
Identifier:      com.keithsmiley.SailOSX
Version:         1.2.0 (4)
Code Type:       X86-64 (Native)
Parent Process:  launchd [45696]
User ID:         502

Application Specific Information:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
abort() called
terminate called throwing an exception

Application Specific Backtrace 1:
2   CoreFoundation                      0x000000010c5008ec -[__NSArrayM objectAtIndex:] + 252
3   Sail                                0x000000010b838eed Sail + 61165
4   Sail                                0x000000010b838f36 Sail + 61238
5   Sail                                0x000000010b838b19 Sail + 60185
6   Sail                                0x000000010b83785d Sail + 55389
7   libdispatch.dylib                   0x000000010fa07f01 _dispatch_call_block_and_release + 15

Binary Images:
  0x10b82a000 - 0x10b89dff7 +com.keithsmiley.SailOSX (1.2.0 - 4) &lt;47EC2733-B543-31EA-A6AA-9D998FB65803&gt;
</code></pre></div></div>

<p>Obviously this was caused by an invalid access to an array but that's a little harder to track down. So I again used <code class="language-plaintext highlighter-rouge">atos</code> with the dSYM and new memory location.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>atos -arch x86_64 -o Sail.app.dSYM/Contents/Resources/DWARF/Sail -l 0x10b82a000
</code></pre></div></div>

<p>I got this output for my memory addresses</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0x000000010b838eed
-[KSAccountsPreferences selectedADNUser] (in Sail) (KSAccountsPreferences.m:238)
0x000000010b838f36
-[KSAccountsPreferences selectTwitterUsername] (in Sail) (KSAccountsPreferences.m:243)
0x000000010b838b19
-[KSAccountsPreferences populateTwitterAccounts] (in Sail) (KSAccountsPreferences.m:211)
0x000000010b83785d
__36-[KSAccountsPreferences viewDidLoad]_block_invoke (in Sail) (KSAccountsPreferences.m:70)
</code></pre></div></div>

<p>This ended up giving me the exact line where out of bounds issue was happening
depending on a certain number of accounts. I noticed that this issue had been
fixed since my last release so I did a <code class="language-plaintext highlighter-rouge">diff</code> on the tag I created for that
specific release with</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git difftool HEAD..1.2.0\(3\)
</code></pre></div></div>

<p>Then in <a href="http://www.kaleidoscopeapp.com/">Kaleidoscope</a> I was able to figure out
what changed had fixed the issue. Just because it's so pretty here is what it
looked like.</p>

<p><img src="/images/symbolication/diff.png" alt="Diff" /></p>

<p>Symbolicating crash reports is definitely vital to tracking down bugs your users
are experiencing when you can't reproduce them yourself. Once you figure out how
it's obviously worth it.</p>

]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[The 'Best' Text Editor]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/05/22/the-best-text-editor/" />
      <id>https://smileykeith.com/2013/05/22/the-best-text-editor</id>
      <updated>2013-05-22T00:59:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>I'm tired of people asking about the 'best' IDE for xyz purpose. The answer to this question is there is no best. The answer is always 'it depends.' Not only does it depend on what you're doing but more importantly it depends on you. It depends on your work flow. It depends tons of other indiscernible factors.</p>

<p>It seems like people think they work in exactly the same way as enough other people. That asking this question will yield a useful result. The truth is that there are far fewer text editors than people who need text editors so it's impossible not to overlap with someone. We misconstrue this overlap in thinking that now this person knows exactly what we want. In reality they just happen to share some arbitrary subset of the way we  work and therefore ended up with the same text editor.</p>

<p>So how can you decide which editor is best for you? Try them. This sounds obvious to you? Good, this article is not for you and you can safely leave now. These days text editors are either free, cheap or have trials. So download them all try them out and see if they make sense to you. Weed out the ones you really hate or the ones that crash and spend a little more time with the remaining editors. Some, like Vim, you may have to spend a little more time with to grasp but this still doesn't seem like a high order.</p>

<p>But please stop asking questions on StackOverflow and similar sites where you expect people to throw their vote into the hat for the 'best' editor and make a decision for yourself.</p>

]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Objective-C on Travis-CI]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/04/11/objective-c-on-travis-ci/" />
      <id>https://smileykeith.com/2013/04/11/objective-c-on-travis-ci</id>
      <updated>2013-04-11T15:58:00-07:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>Recently <a href="https://travis-ci.org/">Travis</a> added support for Objective-C and there for OS X and iOS projects for continuous integration testing. I gather that people have previously done this with self-hosted dedicated <a href="http://jenkins-ci.org/">Jenkins</a> machines but since Apple is so aggressive about dropping support for previous versions of the OS it seems like a pain to have to replace your build server every few years. Enter Travis, a great hosted <a href="http://en.wikipedia.org/wiki/Continuous_integration">continuous integration</a> server that hosts a huge amount of open source projects. I figured with this new support I could host some of my <a href="https://github.com/keith/KSADNTwitterFormatter">smaller</a> libraries just to set how well it worked. The initial setup process was a bit tedious but I eventually got it to work.</p>

<h3 id="assumtions">Assumtions:</h3>

<ul>
  <li>You have a test framework already integrated with your project (I like <a href="https://github.com/petejkim/specta">Specta</a>/<a href="https://github.com/petejkim/expecta">Expecta</a>)</li>
  <li>You have your project on Github in a public repository. Travis offers a <a href="http://about.travis-ci.org/docs/user/travis-pro/">pro</a> account if you'd rather</li>
</ul>

<h3 id="steps">Steps</h3>

<ul>
  <li>Create a <code class="language-plaintext highlighter-rouge">.travis.yml</code> file in the root of your repository (leading dot is intentional). For many projects a file may just look like:</li>
</ul>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">language</span><span class="pi">:</span> <span class="s">objective-c</span></code></pre></figure>

<p><del>By default Travis runs <a href="https://gist.github.com/henrikhodne/73151fccea7af3201f63">this script</a> for Objective-C projects</del> I was informed <a href="https://twitter.com/henrikhodne/status/322665896806060032">on Twitter</a> that the current script that runs Objective-C projects is actually located <a href="https://github.com/travis-ci/travis-cookbooks/blob/osx/ci_environment/travis_build_environment/files/default/ci_user/travis-utils/osx-cibuild.sh">here</a>. It was originally created by <a href="https://github.com/jspahrsummers">Justin Spahr-Summers</a> <a href="https://github.com/jspahrsummers/objc-build-scripts">here</a>. This script seems to run my projects without any issue, they just occasionally require more initial setup (we'll get to that).</p>

<ul>
  <li>
    <p>Enable your repository in Travis' settings. From your <a href="https://travis-ci.org/profile">Travis profile page</a> (after signing in with Github) you should see a list of your repositories, you may have to press 'Sync now', where you can switch on the repository you're planning on adding.</p>
  </li>
  <li>
    <p>Configure your project within Xcode. As I assumed above you already have a test target setup. You do have to do a few things in Xcode to make everything work correctly.</p>

    <ol>
      <li>Go to 'Manage Schemes' in Xcode. <img src="/images/travis/manageschemes.png" alt="Manage Schemes" /></li>
      <li>Check the 'Shared' box for the scheme that needs to be run. <img src="/images/travis/shareschemes.png" alt="Shared Scheme" /></li>
      <li>Click 'Edit…' in the bottom left and go to your build action. <img src="/images/travis/editscheme.png" alt="Edit Scheme" /></li>
      <li>On the row of your Tests target check the box in the 'Run' column. <img src="/images/travis/runtest.png" alt="Run Test" /></li>
    </ol>
  </li>
  <li>
    <p>At this point for a simple project or a project using <a href="http://cocoapods.org/">CocoaPods</a> you should be good to go. If Travis finds a <code class="language-plaintext highlighter-rouge">Podfile</code> in the root of your repository it automatically runs <code class="language-plaintext highlighter-rouge">pod install</code> to get your dependencies (from their <a href="http://about.travis-ci.org/docs/user/languages/objective-c/">docs</a>). Otherwise there are a ton of <a href="http://about.travis-ci.org/docs/user/build-configuration/">configuration options</a> for your <code class="language-plaintext highlighter-rouge">.travis.yml</code> depending on how your repo is setup.</p>
  </li>
</ul>

<p>For one of my projects I created a <code class="language-plaintext highlighter-rouge">setup.sh</code> file at the root of my repo that looks like this:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/usr/bin/env bash</span>

git submodule update <span class="nt">--init</span> <span class="nt">--recursive</span>
<span class="nb">echo</span> <span class="s2">"Setting up test frameworks..."</span>
<span class="nb">cd </span>Example/Vendor/Specta<span class="p">;</span> rake <span class="o">&gt;</span> /dev/null
<span class="nb">cd</span> ../Expecta<span class="p">;</span> rake <span class="o">&gt;</span> /dev/null
<span class="nb">echo</span> <span class="s2">"Done"</span>
<span class="nb">cd</span> ../../../</code></pre></figure>

<p>This script which I run using the <code class="language-plaintext highlighter-rouge">before_install: ./setup.sh</code> option in my <code class="language-plaintext highlighter-rouge">.travis.yml</code> gets all my submodules, sets up Specta and Expecta and then goes back to the root directory for running. If you just have a few simple steps you can also have multiple <code class="language-plaintext highlighter-rouge">before_install</code> actions like:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">before_install</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">cd Example</span>
  <span class="pi">-</span> <span class="s">make</span></code></pre></figure>

<p>You can read more about other Travis configuration options in their <a href="http://about.travis-ci.org/docs/user/build-configuration/">documentation</a>.</p>

]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[OS X + ZFS]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/02/21/os-x-plus-zfs/" />
      <id>https://smileykeith.com/2013/02/21/os-x-plus-zfs</id>
      <updated>2013-02-21T11:28:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>For a long time people have talked about how horrible <a href="http://en.wikipedia.org/wiki/HFS_Plus">HFS+</a> is. Most notably, in my opinion, <a href="http://en.wikipedia.org/wiki/Linus_Torvalds">Linus Torvalds</a> (the creator of Linux and Git) who <a href="http://www.smh.com.au/news/technology/torvalds-pans-apples-os-x/2008/02/05/1202090393959.html">said</a> "Their file system is complete and utter crap, which is scary." <a href="https://twitter.com/siracusa">John Siracusa</a> also wrote about the problems with HFS+ in his <a href="http://arstechnica.com/apple/2011/07/mac-os-x-10-7/12/#hfs-problems">10.7 review</a> along with talking about it on his podcast, <a href="http://5by5.tv/hypercritical/">Hypercritical</a> in episodes <a href="http://5by5.tv/hypercritical/56">56</a> and <a href="http://5by5.tv/hypercritical/57">57</a>.</p>

<p>The gist of all this really is that Apple needs a new filesystem. It has been a while since Apple had <a href="http://en.wikipedia.org/wiki/ZFS">ZFS</a> support <a href="http://gizmodo.com/5018512/mac-os-x-snow-leopard-for-servers-getting-zfs">on their website</a> for release in Snow Leopard. After which Apple dropped ZFS support because of <a href="http://arstechnica.com/apple/2009/10/apple-abandons-zfs-on-mac-os-x-project-over-licensing-issues/">licensing issues</a>. There had been talks of Apple developing their own file system, although I think that's the wrong way to go. I think Apple needs to approach file systems the way they approached Safari with <a href="http://www.webkit.org/">Webkit</a>. I think they would be best served by picking up a liberally open source file system, such as <a href="http://en.wikipedia.org/wiki/Btrfs">BTRFS</a>, or grabbing up one of the ZFS <a href="http://code.google.com/p/maczfs/">ports</a> and continuing development while leaving it open for anyone to use. One of the great things about this approach is exactly what happened with Webkit and Google Chrome. Google decided to use the Webkit engine to make a product that competes with the main developers of the Webkit project. By doing this with a file system other vendors could use the same implementation and therefore increase development and in turn stability of whichever system was chosen (not to mention compatibility).</p>

<p>One thing is for sure. People who understand how integral a file system is <a href="http://arstechnica.com/apple/2013/02/zfs-loving-mac-users-demand-support-in-os-x-10-9/">want</a> something <a href="https://www.change.org/petitions/apple-os-x-10-9-support-opengl-4-3-and-zfs">modern</a>.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Terminal Shortcut in OS X]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/02/08/terminal-shortcut-in-os-x/" />
      <id>https://smileykeith.com/2013/02/08/terminal-shortcut-in-os-x</id>
      <updated>2013-02-08T09:59:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>One of my favorite defaults in some Linux distros is the ability to use CTRL+ALT+T to open a new terminal window. I wanted to enable this same functionality in OS X using <a href="http://qsapp.com/">Quicksilver</a>. I did this using <a href="http://www.iterm2.com/">iTerm 2</a> but you can do it with the default Terminal if that's what you want.</p>

<ol>
  <li>
    <p>Enable the <code class="language-plaintext highlighter-rouge">Terminal</code> and <code class="language-plaintext highlighter-rouge">iTerm2</code> Quicksilver plugins.
<img src="/images/qs-terminal/qs-plugins.png" alt="Quicksilver plugins" /></p>
  </li>
  <li>
    <p>Create a new custom hotkey trigger. Using the <code class="language-plaintext highlighter-rouge">Home</code> directory with the action <code class="language-plaintext highlighter-rouge">Open Directory in Terminal</code>
<img src="/images/qs-terminal/qs-triggers.png" alt="Quicksilver trigger" /></p>
  </li>
  <li>
    <p>Set it's hotkey using the drawer to whatever you want.
<img src="/images/qs-terminal/qs-hotkey.png" alt="Quicksilver hotkey" /></p>
  </li>
  <li>
    <p>Set your default <code class="language-plaintext highlighter-rouge">Command Line Interface</code> <code class="language-plaintext highlighter-rouge">Trigger</code> to <code class="language-plaintext highlighter-rouge">iTerm</code> (if that's what you want)
<img src="/images/qs-terminal/qs-cli.png" alt="Quicksilver CLI" /></p>
  </li>
</ol>

<p>You're done! Now you can easily press your hotkey and pull up a new iTerm/Terminal window whenever and wherever.</p>

]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Automated Google Reader Backups]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/02/01/automated-google-reader-backups/" />
      <id>https://smileykeith.com/2013/02/01/automated-google-reader-backups</id>
      <updated>2013-02-01T12:30:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>I spend a lot of time in my RSS <a href="http://reederapp.com/">Reeder</a> (see what I did there?). I still find Google Reader to be the best and easiest way to manage my subscriptions, although I've been wanting to switch to <a href="http://feedafever.com/">Fever</a> for a while.</p>

<p>One thing I wanted to do when I launched my new site (the one you're reading) was to have a downloadable up to date export of my Google Reader OPML file (which of course I never did). I looked around for good ways to automate this and I found a simple Python script to do it with (sorry I couldn't find it again for this post). I decided to rewrite it in Ruby and set it up on my server as an automated cron job.</p>

<p>To run the script I came up with use something like:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">ruby path/to/googleReaderOPML.rb username@gmail.com SekretPassword</code></pre></figure>

<p>To add it to your crontab (to run every Sunday at 1:01am) use something like:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">1 1 <span class="k">*</span> <span class="k">*</span> 7 ruby path/to/googleReaderOPML.rb username@gmail.com SekretPassword</code></pre></figure>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#!/usr/bin/env ruby</span>

<span class="c1">#</span>
<span class="c1"># =&gt; This script will authorize your Google credentials and download your Google Reader subscriptions</span>
<span class="c1"># =&gt; Usage: ./googleReaderOPML.rb GOOGLEUSERNAME PASSWORD</span>
<span class="c1">#</span>

<span class="c1"># The required networking shenanigans</span>
<span class="nb">require</span> <span class="s1">'uri'</span>
<span class="nb">require</span> <span class="s1">'net/http'</span>
<span class="nb">require</span> <span class="s1">'open-uri'</span>

<span class="nb">require</span> <span class="s1">'rubygems'</span>
<span class="c1"># This requires the 'colorize' gem. Install with '[sudo] gem install colorize'</span>
<span class="nb">require</span> <span class="s1">'colorize'</span>


<span class="c1"># The base Google URLs for callback, authentication, and subscription export</span>
<span class="vg">$GOOGLE_URL</span> <span class="o">=</span> <span class="s2">"http://www.google.com"</span>
<span class="vg">$LOGIN_URL</span> <span class="o">=</span> <span class="s2">"https://www.google.com/accounts/ClientLogin"</span>
<span class="vg">$READER_URL</span> <span class="o">=</span> <span class="s2">"http://www.google.com/reader/subscriptions/export"</span>

<span class="c1"># The user agent string, for some reason this is required, feel free to change it</span>
<span class="vg">$SOURCE</span> <span class="o">=</span> <span class="s2">"keith.so"</span>

<span class="c1"># The default output filename, it is automatically overwritten if one already exists</span>
<span class="vg">$FILE_NAME</span> <span class="o">=</span> <span class="s2">"googlereadersubscriptions.opml"</span>


<span class="c1"># Make sure there is the correct number of arguments</span>
<span class="k">if</span> <span class="no">ARGV</span><span class="p">.</span><span class="nf">count</span> <span class="o">!=</span> <span class="mi">2</span>
	<span class="c1"># Print the instruction</span>
	<span class="nb">puts</span> <span class="s2">"Usage: ./</span><span class="si">#{</span> <span class="no">File</span><span class="p">.</span><span class="nf">basename</span><span class="p">(</span><span class="kp">__FILE__</span><span class="p">)</span> <span class="si">}</span><span class="s2"> USERNAME PASSWORD"</span><span class="p">.</span><span class="nf">red</span>
	<span class="nb">exit</span>
<span class="k">end</span>

<span class="c1"># Build the request URL</span>
<span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="vg">$LOGIN_URL</span><span class="p">)</span>

<span class="c1"># Setup the Parameters</span>
<span class="n">params</span> <span class="o">=</span> <span class="p">{</span> <span class="no">Email</span><span class="p">:</span> <span class="no">ARGV</span><span class="p">.</span><span class="nf">first</span><span class="p">,</span> <span class="no">Passwd</span><span class="p">:</span> <span class="no">ARGV</span><span class="p">.</span><span class="nf">last</span><span class="p">,</span> <span class="ss">service: </span><span class="s2">"reader"</span><span class="p">,</span> <span class="ss">source: </span><span class="vg">$SOURCE</span><span class="p">,</span> <span class="ss">continue: </span><span class="vg">$GOOGLE_URL</span> <span class="p">}</span>

<span class="c1"># Add the user-agent string, my website (feel free to replace it) to the headers</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"User-agent"</span> <span class="o">=&gt;</span> <span class="vg">$SOURCE</span> <span class="p">}</span>

<span class="c1"># Encode the parameters into the url</span>
<span class="n">uri</span><span class="p">.</span><span class="nf">query</span> <span class="o">=</span> <span class="no">URI</span><span class="p">.</span><span class="nf">encode_www_form</span><span class="p">(</span><span class="n">params</span><span class="p">)</span>

<span class="c1"># Create a new NET:HTTP object with the request URL</span>
<span class="n">http</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">uri</span><span class="p">.</span><span class="nf">host</span><span class="p">,</span> <span class="n">uri</span><span class="p">.</span><span class="nf">port</span><span class="p">)</span>

<span class="c1"># Require HTTPS without this net/http will not be happy with you</span>
<span class="n">http</span><span class="p">.</span><span class="nf">use_ssl</span> <span class="o">=</span> <span class="kp">true</span>

<span class="c1"># Execute the request</span>
<span class="n">request</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">::</span><span class="no">Get</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">uri</span><span class="p">.</span><span class="nf">request_uri</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>

<span class="c1"># Get the data from the request</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">http</span><span class="p">.</span><span class="nf">request</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>

<span class="c1"># Check for valid response code, should ONLY be 200</span>
<span class="k">if</span> <span class="n">response</span><span class="p">.</span><span class="nf">code</span> <span class="o">!=</span> <span class="s1">'200'</span>
	<span class="nb">puts</span> <span class="s2">"Google returned </span><span class="si">#{</span> <span class="n">response</span><span class="p">.</span><span class="nf">code</span> <span class="si">}</span><span class="s2">, check your username and password"</span><span class="p">.</span><span class="nf">red</span>
	<span class="nb">exit</span>
<span class="k">end</span>

<span class="c1"># split each token into a different item then load them each into a hash with the key as the token key</span>
<span class="n">auth_hash</span> <span class="o">=</span> <span class="no">Hash</span><span class="p">.</span><span class="nf">new</span>
<span class="n">response</span><span class="p">.</span><span class="nf">body</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sr">/\n/</span><span class="p">).</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">token</span><span class="o">|</span>
	<span class="n">split_array</span> <span class="o">=</span> <span class="n">token</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="s1">'='</span><span class="p">)</span>
	<span class="n">auth_hash</span><span class="p">[</span><span class="n">split_array</span><span class="p">.</span><span class="nf">first</span><span class="p">.</span><span class="nf">downcase</span><span class="p">]</span> <span class="o">=</span> <span class="n">split_array</span><span class="p">.</span><span class="nf">last</span>
<span class="k">end</span>

<span class="c1"># Create a header hash for the request of the XML file</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"user-agent"</span> <span class="o">=&gt;</span> <span class="vg">$SOURCE</span><span class="p">,</span> <span class="s2">"cookie"</span> <span class="o">=&gt;</span> <span class="s2">"Name=SID;SID=</span><span class="si">#{</span> <span class="n">auth_hash</span><span class="p">[</span><span class="s1">'sid'</span><span class="p">]</span> <span class="si">}</span><span class="s2">;Domain=.google.com;Path=/;Expires=160000000000"</span><span class="p">,</span> <span class="s2">"authorization"</span> <span class="o">=&gt;</span> <span class="s2">"GoogleLogin auth=</span><span class="si">#{</span> <span class="n">auth_hash</span><span class="p">[</span><span class="s1">'auth'</span><span class="p">]</span> <span class="si">}</span><span class="s2">"</span> <span class="p">}</span>

<span class="c1"># Open the URL for the Google Reader export with the setup headers</span>
<span class="n">request</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="vg">$READER_URL</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>

<span class="c1"># Open the received XML feeds file</span>
<span class="n">google_reader_file</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s1">'r'</span><span class="p">)</span>

<span class="c1"># Read the entire feeds file into 'subscriptions'</span>
<span class="n">subscriptions</span> <span class="o">=</span> <span class="n">google_reader_file</span><span class="p">.</span><span class="nf">read</span>

<span class="c1"># Close the downloaded file</span>
<span class="n">google_reader_file</span><span class="p">.</span><span class="nf">close</span><span class="p">()</span>

<span class="c1"># Open a new file with the global filename to write to, overwrite it if it exists</span>
<span class="n">subscriptions_file</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="vg">$FILE_NAME</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">)</span>

<span class="c1"># Verify the file was created</span>
<span class="k">if</span> <span class="no">File</span><span class="p">.</span><span class="nf">exists?</span><span class="p">(</span><span class="n">subscriptions_file</span><span class="p">)</span>
	<span class="c1"># Write the subscriptions to the file and close it</span>
	<span class="n">subscriptions_file</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">subscriptions</span><span class="p">)</span>
	<span class="n">subscriptions_file</span><span class="p">.</span><span class="nf">close</span><span class="p">()</span>

	<span class="c1"># Display a success message</span>
	<span class="nb">puts</span> <span class="s2">"Wrote Google Reader subscriptions to </span><span class="si">#{</span> <span class="vg">$FILE_NAME</span> <span class="si">}</span><span class="s2">"</span><span class="p">.</span><span class="nf">green</span>
<span class="k">else</span>
	<span class="c1"># If the file wasn't created print an error</span>
	<span class="nb">puts</span> <span class="s2">"Couldn't write to </span><span class="si">#{</span> <span class="vg">$FILE_NAME</span> <span class="si">}</span><span class="s2"> (the process running this script may not have sufficient privileges"</span><span class="p">.</span><span class="nf">red</span>
<span class="k">end</span></code></pre></figure>

]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Boredom]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/02/01/boredom/" />
      <id>https://smileykeith.com/2013/02/01/boredom</id>
      <updated>2013-02-01T11:03:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>Working as a developer full-time can be very exciting. Dealing with new interesting problems gets me up in the morning. But I still find that after a few months of a specific project I find myself less and less interested with it. Not only projects but concepts and languages start to become less interesting. First it was websites then Objective-C then Ruby then C and now who knows. This scares me. At this point in my life I hope to work in this field for my 'career' meaning a significant amount of time. Yet I can't even keep myself working on a single project now, much less one that doesn't interest me.</p>

<p>I typically blame this on the difficulty of the project. As a lone developer I find that most projects I work on are pretty small in scope, since I just don't have the time or people-power to work on anything larger. Therefore I look at most of my projects without interest. I'm not sure what it will take to keep myself engaged and interested on my next project.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Learning C]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/01/27/learning-c/" />
      <id>https://smileykeith.com/2013/01/27/learning-c</id>
      <updated>2013-01-27T22:59:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>I've been using Objective-C and C++ for a while now. While I feel like I know them pretty well I had absolutely no grasp on C itself. Obviously when using those languages you pick up a bit about data types and conditionals but you couldn't take that knowledge and completely write something non-trivial in C. Because of this I figured it might be worth some time so I started looking for viable resources. In that search I found Learn Code the Hard way. They publish physical and online books on different languages and so far I've found it to be a delight. It's not your typical easy walk through and on lessons 17 (of 51) you'll start actually having to think. This for me has been much better so far than typically online learning where the introductory course ends on how to write a for loop.</p>

<p>If you're interested in picking up a C background I would definitely recommend you start here.</p>

]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[NSTableView vim keys]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/01/08/nstableview-vim-keys/" />
      <id>https://smileykeith.com/2013/01/08/nstableview-vim-keys</id>
      <updated>2013-01-08T16:19:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>I'm currently working on a OS X application that uses a few different NSTableViews to display user data. I was testing them out a bit to make sure multiple deletions worked correctly from my database and I found myself pressing 'j' and 'k' to try and move down and up. I decided it would be pretty cool to implement those two vim shortcuts into my table view just in case anyone else thinks like me.</p>

<p>This functionality already exists in <a href="http://www.potionfactory.com/thehitlist/">The Hit List</a> an awesome GTD app that has a lot of baggage with me, and I'm sure it exists in other applications as well.</p>

<p>In my <code class="language-plaintext highlighter-rouge">NSTableView</code> subclass' <code class="language-plaintext highlighter-rouge">keyDown:</code> method I tried a few things.</p>

<p>Attempt 1: First I tried to re implement the functionality myself. In retrospect this doesn't make any sense but at first it was pretty simple. It looked something like this.</p>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="n">NSUInteger</span> <span class="n">flags</span> <span class="o">=</span> <span class="p">[</span><span class="n">theEvent</span> <span class="nf">modifierFlags</span><span class="p">]</span> <span class="o">&amp;</span> <span class="n">NSDeviceIndependentModifierFlagsMask</span><span class="p">;</span>
<span class="n">NSNumber</span> <span class="o">*</span><span class="n">shiftPressed</span> <span class="o">=</span> <span class="p">(</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">NSShiftKeyMask</span><span class="p">);</span>
 
<span class="k">if</span> <span class="p">([</span><span class="n">theEvent</span> <span class="nf">keyCode</span><span class="p">]</span> <span class="o">==</span> <span class="mi">38</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// j</span>
    <span class="n">NSUInteger</span> <span class="n">index</span> <span class="o">=</span> <span class="p">[[</span><span class="n">self</span> <span class="nf">selectedRowIndexes</span><span class="p">]</span> <span class="nf">lastIndex</span><span class="p">]</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">([</span><span class="n">shiftPressed</span> <span class="nf">boolValue</span><span class="p">])</span> <span class="p">{</span>
        <span class="p">[</span><span class="n">self</span> <span class="nf">selectRowIndexes</span><span class="p">:[</span><span class="n">NSIndexSet</span> <span class="nf">indexSetWithIndex</span><span class="p">:</span><span class="n">index</span><span class="p">]</span> <span class="nf">byExtendingSelection</span><span class="p">:</span><span class="nb">YES</span><span class="p">];</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="p">[</span><span class="n">self</span> <span class="nf">selectRowIndexes</span><span class="p">:[</span><span class="n">NSIndexSet</span> <span class="nf">indexSetWithIndex</span><span class="p">:</span><span class="n">index</span><span class="p">]</span> <span class="nf">byExtendingSelection</span><span class="p">:</span><span class="nb">NO</span><span class="p">];</span>
    <span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="nf">if</span> <span class="p">([</span><span class="n">theEvent</span> <span class="nf">keyCode</span><span class="p">]</span> <span class="o">==</span> <span class="mi">40</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// k</span>
    <span class="n">NSUInteger</span> <span class="n">index</span> <span class="o">=</span> <span class="p">[[</span><span class="n">self</span> <span class="nf">selectedRowIndexes</span><span class="p">]</span> <span class="nf">lastIndex</span><span class="p">]</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">([</span><span class="n">shiftPressed</span> <span class="nf">boolValue</span><span class="p">])</span> <span class="p">{</span>
        <span class="p">[</span><span class="n">self</span> <span class="nf">selectRowIndexes</span><span class="p">:[</span><span class="n">NSIndexSet</span> <span class="nf">indexSetWithIndex</span><span class="p">:</span><span class="n">index</span><span class="p">]</span> <span class="nf">byExtendingSelection</span><span class="p">:</span><span class="nb">YES</span><span class="p">];</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="p">[</span><span class="n">self</span> <span class="nf">selectRowIndexes</span><span class="p">:[</span><span class="n">NSIndexSet</span> <span class="nf">indexSetWithIndex</span><span class="p">:</span><span class="n">index</span><span class="p">]</span> <span class="nf">byExtendingSelection</span><span class="p">:</span><span class="nb">NO</span><span class="p">];</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>The issue with this is the way <code class="language-plaintext highlighter-rouge">NSTableView</code> typically expands it's selection. I think of it as a pivot point where you start. Then you go up and down relative to that point. So if you start at index 2 and go down till index 4, you should have 2 rows selected. Then when you go back up you should deselect the rows and indexes 3 and 4 and select the rows and index 1 and 0. At this point I realized it was more difficult than I realized at first and went in search on another solution.</p>

<p>Attempt 2: The next solution I discovered used the <a href="https://developer.apple.com/library/mac/#documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html">Quartz Event Services</a> APIs.</p>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="k">if</span> <span class="p">([</span><span class="n">theEvent</span> <span class="nf">keyCode</span><span class="p">]</span> <span class="o">==</span> <span class="mi">38</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// The letter 'j'</span>
    <span class="n">CGEventRef</span> <span class="n">e</span> <span class="o">=</span> <span class="n">CGEventCreateKeyboardEvent</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="p">(</span><span class="n">CGKeyCode</span><span class="p">)</span><span class="mi">125</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
    <span class="n">CGEventPost</span><span class="p">(</span><span class="n">kCGSessionEventTap</span><span class="p">,</span> <span class="n">e</span><span class="p">);</span>
    <span class="n">CFRelease</span><span class="p">(</span><span class="n">e</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="nf">if</span> <span class="p">([</span><span class="n">theEvent</span> <span class="nf">keyCode</span><span class="p">]</span> <span class="o">==</span> <span class="mi">40</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// The letter 'k'</span>
    <span class="n">CGEventRef</span> <span class="n">e</span> <span class="o">=</span> <span class="n">CGEventCreateKeyboardEvent</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="p">(</span><span class="n">CGKeyCode</span><span class="p">)</span><span class="mi">126</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
    <span class="n">CGEventPost</span><span class="p">(</span><span class="n">kCGSessionEventTap</span><span class="p">,</span> <span class="n">e</span><span class="p">);</span>
    <span class="n">CFRelease</span><span class="p">(</span><span class="n">e</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>This solution worked perfectly, at first. This mainly emulates a key press with a different key code. So as you can see I was catching j and k and spitting them out as down and up. I spent a few minutes testing this before I remembered that I had sandboxing disabled so I could more easily delete my application support folder while messing with my Core Data stack. There went that solution.</p>

<p>Attempt 3: Before I used the weird <code class="language-plaintext highlighter-rouge">CGEventRef</code> solution I tried to create my own <code class="language-plaintext highlighter-rouge">NSEvent</code> passing it all the same attributes from the original event (all this code is being used in the <code class="language-plaintext highlighter-rouge">keyDown:</code> function of my subclass) but I couldn't figure out how to get the correct character string for the up and down arrows. I typically use <a href="http://manytricks.com/keycodes/">Key Codes</a> to get all the possible information you could want about each key you press. But for some keys, including the arrow keys, it returns garbage for the character code. Then I discovered <a href="http://stackoverflow.com/a/4434934/902968">this answer</a> on StackOverflow where there is a brief mention of <code class="language-plaintext highlighter-rouge">NSUpArrowFunctionKey</code>. With that I came up with this.</p>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="k">if</span> <span class="p">([</span><span class="n">theEvent</span> <span class="nf">keyCode</span><span class="p">]</span> <span class="o">==</span> <span class="mi">38</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// j</span>
    <span class="n">unichar</span> <span class="n">down</span> <span class="o">=</span> <span class="n">NSDownArrowFunctionKey</span><span class="p">;</span>
    <span class="n">NSString</span> <span class="o">*</span><span class="n">downString</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSString</span> <span class="nf">stringWithCharacters</span><span class="p">:</span><span class="o">&amp;</span><span class="n">down</span> <span class="nf">length</span><span class="p">:</span><span class="mi">1</span><span class="p">];</span>
    <span class="n">NSEvent</span> <span class="o">*</span><span class="n">newEvent</span> <span class="o">=</span><span class="p">[</span><span class="n">NSEvent</span> <span class="nf">keyEventWithType</span><span class="p">:</span><span class="n">NSKeyDown</span>
                                        <span class="nl">location:</span><span class="n">theEvent</span><span class="p">.</span><span class="n">locationInWindow</span>
                                   <span class="nl">modifierFlags:</span><span class="n">theEvent</span><span class="p">.</span><span class="n">modifierFlags</span>
                                       <span class="nl">timestamp:</span><span class="n">theEvent</span><span class="p">.</span><span class="n">timestamp</span>
                                    <span class="nl">windowNumber:</span><span class="n">theEvent</span><span class="p">.</span><span class="n">windowNumber</span>
                                         <span class="nl">context:</span><span class="nb">nil</span>
                                      <span class="nl">characters:</span><span class="n">downString</span>
                     <span class="nl">charactersIgnoringModifiers:</span><span class="n">downString</span>
                                       <span class="nl">isARepeat:</span><span class="n">theEvent</span><span class="p">.</span><span class="n">isARepeat</span>
                                         <span class="nl">keyCode:</span><span class="n">down</span><span class="p">];</span>
    
    <span class="p">[</span><span class="n">super</span> <span class="nf">keyDown</span><span class="p">:</span><span class="n">newEvent</span><span class="p">];</span>
<span class="p">}</span> <span class="k">else</span> <span class="nf">if</span> <span class="p">([</span><span class="n">theEvent</span> <span class="nf">keyCode</span><span class="p">]</span> <span class="o">==</span> <span class="mi">40</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// k</span>
    <span class="n">unichar</span> <span class="n">up</span> <span class="o">=</span> <span class="n">NSUpArrowFunctionKey</span><span class="p">;</span>
    <span class="n">NSString</span> <span class="o">*</span><span class="n">upString</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSString</span> <span class="nf">stringWithCharacters</span><span class="p">:</span><span class="o">&amp;</span><span class="n">up</span> <span class="nf">length</span><span class="p">:</span><span class="mi">1</span><span class="p">];</span>
    <span class="n">NSEvent</span> <span class="o">*</span><span class="n">newEvent</span> <span class="o">=</span><span class="p">[</span><span class="n">NSEvent</span> <span class="nf">keyEventWithType</span><span class="p">:</span><span class="n">NSKeyDown</span>
                                        <span class="nl">location:</span><span class="n">theEvent</span><span class="p">.</span><span class="n">locationInWindow</span>
                                   <span class="nl">modifierFlags:</span><span class="n">theEvent</span><span class="p">.</span><span class="n">modifierFlags</span>
                                       <span class="nl">timestamp:</span><span class="n">theEvent</span><span class="p">.</span><span class="n">timestamp</span>
                                    <span class="nl">windowNumber:</span><span class="n">theEvent</span><span class="p">.</span><span class="n">windowNumber</span>
                                         <span class="nl">context:</span><span class="nb">nil</span>
                                      <span class="nl">characters:</span><span class="n">upString</span>
                     <span class="nl">charactersIgnoringModifiers:</span><span class="n">upString</span>
                                       <span class="nl">isARepeat:</span><span class="n">theEvent</span><span class="p">.</span><span class="n">isARepeat</span>
                                         <span class="nl">keyCode:</span><span class="n">up</span><span class="p">];</span>
    
    <span class="p">[</span><span class="n">super</span> <span class="nf">keyDown</span><span class="p">:</span><span class="n">newEvent</span><span class="p">];</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="p">[</span><span class="n">super</span> <span class="nf">keyDown</span><span class="p">:</span><span class="n">theEvent</span><span class="p">];</span>
<span class="p">}</span></code></pre></figure>

<p>Not the prettiest solution I but one that seems to work perfectly, even sandboxed, to provide the expected behavior in an <code class="language-plaintext highlighter-rouge">NSTableView</code> subclass.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Backing up with Capistrano]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/01/05/backing-up-with-capistrano/" />
      <id>https://smileykeith.com/2013/01/05/backing-up-with-capistrano</id>
      <updated>2013-01-05T12:12:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>We all know not backing up has <a href="http://www.wired.com/gadgetlab/2012/08/apple-amazon-mat-honan-hacking/">consequences</a>. While losing sentimental files would definitely ruin your day, losing your web server's data could be even worse. I've mentioned <a href="http://smileykeith.com/2013/01/02/linode-setup/">before</a> that I use <a href="http://www.linode.com/?r=c190426bf1ff0f144b48997675bae8b32d339824">Linode</a> for my server hosting, and while they do offer an <a href="http://library.linode.com/backup-service">automated backup service</a> I decided I'd rather setup my own solution to back up periodically to my local machine.</p>

<p>Many people use <a href="http://en.wikipedia.org/wiki/Rsync">rsync</a> to do their server backups. In fact Linode even has a <a href="http://library.linode.com/linux-tools/utilities/rsync#sph_use-rsync-to-back-up-production-environments">guide</a> on how to set it up (there's a better one <a href="http://feross.org/how-to-setup-your-linode/">here</a>). I decided that instead of a 1 for 1 directory backup, I would prefer to have a <a href="http://en.wikipedia.org/wiki/Tar_(file_format)">tarball</a> of the contents. While I could've easily done this with a few bash commands from the server that's not particular ideal for my setup. My local machines don't run 24/7 so if I set it up on the server to automate the backup every week, it may try to initiate the backup when my machine was off (I could try to guess when it's on every week but that's not ideal either).</p>

<p>The obvious solution to this is run it from my local machine instead every week. That way once a week when it's powered up it would log in to the server, create the tarball and pull it down. Insert <a href="https://github.com/capistrano/capistrano">Capistrano</a> (<code class="language-plaintext highlighter-rouge">[sudo] gem install capistrano</code>) a <a href="http://rubygems.org/">RubyGem</a> for 'Remote multi-server automation.' So I wrote a very basic <code class="language-plaintext highlighter-rouge">Capfile</code> to automate this for me (replace the path to your <code class="language-plaintext highlighter-rouge">www</code> folder accordingly).</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">load</span> <span class="s1">'deploy'</span>

<span class="vg">$SERVER_USER</span> <span class="o">=</span> <span class="s2">"username"</span>
<span class="vg">$SERVER_IP</span>   <span class="o">=</span> <span class="s2">"1.1.1.1"</span>

<span class="n">desc</span> <span class="s2">"Backs up server www files"</span>
<span class="n">task</span> <span class="ss">:backup</span><span class="p">,</span> <span class="ss">:hosts</span> <span class="o">=&gt;</span> <span class="vg">$SERVER_IP</span> <span class="k">do</span>
  <span class="n">run</span> <span class="s2">"cd /srv; tar -pvczf ~/backup.tar.gz www/"</span>
  <span class="n">run_locally</span> <span class="s2">"scp </span><span class="si">#{</span> <span class="vg">$SERVER_USER</span> <span class="si">}</span><span class="s2">@</span><span class="si">#{</span> <span class="vg">$SERVER_IP</span> <span class="si">}</span><span class="s2">:~/backup.tar.gz ~/Dropbox/Backups/Server"</span>
<span class="k">end</span></code></pre></figure>

<p>Then I added this to my crontab on my local machine by running <code class="language-plaintext highlighter-rouge">crontab -e</code> and adding the line:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">@weekly /Users/ksmiley/.rbenv/shims/cap <span class="nt">-f</span> ~/path/to/Capfile backup</code></pre></figure>

<p>I included the path to the Capistrano executable since cron (on OS X) executes tasks with <code class="language-plaintext highlighter-rouge">sh</code>, which isn't setup with my <code class="language-plaintext highlighter-rouge">$PATH</code>.</p>

]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Raking Podspecs]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/01/04/raking-podspecs/" />
      <id>https://smileykeith.com/2013/01/04/raking-podspecs</id>
      <updated>2013-01-04T11:09:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>I spend a decent amount of time these days helping maintain the <a href="http://cocoapods.org/">CocoaPods</a> <a href="http://github.com/cocoapods/specs">specs repo</a> by managing pull requests and issues. CococaPods is an awesome dependency manager similar to <a href="http://rubygems.org/">Rubygems</a> for Objective-C projects. Unfortunately a lot of submitted podspecs haven't been correctly formatted or they're missing required information. CocoaPods has an awesome build in command <code class="language-plaintext highlighter-rouge">pod spec lint</code> that allows you to make sure the spec is valid and complete. Understandably people who are new to CocoaPods trying to submit their libraries are unaware of this awesome tool. Therefore when I look through the pull requests, I like to lint them myself (CocoaPods does <a href="https://travis-ci.org/CocoaPods/Specs">utilize Travis</a> but unfortunately it can't do everything).</p>

<p>Since CocoaPods supports multiple versions of Ruby (1.8.7 and 1.9.3) to be complete ideally you'd lint them on both versions. Tools like <a href="https://rvm.io/">RVM</a> and <a href="https://github.com/sstephenson/rbenv">rbenv</a>(my tool of choice) make it easy to quickly switch between different versions of Ruby using <code class="language-plaintext highlighter-rouge">.rvmrc</code> and <code class="language-plaintext highlighter-rouge">.rbenv-version</code> respectively. As you can probably assume I wanted to automate this. So I wrote a quick <a href="http://rake.rubyforge.org/">Rakefile</a> to do this for me.</p>

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#!/usr/bin/env rake</span>

<span class="c1"># NOTE: Must be using rbenv 4.0 to use `system` and `.ruby-version`</span>
<span class="c1">## Set your preferred ruby versions</span>
<span class="vg">$V18</span> <span class="o">=</span> <span class="s1">'system'</span>
<span class="vg">$V19</span> <span class="o">=</span> <span class="s1">'1.9.3-p385'</span>
<span class="vg">$RBENV</span> <span class="o">=</span> <span class="s1">'.ruby-version'</span>

<span class="c1"># The gem to use</span>
<span class="vg">$GEM</span> <span class="o">=</span> <span class="s1">'cocoapods'</span>

<span class="n">task</span> <span class="ss">:default</span> <span class="o">=&gt;</span> <span class="ss">:lint</span>
<span class="n">task</span> <span class="ss">:c</span>       <span class="o">=&gt;</span> <span class="ss">:clean</span>

<span class="n">desc</span> <span class="s2">"Lint podspecs on multiple versions of ruby with rbenv"</span>
<span class="n">task</span> <span class="ss">:lint</span> <span class="k">do</span>
  <span class="k">if</span> <span class="no">Dir</span><span class="p">.</span><span class="nf">glob</span><span class="p">(</span><span class="s1">'*.podspec'</span><span class="p">).</span><span class="nf">count</span> <span class="o">&lt;</span> <span class="mi">1</span>
    <span class="nb">puts</span> <span class="s2">"No podspecs in </span><span class="si">#{</span> <span class="no">Dir</span><span class="p">.</span><span class="nf">pwd</span> <span class="si">}</span><span class="s2">"</span>
    <span class="nb">exit</span>
  <span class="k">end</span>

  <span class="n">existed</span> <span class="o">=</span> <span class="n">versionFileExists?</span>
  <span class="k">if</span> <span class="n">existed</span>
    <span class="n">old_version</span> <span class="o">=</span> <span class="n">currentVersion</span>
  <span class="k">end</span>

  <span class="c1"># Loop through all podspecs</span>
  <span class="no">Dir</span><span class="p">.</span><span class="nf">glob</span><span class="p">(</span><span class="s1">'*.podspec'</span><span class="p">).</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span>
    <span class="c1"># Loop through ruby versions</span>
    <span class="mi">2</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span>
      <span class="n">version</span> <span class="o">=</span> <span class="n">x</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">?</span> <span class="vg">$V18</span> <span class="p">:</span> <span class="vg">$V19</span>
      <span class="n">writeVersion</span><span class="p">(</span><span class="n">version</span><span class="p">)</span>

      <span class="nb">puts</span> <span class="s2">"Linting </span><span class="si">#{</span> <span class="n">file</span> <span class="si">}</span><span class="s2"> on Ruby version </span><span class="si">#{</span> <span class="n">currentVersion</span> <span class="si">}</span><span class="s2">"</span>
      <span class="nb">puts</span> <span class="n">lint</span><span class="p">(</span><span class="n">file</span><span class="p">)</span>
    <span class="k">end</span>
  <span class="k">end</span>

  <span class="c1"># If the dotfile already existed rewrite the original code</span>
  <span class="k">if</span> <span class="n">existed</span>
    <span class="n">writeVersion</span><span class="p">(</span><span class="n">old_version</span><span class="p">)</span>
  <span class="k">else</span>
    <span class="no">File</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="vg">$RBENV</span><span class="p">)</span> <span class="k">if</span> <span class="n">versionFileExists?</span>
  <span class="k">end</span>
<span class="k">end</span>

<span class="n">desc</span> <span class="s2">"Delete all podspec files"</span>
<span class="n">task</span> <span class="ss">:clean</span> <span class="k">do</span>
  <span class="no">Dir</span><span class="p">.</span><span class="nf">glob</span><span class="p">(</span><span class="s1">'*.podspec'</span><span class="p">).</span><span class="nf">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span> <span class="no">File</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="n">file</span><span class="p">)</span> <span class="p">}</span>
  <span class="no">Dir</span><span class="p">.</span><span class="nf">glob</span><span class="p">(</span><span class="vg">$RBENV</span><span class="p">).</span><span class="nf">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span> <span class="no">File</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="n">file</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>

<span class="c1"># Check to see if the current dotfile exists</span>
<span class="k">def</span> <span class="nf">versionFileExists?</span>
  <span class="no">File</span><span class="p">.</span><span class="nf">exists?</span><span class="p">(</span><span class="vg">$RBENV</span><span class="p">)</span>
<span class="k">end</span>

<span class="c1"># Retrieve the current version from the rbenv dotfile</span>
<span class="k">def</span> <span class="nf">currentVersion</span>
  <span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="vg">$RBENV</span><span class="p">,</span> <span class="s2">"r"</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">io</span><span class="o">|</span> <span class="n">io</span><span class="p">.</span><span class="nf">read</span> <span class="p">}</span>
<span class="k">end</span>

<span class="c1"># Write out a version to .rbenv-version</span>
<span class="k">def</span> <span class="nf">writeVersion</span><span class="p">(</span><span class="n">version</span><span class="p">)</span>
  <span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="vg">$RBENV</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span> <span class="n">file</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span> <span class="n">version</span> <span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>

<span class="c1"># Run the lint</span>
<span class="k">def</span> <span class="nf">lint</span><span class="p">(</span><span class="n">podspec</span><span class="p">)</span>
  <span class="sx">%x[pod spec lint "</span><span class="si">#{</span> <span class="n">podspec</span> <span class="si">}</span><span class="sx">"]</span>
<span class="k">end</span></code></pre></figure>

]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Spawning iTerm Windows]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/01/03/spawn-iterm/" />
      <id>https://smileykeith.com/2013/01/03/spawn-iterm</id>
      <updated>2013-01-03T12:34:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>I've recently been searching around for a good way to 'spawn' an <a href="http://www.iterm2.com/">iTerm</a> window (no I don't use tabs in iTerm), at the <code class="language-plaintext highlighter-rouge">pwd</code> in my current iTerm window. I couldn't find any good way to do it so I jumped in to AppleScript Editor and made something happen.</p>

<figure class="highlight"><pre><code class="language-applescript" data-lang="applescript"><span class="k">on</span> <span class="nb">run</span><span class="w"> </span><span class="nv">argv</span><span class="w">
	</span><span class="k">tell</span><span class="w"> </span><span class="nb">application</span><span class="w"> </span><span class="s2">"iTerm"</span><span class="w">
		</span><span class="k">set</span><span class="w"> </span><span class="nv">t</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="nb">make</span><span class="w"> </span><span class="nb">new</span><span class="w"> </span><span class="nv">terminal</span><span class="w">
		</span><span class="k">tell</span><span class="w"> </span><span class="nv">t</span><span class="w">
			</span><span class="nb">activate</span><span class="w"> </span><span class="nv">current</span><span class="w"> </span><span class="nv">session</span><span class="w">
			</span><span class="nb">launch</span><span class="w"> </span><span class="nv">session</span><span class="w"> </span><span class="s2">"Default Session"</span><span class="w">
			</span><span class="k">tell</span><span class="w"> </span><span class="nb">the</span><span class="w"> </span><span class="nb">last</span><span class="w"> </span><span class="nv">session</span><span class="w">
				</span><span class="nb">write</span><span class="w"> </span><span class="nb">text</span><span class="w"> </span><span class="s2">"cd \""</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="nb">item</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nv">argv</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="s2">"\"; clear; pwd"</span><span class="w">
			</span><span class="k">end</span><span class="w"> </span><span class="k">tell</span><span class="w">
		</span><span class="k">end</span><span class="w"> </span><span class="k">tell</span><span class="w">
	</span><span class="k">end</span><span class="w"> </span><span class="k">tell</span><span class="w">
</span><span class="k">end</span><span class="w"> </span><span class="nb">run</span></code></pre></figure>

<p>I then added it to my <a href="http://www.zsh.org/">zsh</a> aliases with:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="k">function </span>spawn <span class="o">{</span>
  osascript ~/Dropbox/Code/Applescript/Spawn/SpawniTerm.applescript <span class="nv">$PWD</span>
<span class="o">}</span></code></pre></figure>

<p>Now I can call <code class="language-plaintext highlighter-rouge">spawn</code> from any iTerm or Terminal window to open a new iTerm session wherever I called it from.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[NSWindow Global Hotkey]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/01/03/nswindow-global-hotkey/" />
      <id>https://smileykeith.com/2013/01/03/nswindow-global-hotkey</id>
      <updated>2013-01-03T10:44:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>For quite a while I was having trouble dealing with a global show/hide hotkey for windows in Objective-C. Global hotkeys are already <a href="http://stackoverflow.com/questions/4807319/register-hotkey">hard</a> <a href="https://github.com/keith/PTHotKeyTest">enough</a>. Although <a href="https://github.com/shpakovski/MASShortcut">MASShortcut</a> has solved that. Yes I know of <a href="http://wafflesoftware.net/shortcut/">ShortcutRecorder</a> but it's very dated (MASShortcut even uses blocks!).</p>

<p>I found that once I had the shortcut working I was having a hard time dealing with opening and closing, showing and hiding the application. What seemed to happen was when the method was called and <code class="language-plaintext highlighter-rouge">[[NSRunningApplication currentApplication] isActive]</code> was evaluated in an <code class="language-plaintext highlighter-rouge">if</code> statement along with an <code class="language-plaintext highlighter-rouge">else</code> clause, if the application was hidden using <code class="language-plaintext highlighter-rouge">[[NSApplication sharedApplication] hide:self];</code> it was reevaluated and it hit the <code class="language-plaintext highlighter-rouge">else</code> case. This also happened with an <code class="language-plaintext highlighter-rouge">if</code> statement checking if the window was already visible with <code class="language-plaintext highlighter-rouge">[myWindow isVisible]</code> even with <code class="language-plaintext highlighter-rouge">return;</code> statements inserted in appropriate places.</p>

<p>My solution was adding <code class="language-plaintext highlighter-rouge">NSNumber</code>s acting as booleans to keep track of the value allowing me to avoid <code class="language-plaintext highlighter-rouge">else</code> statements altogether and use <code class="language-plaintext highlighter-rouge">else if</code>s instead.</p>

<figure class="highlight"><pre><code class="language-objc" data-lang="objc"><span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">showHideMainWindow</span> <span class="p">{</span>
    <span class="n">NSNumber</span> <span class="o">*</span><span class="n">wasActive</span> <span class="o">=</span> <span class="nb">@NO</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">([[</span><span class="n">NSRunningApplication</span> <span class="nf">currentApplication</span><span class="p">]</span> <span class="nf">isActive</span><span class="p">])</span> <span class="p">{</span>
        <span class="n">wasActive</span> <span class="o">=</span> <span class="nb">@YES</span><span class="p">;</span>
        <span class="n">NSNumber</span> <span class="o">*</span><span class="n">wasOpen</span> <span class="o">=</span> <span class="nb">@NO</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">([</span><span class="n">self</span><span class="p">.</span><span class="n">window</span> <span class="nf">isVisible</span><span class="p">])</span> <span class="p">{</span>
            <span class="n">wasOpen</span> <span class="o">=</span> <span class="nb">@YES</span><span class="p">;</span>
            <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">window</span> <span class="nf">close</span><span class="p">];</span>
            <span class="p">[[</span><span class="n">NSApplication</span> <span class="nf">sharedApplication</span><span class="p">]</span> <span class="nf">hide</span><span class="p">:</span><span class="n">self</span><span class="p">];</span>
        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">[</span><span class="n">wasOpen</span> <span class="nf">boolValue</span><span class="p">])</span> <span class="p">{</span>
            <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">window</span> <span class="nf">makeKeyAndOrderFront</span><span class="p">:</span><span class="n">self</span><span class="p">];</span>
        <span class="p">}</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">[</span><span class="n">wasActive</span> <span class="nf">boolValue</span><span class="p">])</span> <span class="p">{</span>
        <span class="p">[[</span><span class="n">NSApplication</span> <span class="nf">sharedApplication</span><span class="p">]</span> <span class="nf">activateIgnoringOtherApps</span><span class="p">:</span><span class="nb">YES</span><span class="p">];</span>
        <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">window</span> <span class="nf">makeKeyAndOrderFront</span><span class="p">:</span><span class="n">self</span><span class="p">];</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[Linode Setup]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2013/01/02/linode-setup/" />
      <id>https://smileykeith.com/2013/01/02/linode-setup</id>
      <updated>2013-01-02T15:19:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>I've been using <a href="http://www.linode.com/?r=c190426bf1ff0f144b48997675bae8b32d339824">Linode</a> for about the past 4 months. I was previously using <a href="http://www.hostmonster.com/">HostMonster</a> for all of my hosting, while I would recommend them 80% of the time, Linode and the nature of a VPS allows many things you cannot do with shared hosting.</p>

<p>I was looking around for some recommendations on how to deal with user accounts and permissions and ran across this great article that has almost every piece of information you need to get your server from nothing to deployed.</p>

<p>Linode allows you complete control over your VPS which, for beginners (like me when I started with Linode), difficult to get started with. Just follow this guide and you'll be good to go.</p>
]]>
      </content>
    </entry>
  
    <entry>
      <title type="html"><![CDATA[The fear of shipping]]></title>
      <link rel="alternate" type="text/html" href="https://smileykeith.com/2012/12/12/the-fear-of-shipping/" />
      <id>https://smileykeith.com/2012/12/12/the-fear-of-shipping</id>
      <updated>2012-12-12T17:42:00-08:00</updated>
      <author>
        
          <name><![CDATA[Keith Smiley]]></name>
        
      </author>
      <content type="html">
        <![CDATA[<p>Something I've become very aware of lately is how difficult it is for me to ship. I have at least a dozen unfinished projects that I could probably ship, yet I find any excuse to hold them back.</p>

<p>I could say forget it and ship it. Then depending on the amount of feedback I received, decide whether or not it was worth putting more time into. I often fall victim to "just one more feature here" or "oh it would be great if I added this first." When I should have just shipped.</p>

<p>After nearly convincing myself to do this, what is holding me back? The fear of ruining my first impressions. Up to this point my programming career, especially in public, has been pretty sparse. I've made a few websites, and I shipped an internal enterprise iOS app, but that doesn't count for much. So as far as most people know I sit at home on my thumbs 24/7 occasionally tweeting about Objective-C frameworks. I want to be perceived well in the community, I respect a lot of indie iOS and OS X developers and the last thing I want is attention for an unfinished or unpolished product. I am starting to realize that this is unrealistic. I would love my first major application to be perfect but that's just not feasible. I hope to get there in the future, but I am very close to accepting that getting there requires stepping stones. For me, those stepping stones might be some useless OS X utilities that I've made for myself and now want to share with the world. No matter how unpolished.</p>
]]>
      </content>
    </entry>
  
</feed>
