#+title:  Installing Emacs on MacOS
#+author: Howard X. Abrams
#+date:   2022-09-02 September
#+tags:   emacs macos readme

These instructions originally came from [[https://jherrlin.github.io/posts/emacs-on-macos-monterey/][this essay]], as it runs Emacs as dæmon with LaunchAgent. Also fetch mails periodically with =mbsync= via LaunchAgent.
* Install
Since I’ve been having difficulties installing Emacs from source on a Mac, I’m now taking advantage of[[https://github.com/jimeh/emacs-builds][Jim Myhrberg's Emacs Build project]], and it is must nicer to simply, download a pre-built binary with all the bells and whistles.

First, install the Homebrew cask:
#+begin_src sh
  brew tap jimeh/emacs-builds
#+end_src
And then, install Emacs:
#+begin_src sh
  brew install --cask emacs-app
#+end_src
* Install from Source
If we can’t install a binary, we build from source.
** Emacs Plus
No longer need to install [[https://apps.apple.com/us/app/xcode/id497799835?mt=12][Apple XCode]], as these instructions require [[https://brew.sh][Homebrew]].

If I want to build from source (and not build from Homebrew), install all the dependencies first, by running:
#+begin_src sh
  brew install pkg-config automake texinfo jpeg giflib\
    libtiff jansson libpng librsvg gnutls cmake
#+end_src

To get the native compilation for Emacs working, I [[https://github.com/d12frosted/homebrew-emacs-plus/issues/680#issuecomment-2481633820][need to *un*-install]], as the new brew recipe will take care of this:
#+begin_src sh
  brew uninstall libgccjit gcc
#+end_src

Oh, and if we are still building with [[https://imagemagick.org/][ImageMagick]], install that first:
#+begin_src sh
  brew install imagemagick
#+end_src

Best success comes from using the [[https://github.com/d12frosted/homebrew-emacs-plus][emacs-plus]] installation. To begin, add the /cask/:
#+begin_src sh
  brew tap d12frosted/emacs-plus
#+end_src

I find that I need to … at least, on my work computer, install two different versions of Emacs that I use to distinguish one for “work” and the other for other activities, like IRC and [[file:ha-feed-reader.org][elfeed]]. To that end, I run the following command to install Emacs:
#+begin_src sh
  brew reinstall $(brew deps emacs-plus@30)
  brew reinstall emacs-plus@30 --with-mailutils
#+end_src
And if it fails, choose =shell= and type:
#+begin_src sh
  make bootstrap
#+end_src
** Build from Scratch
The failures that I often get from installing the Emacs Plus with Libgccjit, means that we might want to build from soure:
#+begin_src sh
mkdir -p ~/src
git clone https://git.savannah.gnu.org/git/emacs.git ~/src/emacs
cd ~/src/emacs
./autogen.sh
#+end_src
And we can issue the same sort of configure we used for
#+begin_src sh
./configure --disable-dependency-tracking --disable-silent-rules  \
            --enable-locallisppath=/opt/homebrew/share/emacs/site-lisp  \
            --infodir=/opt/homebrew/Cellar/emacs-plus@29/29.2/share/info/emacs \
            --prefix=/opt/homebrew/Cellar/emacs-plus@29/29.2 \
            --with-xml2 --with-gnutls --with-native-compilation --without-compress-install \
            --without-dbus --without-imagemagick --with-modules --with-rsvg --without-pop \
            --with-ns --disable-ns-self-contained
#+end_src
Or to install/build into =/usr/local=:
#+begin_src sh
  LDFLAGS=-L/opt/homebrew/opt/libgccjit/lib -L/opt/homebrew/opt/xz/lib
  CPPFLAGS=-I/opt/homebrew/opt/libgccjit/include -I/opt/homebrew/opt/xz/include
  export LDFLAGS CPPFLAGS
  ./configure --disable-dependency-tracking --disable-silent-rules  \
              --prefix=/usr/local \
              --with-xml2 --with-gnutls --with-native-compilation --without-compress-install \
              --without-dbus --without-imagemagick --with-modules --with-rsvg --without-pop \
              --with-ns --disable-ns-self-contained
#+end_src
Assuming that either works, then build it with:
#+begin_src sh
  make -j4
#+end_src
** Ouchie
Sometimes get the following error:
#+begin_example
  ld: symbol(s) not found for architecture x86_64
#+end_example

And [[https://duckduckgo.com/?q=brew+ld%3A+symbol(s)+not+found+for+architecture+x86_64&t=ffab&ia=web][web searches]] yield mixed results. To solve, first /re-touch/ the environment (as it appears the problem is that some dependent library is now out-of-date compared to operating system installation):
#+begin_src sh
  brew update
  brew upgrade
#+end_src

Next make sure that all the dependencies are /reinstalled/ with the current operating system:
#+begin_src sh
  brew reinstall $(brew deps emacs-plus@29)
#+end_src

Then reinstall the =libgccjit= (as it doesn’t seem to get picked up with the /deps/ listing):
#+begin_src sh
  brew uninstall libgccjit gcc
  brew uninstall emacs-plus@29
  brew install libgccjit gcc
#+end_src
And then reinstall Emacs above.

And if that doesn’t work, then we need to delete all packages installed by brew, and essentially start all over to see what sub-sub-sub-package got rebuilt without =libgccjit=. Painful and time-consuming, but I basically let it run all night.
#+begin_src sh
  PKG_FILE=$(mktemp --suffix=.txt)
  brew list --formula > ${PKG_FILE}

  while read PACKAGE
  do
    brew uninstall ${PACKAGE}
  done < ${PKG_FILE}

  brew install libgccjit gcc

  # No, it doesn't seem that reinstall actuall works.
  while read PACKAGE
  do
    brew install ${PACKAGE}
  done < ${PKG_FILE}

  echo "Good luck rebuilding Emacs."
#+end_src
* Afterwards
After Emacs is /kinda/ working, make sure you install *all* the fonts, that is:
#+begin_example
M-x nerd-icons-install-fonts
#+end_example

Everything golden?
#+begin_example
M-x straight-freeze-versions
#+end_example

Before we can build a Telegram server for [[file:ha-aux-apps.org::*Telega][Telega]], we need to install the /latest/ version:
#+begin_src sh
  brew unlink tdlib  # optional
  brew install tdlib --HEAD
#+end_src
* Supporting Packages
Now install all the extras:
#+begin_src sh
  brew install git-delta
  brew install libvterm
  brew install mu
  brew install isync
  brew install gpg
#+end_src
** Mu4a
See [[file:ha-email.org][ha-email]] for better instructions.
#+begin_src sh
  mkdir -p ~/.mail/work ~/.mail/gmail
  mu init --maildir=~/.mail   mu index
  mbsync -Va
  mu index
#+end_src
** Mbsync config
See [[file:ha-email.org][ha-email]] for better instructions.
#+begin_src sh
  cat ~/.mbsyncrc
#+end_src
Basic configuration, that I actually supersede.
#+begin_src conf
  # ========== Gmail ==========
  IMAPAccount gmail
  Host imap.gmail.com
  User username@gmail.com
  PassCmd "/opt/homebrew/bin/gpg --quiet --for-your-eyes-only --no-tty --decrypt ~/.password-store/mbsync/gmail.gpg"
  AuthMechs LOGIN
  SSLType IMAPS

  IMAPStore gmail-remote
  Account gmail

  MaildirStore gmail-local
  Subfolders Verbatim
  Path ~/.mail/gmail/
  Inbox ~/.mail/gmail/Inbox

  Channel gmail
  Far :gmail-remote:
  Near :gmail-local:
  Patterns * ![Gmail]* "[Gmail]/Sent Mail" "[Gmail]/Starred" "[Gmail]/All Mail"
  Expunge None
  CopyArrivalDate yes
  Sync All
  Create Near
  SyncState *
  # ========== Gmail ==========
#+end_src
* Dæmon Processes
On the Mac, =cron= has been removed and replaced with =LaunchAgent=. I find my [[file:ha-capturing-notes.org::*Push MacOS-Specific Content][ICanHazShortcut]] process pretty simple to start Emacs, so I’m not sure about this dæmon, but …
** Emacs dæmon via LaunchAgent
Notice that =UserName= section should be your =$USER= value.
#+begin_src xml :tangle ~/Library/LaunchAgents/gnu.emacs.plist
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  <plist version="1.0">
    <dict>
      <key>KeepAlive</key>
      <true/>
      <key>Label</key>
      <string>gnu.emacs</string>
      <key>ProgramArguments</key>
      <array>
        <string>/opt/homebrew/bin/emacs</string>
        <string>--fg-dæmon</string>
      </array>
      <key>RunAtLoad</key>
      <true/>
      <key>StandardErrorPath</key>
      <string>/tmp/gnu-emacs-dæmon.log</string>
      <key>StandardOutPath</key>
      <string>/tmp/gnu-emacs-dæmon.log</string>
      <key>UserName</key>
      <string>howard</string>
    </dict>
  </plist>
#+end_src

Verify that the plist file is correct.
#+begin_src sh
  plutil -lint ~/Library/LaunchAgents/gnu.emacs.plist
#+end_src

Start, stop and list service.
#+begin_src sh
  launchctl load -w /Users/USERNAME/Library/LaunchAgents/gnu.emacs.plist
  launchctl unload /Users/USERNAME/Library/LaunchAgents/gnu.emacs.plist
  launchctl list
#+end_src
** Fetch mails periodically
Let’s make another dæmon for fetching mail. Again, replace =UserName= with your user account name.
#+begin_src xml :tangle ~/Library/LaunchAgents/periodic.mbsync.plist
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  <plist version="1.0">
    <dict>
      <key>KeepAlive</key>
      <true/>
      <key>Label</key>
      <string>periodic.mbsync</string>
      <key>ProgramArguments</key>
      <array>
        <string>/Users/USERNAME/.bin/mbsync-task</string>
      </array>

      <key>StandardOutPath</key>
      <string>/tmp/mbsync-task.log</string>

      <key>StandardErrorPath</key>
      <string>/tmp/mbsync-task.log</string>

      <key>ThrottleInterval</key>
      <integer>180</integer>

      <key>RunAtLoad</key>
      <true/>

      <key>UserName</key>
      <string>howard</string>
    </dict>
  </plist>
#+end_src

Verify that the plist file is correct.
#+begin_src sh
  plutil -lint ~/Library/LaunchAgents/periodic.mbsync.plist
#+end_src

Start, stop and list service.
#+begin_src sh
  launchctl load -w /Users/USERNAME/Library/LaunchAgents/periodic.mbsync.plist
  launchctl unload /Users/USERNAME/Library/LaunchAgents/periodic.mbsync.plist
  launchctl list
#+end_src

Script that fetches mails and updates the mail index.
#+begin_src sh :tangle ~/.bin/mbsync-task :shebang #!/bin/bash
  echo ""
  echo "Running $(date +"%Y-%m-%d %H:%M")"
  /opt/homebrew/bin/mbsync -Va
  echo "Exit code:"
  echo $?
  /opt/homebrew/bin/emacsclient -e '(mu4e-update-index)'
  echo "Exit code:"
  echo $?
#+end_src
* Emacsclient
Simple /Automator/ script that's wrapped into an application and placed in the =Applications= folder. Select *New Document*, then select *Application*. Open the *Library*, and drag the *Run Shell Script* to the /workflow/. In the box, add this:
#+begin_src sh
  /opt/homebrew/bin/emacsclient -nc --socket-name work $*
#+end_src
Change the *Pass Input* to =as arguments=.

Select to *Save* as =Emacsclient= into the *Applications* folder.
** Utils
Convert a plist XML file into a JSON file. Not sure why this is important to know…
#+begin_src sh
  plutil -convert json -r ~/Library/LaunchAgents/gnu.emacs.plist
#+end_src
Which should look a bit like:
#+begin_src js
  {
      "KeepAlive" : true,
      "Label" : "gnu.emacs",
      "ProgramArguments" : [
          "\/opt\/homebrew\/bin\/emacs",
          "--fg-dæmon"
      ],
      "RunAtLoad" : true,
      "StandardErrorPath" : "\/tmp\/gnu-emacs-dæmon.log",
      "StandardOutPath" : "\/tmp\/gnu-emacs-dæmon.log",
      "UserName" : "USERNAME"
  }
#+end_src

Convert it back to XML
#+begin_src sh
  plutil -convert xml1 ~/Library/LaunchAgents/gnu.emacs.plist
#+end_src
** Resources
#+begin_src sh
  man launchd
  man launchctl
  man launchd.plist
  man plutil
  man plist
#+end_src

#+description: A literate programming file for installing a dæmon version of Emacs on MacOS.

#+property:    header-args:sh :tangle no
#+property:    header-args:emacs-lisp :tangle no
#+property:    header-args   :results none   :eval no-export   :comments no

#+options:     num:nil toc:t todo:nil tasks:nil tags:nil date:nil
#+options:     skip:nil author:nil email:nil creator:nil timestamp:nil
#+infojs_opt:  view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
