はじめに
いやフィーバー(ふっるぅ)してないのかもしれませんが、先駆者の方がいろいろ情報を提供してくれているので、それに便乗してみたいと思います。私のオリジナルは、ほんの少しです。だからWindowsでEmacs Native Compilationオルタみたいな感じでお願いします。
WindowsのGCC実行環境
EmacsのNative Compilationを使う場合、GCCの実行環境が必要となります。.el->.elc->.elnという順にコンパイルされていき、.elnがネイティブな実行コードです。ここではWindowsに限定しているので、.elnはx86のバイナリということになります。まぁ。最近はarmという選択肢もありますが。
.elcから.elnへのコンパイルにはGCCのJITコンパイルが使用されます。このためGCCでコンパイルができる環境が無いとNative Compilationは使用できません。LinuxやBSDなどはgcc関連のパッケージをインストールするだけで良くて、Emacsなんか使っているような方はインストールしている人が多いのではないかと思います。これに対してWindowsでは公式から配布されているバイナリがMSYS2を使ってコンパイルされているのでMSYS2のインストールが必要となります。
もしMSYS2のインストールに躊躇が無いということであれば、MSYS2をインストールしシステムワイドにパスを通せばNative Compilationが使用できると思います。この方法を選択される場合は、ここで終了です。以降の内容は読んでもあまり参考にならないと思います。
MSYS2をインストールしないで使う。
MSYS2自体、そこそこ大きいシステムですし、cygwinのような環境を使っていると、そっちと同じコマンド名などあって干渉してしまいます。また公式配布物のdllはMSYS2のdll(正確にはmingw64or32だと思いますが)で、それをEmacsローカルで使用するようになっています。
Native Compilationも同じような感じで使いたいというのが、このドキュメントの主な内容です。
Native Compilationで必要なexeとdll
大前提として必要なexeとdllを入手するために、MSYS2の環境が必要です。ですので一度はインストールする必要があります。MSYS2の環境は特定のディレクトリ配下にすべてインストールされるので、アンインストールで環境が汚れるとかいうことはありません(レジストリは多少汚れると思いますが)。別は方法としてHyper-Vで仮想環境を作り、そちらにインストールして必要ファイルの入手後、削除するという方法もあると思います。この場合はライセンスに注意してください。無料の試用期間でそういうことやって良いのか私もわかりません。
必要なファイルは先駆者の方々がまとめてくれていますが、以下に載せておきます。
ファイル名 |
---|
MSYS2インストールDIR/mingw64/lib/libadvapi32.a |
MSYS2インストールDIR/mingw64/lib/libgcc_s.a |
MSYS2インストールDIR/mingw64/lib/libkernel32.a |
MSYS2インストールDIR/mingw64/lib/libmingw32.a |
MSYS2インストールDIR/mingw64/lib/libmingwex.a |
MSYS2インストールDIR/mingw64/lib/libmoldname.a |
MSYS2インストールDIR/mingw64/lib/libmsvcrt.a |
MSYS2インストールDIR/mingw64/lib/libpthread.a |
MSYS2インストールDIR/mingw64/lib/libshell32.a |
MSYS2インストールDIR/mingw64/lib/libuser32.a |
MSYS2インストールDIR/mingw64/lib/gcc/x86_64-w64-mingw32/11.3.0/libgcc.a |
MSYS2インストールDIR/mingw64/lib/crtbegin.o |
MSYS2インストールDIR/mingw64/lib/crtend.o |
MSYS2インストールDIR/mingw64/lib/dllcrt2.o |
MSYS2インストールDIR/mingw64/bin/ld.exe |
MSYS2インストールDIR/mingw64/bin/as.exe |
MSYS2インストールDIR/mingw64/bin/libgccjit-0.dll |
MSYS2をインストールしていない環境でのsearch dir
GCCの実行環境では、dll以外の必要なファイルはsearch dirという仕組みで検索されます。どのディレクトリがsearch dirとなっているかは、以下のコマンドで確認できます。
$ gcc -print-search-dirs
install: C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/
programs: =C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/;C:/msys64/mingw64/bin/../lib/gcc/;C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/../../../../x86_64-w64-mingw32/bin/x86_64-w64-mingw32/11.3.0/;C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/../../../../x86_64-w64-mingw32/bin/
libraries: =C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/;C:/msys64/mingw64/bin/../lib/gcc/;C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/../../../../x86_64-w64-mingw32/lib/x86_64-w64-mingw32/11.3.0/;C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/../../../../x86_64-w64-mingw32/lib/../lib/;C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/../../../x86_64-w64-mingw32/11.3.0/;C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/../../../../lib/;D:/a/msys64/mingw64/lib/x86_64-w64-mingw32/11.3.0/;D:/a/msys64/mingw64/lib/../lib/;C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/../../../../x86_64-w64-mingw32/lib/;C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/11.3.0/../../../;D:/a/msys64/mingw64/lib/
C:/msys64で始まるパスはインストールディレクトリからの相対パスです。一方、D:/a/msys64で始まるパスはコンパイル時に指定した絶対パスです。Linuxなどの環境ではコンパイル時に指定したパス以外にインストールされることはあまりないので、相対パスも絶対パスも同じ場所を示しているはずです。一方Windowsでは、MSYS2の環境自体が外様ですので、絶対パスは使用せず相対パスで解決する方式になるはずです。
MSYS2をインストールしていない環境で動作させる場合、先のsearch pathと同じディレクトリを作成し、そこに配置すれば相対パスでの解決が可能になります。しかしもっと良い方法として以下のオプションを指定する方法があります。
$ gcc --help
Usage: gcc.exe [options] file...
Options:
-pass-exit-codes Exit with highest error code from a phase.
...
-B <directory> Add <directory> to the compiler's search paths.
...
-Bオプションを使用すると、任意のディレクトリをsearch dirに追加することができます。
必要ファイルの配置場所
libgccjit-0.dllをemacsのインストールディレクトリのbinに配置してください。emacs.exeが参照できる必要があります。それ以外のファイルは一つのディレクトリにまとめてください。ここを-Bオプションで指定するディレクトリにします。例としてc:/emacs/native-comp/libを使います。
Native Compilationへの実行オプションの指定。
native-comp-driver-options
がコンパイル実行時に使用されるオプションの変数です。先ほどの-Bオプションをここで指定します。
オプションにはnative-comp-compiler-options
という変数も存在します。JITコンパイルは、.elcからオブジェクトファイル(.o)を作る段階と、オブジェクトファイルをリンクして.dllを作る段階の2段階が存在します。こちらのオプションは.oを作る段階で使用されるオプションの変数です。.aやランタイム用の.oはリンク時に必要となるファイルなのでnative-comp-driver-options
にだけオプションを設定します。
以下の内容を初期化ファイルで書けば良いでしょう。ディレクトリはお使いの場所に書き換えてください。
(custom-set-variables
'(native-comp-driver-options '("-B" "c:/emacs/native-comp/lib" ))
)
ここまで来たならMSYS2の環境は不要です。他の環境は持っていく場合でもlibgccjit-0.dllとc:/emacs/native-comp/lib配下のファイルを一緒にもっていけばNative Compilationが可能です。
起動時のエラーへの対応
EmacsのNative Compilationは起動時にlibgccjit-0.dllが読み込めるとオンになるそうです。オンになるとnative-comp-available-p
がtを返すようになります。
(native-comp-available-p)
t
公式配布のEmacsには、lib/emacs/28.1/native-lisp/28.1-f77b20d2内に配布物の.elnが含まれています。理屈は良く分からないのですが、ここに.elnが存在する.elcも実行環境でコンパイルが行われます。このコンパイル処理は裏で非同期に行われます。*Async-native-compile-log*バッファは非同期処理の実行内容がダンプされます。またコンパイルエラーが発生すると*Warnings*バッファにエラー内容がダンプされます。
native-comp-driver-options
の設定は初期化ファイルで行っていますが、初期化ファイルが読み込まれる前にコンパイルが発生すると以下のようなエラーが*Async-native-compile-log*に出力されます。
Compiling c:/emacs/share/emacs/28.1/lisp/language/japan-util.el...
ld: dllcrt2.o が見つかりません: No such file or directory
ld: crtbegin.o が見つかりません: No such file or directory
ld: -lmingw32 が見つかりません
ld: -lgcc_s が見つかりません
ld: -lgcc が見つかりません
ld: -lmoldname が見つかりません
ld: -lmingwex が見つかりません
ld: -lmsvcrt が見つかりません
ld: -lmingw32 が見つかりません
ld: -lgcc_s が見つかりません
ld: -lgcc が見つかりません
ld: -lmoldname が見つかりません
ld: -lmingwex が見つかりません
ld: -lmsvcrt が見つかりません
ld: crtend.o が見つかりません: No such file or directory
c:\emacs\bin\libgccjit-0.dll: error: error invoking gcc driver
c:/emacs/share/emacs/28.1/lisp/language/japan-util.el: Error: Internal native compiler error failed to compile
-B未指定でリンカが動作しているため、.aや.oを見つけることができないというエラーになっています。コンパイルの処理タイミングを遅らせたりする方法はないので、次回起動時も同様にエラーになってしまいます。これを避けるために事前コンパイルをしてしまいましょう。*scratch*で以下のコマンドを実行します。行毎に行末でC-j
です。
(native-compile "c:/emacs/share/emacs/28.1/lisp/language/japan-util.el")
私の環境では以下のファイルの事前コンパイルが必要でした。もしあなたの環境で他のファイルもエラーになるようなら、同様にコンパイルしてください。
(native-compile "c:/emacs/share/emacs/28.1/lisp/language/japan-util.el")
(native-compile "c:/emacs/share/emacs/28.1/lisp/emacs-lisp/cl-lib.el")
(native-compile "c:/emacs/share/emacs/28.1/lisp/emacs-lisp/seq.el")
(native-compile "c:/emacs/share/emacs/28.1/lisp/emacs-lisp/gv.el")
(native-compile "c:/emacs/share/emacs/28.1/lisp/emacs-lisp/cconv.el")
(native-compile "c:/emacs/share/emacs/28.1/lisp/emacs-lisp/bytecomp.el")
エラーメッセージが環境依存していたので追記
私はcygwinを使っているのですが、上記エラーメッセージ、どうもcygwinのas.exeやld.exeが動いた結果、出力されたエラーメッセージのようです。エラーメッセージが特定環境用の例になっていました。GCC実行環境が全くない環境で設定すると以下のようなエラーになります。
Compiling c:/emacs/share/emacs/28.1/lisp/emacs-lisp/cconv.el...
x86_64-w64-mingw32-gcc-11.3.0: fatal error: cannot execute 'as': CreateProcess: No such file or directory
compilation terminated.
as.exeがそもそも見つけらずエラーになるようです。全く別なPCに設定を行っていて気付きました。対応方は先に書いた内容と同じです。事前コンパイルを行ってください。
あとがき
昔、新世紀エヴァンゲリオンの考察同人誌というのを読んだことがあって、”あとがき”みたいな部分に、”この本を書く上でいろいろなことを調べた(死海文書?とか)。異論や反論があるなら、最低限自分と同じレベルの調査を行ってからじゃないと聞く耳持たない。”というようなことが書いてあって同人誌っていうのは凄い世界だなぁと思いました。