Windows11に Emacs 30.1を Installして Native Code Compileを試すまで

emacs 30.1リリースのニュースを見かけて 久しぶりに emacsを使ってみようかなと。

## 方針とか

インストーラを使わない様にしたいので、emacsは Zipから展開します。

native compileを有効にして使ってみたいのですが、MSYS2をインストールしたくありません。
このため、MSYS2のパッケージのうち native compile に必要なものだけを emacs環境専用に手動展開します。

## 環境と必要なもの

- Windows11 x86_64
- zip/tar.zstの展開ツール（私は7z.exeを利用しました）

## ダウンロードと展開

ダウンロードしたファイルは `%HOME%\.local\emacs` へ展開することにしました。

### emacs本体

[emacs公式] から [近くのミラー](http://ftpmirror.gnu.org/emacs/windows) で `emacs-30.1.zip` をダウンロードします。

これは普通に展開するだけでOKです。

[emacs公式]: https://www.gnu.org/savannah-checkouts/gnu/emacs/emacs.html

### [gcc]の入手と展開

[gcc]: https://packages.msys2.org/packages/mingw-w64-x86_64-gcc

Native Compileには [gcc] が必要なのですが、公式のアーカイブには
必要なコンパイラ、ライブラリが含まれていないため追加します。

https://packages.msys2.org/packages/mingw-w64-x86_64-gcc

このページの File: の欄にある…

https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-gcc-14.2.0-3-any.pkg.tar.zst

をダウンロードします。

Emacs本体とは違い、MSYS2向けのパッケージです。

基本ダウンロードしたパッケージの mingw64 以下を Emacsと同じ場所へ展開します。

### [gcc]の依存パッケージの入手

gcc と同様に以下のパッケージもEmacsと同じ場所へ展開します。

- [binutils](https://packages.msys2.org/packages/mingw-w64-x86_64-binutils)
- [crt-git](https://packages.msys2.org/packages/mingw-w64-x86_64-crt-git)
- [gcc-libs](https://packages.msys2.org/packages/mingw-w64-x86_64-gcc-libs)
- [gmp](https://packages.msys2.org/packages/mingw-w64-x86_64-gmp)
- [isl](https://packages.msys2.org/packages/mingw-w64-x86_64-isl)
- [mpc](https://packages.msys2.org/packages/mingw-w64-x86_64-mpc)
- [mpfr](https://packages.msys2.org/packages/mingw-w64-x86_64-mpfr)
- [winpthreads-git](https://packages.msys2.org/packages/mingw-w64-x86_64-winpthreads-git)
- [zlib](https://packages.msys2.org/packages/mingw-w64-x86_64-zlib)
- [zstd](https://packages.msys2.org/packages/mingw-w64-x86_64-zstd)

:::note info
私には、どこまで必要なのかが判断付きませんでしたので、gccが依存するパッケージは全部追加しています。
ただ、.elnファイルは基本的なライブラリ以外に依存していないように見えます。
Lispの Native Compileだけを目的とした場合かなり（ほとんど？）ダイエットできると思います。
:::

gcc の依存関係には、以下も含まれているが これらはなくても .eln が生成されたので多分問題なさそうです。気になる方は合わせて入れても良いかと思います。

- [headers-git](https://packages.msys2.org/packages/mingw-w64-x86_64-headers-git)
- [windows-default-manifest](https://packages.msys2.org/packages/mingw-w64-x86_64-windows-default-manifest)

また、binutilsはさらに以下のパッケージにも依存しています。こちらも気になる方は合わせて入れてください。

- https://packages.msys2.org/base/mingw-w64-gettext
    - https://packages.msys2.org/packages/mingw-w64-x86_64-gettext-libtextstyle
    - https://packages.msys2.org/packages/mingw-w64-x86_64-gettext-runtime
    - https://packages.msys2.org/packages/mingw-w64-x86_64-gettext-tools
- https://packages.msys2.org/packages/mingw-w64-x86_64-iconv
- https://packages.msys2.org/packages/mingw-w64-x86_64-libiconv

なお、windows-default-manifestがあれば、普通に .c をコンパイルして .exe が作れます。Headersがあれば、普通に使える GCC環境になるかと思いますが、セキュリティ上は無い方が良いかなと思っています[^security_considerations]。

[^security_considerations]: これ言ったら Native Compile 自体をセキュリティ上避けるべき話な気もします。

mingw-w64-x86_64-gcc-libsは dllしか含まず、Emacs本体が必要とする dllファイルは emacs本体に含まれています。パッケージには追加で以下のdllが含まれています。

- gcc-libs
    - libatomic-1.dll
    - libgomp-1.dll
    - libquadmath-0.dll

Emacs本体に含まれないという意味ではgmpにも追加のdllが存在します。

- gmp
    - libgmpxx-4.dll

### crtの入手についての注意

[crt-git]パッケージは 4.5MBほどで 1000ファイル弱含まれています。

私の環境では、このファイルを Microsoft Defenderでスキャンすると、なぜか無限ループに陥ってスキャン済みファイル数が増え続けました。MS Defenderを使っている方は注意してください。ブラウザを使ったダウンロードもダウンロード後にScanが終わるまで完了としないので、永遠に終わりません。

curl で取得後に展開し、それを MS Defenderでスキャンする等すればよいかと思います。

[crt-git]: https://packages.msys2.org/packages/mingw-w64-x86_64-crt-git

### libgccjitの入手

Native Compileはさらに [libgccjit] を必要とします。

これまでと同じように パッケージをダウンロードして展開します。

[libgccjit]: https://packages.msys2.org/packages/mingw-w64-x86_64-libgccjit

### 全部のファイルをダウンロードするバッチファイル

2025/03/01 時点

```bat:download_all.bat
curl -LOJR http://ftpmirror.gnu.org/emacs/windows/emacs-30/emacs-30.1.zip
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-gcc-14.2.0-3-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-binutils-2.44-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-crt-git-12.0.0.r509.g079e6092b-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-gcc-libs-14.2.0-3-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-gmp-6.3.0-2-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-isl-0.27-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-mpc-1.3.1-2-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-mpfr-4.2.1-2-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-winpthreads-git-12.0.0.r509.g079e6092b-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libwinpthread-git-12.0.0.r509.g079e6092b-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-zlib-1.3.1-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-zstd-1.5.7-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-headers-git-12.0.0.r509.g079e6092b-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-windows-default-manifest-6.4-4-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-gettext-libtextstyle-0.24-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-gettext-runtime-0.24-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-gettext-tools-0.24-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-iconv-1.18-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libiconv-1.18-1-any.pkg.tar.zst
curl -LOJR https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libgccjit-14.2.0-3-any.pkg.tar.zst
```

### 全部のファイルを展開するバッチファイル

`emacs-30.1.zip` に付属された dllが更新されてしまうのを防ぎたい場合、最後にemacs-30.1.zipを解凍するとよいかと思います。

```bat:extract_all.bat
MKDIR mingw64
7z x -omingw64 emacs-30.1.zip

REM extract all .tar.zst files
FOR /R %%F IN (*.zst) DO 7z x %%F
FOR /R %%F IN (*.tar) DO 7z x -y %%F

REN mingw64 emacs
```

中間ファイル等の削除はしませんので適宜削除してください。
解凍されたemacsフォルダを 設置場所に移動して使います

## emacs起動用バッチファイルの作成

:::note warn
この記事で作っている emacs環境は、libgccjit以外に mingw64の gcc/binutilsを追加しています。

以下の起動バッチファイルでは emacs 環境に向けたPATHを最優先（先頭）で追加しています。このため、既存で他にgcc等が存在する場合、emacs環境から呼び出したプロセスの gccの実行等に影響を与えます。

少し試してみたところ emacs.exe のある場所に pathを通さないでも一通りファイルがあれば、 Native Compileは正常に動作しました。gccを定常的に使う環境だったり*nix系のコマンドを追加している場合、このバッチファイルは使わない方がよいでしょう。
:::

私は以下のバッチファイルで　emacsを起動するようにしています。

```bat
@ECHO OFF
SETLOCAL
SET EMACS_HOME=%USERPROFILE%\.local\emacs
SET EMACS_BIN=%EMACS_HOME%\bin
SET PATH=%EMACS_BIN%;%PATH%
START "" "%EMACS_BIN%\runemacs.exe" %*
ENDLOCAL
```

emacsフォルダを7zで圧縮したところ151MiBのサイズになりました。
他の環境で使うときにはこのファイルを持ち運ぶのが楽に思います。ただ展開すると1GiB超えます。

## 確認

`dir %APPDATA%\.emacs.d\eln-cache\` で コンパイルされた eln ファイルを確認できます。
`objdump -x` 等でNativeコンパイルされていることが確認できるかと思います。

```console
C:\Users\username\AppData\Roaming\.emacs.d\eln-cache\30.1-50a58833\japanese-0ff63ea1-0f77ab2c.eln:     file format pei-x86-64
C:\Users\username\AppData\Roaming\.emacs.d\eln-cache\30.1-50a58833\japanese-0ff63ea1-0f77ab2c.eln
architecture: i386:x86-64, flags 0x0000013b:
HAS_RELOC, EXEC_P, HAS_DEBUG, HAS_SYMS, HAS_LOCALS, D_PAGED
start address 0x0000000361d41320

Characteristics 0x2026
        executable
        line numbers stripped
        large address aware
        DLL
```

## いろいろ

まだ全然使っていないのですが、安定して使えるといいなー。

emacs使いだったのですが、Windowsで IMEを使ったときに、たまにハングしてしまうので emacs利用は諦めてNeoVimに移行した過去があります。

あと、デフォルトでは `.emacs.d\eln-cache` が `%APPDATA%` 配下なので、Roaming 対象になります。
このため、Roaming User Profile が有効な環境で x86_64 と armな Windows使うと混乱するんじゃ……？なんて思いました。