Emacs を自分でビルドしよう (MacOS)
この記事は Emacs Advent Calendar 2023 の 7 日目の記事です。
はじめに
みなさんは自分の好きなエディタはありますか?
私はなんだかんだいってEmacsを愛用しています。
自分で使うツールは自分でビルドしたいですよね?
ということで、Emacsをパッケージマネージャなどを使わずに自分でビルドして入れる方法を紹介します。
私の環境
- Macbook Pro (MacOS 13.4, M1)
必要なものを揃える
XCode の Command line tools と、Homebrew は用意しておきましょう (大体の人は入れてると思いますが)。
libgccjit が必要になるので入れます。
$ brew install libgccjit
ビルドの仕方
まずは git プロジェクトの clone
$ git clone git://git.sv.gnu.org/emacs.git
次に、自分が使いそうなオプションを追加して configure します。
git switch などで特定のリリースをビルドしましょう。
私は master ブランチのままビルドしています。
configure するときにいくつか引数を渡すことで、特定のビルドオプションを ON / OFF できます。 詳しくはここに書いています。
絶対に入れておくべきなのは、--with-native-compilation
です。
Emacs 28 以降、Native Compile にすることで高速に使うことができます。
$ cd ./emacs $ git pull $ ./autogen.sh $ ./configure --with-native-compilation \ --without-x \ --with-ns \ --with-imagemagick \ --with-json
ここまできたら、あとは build のみです。
$ make bootstrap $ make install
configure や bootstrap 中にエラー終了することがありますが、気づきにくいのでよくログを見ておきましょう。
たいていはなにかのパッケージが足りてないので、都度エラーの内容から入れるべきパッケージを入れましょう。
make install
が終わると、 nextstep/
配下に Emacs.app
ができているので、これを /Application
内に移動して完了です。
注意点
結構ビルドが失敗する要因として、libgccjit
と openssl
のバージョンです。
Emacs のバージョンによって最低要件のバージョンなどがあるので、コケたらよく調べておきましょう。
さいごに
「ビルドがコケたら」の話は人それぞれなのと、Emacs を自分でビルドして使いたい人はビルド環境などを調べるのも好きな人が多いと思うので、
解決方法を根気よく探しましょう。
Emacs はオープンソースでビルドが簡単なので、ぜひ思い思いのオプションをつけてビルドしてみましょう!
お世話になった記事
はじめてビルドするとき、大変お世話になりました。
SendGrid の Dynamic Template では equals~else equals 構文も使える
SendGrid の Dynamic Template では、簡易的に if 文などの構文が使える。
このドキュメントにおいて、if ~ else if の構文が使えることが紹介されている。
実はドキュメントに書いてないが、 equals や notEquals でも同様の構文が使える。
{{#equals var "hoge"}} ホゲだよ {{else equals var "fuga"}} フガでござる {{else}} 知らんがな {{/equals}}
上記の例では、
- {"var": "hoge"} のとき => ホゲだよ
- {"var": "fuga"} のとき => フガでござる
- {"var": "piyo"} のとき => 知らんがな
という置換がされる。
なお注意として、これもifの項でしかドキュメントに書いてないのだが equals スコープ内で変数置換する場合、その参照の仕方に注意しないといけない。
# 渡すjson {"weather": "rain", "date": "2021-12-31"} # テンプレート {{#equals weather "rain"}} 雨量が少なければ様子を見て開催 {{else equals weather "snow"}} 中止 {{else}} 開催 ({{@root.date}}) {{/equals}}
lsp-mode の設定を晒す (2021-02-06 時点)
自分の設定を晒す。
(lsp-document-sync-method . 'lsp--sync-incremental)
がコメントアウトされているが、他の記事に書いてあったこの設定を入れていたらlspがちゃんと動かなかったので、指定を外した。
こういう設定系は誰かが最新の環境で動くものを晒さないと構成のためにならないと思うので、自分も書いておく。
(leaf lsp-mode :doc "LSP mode" :req "emacs-26.1" "dash-2.14.1" "dash-functional-2.14.1" "f-0.20.0" "ht-2.0" "spinner-1.7.3" "markdown-mode-2.3" "lv-0" :tag "languages" "emacs>=26.1" :added "2020-08-27" :url "https://github.com/emacs-lsp/lsp-mode" :emacs>= 26.1 :ensure t :after spinner markdown-mode lv :custom `((lsp-keymap-prefix . "C-c l") (lsp-inhibit-message . t) (lsp-message-project-root-warning . t) (create-lockfiles . nil) (lsp-signature-auto-activate . t) (lsp-signature-doc-lines . 1) (lsp-print-performance . t) (lsp-log-io . t) (lsp-eldoc-render-all . t) (lsp-enable-completion-at-point . t) (lsp-enable-xref . t) (lsp-keep-workspace-alive . nil) (lsp-enable-snippet . t) (lsp-server-trace . nil) (lsp-auto-guess-root . nil) ;; (lsp-document-sync-method . 'lsp--sync-incremental) (lsp-document-sync-method . 2) (lsp-diagnostics-provider . :flycheck) (lsp-response-timeout . 5) (lsp-idle-delay . 0.500) (lsp-enable-file-watchers . nil) (lsp-completion-provider . :capf) (lsp-headerline-breadcrumb-segments . '(symbols))) :commands (lsp lsp-deferred) :hook (prog-major-mode . lsp-prog-major-mode-enable) (lsp-mode-hook . lsp-ui-mode) (lsp-mode-hook . lsp-headerline-breadcrumb-mode) :init (leaf lsp-ui :doc "UI modules for lsp-mode" :req "emacs-26.1" "dash-2.14" "dash-functional-1.2.0" "lsp-mode-6.0" "markdown-mode-2.3" :tag "tools" "languages" "emacs>=26.1" :added "2020-08-27" :url "https://github.com/emacs-lsp/lsp-ui" :emacs>= 26.1 :ensure t :after lsp-mode markdown-mode :custom ((lsp-ui-doc-enable . t) (lsp-ui-doc-deley . 0.5) (lsp-ui-doc-header . t) (lsp-ui-doc-include-signature . t) (lsp-ui-doc-position . 'at-point) (lsp-ui-doc-max-width . 150) (lsp-ui-doc-max-height . 30) (lsp-ui-doc-use-childframe . nil) (lsp-ui-doc-use-webkit . nil) (lsp-ui-flycheck-enable . t) (lsp-ui-peek-enable . t) (lsp-ui-peek-peek-height . 20) (lsp-ui-peek-list-width . 50) (lsp-ui-peek-fontify . 'on-demand) ;; never, on-demand, or always ) :hook ((lsp-mode-hook . lsp-ui-mode)) ) (leaf lsp-treemacs :doc "LSP treemacs" :req "emacs-26.1" "dash-2.14.1" "dash-functional-2.14.1" "f-0.20.0" "ht-2.0" "treemacs-2.5" "lsp-mode-6.0" :tag "languages" "emacs>=26.1" :added "2020-10-22" :url "https://github.com/emacs-lsp/lsp-treemacs" :emacs>= 26.1 :ensure t :after treemacs lsp-mode) )
なお動作環境は Emacs 28.0.50。
起動時に Warning (comp): el-get-status.el:22:1: Warning: Package cl is deprecated
みたいなのが出まくるんだけど。
init.el に
(setq byte-compile-warnings '(not cl-functions obsolete))
を書いているのに直らない。
Emacs-27 では出なかったんだけど、既知の問題なんだろうか?
鬱陶しいだけなのだが、鬱陶しいのでなんとかしたい…。
だれか教えてください。
quick search を Go で書く
タイトル通り。
参考記事を元に書いたけど、正しく実装できてるのか自信ない。
愚直な実装も一緒に書いておいて、結果の比較をできるようにしてある。
package main import ( "bufio" "fmt" "os" "strings" ) const SIGMA = 256 func native(ts, ps []string) { n := len(ts) m := len(ps) if n == 0 || m == 0 { return } for i := 0; i <= n-m; i++ { j := 0 for j = 0; j < m && ps[j] == ts[i+j]; { j++ } if j == m { fmt.Println(i) } } } func quickSearch(ts, ps []string) { n := len(ts) m := len(ps) if n == 0 || m == 0 { return } // 要素数+1を参照するため ts = append(ts, "") // 移動表 shift := make(map[string]int) for i := 0; i < m; i++ { shift[ps[i]] = m - i } for i := 0; i <= n-m; { j := 0 for j = 0; j < m && ps[j] == ts[i+j]; j++ { } if j == m { fmt.Println(i) } if shift[ts[i+m]] != 0 { i += shift[ts[i+m]] } else { i += m } } } func main() { var t, p string var sc = bufio.NewScanner(os.Stdin) if sc.Scan() { t = sc.Text() } if sc.Scan() { p = sc.Text() } ts := strings.Split(t, "") ps := strings.Split(p, "") fmt.Println("===== NATIVE =====") native(ts, ps) fmt.Println("===== QS =====") quickSearch(ts, ps) }
Gist に残した: quick search · GitHub
参考記事
Doom Emacs から Vanilla Emacs に乗り換えた
もう数年間 Emacs を使っているものの、ちょくちょく環境を変えてきた。
最初は Spacemacs を使っていたが、起動が重くて Doom Emacs に切り替えて使ってきた。
しかし、leaf.el の登場で設定が結構簡単に書けるようになるので、これを期に Vanilla の Emacs に乗り換えることにした。
使っているパッケージを整理する
新しく設定を書くにあたって、 Spacemacs や Doom Emacs でサラッと利用していたパッケージがあるので、よく使う機能を整理した。
中には、ディストリビューションが初期状態で搭載しているものもあったので、ここが結構大変だった。
などなど…
他にも、使う言語に関連するパッケージ (tide とか) も整理した。
設定を書く
init.el に leaf.el の例のインストールのやつを書いて、その後各パッケージの設定を書く。
init.el を更新したら、以下のコマンドでバイトコンパイルする。
$ cd ~/.emacs.d $ emacs --batch -f batch-byte-compile init.el
built-in のパッケージの設定
特に、当たり前に付いてるけど忘れがちなパッケージの設定は結構ハマった。
ちなみに、各設定は、パッケージが登録されてれば leaf-convert-insert-template
でパッケージ名を入れれば、デフォルト設定が簡単に入れられる。
- 行番号の表示を linum で表示
(leaf linum :doc "display line numbers in the left margin" :tag "builtin" :added "2020-08-27" :init (global-linum-mode 1))
- 対応する括弧の自動挿入
(leaf electric :doc "window maker and Command loop for `electric' modes" :tag "builtin" :added "2020-08-27" :init (electric-pair-mode 1))
- ispell を aspell で使う
(leaf ispell :doc "interface to spell checkers" :tag "builtin" :added "2020-08-27" :setq-default (ispell-program-name . "aspell"))
追加パッケージの設定
exec-path-from-shell
を入れないとシェルの環境変数が読み込まれず、これが一番ハマった。
具体的には、typescript の lspを使うにあたって npm が読み込めず、解決するまでめちゃくちゃ時間かかった。
(leaf exec-path-from-shell :doc "Get environment variables such as $PATH from the shell" :req "emacs-24.1" :tag "environment" "unix" "emacs>=24.1" :added "2020-08-27" :url "https://github.com/purcell/exec-path-from-shell" :emacs>= 24.1 :ensure t)
なお、exec-path-from-shell
と server-start
を、init.el の最後の方に追加した。
... (provide 'init) (exec-path-from-shell-initialize) (server-start) ;; Local Variables: ;; indent-tabs-mode: nil ;; byte-compile-warnings: (not cl-functions obsolete) ;; End:
その他
なんか見づらいな〜と思ってたら、フォントの設定を忘れてた。
設定の仕方は、シンプルに
(setq default-frame-alist '( (font . "Cica 16")))
って感じ。
あと、キーバインドの設定も忘れずに。
(leaf-keys (("C-h" . backward-delete-char) ("C-c M-a" . align-regexp) ("C-c ;" . comment-region) ("C-c M-;" . uncomment-region) ("C-/" . undo) ("C-C M-R" . replace-regexp) ("C-c r" . replace-string) ("<home>" . beginning-of-buffer) ("C-c M-l" . toggle-truncate-lines) ("C-M-%" . vr/query-replace) ("C-c v g" . magit-status) ("C-c m e" . emoji-cheat-sheet-plus-insert)))
やってみた感想
起動がめっちゃ早くなった。バイトコンパイルの影響もあるのかな?
当たり前だけど、必要なパッケージだけを入れたので、シンプルになった。
個人的に leaf.el のいいな〜と思った点は、 init.el に設定を詰め込んでも leaf-tree-mode
で見たい設定のところまで飛べるところ。
大変便利だった。
まだまだ lsp のところが不安。
リモートワークなので tramp-mode を活用したいが、lsp over tramp-mode がどうなるのか分からなくて、そこでまた色々苦戦しそうだなと思っている。
今の Emacs 事情は、viper とか使わないなら変にディストリを使わなくてよくなったなあと思う。
leaf.el 、偉大だなぁ。