Patching and building Emacs from source on macOS is fairly straightforward, but what if I'd like to patch my Emacs Plus Homebrew builds?Let's cover both ways of patching our favourite editor...Patching Emacs upstream sourcesIf you'd like to build from the master branch, you can check its sources out like so:git clone git://git.sv.gnu.org/emacs.gitcd emacsNext, we'll patch Emacs source as needed. For example, I recently wanted to patch src/nsterm.m to see if I could fix a macOS dictation regression introduced on Emacs 30.diff --git a/src/nsterm.m b/src/nsterm.mindex 003aadb9782..2b34894f36e 100644--- a/src/nsterm.m+++ b/src/nsterm.m@@ -7413,7 +7413,24 @@ - (NSRange)selectedRange { if (NS_KEYLOG) NSLog (@"selectedRange request");- return NSMakeRange (NSNotFound, 0);++ struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe));+ struct buffer *buf = XBUFFER (w->contents);+ ptrdiff_t point = BUF_PT (buf);++ if (NILP (BVAR (buf, mark_active)))+ {+ NSUInteger selection_location = point - BUF_BEGV (buf);+ return NSMakeRange (selection_location, 0);+ }++ ptrdiff_t mark = marker_position (BVAR (buf, mark));+ ptrdiff_t region_start = min (point, mark);+ ptrdiff_t region_end = max (point, mark);+ NSUInteger selection_location = region_start - BUF_BEGV (buf);+ NSUInteger selection_length = region_end - region_start;++ return NSMakeRange (selection_location, selection_length); } #if defined (NS_IMPL_COCOA) || GNUSTEP_GUI_MAJOR_VERSION > 0 || \Now we're ready to configure and build../autogen.sh./configure --with-ns --prefix="$PWD/nextstep/Emacs.app/Contents/MacOS" --enable-locallisppath="${PWD}/nextstep/Emacs.app/Contents/MacOS"make installWe've used nextstep/Emacs.app/Contents/MacOS as our target directory, so we can test our builds from exactly there… and there you have it, your newly built Emacs.app, ready for opening.open nextstep/Emacs.appPatching Emacs PlusWhile patching and building upstream Emacs sources as above is fairly common, patching Emacs Plus may not be as well known.I'm a big fan of Boris Buliga's excellent Homebrew recipe and use it daily, so it makes sense for me to get acquainted with its patching capabilities.First we check out the Emacs Plus Homebrew repo:git clone https://github.com/d12frosted/homebrew-emacs-plus.gitcd homebrew-emacs-plusAt this point, it's worth noting Emacs Plus enables building different Emacs versions (29, 30, and even master). It extends Emacs by applying its own patches at build time. For example, if we'd like to patch Emacs 30, we'll customise the 30 build to apply our own changes.Remember our src/nsterm.m patch above? We'll rebased it against the Emacs 30 tarball and save in the Emacs Plus patches directory:./patches/emacs-30/dictation.patchNext we'll need to generate a hash to complete our Emacs 30 patch details:sha256sum ./patches/emacs-30/dictation.patchNow we add our patch details to ./Formula/emacs-plus@30.rb:local_patch "dictation", sha: "cb102525bba7385d7d85c52d31101af3d2cbbf076468dacbf505039082ec521c"With that, we're ready to build Emacs Plus, which comes with a handy build script. I'm a fan of Valeriy Savchenko's macOS icons, so I'll throw in the optional icon flag. I should also mention Emacs Plus offers a rich catalog of app icons. Now back to building…./build 30 --with-savchenkovaleriy-big-sur-curvy-3d-iconThe build output should look a little something like the following (look out for our applied patch)./Formula/emacs-plus-local.rb --with-savchenkovaleriy-big-sur-curvy-3d-icon==> Fetching downloads for: emacs-plus-local==> Fetching emacs-plus-local==> Downloading https://ftp.gnu.org/gnu/emacs/emacs-30.1.tar.xzAlready downloaded: /Users/alvaro/Library/Caches/Homebrew/downloads/b2b29daf5d872710063495e32b8b5234c2fbfffccec82222b65e7f2b4e7fb4da--emacs-30.1.tar.xz==> Patching==> Applying fix-window-role.patch==> Applying system-appearance.patch==> Applying round-undecorated-frame.patch==> Applying dictation.patch ./autogen.sh==> ./configure --disable-silent-rules --enable-locallisppath=/opt/homebrew/share/emacs/site-lisp --infodir=/opt/homebrew/Cellar/emacs-plus-local/30.1/share/info/emacs --with-native-compilation=aot --with-xml2 --==> gmake==> gmake install==> Injecting PATH value to Emacs.app/Contents/Info.plistPatching plist at /opt/homebrew/Cellar/emacs-plus-local/30.1/Emacs.app/Contents/Info.plist with following PATH value:...==> CaveatsEmacs.app was installed to: /opt/homebrew/opt/emacs-plus-localTo link the application to default Homebrew App location: osascript -e 'tell application "Finder" to make alias file to posix file "/opt/homebrew/opt/emacs-plus-local/Emacs.app" at posix file "/Applications" with properties {name:"Emacs.app"}'Your PATH value was injected into Emacs.app/Contents/Info.plistReport any issues to https://github.com/d12frosted/homebrew-emacs-plusTo start emacs-plus-local now and restart at login: brew services start emacs-plus-localOr, if you don't want/need a background service you can just run: /opt/homebrew/opt/emacs-plus-local/bin/emacs --fg-daemon==> Summary🍺 /opt/homebrew/Cellar/emacs-plus-local/30.1: 7,470 files, 585.0MB, built in 8 minutes 59 seconds==> Running `brew cleanup emacs-plus-local`...Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).alvaro@MacBookPro homebrew-emacs-plus % osascript -e 'tell application "Finder" to make alias file to posix file "/opt/homebrew/opt/emacs-plus-local/Emacs.app" at posix file "/Applications" with properties {name:"Emacs.app"}'alias file Emacs.app of folder Applications of startup diskAt this point, your brand new patched Emacs Plus is ready for opening:open /opt/homebrew/opt/emacs-plus-local/Emacs.appNotice how Emacs.app is saved under emacs-plus-local, which is different from the default location: emacs-plus. In other words, your patched and regular builds can coexist.I've pushed the changes to my Emacs Plus fork. If keen to take a closer look, check out the git commit. Should this change be sent to Emacs Plus in a pull request? Maybe not just yet. We'll try to get it admitted upstream first.Now you know at least two ways of building and patching Emacs on macOS. There are more, but these are my favourite two.Make it all sustainableLearned something new? Enjoying this blog or my projects? Help make it sustainable.Need a blog? I can help with that. Maybe buy my iOS apps too ;)