# Keith Smiley

Published articles for Keith Smiley.

This is one page of public article previews, not the complete archive. Follow Next page to continue. Summaries are not the original full articles.

## Bazel rule extensions

DevFeed: [Bazel rule extensions](<https://devfeed.tech/articles/bazel-rule-extensions-25413.md>)

Original publisher: [Read original article](<https://smileykeith.com/2025/10/31/bazel-rule-extensions/>)

Author: Keith Smiley

Published: 2025-10-31T17:00:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Programming](<https://devfeed.tech/topics/programming.md>), [Code](<https://devfeed.tech/topics/code.md>), [Polymorphism](<https://devfeed.tech/topics/polymorphism.md>)

Tags: [bazel](<https://devfeed.tech/tags/bazel.md>), [extensions](<https://devfeed.tech/tags/extensions.md>), [inheritance](<https://devfeed.tech/tags/inheritance.md>)

### AI overview

This article explains Bazel 8.0 rule extensions, which let developers inherit and modify the behavior of existing rules without reimplementing them or maintaining a fork. It presents examples of post-processing rule outputs and manipulating providers, including a use case for consistent debug information across macOS and Linux.

### Source excerpt

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). 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 rule extensions feature and some practical use cases I have found for it. Basic rule extensions 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. Let's say you have a rule that concatenates the given srcs: def _foo_impl(ctx): output = ctx.actions.declare_file("output.txt") ctx.actions.run_shell( inputs = ctx.files.srcs, outputs = [output], command = "cat {} > {}".format(" ".join([src.path for src in ctx.files.srcs]), output.path), ) return [DefaultInfo(files = depset([output]))] foo = rule( implementation = _foo_impl, attrs = { "srcs": attr.label_list(allow_files = True), }, ) 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 _foo_impl 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: def _bar_impl(ctx): providers = ctx.super() # Invoke 'foo' and get the providers # NOTE: This assumes there's always only the provider we want. original_output = providers[0].files.to_list()[0] new_output = ctx.actions.declare_file("new_output.tx

## Understanding Apple Debug Info

DevFeed: [Understanding Apple Debug Info](<https://devfeed.tech/articles/understanding-apple-debug-info-25412.md>)

Original publisher: [Read original article](<https://smileykeith.com/2025/09/21/understanding-apple-debug-info/>)

Author: Keith Smiley

Published: 2025-09-21T17:00:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [debugging](<https://devfeed.tech/topics/debugging.md>), [Xcode](<https://devfeed.tech/topics/xcode.md>), [C](<https://devfeed.tech/topics/c.md>), [Linux](<https://devfeed.tech/topics/linux.md>)

Tags: [apple](<https://devfeed.tech/tags/apple.md>), [binaries](<https://devfeed.tech/tags/binaries.md>), [build](<https://devfeed.tech/tags/build.md>), [c](<https://devfeed.tech/tags/c.md>), [clang](<https://devfeed.tech/tags/clang.md>), [compiler](<https://devfeed.tech/tags/compiler.md>), [debug](<https://devfeed.tech/tags/debug.md>), [debugging](<https://devfeed.tech/tags/debugging.md>), [ios](<https://devfeed.tech/tags/ios.md>), [llvm](<https://devfeed.tech/tags/llvm.md>), [macos](<https://devfeed.tech/tags/macos.md>), [xcode](<https://devfeed.tech/tags/xcode.md>)

### AI overview

This article explains how debug information works for Apple platforms and Mach-O binaries, contrasting the approach with Linux ELF binaries. It shows how compiler-generated debug metadata is stored in intermediate object files and located by lldb, then introduces issues that can arise in complex or distributed Bazel builds, including invalid absolute paths.

### Source excerpt

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 lldb so that you can fix them. If you have a particularly complex build, potentially managed by Bazel1 or another tool, especially if you are using distributed builds, you are even more likely to hit issues. Let's dive in to how the pieces fit together. A brief explanation of debug info Debug info is metadata produced by the compiler that is consumed by debuggers (like lldb), 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 lldb shows primarily raw instructions and addresses, which is rarely acceptable for common debugging workflows. Inspecting debug info 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 lldb can find it (this is conceptually similar to if you use -gsplit-dwarf on Linux). Let's inspect some binaries to see what this really means. First we create a small binary: $ cat main.c int main() { return 0; } $ clang main.c -g -c -o main.o $ clang main.o -o main If we attempt to inspect the debug info contained in main, we find nothing: $ dwarfdump main # use llvm-dwarfdump if not on macOS main: file format Mach-O arm64 .debug_info contents: However when we debug this binary in lldb, you will correctly see the source file and line number information: $ lldb -- main (lldb) target create "main" Current executable set to '/tmp/demo/main' (arm64). (l

## Finding unused targets with bazel

DevFeed: [Finding unused targets with bazel](<https://devfeed.tech/articles/finding-unused-targets-with-bazel-25411.md>)

Original publisher: [Read original article](<https://smileykeith.com/2025/03/24/unused-bazel-targets/>)

Author: Keith Smiley

Published: 2025-03-24T18:00:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Graphs](<https://devfeed.tech/topics/graphs.md>), [coding](<https://devfeed.tech/topics/coding.md>), [Code](<https://devfeed.tech/topics/code.md>), [toolchains](<https://devfeed.tech/topics/toolchains.md>)

Tags: [bazel](<https://devfeed.tech/tags/bazel.md>), [build](<https://devfeed.tech/tags/build.md>), [code](<https://devfeed.tech/tags/code.md>), [coding](<https://devfeed.tech/tags/coding.md>), [dependencies](<https://devfeed.tech/tags/dependencies.md>), [graph](<https://devfeed.tech/tags/graph.md>), [toolchains](<https://devfeed.tech/tags/toolchains.md>), [use-cases](<https://devfeed.tech/tags/use-cases.md>)

### AI overview

This article explains how to use bazel query to inspect a build graph and identify unused targets. It presents a query that subtracts dependencies of top-level targets from all first-party targets, then discusses handling edge cases with kind filters and special tags such as allow-unused. It also describes tagging toolchains and other targets whose dependencies should be treated as used.

### Source excerpt

Once you've fully migrated a codebase to bazel, one of the many advantages is that you can easily inspect your build graph using bazel query. 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. The simplest version of this query starts like this: let all_targets = //... in let top_level_targets = tests($all_targets) union kind("(.*_binary) rule", $all_targets) in $all_targets - deps($top_level_targets) 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. 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 testonly dependencies, although you could special case those as shown below). 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 kind filter: kind("(.*_binary|platform|test_suite) rule", $all_targets) 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: 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 Then you can add tags = ["allow-un

## Bazel caching and compressed debug info

DevFeed: [Bazel caching and compressed debug info](<https://devfeed.tech/articles/bazel-caching-and-compressed-debug-info-25410.md>)

Original publisher: [Read original article](<https://smileykeith.com/2025/02/14/compressed-debug-info/>)

Author: Keith Smiley

Published: 2025-02-14T18:00:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [C++](<https://devfeed.tech/topics/c-plus-plus.md>), [Caching](<https://devfeed.tech/topics/caching.md>), [LLVM](<https://devfeed.tech/topics/llvm.md>), [gcc](<https://devfeed.tech/topics/gcc.md>), [C](<https://devfeed.tech/topics/c.md>)

Tags: [bazel](<https://devfeed.tech/tags/bazel.md>), [build](<https://devfeed.tech/tags/build.md>), [c-plus-plus](<https://devfeed.tech/tags/c-plus-plus.md>), [caching](<https://devfeed.tech/tags/caching.md>), [clang](<https://devfeed.tech/tags/clang.md>), [cmake](<https://devfeed.tech/tags/cmake.md>), [config](<https://devfeed.tech/tags/config.md>), [configuration](<https://devfeed.tech/tags/configuration.md>)

### AI overview

This article explains how compressing debug information can reduce C and C++ binary sizes and improve Bazel remote-cache efficiency. Using llvm-objcopy, the example reduces a roughly 536 MB binary to about 290 MB, and the authors report nearly 60% fewer cache reads after deployment.

### Source excerpt

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. 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. For codebases with lots of C or C++ one of the potentially easiest wins is to enable compressed debug information1. Let's look at an example from our codebase. Looking at the size of a non-trivial C++ binary built with -g -O2 (similar to cmake's RelWithDebInfo configuration), or binary clocks in at ~530mbs: % du -sh bin 536M bin To get a sense of what percentage of this binary is debug info, we can use llvm-objcopy to strip the debug info entirely: % llvm-objcopy --strip-debug bin strippedbin % du -sh strippedbin 159M strippedbin 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 --strip argument, but for developer builds, or other use cases where you need debug info, we can still improve this. If we use llvm-objcopy again, this time to compress the debug info, we can immediately see our potential gains: % llvm-objcopy --compress-debug-sections bin compressedbin % du -sh compressedbin 290M compressedbin This shows us we can get an almost 50%(!!) improvement in binary size in this example. To enable this in bazel, assuming you're using a relatively recent version of gcc or clang, you can add something like this to your .bazelrc2: build --enable_platform_specific_config build:linux --copt=-gz --host_copt=-gz build:linux --linkopt=-gz --host_linkopt=-gz In practice we saw cache reads drop by nearly 60% when we rolled out this change. Reducing binary size with this approach has a lot of benefits, but it's even more pronounced

## Printing rpaths with objdump

DevFeed: [Printing rpaths with objdump](<https://devfeed.tech/articles/printing-rpaths-with-objdump-25409.md>)

Original publisher: [Read original article](<https://smileykeith.com/2022/03/16/objdump-rpaths/>)

Author: Keith Smiley

Published: 2022-03-16T17:00:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [LLVM](<https://devfeed.tech/topics/llvm.md>), [macOS](<https://devfeed.tech/topics/macos.md>), [Xcode](<https://devfeed.tech/topics/xcode.md>), [Swift](<https://devfeed.tech/topics/swift.md>), [debugging](<https://devfeed.tech/topics/debugging.md>), [Tooling](<https://devfeed.tech/topics/tooling.md>)

Tags: [binaries](<https://devfeed.tech/tags/binaries.md>), [commands](<https://devfeed.tech/tags/commands.md>), [debugging](<https://devfeed.tech/tags/debugging.md>), [llvm](<https://devfeed.tech/tags/llvm.md>), [macos](<https://devfeed.tech/tags/macos.md>), [swift](<https://devfeed.tech/tags/swift.md>), [xcode](<https://devfeed.tech/tags/xcode.md>)

### AI overview

The article explains how to inspect runtime library search paths in Mach-O binaries. It compares a verbose otool pipeline with the more concise LLVM objdump --macho --rpaths command, introduced with LLVM 13 and Xcode 13.3 on macOS.

### Source excerpt

MachO binaries contain load commands to indicate to dyld where it should search for the libraries the binary depends on. These paths are often useful to inspect when debugging why your binary isn't discovering the libraries you'd expect. Previously you could discover these with: % 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 This example is quite verbose and fragile for such a common action, so recently I committed a change to add an easier option with LLVM's objdump. This change shipped with LLVM 13 or Xcode 13.3 on macOS, allowing you to run: % 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 This is much more succinct and memorable, but also has slightly different output. This is because objdump 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 --no-leading-headers. Hopefully you find this as useful as I do!

## Debugging bazel actions

DevFeed: [Debugging bazel actions](<https://devfeed.tech/articles/debugging-bazel-actions-25408.md>)

Original publisher: [Read original article](<https://smileykeith.com/2022/03/02/debugging-bazel-actions/>)

Author: Keith Smiley

Published: 2022-03-03T02:00:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [debugging](<https://devfeed.tech/topics/debugging.md>), [Command-line interface](<https://devfeed.tech/topics/cli.md>), [configuration](<https://devfeed.tech/topics/configuration.md>), [Tooling](<https://devfeed.tech/topics/tooling.md>)

Tags: [bazel](<https://devfeed.tech/tags/bazel.md>), [build](<https://devfeed.tech/tags/build.md>), [command-line](<https://devfeed.tech/tags/command-line.md>), [configuration](<https://devfeed.tech/tags/configuration.md>), [debugging](<https://devfeed.tech/tags/debugging.md>), [sandbox](<https://devfeed.tech/tags/sandbox.md>)

### AI overview

This tutorial explains how to reproduce Bazel actions outside Bazel's infrastructure for debugging and benchmarking. It covers preparing the build environment, disabling or preserving sandboxing, obtaining an action's command line, and forcing actions to rerun by invalidating inputs or changing flags.

### Source excerpt

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. 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. Since bazel has a lot of infrastructure for keeping builds hermetic, there are a few steps you need to take to roughly reproduce what bazel is doing so your debugging is as close to what it runs as possible. 1. Build and disable sandboxing 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 --spawn_strategy=standalone, or make it leave the sandbox base around by passing --sandbox_debug. 2. Grab your action's command line 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 -s flag (also known as --subcommands) 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: Invalidate the inputs for the action. Unlike other build systems touching input files isn't enough, I often add newlines or comments to files to force actions to re-run. Change the flags for a command line. For some actions such as C++ compiles, or native binary linking, there

## Auto linking with Mach-O binaries

DevFeed: [Auto linking with Mach-O binaries](<https://devfeed.tech/articles/auto-linking-with-mach-o-binaries-25407.md>)

Original publisher: [Read original article](<https://smileykeith.com/2022/02/23/lc-linker-option/>)

Author: Keith Smiley

Published: 2022-02-24T02:00:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Objective-C](<https://devfeed.tech/topics/objective-c.md>), [Swift](<https://devfeed.tech/topics/swift.md>), [toolchain](<https://devfeed.tech/topics/toolchain.md>)

Tags: [assembly](<https://devfeed.tech/tags/assembly.md>), [clang](<https://devfeed.tech/tags/clang.md>), [objective-c](<https://devfeed.tech/tags/objective-c.md>), [swift](<https://devfeed.tech/tags/swift.md>)

### AI overview

This article explains automatic linking for Mach-O binaries. It shows how Objective-C compilation can embed linker options for dependencies such as Foundation, how to inspect those options, and how omitting module support causes explicit linking to be required. It also covers module maps, Swift standard-library dependencies, and several ways to add linker options manually with clang, swiftc, or assembly directives.

### Source excerpt

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. For example, with this Objective-C file: #include <Foundation/Foundation.h> int main() { NSLog(@"Hello, World!"); return 1; } Compiled with: $ clang -fmodules -c foo.m -o foo.o You can then inspect the options added for use at link time: $ otool -l foo.o | grep LC_LINKER_OPTION -A3 cmd LC_LINKER_OPTION cmdsize 40 count 2 string #1 -framework string #2 Foundation ... Now when linking this binary you don't have to pass any extra flags to the linker to make sure you link Foundation: $ ld foo.o -syslibroot `xcrun --show-sdk-path` To compare, if you compile the binary without -fmodules1: $ clang -c foo.m -o foo.o You don't get any LC_LINKER_OPTIONs. Then when linking the binary with the same command as before, it fails with these errors: $ 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 To make it succeed you must explicitly link Foundation through an argument to your linker invocation: $ ld foo.o -syslibroot `xcrun --show-sdk-path` -framework Foundation Auto linking is also applied when using module maps that use the link directive. For example with this module map file: // module.modulemap module foo { link "foo" link framework "Foundation" } That you include with in this source file: @import foo; int main() { return 1; } And compile (with an include path to the module.modulemap file): $ clang -fmodules -c foo.m -o foo.o -I. The produced object depends on foo and Foundation. This can be useful for handwriting module map files for prebuilt libraries, and for quite a few other cases. You can read about thi

## Silencing iOS simulator log noise

DevFeed: [Silencing iOS simulator log noise](<https://devfeed.tech/articles/silencing-ios-simulator-log-noise-25406.md>)

Original publisher: [Read original article](<https://smileykeith.com/2021/11/16/simulator-log-spew/>)

Author: Keith Smiley

Published: 2021-11-17T02:00:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [iOS](<https://devfeed.tech/topics/ios.md>), [simulator](<https://devfeed.tech/topics/simulator.md>), [configuration](<https://devfeed.tech/topics/configuration.md>)

Tags: [commands](<https://devfeed.tech/tags/commands.md>), [configuration](<https://devfeed.tech/tags/configuration.md>), [ios](<https://devfeed.tech/tags/ios.md>), [logs](<https://devfeed.tech/tags/logs.md>), [scheme](<https://devfeed.tech/tags/scheme.md>), [simulator](<https://devfeed.tech/tags/simulator.md>), [streaming](<https://devfeed.tech/tags/streaming.md>)

### AI overview

This article explains how to reduce noisy iOS simulator logs without disabling os_log entirely. It recommends using log configuration commands to disable selected subsystems or categories and describes targeting either the booted simulator or a specific simulator UDID.

### Source excerpt

When Apple transitioned to using os_log for system logs it seemed they also decided to open the floodgates for what logs were surfaced in our apps. This lead to a plethora of stackoverflow questions recommending you disable os_log entirely by setting OS_ACTIVITY_MODE=disable 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 os_log for its feature-set. os_log has a nice set of categorization for logs, for example when viewing streaming logs in Console.app you can show the process, subsystem, and category for each log. 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: 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 You can replace booted here with a specific iOS simulator UDID, which can be found by running xcrun simctl list devices. Since this is simulator specific, you will have to re-run whatever commands you decide on when you create new simulators. More options for the log command can be found with man log

## Reproducible codesigning on Apple Silicon

DevFeed: [Reproducible codesigning on Apple Silicon](<https://devfeed.tech/articles/reproducible-codesigning-on-apple-silicon-25405.md>)

Original publisher: [Read original article](<https://smileykeith.com/2021/10/05/codesign-m1/>)

Author: Keith Smiley

Published: 2021-10-06T03:00:00Z

Content type: article

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Arm](<https://devfeed.tech/topics/arm.md>), [x86](<https://devfeed.tech/topics/x86.md>), [C](<https://devfeed.tech/topics/c.md>), [Command-line interface](<https://devfeed.tech/topics/cli.md>), [toolchain](<https://devfeed.tech/topics/toolchain.md>), [Open Source](<https://devfeed.tech/topics/open-source.md>)

Tags: [apple](<https://devfeed.tech/tags/apple.md>), [architecture](<https://devfeed.tech/tags/architecture.md>), [arm](<https://devfeed.tech/tags/arm.md>), [binaries](<https://devfeed.tech/tags/binaries.md>), [c](<https://devfeed.tech/tags/c.md>), [clang](<https://devfeed.tech/tags/clang.md>), [code](<https://devfeed.tech/tags/code.md>), [command-line](<https://devfeed.tech/tags/command-line.md>), [identifier](<https://devfeed.tech/tags/identifier.md>), [open-source](<https://devfeed.tech/tags/open-source.md>), [reproducible-builds](<https://devfeed.tech/tags/reproducible-builds.md>), [signing](<https://devfeed.tech/tags/signing.md>), [source](<https://devfeed.tech/tags/source.md>), [x86](<https://devfeed.tech/tags/x86.md>)

### AI overview

The article explains why codesigning universal macOS binaries can produce different results on Apple Silicon and Intel Macs, despite the binaries being identical before signing. It traces the issue through Apple's open-source code and identifies architecture-dependent UUID handling as the cause of non-reproducible codesigning.

### Source excerpt

For people who expect reproducible builds, Apple Silicon machines provide an interesting challenge. Apple Silicon requires arm64 binaries, including command line tools you build yourself, be codesigned. This change is mostly transparent to developers, because Apple updated their linker to automatically ad-hoc sign binaries1. Unfortunately, if you're interested in producing binaries that support both Intel Macs and Apple Silicon Macs, you likely want to produce a fat binary. When codesigning this binary you hit some behavior that depends on your current machine's architecture. Example 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: $ echo "int main() { return 0; }" > main.c $ clang main.c -Wl,-no_adhoc_codesign -arch arm64 -arch x86_64 -o main $ shasum main 113033b3d9a247210b49a476bbfadb2e347846fe main The shasum of main should always be the same regardless of your host machine2. On Apple Silicon machines you can see this binary has the same sha1 even if you run clang under Rosetta 23: $ arch -x86_64 clang main.c -Wl,-no_adhoc_codesign -arch arm64 -arch x86_64 -o main $ shasum main 113033b3d9a247210b49a476bbfadb2e347846fe main The issue is introduced when you codesign the binary on Apple Silicon machines versus Intel machines. You can immediately see the difference3: $ codesign --force --sign - main $ shasum main 84631e812bd480c306766ba03a728dd2565dd672 main % arch -x86_64 codesign --force --sign - main % shasum main f631b6c0daf3ffd0bb5f65d19fa045acf447a72d main We get closer to identifying the problem when you compare the details of these differences: $ codesign --force --sign - main $ codesign -dvvv main > arm.txt 2>&1 $ arch -x86_64 codesign --force --sign - main $ codesign -dvvv main > intel.txt 2>&1 $ diff -Nur intel.txt arm.txt --- intel.txt 2021-10-05 21:26:32.731918710 -0700 +++ arm.txt 2021-10-05 21:26:29.473702845 -0700 @@ -1,14 +1,14 @@ Executable=/

## Switching Xcode versions without a password

DevFeed: [Switching Xcode versions without a password](<https://devfeed.tech/articles/switching-xcode-versions-without-a-password-25404.md>)

Original publisher: [Read original article](<https://smileykeith.com/2021/08/12/xcode-select-sudoers/>)

Author: Keith Smiley

Published: 2021-08-12T20:40:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Xcode](<https://devfeed.tech/topics/xcode.md>), [ci](<https://devfeed.tech/topics/ci.md>), [configuration](<https://devfeed.tech/topics/configuration.md>), [macOS](<https://devfeed.tech/topics/macos.md>), [passwords](<https://devfeed.tech/topics/passwords.md>), [Vim](<https://devfeed.tech/topics/vim.md>)

Tags: [ci](<https://devfeed.tech/tags/ci.md>), [configuration](<https://devfeed.tech/tags/configuration.md>), [macos](<https://devfeed.tech/tags/macos.md>), [password](<https://devfeed.tech/tags/password.md>), [vim](<https://devfeed.tech/tags/vim.md>), [xcode](<https://devfeed.tech/tags/xcode.md>)

### AI overview

This tutorial explains how to switch between Xcode versions without entering a password. It configures sudoers on macOS so CI machines can run xcode-select and the restricted xcodebuild -runFirstLaunch command without password prompts, while keeping the custom rule in a separate sudoers.d file.

### Source excerpt

When switching between multiple Xcode versions one way to globally update the version you want to use is by running xcode-select like this: sudo xcode-select -s /Applications/Xcode-12.5.1.app 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: sudo xcodebuild -runFirstLaunch 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 sudoers file format, which configures the sudo command, allows us to skip password entry for specific commands with a bit of configuration. The easiest way to edit this configuration is by running: sudo visudo This opens the default /etc/sudoers 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: #includedir /private/etc/sudoers.d This tells sudo to load all the files in /etc/sudoers.d1 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. To setup our custom configuration we can run this command: echo "%admin ALL=NOPASSWD: /usr/bin/xcode-select,/usr/bin/xcodebuild -runFirstLaunch" | sudo tee /etc/sudoers.d/xcode Let's break this down2. The %admin component makes this configuration apply to all users that are in the admin group3. 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 ksmiley. The second compo

## Locking Xcode versions in bazel

DevFeed: [Locking Xcode versions in bazel](<https://devfeed.tech/articles/locking-xcode-versions-in-bazel-25403.md>)

Original publisher: [Read original article](<https://smileykeith.com/2021/03/08/locking-xcode-in-bazel/>)

Author: Keith Smiley

Published: 2021-03-08T16:40:00Z

Content type: article

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Xcode](<https://devfeed.tech/topics/xcode.md>), [Cache](<https://devfeed.tech/topics/cache.md>), [macOS](<https://devfeed.tech/topics/macos.md>), [iOS](<https://devfeed.tech/topics/ios.md>), [SDK](<https://devfeed.tech/topics/sdk.md>)

Tags: [bazel](<https://devfeed.tech/tags/bazel.md>), [cache](<https://devfeed.tech/tags/cache.md>), [configuration](<https://devfeed.tech/tags/configuration.md>), [cpu](<https://devfeed.tech/tags/cpu.md>), [cycles](<https://devfeed.tech/tags/cycles.md>), [ios](<https://devfeed.tech/tags/ios.md>), [macos](<https://devfeed.tech/tags/macos.md>), [sdk](<https://devfeed.tech/tags/sdk.md>), [watchos](<https://devfeed.tech/tags/watchos.md>), [xcode](<https://devfeed.tech/tags/xcode.md>)

### AI overview

This article explains how to lock supported Xcode versions in Bazel so team members can share remote build-cache artifacts reliably. It describes replacing Bazel's automatic Xcode discovery with a project-managed configuration and supporting multiple Xcode versions using build numbers.

### Source excerpt

When using bazel on a team, one of the things you quickly want to do is stand up a remote cache. This allows bazel to download build artifacts instead of spending CPU cycles reproducing things that have already been built by someone else. 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.1 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. Bazel discovers your currently installed Xcode versions by running xcode_locator, and then generating a BUILD file that contains an entry for every version you currently have installed. The result looks something like this:2 load("@apple_support//xcode:xcode_config.bzl", "xcode_config") load("@apple_support//xcode:xcode_version.bzl", "xcode_version") xcode_version( name = "version12_4_0_12D4e", version = "12.4.0.12D4e", aliases = ["12.4.0", "12.4", "12.4.0.12D4e"], default_ios_sdk_version = "14.4", default_tvos_sdk_version = "14.3", default_macos_sdk_version = "11.1", default_watchos_sdk_version = "7.2", ) xcode_version( name = "version12_2_0_12B45b", version = "12.2.0.12B45b", aliases = ["12.2.0", "12", "12.2", "12.2.0.12B45b"], default_ios_sdk_version = "14.2", default_tvos_sdk_version = "14.2", default_macos_sdk_version = "11.0", default_watchos_sdk_version = "7.1", ) xcode_config( name = "host_xcodes", versions = [":version12_4_0_12D4e", ":version12_2_0_12B45b"], default = ":version12_4_0_12D4e", ) To fetch the contents of this file on your machine you can run: cat bazel-$(basename $PWD)/external/local_config_xcode/BUILD In order to enforce developers use the same version, you can short circuit bazel's Xcode discovery and instead reference a local target that you provide.3 To do this, you can setup your target in the BUILD file at the roo

## Supporting relative paths: XCTest failures in Xcode

DevFeed: [Supporting relative paths: XCTest failures in Xcode](<https://devfeed.tech/articles/supporting-relative-paths-xctest-failures-in-xcode-25402.md>)

Original publisher: [Read original article](<https://smileykeith.com/2021/03/04/supporting-relative-paths/>)

Author: Keith Smiley

Published: 2021-03-05T04:48:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Xcode](<https://devfeed.tech/topics/xcode.md>), [Swift](<https://devfeed.tech/topics/swift.md>), [iOS](<https://devfeed.tech/topics/ios.md>), [debugging](<https://devfeed.tech/topics/debugging.md>)

Tags: [debugger](<https://devfeed.tech/tags/debugger.md>), [ios](<https://devfeed.tech/tags/ios.md>), [swift](<https://devfeed.tech/tags/swift.md>), [xcode](<https://devfeed.tech/tags/xcode.md>)

### AI overview

This article explains how relative source paths from the Bazel build system can cause XCTest failures in Xcode to reference relative paths, preventing the issue navigator from jumping to the failed test. It traces the behavior to Swift compiler path handling and discusses swizzling an initializer as a possible fix.

### Source excerpt

If you build your iOS app with an alternate build system such as bazel, it's likely that you use relative paths, instead of absolute paths, for compilation. Specifically, when building swift code, Xcode calls the compiler with something like: swiftc [ARGS] /path/to/srcroot/path/to/file1.swift /path/to/srcroot/path/to/file2.swift Where bazel will call the compiler with something like: swiftc [ARGS] path/to/file1.swift path/to/file2.swift 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. 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 XCTIssue that XCTest creates is expected to be instantiated with an absolute path. This absolute path is populated from the #filePath (previously #file) keyword which is supposed to reference the absolute path of the current source file. 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 -working-directory 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 #filePath keyword ends up translating to a relative path, which means the XCTIssue is created with a relative path. With relative paths when you run your tests in Xcode and

## Editing rpaths for \_InternalSwiftSyntaxParser

DevFeed: [Editing rpaths for \_InternalSwiftSyntaxParser](<https://devfeed.tech/articles/editing-rpaths-for-internalswiftsyntaxparser-25401.md>)

Original publisher: [Read original article](<https://smileykeith.com/2021/03/03/editing-rpaths/>)

Author: Keith Smiley

Published: 2021-03-04T03:48:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Swift](<https://devfeed.tech/topics/swift.md>), [Xcode](<https://devfeed.tech/topics/xcode.md>), [Command-line interface](<https://devfeed.tech/topics/cli.md>), [toolchain](<https://devfeed.tech/topics/toolchain.md>), [Library](<https://devfeed.tech/topics/library.md>)

Tags: [code](<https://devfeed.tech/tags/code.md>), [command-line](<https://devfeed.tech/tags/command-line.md>), [compatibility](<https://devfeed.tech/tags/compatibility.md>), [swift](<https://devfeed.tech/tags/swift.md>), [toolchain](<https://devfeed.tech/tags/toolchain.md>), [xcode](<https://devfeed.tech/tags/xcode.md>)

### AI overview

This tutorial explains how to edit runtime search paths (rpaths) so a Swift command-line tool can ship and load the compatible _InternalSwiftSyntaxParser dynamic library from Xcode. It covers inspecting dependencies and rpaths with otool, copying the library, and modifying the binary's rpath entries.

### Source excerpt

One of the issues with shipping a tool that depends on SwiftSyntax is that it depends on a dynamic library that is provided with Xcode called _InternalSwiftSyntaxParser. 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: <unknown>:0:0: error: The loaded '_InternalSwiftSyntaxParser' library is from a toolchain that is not compatible with this version of SwiftSyntax Ideally, this library would be statically linked to your executable (and I'm hoping we can find a solution 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. The key to this workaround relies on how dyld works. dyld 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 otool. For example: % 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) Here you can see many libraries are directly referenced with their absolute paths while lib_InternalSwiftSyntaxParser.dylib, the library we're specifically interested in, is referenced via a rpath. You can run this command to see your binary's rpaths (yours may differ depending on your absolute path to Xcode): % 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

## Cross compiling for Apple Silicon with Swift Package Manager

DevFeed: [Cross compiling for Apple Silicon with Swift Package Manager](<https://devfeed.tech/articles/cross-compiling-for-apple-silicon-with-swift-package-manager-25400.md>)

Original publisher: [Read original article](<https://smileykeith.com/2020/12/24/swiftpm-cross-compile/>)

Author: Keith Smiley

Published: 2020-12-24T22:32:00Z

Content type: article

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Swift](<https://devfeed.tech/topics/swift.md>), [Package manager](<https://devfeed.tech/topics/package-manager.md>), [macOS](<https://devfeed.tech/topics/macos.md>), [x86](<https://devfeed.tech/topics/x86.md>), [Xcode](<https://devfeed.tech/topics/xcode.md>), [Command-line interface](<https://devfeed.tech/topics/cli.md>), [Linux](<https://devfeed.tech/topics/linux.md>)

Tags: [apple](<https://devfeed.tech/tags/apple.md>), [architecture](<https://devfeed.tech/tags/architecture.md>), [architectures](<https://devfeed.tech/tags/architectures.md>), [binaries](<https://devfeed.tech/tags/binaries.md>), [build](<https://devfeed.tech/tags/build.md>), [command-line](<https://devfeed.tech/tags/command-line.md>), [linux](<https://devfeed.tech/tags/linux.md>), [macos](<https://devfeed.tech/tags/macos.md>), [performance](<https://devfeed.tech/tags/performance.md>), [swift](<https://devfeed.tech/tags/swift.md>), [swift-package](<https://devfeed.tech/tags/swift-package.md>), [swift-package-manager](<https://devfeed.tech/tags/swift-package-manager.md>), [x86](<https://devfeed.tech/tags/x86.md>), [x86-64](<https://devfeed.tech/tags/x86-64.md>), [xcode](<https://devfeed.tech/tags/xcode.md>)

### AI overview

This article explains how to cross-compile Swift Package Manager command-line binaries for Apple Silicon. It shows how to create a universal macOS binary containing both arm64 and x86_64 slices using the hidden --arch flag, and how to build separately for each architecture and combine the results with lipo, including an approach that also works on Linux.

### Source excerpt

If you distribute binaries for command line tools built with Swift Package Manager, you might have previously built your distribution binary with: % swift build --configuration release If you inspect the binary, you can see it was built for the current machine's architecture by default: % file .build/release/package .build/release/package: Mach-O 64-bit executable x86_64 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 fat binary that contains a slice for both x86_64 and arm64. 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 --arch flag once for each architecture: % swift build --configuration release --arch arm64 --arch x86_64 This goes through a different code path 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: % 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 Another option is to build once for each architecture, and then combine the binaries using lipo. Unlike the --arch option, this approach also works on Linux. Here's an example: % swift build --configuration release --triple arm64-apple-macosx % swift build --configuration release --triple x86_64-apple-macosx % lipo -create -output package .build/arm64-apple-macosx/release/package .build/x86_64-apple-macosx/release/package Inspecting our final binary we can see it correctly

## LLDB Reproducers

DevFeed: [LLDB Reproducers](<https://devfeed.tech/articles/lldb-reproducers-25399.md>)

Original publisher: [Read original article](<https://smileykeith.com/2020/09/29/lldb-reproducers/>)

Author: Keith Smiley

Published: 2020-09-30T02:39:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [debugging](<https://devfeed.tech/topics/debugging.md>), [Xcode](<https://devfeed.tech/topics/xcode.md>), [Swift](<https://devfeed.tech/topics/swift.md>), [macOS](<https://devfeed.tech/topics/macos.md>), [Terminal](<https://devfeed.tech/topics/terminal.md>), [Command-line interface](<https://devfeed.tech/topics/cli.md>), [iOS](<https://devfeed.tech/topics/ios.md>), [simulator](<https://devfeed.tech/topics/simulator.md>), [bug](<https://devfeed.tech/topics/bug.md>)

Tags: [apple](<https://devfeed.tech/tags/apple.md>), [bug](<https://devfeed.tech/tags/bug.md>), [cli](<https://devfeed.tech/tags/cli.md>), [debugging](<https://devfeed.tech/tags/debugging.md>), [ios](<https://devfeed.tech/tags/ios.md>), [macos](<https://devfeed.tech/tags/macos.md>), [simulator](<https://devfeed.tech/tags/simulator.md>), [swift](<https://devfeed.tech/tags/swift.md>), [terminal](<https://devfeed.tech/tags/terminal.md>), [xcode](<https://devfeed.tech/tags/xcode.md>)

### AI overview

This article explains how LLDB reproducers capture debugging-session information so Swift developers can provide Apple with more useful and repeatable bug reports. It covers the limitation that launching LLDB from Xcode does not enable capture mode by default, privacy risks involving binaries and memory, and workflows for macOS programs and iOS apps running in the simulator.

### Source excerpt

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. Enter reproducers. Reproducers provide a way to run LLDB while also capturing information about your debugging session. With this information you can submit a more useful bug report to Apple with a reliable reproduction case. How? Although the steps to use reproducers are mostly straightforward, launching LLDB from Xcode does not enable --capture 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. Update: the folks from PSPDFKit pointed out as of Xcode 12 there is a private default for enabling capture mode for debugging sessions launched from Xcode: defaults write com.apple.dt.Xcode IDEDebuggerEnableReproducerCapture -bool YES 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 pointed out on Twitter this will also contain anything in memory at the time of capture. Be sure to verify what you're sharing with Apple before you send it. CLI / macOS app If you're debugging a program on your Mac, there are a few steps: Run the app in Xcode and stop it. This way you know the binary is up to date. 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"). Run lldb --capture /path/to/Your.app. In the LLDB session run process launch --stop-at-entry. Now you're in a paused LLDB session. Here you can set the breakpoints you need to reproduce your issue. Often for me this means breaking at a specific place, and running some version of po foo that causes an issue. Once you're done reproducing the issue, run reproducer generate in LLDB. This w

## NSProgress with Asynchronous Tasks

DevFeed: [NSProgress with Asynchronous Tasks](<https://devfeed.tech/articles/nsprogress-with-asynchronous-tasks-25396.md>)

Original publisher: [Read original article](<https://smileykeith.com/2015/03/14/nsprogress-with-asynchronous-tasks/>)

Author: Keith Smiley

Published: 2015-03-15T01:57:00Z

Content type: article

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [API](<https://devfeed.tech/topics/api.md>)

Tags: [api](<https://devfeed.tech/tags/api.md>), [asynchronous](<https://devfeed.tech/tags/asynchronous.md>), [blog](<https://devfeed.tech/tags/blog.md>), [post](<https://devfeed.tech/tags/post.md>)

### AI overview

An article titled "NSProgress with Asynchronous Tasks" explains that the author explored the NSProgress API and wrote about using it with asynchronous tasks.

### Source excerpt

After having a use for NSProgress 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, NSProgress with Asynchronous Tasks, here.

## Writing Vim Syntax Plugins

DevFeed: [Writing Vim Syntax Plugins](<https://devfeed.tech/articles/writing-vim-syntax-plugins-25398.md>)

Original publisher: [Read original article](<https://smileykeith.com/2015/03/14/writing-vim-syntax-plugins/>)

Author: Keith Smiley

Published: 2015-03-15T01:48:00Z

Content type: article

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Vim](<https://devfeed.tech/topics/vim.md>)

Tags: [plugins](<https://devfeed.tech/tags/plugins.md>), [syntax](<https://devfeed.tech/tags/syntax.md>), [vim](<https://devfeed.tech/tags/vim.md>), [writing](<https://devfeed.tech/tags/writing.md>)

### AI overview

An article about writing Vim syntax plugins, presented as a follow-up to an earlier article about Clojure.

### Source excerpt

After writing about Clojure I wrote about writing Vim syntax plugins. Check it out here.

## Clojure

DevFeed: [Clojure](<https://devfeed.tech/articles/clojure-25395.md>)

Original publisher: [Read original article](<https://smileykeith.com/2015/03/14/clojure/>)

Author: Keith Smiley

Published: 2015-03-15T01:27:00Z

Content type: article

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Clojure](<https://devfeed.tech/topics/clojure.md>), [Vim](<https://devfeed.tech/topics/vim.md>)

Tags: [articles](<https://devfeed.tech/tags/articles.md>), [blog](<https://devfeed.tech/tags/blog.md>), [clojure](<https://devfeed.tech/tags/clojure.md>), [posts](<https://devfeed.tech/tags/posts.md>), [vim](<https://devfeed.tech/tags/vim.md>)

### AI overview

A brief personal note linking several posts about Clojure, including getting started with Liberator, using Yesql, and writing Clojure in Vim. The author says they were not a big fan of Clojure and prefer typed functional languages.

### Source excerpt

I haven't had anything to write in a while. Mainly because, as I've been working at thoughtbot, I've been writing everything I can on the thoughtbot blog. I spent a little while writing Clojure, and out of that came a few posts. Overall I wasn't a big fan of Clojure. I definitely lean more towards functional languages with types but I wanted to link these articles here regardless. Getting Started with Liberator Using Yesql in Clojure Writing Clojure in Vim

## Vim TagBar with Objective-C

DevFeed: [Vim TagBar with Objective-C](<https://devfeed.tech/articles/vim-tagbar-with-objective-c-25394.md>)

Original publisher: [Read original article](<https://smileykeith.com/2014/02/14/vim-tagbar-with-objective-c/>)

Author: Keith Smiley

Published: 2014-02-14T21:53:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Objective-C](<https://devfeed.tech/topics/objective-c.md>), [Vim](<https://devfeed.tech/topics/vim.md>)

Tags: [article](<https://devfeed.tech/tags/article.md>), [implementation](<https://devfeed.tech/tags/implementation.md>), [interface](<https://devfeed.tech/tags/interface.md>), [objective-c](<https://devfeed.tech/tags/objective-c.md>), [property](<https://devfeed.tech/tags/property.md>), [protocol](<https://devfeed.tech/tags/protocol.md>), [regex](<https://devfeed.tech/tags/regex.md>), [vim](<https://devfeed.tech/tags/vim.md>)

### AI overview

A tutorial on configuring Vim's Tagbar to recognize and list Objective-C constructs. It discusses limitations in existing ctags and Tagbar support, then provides custom ctags regular expressions for Objective-C interfaces, implementations, protocols, properties, methods, constants, typedefs, and enums.

### Source excerpt

When working with large files in Vim, Tagbar 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. Hopefully in the future it won't be difficult to set this up in Vim. Currently ctags already has built in support for Objective-C. Unfortunately there hasn't been a release of ctags since 2009. As recommended in the canonical how to article 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 an issue on the Tagbar Github repo hoping that Objective-C support will be added by default in the future. The only other resource I could find about this issue was this gist. 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. --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:]]+-?[[:s

## IPSEC/L2TP VPN on a Raspberry Pi running Arch Linux

DevFeed: [IPSEC/L2TP VPN on a Raspberry Pi running Arch Linux](<https://devfeed.tech/articles/ipsec-l2tp-vpn-on-a-raspberry-pi-running-arch-linux-25393.md>)

Original publisher: [Read original article](<https://smileykeith.com/2014/01/27/ipsec-l2tp-vpn-on-a-raspberry-pi-running-arch-linux/>)

Author: Keith Smiley

Published: 2014-01-28T04:49:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Virtual Private Network](<https://devfeed.tech/topics/vpn.md>), [Arch Linux](<https://devfeed.tech/topics/archlinux.md>), [Raspberry Pi](<https://devfeed.tech/topics/raspberry-pi.md>), [Linux](<https://devfeed.tech/topics/linux.md>), [Arm](<https://devfeed.tech/topics/arm.md>), [Firewall](<https://devfeed.tech/topics/firewall.md>), [pacman](<https://devfeed.tech/topics/pacman.md>), [Script](<https://devfeed.tech/topics/script.md>), [systemd](<https://devfeed.tech/topics/systemd.md>), [configuration](<https://devfeed.tech/topics/configuration.md>)

Tags: [arm](<https://devfeed.tech/tags/arm.md>), [article](<https://devfeed.tech/tags/article.md>), [configuration](<https://devfeed.tech/tags/configuration.md>), [firewall](<https://devfeed.tech/tags/firewall.md>), [ipv4](<https://devfeed.tech/tags/ipv4.md>), [linux](<https://devfeed.tech/tags/linux.md>), [pacman](<https://devfeed.tech/tags/pacman.md>), [raspberry-pi](<https://devfeed.tech/tags/raspberry-pi.md>), [script](<https://devfeed.tech/tags/script.md>), [systemd](<https://devfeed.tech/tags/systemd.md>), [vpn](<https://devfeed.tech/tags/vpn.md>)

### AI overview

This tutorial explains how to configure an IPsec/L2TP VPN on a Raspberry Pi running the ARM version of Arch Linux. It covers installing the required packages, configuring firewall rules and redirects, and creating a systemd-launched script so the configuration persists after restart.

### Source excerpt

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 interesting ideas from Googling "uses for a Raspberry Pi," I didn't particularly find them any more than a thought exercise. Making a VPN stood out as an actually useful configuration. Originally when I got my (accidentally chosen) Model A, I spent a little while going through this guide 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. I started out by following this guide 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 downloads page. I'm not sure I would recommend Arch for people who haven't installed it before or at least gotten through their Beginners' Guide. The ARM Image, like the normal image, doesn't come with a GUI, perfect for this use of the Pi. 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. Start by installing the necessary components: pacman -Sy openswan xl2tpd ppp lsof python2 You need to do some configuration of the firewall and redirects: echo "net.ipv4.

## iTerm theme based on the time of day

DevFeed: [iTerm theme based on the time of day](<https://devfeed.tech/articles/iterm-theme-based-on-the-time-of-day-25392.md>)

Original publisher: [Read original article](<https://smileykeith.com/2013/09/03/iterm-theme-based-on-the-time-of-day/>)

Author: Keith Smiley

Published: 2013-09-03T17:56:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Vim](<https://devfeed.tech/topics/vim.md>), [Scripting](<https://devfeed.tech/topics/scripting.md>), [Zsh](<https://devfeed.tech/topics/zsh.md>), [Objective-C](<https://devfeed.tech/topics/objective-c.md>), [Terminal](<https://devfeed.tech/topics/terminal.md>), [App](<https://devfeed.tech/topics/app.md>)

Tags: [github](<https://devfeed.tech/tags/github.md>), [keyboard](<https://devfeed.tech/tags/keyboard.md>), [objective-c](<https://devfeed.tech/tags/objective-c.md>), [process](<https://devfeed.tech/tags/process.md>), [script](<https://devfeed.tech/tags/script.md>), [scripting](<https://devfeed.tech/tags/scripting.md>), [terminal](<https://devfeed.tech/tags/terminal.md>), [vim](<https://devfeed.tech/tags/vim.md>)

### AI overview

This tutorial explains how to switch an iTerm color scheme between Solarized light and dark based on the time of day. It uses AppleScript to control iTerm, an Objective-C app to automate RGB color conversion, and a zsh function configured to update the theme when a new terminal session opens.

### Source excerpt

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 this. Having this functionality in Vim with the Solarized theme at night really made me want this in iTerm 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 pull request two years ago from Piet Jaspers, support was added for scripting iTerm's entire colorscheme with AppleScript. Using these AppleScript bindings I was able to create a script that changes the entire colorscheme of iTerm based on the time of day between Solarized light and dark. As you can see the bulk 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 on Github. You can download the signed binary here. Using this newly created AppleScript I then made a zsh function so that I could call colorize from anywhere to update the color scheme of the current terminal. I also chose to do this at the end of my .zshrc here. This way everytime I open a new session my theme is automatically set. If you have any input on how I could optimize this let me know.

## Global htaccess

DevFeed: [Global htaccess](<https://devfeed.tech/articles/global-htaccess-25391.md>)

Original publisher: [Read original article](<https://smileykeith.com/2013/08/14/global-htaccess/>)

Author: Keith Smiley

Published: 2013-08-14T20:12:00Z

Content type: article

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [configuration](<https://devfeed.tech/topics/configuration.md>), [Boilerplate](<https://devfeed.tech/topics/boilerplate.md>), [HTML5](<https://devfeed.tech/topics/html5.md>), [Linode](<https://devfeed.tech/topics/linode.md>), [Ubuntu](<https://devfeed.tech/topics/ubuntu.md>), [Server](<https://devfeed.tech/topics/server.md>), [Web Development](<https://devfeed.tech/topics/web-development.md>)

Tags: [article](<https://devfeed.tech/tags/article.md>), [config](<https://devfeed.tech/tags/config.md>), [how-to](<https://devfeed.tech/tags/how-to.md>), [html5](<https://devfeed.tech/tags/html5.md>), [linode](<https://devfeed.tech/tags/linode.md>), [performance](<https://devfeed.tech/tags/performance.md>), [server](<https://devfeed.tech/tags/server.md>), [ubuntu](<https://devfeed.tech/tags/ubuntu.md>)

### AI overview

This article explains how to move Apache configuration from per-site .htaccess files into a global server configuration. It describes using a Directory block and a conf.d file on an Ubuntu 10.04 Linode VPS to improve performance and reduce site-specific configuration.

### Source excerpt

When starting a new web project one of the first things I do is download the most up to date HTML5 Boilerplate. It provides a great starting point for the HTML you need in a project. It also comes with an extremely complete .htaccess file. While this is very nice for a single site they recommend you do something different for multiple sites at the very top. (!) 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. This got me to their awesome collection of server configs which has their, and in many ways the communities, recommended settings depending on your webserver. The apache configs have the same .htaccess file so I decided to dig into how to do this. They direct you to the apache article about using .htaccess files which has a similar comment about their use. 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. So I decided to set this up on my Linode VPS which is running Ubuntu 10.04. As stated in the original file comment they recommend using the httpd.conf file for your custom configuration like this. But apparently that file could be overwritten on updates of Apache which would be pretty annoying. Luckily the default Apache config file (apache2.conf on 10.04) includes the contents of the conf.d folder which is in the same location. By creating a foo.conf 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 Directory block. The block expects you to provide a path to the files you want to be affected by the contained configuration

## OS X Crash Report Symbolication

DevFeed: [OS X Crash Report Symbolication](<https://devfeed.tech/articles/os-x-crash-report-symbolication-25390.md>)

Original publisher: [Read original article](<https://smileykeith.com/2013/08/09/os-x-crash-symbolication/>)

Author: Keith Smiley

Published: 2013-08-09T20:26:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [macOS](<https://devfeed.tech/topics/macos.md>), [Xcode](<https://devfeed.tech/topics/xcode.md>), [Command-line interface](<https://devfeed.tech/topics/cli.md>), [x86](<https://devfeed.tech/topics/x86.md>)

Tags: [apple](<https://devfeed.tech/tags/apple.md>), [build](<https://devfeed.tech/tags/build.md>), [command-line](<https://devfeed.tech/tags/command-line.md>), [crash](<https://devfeed.tech/tags/crash.md>), [crashlytics](<https://devfeed.tech/tags/crashlytics.md>), [issue](<https://devfeed.tech/tags/issue.md>), [macos](<https://devfeed.tech/tags/macos.md>), [x86-64](<https://devfeed.tech/tags/x86-64.md>), [xcode](<https://devfeed.tech/tags/xcode.md>)

### AI overview

This article explains how to symbolicate OS X crash reports for a macOS application when the issue cannot be reproduced. It describes using the archived build's dSYM file, the application's x86_64 architecture, and the crash report's load address to identify the application methods involved.

### Source excerpt

As you may know I write a small OS X called Sail. 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. This lead me down the rabbit hole of symbolication, 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 two links 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 post about these but his exact method didn't seem to work for me. Here is what did work for me. For my first abridged crash report I had this 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) <D1F313B6-21F6-341B-8627-5480C5D1DB20> /Applications/Sail.app/Contents/MacOS/Sail

## The 'Best' Text Editor

DevFeed: [The 'Best' Text Editor](<https://devfeed.tech/articles/the-best-text-editor-25389.md>)

Original publisher: [Read original article](<https://smileykeith.com/2013/05/22/the-best-text-editor/>)

Author: Keith Smiley

Published: 2013-05-22T07:59:00Z

Content type: opinion

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [ide](<https://devfeed.tech/topics/ide.md>), [Vim](<https://devfeed.tech/topics/vim.md>), [Stack Overflow](<https://devfeed.tech/topics/stackoverflow.md>)

Tags: [article](<https://devfeed.tech/tags/article.md>), [ide](<https://devfeed.tech/tags/ide.md>), [vim](<https://devfeed.tech/tags/vim.md>)

### AI overview

The article argues that there is no universally best IDE or text editor because the right choice depends on the task, the individual, and their workflow. It recommends trying available editors, removing those that are unsuitable or unstable, and spending more time learning promising options such as Vim.

### Source excerpt

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. 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. 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. 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.

[Next page](<https://devfeed.tech/sources/keith-smiley.md?cursor=WyIyMDEzLTA1LTIyVDA3OjU5OjAwKzAwOjAwIiwgImIwNmNjZmEyLTI4YTQtNGE2ZS04NzM2LTU4NGY2NDFhZThiOSJd>)