emacs on MINGW64

Table of Contents

emacs on MINGW64

ArchLinuxがVirtualBoxとVMWarePlayerの両方に入っていた。Diskが手狭になっ たので、VB側の方を削除した。こうしてDiskに余裕が出来たんで、今までうっちゃっていたmingw64を使って見る。そうemacsね。インストールは下記の通りにする。タダのemacsを指定しちゃうと、古い版が入ってしまうので注意。

pacman -S mingw-w64-x86_64-emacs

sakae@atom MINGW64 ~
$ ls -l /mingw64/bin/emacs*
-rwxr-xr-x 2 sakae なし 8301321 Oct 20 01:15 /mingw64/bin/emacs-29.1.exe*
-rwxr-xr-x 2 sakae なし 8301321 Oct 20 01:15 /mingw64/bin/emacs.exe*
-rwxr-xr-x 1 sakae なし   75528 Oct 20 01:15 /mingw64/bin/emacsclient.exe*
-rwxr-xr-x 1 sakae なし  441659 Oct 20 01:15 /mingw64/bin/emacsclientw.exe*

こんな場所に鎮座してた。

起動は、 emacs z.org & としてる。一応mingw64のshellを活かしておきたいからね。だってフォーグラウンドで起動したのをバックグラウンドへ移動できないんだもの。えせ、UNIXだね。そう思って使うしかないか。

diff then config

生意気に、ソースが付属してた(/mingw64/share/emacs/29.1/src)。オリジナ ルの奴と言うか、LINUXでコンパイルした時の残骸とソースの個数を比較する と、微妙に異なる。んで、どんな違いがあるかソースリストを作成して確認してみた。

[sakae@deb tmp]$ diff mingw linux
9a10
> buildobj.h
33a35
> config.h
55a58,59
> emacs-module.h
> epaths.h
73a78
> globals.h

コンパイルした時に自動生成されるファイルは当然同梱されていないのね。面白そうな、 config.h を覗いてみた。

/* Summary of some of the main features enabled by configure. */
#define EMACS_CONFIG_FEATURES "GMP GNUTLS LIBXML2 MODULES NOTIFY INOTIFY PDUMPE\
R SECCOMP SOUND SQLITE3 THREADS XIM ZLIB"

/* Define to the options passed to configure. */
#define EMACS_CONFIG_OPTIONS "--prefix=/opt --without-x --without-dbus --withou\
t-gsettings --without-harfbuzz --without-jpeg --without-lcms2 --without-libotf \
--without-m17n-flt"

そして、楽しい設定を発見。どこに反映されてるか調査。

[sakae@deb us]$ grep EMACS_CONFIG_OPTIONS *.c
emacs.c:  Vsystem_configuration_options = build_string (EMACS_CONFIG_OPTIONS);
[sakae@deb us]$ grep  Vsystem_configuration_options *.c
comp.c:                 Vsystem_configuration_options),
emacs.c:  DEFVAR_LISP ("system-configuration-options", Vsystem_configuration_options,
emacs.c:  Vsystem_configuration_options = build_string (EMACS_CONFIG_OPTIONS);

ふーん、emacsからも参照出来るのね。MINGWのemacsで、素性を点検。

ELISP> system-configuration-features
"ACL GIF GMP GNUTLS HARFBUZZ JPEG JSON LIBXML2 MODULES NATIVE_COMP NOTIFY W32NOTIFY PDUMPER PNG RSVG SOUND SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS TREE_SITTER WEBP XPM ZLIB"
ELISP> system-configuration-options
"--prefix=/mingw64 --host=x86_64-w64-mingw32 --build=x86_64-w64-mingw32 --with-modules --without-dbus --without-compress-install --with-tree-sitter --with-native-compilation 'CFLAGS=-march=nocona -msahf -mtune=generic -O2 -pipe  -fstack-protector-strong -fno-optimize-sibling-calls' CPPFLAGS=-D__USE_MINGW_ANSI_STDIO=1 'LDFLAGS=-pipe -lpthread'"

なんかこういうの、 カッコウはコンピュータに卵を産む のノリで面白いな。カッコウ話は75セントの請求差に疑問を持ち、そこからク ラッカーとの攻防に至る実録。オイラーの話は、上のようにショボくて、ファ イル個数の差から、emacs内の変数を発見する過程だ。

diff emacs/mingw64 and emacs/windows

今回入れたもの。 emacs-version on Messages

GNU Emacs 29.1 (build 2, x86_64-w64-mingw32) of 2023-10-20

昔から使っている奴。zip版を取ってきて、/c/app/に展開したのかな。ネイティ ブ・コンパイルが有効になってたんで、試してみたのさ。余り恩恵が無かった ような。。。 emacs for Windows

GNU Emacs 28.2 (build 2, x86_64-w64-mingw32) of 2022-09-14

EmacsLisp Benchmark

少し実用って事で、200エレメントのリストを100回ソート。バイトコンパイル 無し。@ 29.1

(benchmark-run 100
      (let* ((list (mapcar 'random (make-list 200 most-positive-fixnum)))
              (i (length list)))
         (while (> i 1)
           (let ((b list))
             (while (cdr b)
               (when (< (cadr b) (car b))
                 (setcar b (prog1 (cadr b)
                             (setcdr b (cons (car b) (cddr b))))))
               (setq b (cdr b))))
           (setq i (1- i)))
         list))

;; ===> (1.749432 10 0.2790549999999996) on Messages

EmacsのNative Compilationの性能を測定する こういう方がおられた。 M-x emacs-lisp-native-compile-and-load で開いているelファイルをネイティ ブコンパイルした上でロードできるのか、はじめて知ったな。

FreeBSDでNative compilation を見ると、gcc系が顔を出しているなあ。無理 してネィティブにしなくとも、バイトコンパイルでいいかな。何処でも動くの が重要。画餅じゃ困るからね。

(8.711196000000001 223 6.311959000000002) on ielm, why too slow? GCが頻発してるって事は、窮屈な環境を用意してるんだな。曰く、実験室なん で、火災とかが発生しても、他に影響しないようにしてる、かも。

IME

EMACS.JPの記事を見ていたら、emacsでIMEが簡単に使えるよと紹介されてた。普段はSKKなんだけど、予備ということで試してみた。

tr-emacs-ime-module

オイラーのパソコンは英語キーボードなんで、半角全角とか変換キーが無い。使えるかなと思ったら何とか使えた。

起動すると、英語入力になってる。コントロール+バックスラッシュすると、IMEがONになる。 やおらWINDOWS+SPACEでググルのIMEに切り替える。以後はずっとこのままだ。OFFにしようとしても No recurcive editとか言われて、切り替えができない。

かな漢字の入力はなんとかなる。カタカナもOK。問題は小文字の英語。emacsは、もしかしてが出てくるんで何とかなるけど、特殊な英単語は、もしかしても働かない。まあ、こういう物だと諦めるしかないかな。

Emacsのダイナミックモジュール の機能を使用しているんだな。生きた実例を 参照できるって、OSSの醍醐味ですよ。

DEBUG

灯台元暗しで、share/emacs/29.1/etcの中に、すばらしい資料が用意されてた。 これ、バグ出しに協力してくださいって、たってのお願いだ。

etc/DEBUG manual

これ、ただのテキスト・ファイルだけど、各段落に * が付けられている。 M-x org-mode して、 * の所でTABして、畳んでみた。もう一度TABすると、展 開される。結んで開いてって奴だ。

Debugging GNU Emacs

Copyright (C) 1985, 2000-2023 Free Software Foundation, Inc.
See the end of the file for license conditions.

 ** Preliminaries...
 ** When you are trying to analyze failed assertions or backtraces, it...
 ** It is a good idea to run Emacs under GDB (or some other suitable...
 ** If Emacs hangs, or seems to be stuck in some infinite loop, typing...
 ** Getting control to the debugger...
 ** Examining Lisp object values....
 ** Getting Lisp-level backtrace information within GDB...
 ** Debugging Emacs redisplay problems...
 ** Debugging problems with native-compiled Lisp....
 ** Following longjmp call....
 ** Using GDB in Emacs...
 ** Debugging what happens while preloading and dumping Emacs...
 ** If you encounter X protocol errors...
 ** If Emacs causes errors or memory leaks in your X server...
 ** If the symptom of the bug is that Emacs fails to respond...
 ** If certain operations in Emacs are slower than they used to be, here...
 ** If GDB does not run and your debuggers can't load Emacs....
 ** Debugging incorrect screen updating on a text terminal....
 ** Debugging LessTif...
 ** Debugging problems which happen in GC...
 ** Debugging the TTY (non-windowed) version...
 ** Running Emacs with undefined-behavior sanitization...
 ** Running Emacs with address sanitization...
 ** Running Emacs under Valgrind...
 ** How to recover buffer contents from an Emacs core dump file...

最初から、こうしておいてくれればいいのに、と思うけど、org-modeを知らな い人には、難度が高いから、平凡なファイルにしてるんだな。outlineって、 便利なんだけどな。Org Mode

.gdbinit

gdbの拡張が、src/ に鎮座してたんで、閲覧しましょ。

# Print out s-expressions
define pp
  set $tmp = $arg0
  set $output_debug = print_output_debug_flag
  set print_output_debug_flag = 0
  call safe_debug_print ($tmp)
  set print_output_debug_flag = $output_debug
end
document pp
Print the argument as an emacs s-expression
Works only when an inferior emacs is executing.
end

こんなgdb語のスクリプトがズラッと定義されてる。gdbコマンドの拡張だな。

define xbacktrace
  set $bt = backtrace_top ()
  while backtrace_p ($bt)
    set $fun = backtrace_function ($bt)
    xgettype $fun
    if $type == Lisp_Symbol
      xprintsym $fun
      printf " (0x%x)\n", backtrace_args ($bt)
    else
     :
document xbacktrace
  Print a backtrace of Lisp function calls from backtrace_list.
  Set a breakpoint at Fsignal and call this to see from where
  an error was signaled.
end

有用な拡張コマンドは、冒頭にxが付けられている。xで補完してみるとヘルプ の代わりになるかな。help xbacktrace とやると、そのドキュメントが表示さ れる。

show environment DISPLAY
if defined_HAVE_PGTK
  show environment WAYLAND_DISPLAY
end
show environment TERM

最後は、こんな風に、大事な環境を報告してる。この後に、pythonの部が続い ているけど、みなかった事にしておく。

on mingw64

折角なのでgdbしてみる。怖いもの見たさって事です。

$ gdb emacs-29.1.exe
GNU gdb (GDB) 14.1
 :
Type "apropos word" to search for commands related to "word"...
Reading symbols from emacs-29.1.exe...
(No debugging symbols found in emacs-29.1.exe)
(gdb) set new-console 1
(gdb) run
Starting program: C:\msys64\mingw64\bin\emacs-29.1.exe
[New Thread 13180.0x1cf0]
 :
[New Thread 13180.0x102c]  ;;; C-c C-c
Thread 9 received signal SIGTRAP, Trace/breakpoint trap.
[Switching to Thread 13180.0x2860]
0x00007fff2df30b11 in ntdll!DbgBreakPoint () from C:\WINDOWS\SYSTEM32\ntdll.dll
(gdb) info threads
  Id   Target Id           Frame
  1    Thread 13180.0x2424 0x00007fff2b9ca104 in win32u!NtUserMsgWaitForMultipleObjec
tsEx () from C:\WINDOWS\System32\win32u.dll
  5    Thread 13180.0x6a8  0x00007fff2df2d664 in ntdll!ZwDelayExecution ()
   from C:\WINDOWS\SYSTEM32\ntdll.dll
  6    Thread 13180.0x1860 0x00007fff2b9c1104 in win32u!NtUserGetMessage ()
   from C:\WINDOWS\System32\win32u.dll
  7    Thread 13180.0x28d8 0x00007fff2df30a74 in ntdll!ZwWaitForWorkViaWorkerFactory
() from C:\WINDOWS\SYSTEM32\ntdll.dll
  8    Thread 13180.0x102c 0x00007fff2df30a74 in ntdll!ZwWaitForWorkViaWorkerFactory
() from C:\WINDOWS\SYSTEM32\ntdll.dll
 * 9    Thread 13180.0x2860 0x00007fff2df30b11 in ntdll!DbgBreakPoint ()
   from C:\WINDOWS\SYSTEM32\ntdll.dll
(gdb) b Ftan
Function "Ftan" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (Ftan) pending.
(gdb) c

ちゃんと使おうとしたら、自前でコンパイルしてね。ど壺にはまる事受けあい なので、やりません。だって、エセunixですから。

on OpenBSD

emacsからgdbを起動すると、冒頭付近にこんな報告が出てくる。

DISPLAY = xx.xx.xx.xx:0
TERM = dumb
(gdb) r

途中で、こんな教育的指導が出て終了。 init_tty なんて、いやらしい場所 にあった。馬鹿端末は相手にしませんと言う高らかな宣言である。

emacs: Terminal type "dumb" is not powerful enough to run Emacs.
It lacks the ability to position the cursor.
If that is not the actual type of terminal you have,
use the Bourne shell command 'TERM=...; export TERM' (C-shell:
'setenv TERM ...') to specify the correct type.  It may be necessary
to do 'unset TERMINFO' (C-shell: 'unsetenv TERMINFO') as well.

こちらは、普通の起動。

vbox$ gdb -q emacs
Reading symbols from emacs...
SIGINT is used by the debugger.
DISPLAY = xxx.xxx.xxx.xxx:0
TERM = screen
Breakpoint 1 at 0x115b08: file emacs.c, line 427.
Breakpoint 2 at 0x1b394a: file floatfns.c, line 144.

ちゃんとshell設定の環境変数が使われている。ぐぐると、 端末上の emacs が、シェルから書き換える環境変数は何? やはり、emacsにも都合ってものが有るんですって事か。

(setenv "TERM" "screen") しておいてから、M-x gdb しても、dumbになっちゃ うし、どこで再設定してるかも判明せず。もう深入りしないでおこう。

ps for emacsOS

ちょいと気分を変えて、emacsとgdbの関係って、どうなってるの? そこが疑問だ、って事で、 プロセス関係の状態を調べてみた。

ELISP> (process-list)
(#<process gdb-inferior> #<process gud-emacs> #<process ielm>)

ELISP> (process-command (get-process "gdb-inferior"))
nil
ELISP> (process-command (get-process "gud-emacs"))
("gdb" "-i=mi" "emacs" "-p" "58560")

ELISP> (process-type (get-process "gdb-inferior"))
real
ELISP> (process-contact (get-process "gdb-inferior"))
t
ELISP> (process-tty-name (get-process "gdb-inferior"))
"/dev/ttyp5"

あれ、ielmはコマンドの補完が効くので、上の調査は、ielm上でやったんだけ ど、こやつの正体が判明。

ELISP> (process-command (get-process "ielm"))
("hexl")

emacsとは独立したプロセスになってる。隔離されてるんで、どんなに暴走し ようが、emacsには影響が無いとな。

9848 p3  I+       0:00.02 /opt/libexec/emacs/29.1/i386-unknown-openbsd7.4/hexl

と、糠喜びしたら、違った。ただの hex dumpプログラムだった (lib-src/hexl.c)。

with sub process

emacsから外部プロセスを呼ぶのも得意だろう。この間、viで下記が出来たん で感激したんだ。

10から1まで降順になったファイルがあります。
そのうちの10から6までを、昇順に変換するには、vi/vimなら
下記のコマンドで可能です。

:.,/6/!sort -n
5 lines added; 5 lines deleted

同じ事を、emacsでは、どのようにして実現できますか?

聞いてみた。例によって嘘を提示されちゃったので、自分で調べた。

変換したい範囲をリージョン指定する。続いて、C-u M-| Ask on mini-buffer: Shell command on region: sort -n

easy

(list-processes) で、下記の様なバッファーが表示される。emacsのpsみたい で、便利だな。

Process v       PID     Status  Buffer                    TTY          Thread       Command
gdb-inferior    -2      run     *input/output of emacs*   /dev/ttyp6   Main
gud-emacs       28306   run     *gud-emacs*               /dev/ttyp5   Main         gdb -i=mi emacs -p 80509
ielm            8968    run     *ielm*                    /dev/ttyp3   Main         hexl
shell           14383   run     *shell*                   /dev/ttyp4   Main         /bin/ksh -i

on MINGW64

ielm            5068    run     *ielm*                    --           Main         hexl
shell           13908   run     *shell*                   --           Main         C:\msys64\usr\bin\bash.exe -i

on Windows10

ielm            8124    run     *ielm*                    --           Main         hexl
shell           8412    run     *shell*                   --           Main         c:/app/emacs-28.2/libexec/emacs/28.2/x86_64-w64-mingw32/cmdproxy.exe -i

各OSによって、ファイル・セパレータが異なるってのは、発狂の原因になるな。 やはり根がWindows系だと、無理してemacsを移植してる感がする。なるべく Windowsでは、unixからの移植は使うな、が心の平成を保つには必要なんだろ うな。

便利と言えば、debug-on-entry しようとしたら、そんなの、 deb-en でもいいよって、mini-buffer に一瞬表示された。後で、メッセージ・ バッファーを見たら、確かにそんな記録が残っていた。見逃したら、そこを見 ろですね。

2点の例を挙げてみた。

You can run the command ‘shell-command-on-region’ with M-|
You can run the command ‘debug-on-entry’ with M-x deb-en

これ、どういう機構で出しているんだろう?

関数名から、説明を調べる、man相当。C-h f shell-command-on-region

shell-command-on-region is an interactive byte-compiled Lisp function
in ‘simple.el’.

It is bound to M-|.

キーバインドから説明を調べる。C-h k M-|

M-| runs the command shell-command-on-region (found in global-map),
which is an interactive byte-compiled Lisp function in ‘simple.el’.

It is bound to M-|.

残念ながら、debug-on-entryについては、キーバインドは提供されていなかっ た。でも、関数名の短縮系とおぼしき語句が提案された。

ielmで、debTAB すると、debugまで展開されて、下記の候補が提示された。

46 possible completions:
debug
debug--function-list
debug--implement-debug-on-entry
debug--implement-debug-watch
debug--variable-list
debug-early
debug-early-backtrace
debug-help-follow
debug-on-entry
debug-on-variable-change
debug-watch
debugger--backtrace-base
 :

ここから、entryに行くには、-on-e かな。よって、途中を省略して、deb-en で、一意に定まる?

3 possible completions:
debug--implement-debug-on-entry
debug-on-entry
debugger-env-macro

候補は、絞りこんだから、後は希望の文字を追加して、決定してくれ。ここま では、ielm上だけど、実際は、ミニバッファーでの会話入力って文脈に配慮し て、先回り補完が働くんだろうね。そして、その結果がメッセージ・バッファー に表示されるんだろうね。

このミニAIっぽい事を実現してるのは、simple.el

(defun execute-extended-command--describe-binding-msg (function binding shorter)
  (format-message "You can run the command `%s' with %s"
                  function
                  (propertize (cond (shorter (concat "M-x " shorter))
                                    ((stringp binding) binding)
                                    (t (key-description binding)))
                              'face 'help-key-binding)))
(defun execute-extended-command (prefixarg &optional command-name typed)
  "Read a command name, then read the arguments and call the command.
To pass a prefix argument to the command you are
invoking, give a prefix argument to `execute-extended-command'.

This command provides completion when reading the command name.
Which completion candidates are shown can be controlled by
customizing `read-extended-command-predicate'."
                    :
                 (when (eq function real-last-command)
                   ;; Find shorter string.
                   (when find-shorter
                     (while-no-input
                       ;; FIXME: Can be slow.  Cache it maybe?
                       (setq shorter (execute-extended-command--shorter
                                      (symbol-name function) typed))))
                   (when (or binding shorter)
                     (with-temp-message
                         (execute-extended-command--describe-binding-msg
                          function binding shorter)
                       (sit-for (if (numberp suggest-key-bindings)
                                    suggest-key-bindings
                                  2))))))))))))

2秒って、短かくて長い時間、かな?

更に周辺を漁っていると、これが発動するのは、5文字以上の長いコマンド(確 かに退屈だからね)だよとか、

(mapatoms (lambda (s) (when (commandp s) (push s commands))))

こんな風に、登録簿から抽出してますとか。

mapatoms is a built-in function in ‘C source code’.

(mapatoms FUNCTION &optional OBARRAY)

Call FUNCTION on every symbol in OBARRAY.
OBARRAY defaults to the value of ‘obarray’.

とんでもない語句が収監されている。そして、ちょっとアクセスしてみる。

ELISP> (length obarray)
15121 (#o35421, #x3b11)
ELISP> (elt obarray 100)
Info-directory-find-file
ELISP> (elt obarray 12345)
flymake-json
ELISP> (aref obarray 12345)
flymake-json
ELISP> (vectorp obarray)
t
ELISP> (arrayp obarray)
t

ちょいと、コマンドを抽出して、その個数を確認。

ELISP> (defvar commands nil)
commands
ELISP> (mapatoms (lambda (s) (when (commandp s) (push s commands))))
nil
ELISP> (length commands)
3263 (#o6277, #xcbf)

speed

emacs-headを2時間かけてコンパイルしてみた。出来ばえは下記。

GNU Emacs 30.0.50 (build 1, i686-pc-linux-gnu, X toolkit, Xaw scroll bars) of 2023-12-24

何をやりたかったかと言うと、ネイティブ・コンパイル。

(defun mysort (n)
      (let* ((list (mapcar 'random (make-list n most-positive-fixnum)))
              (i (length list)))
         (while (> i 1)
           (let ((b list))
             (while (cdr b)
               (when (< (cadr b) (car b))
                 (setcar b (prog1 (cadr b)
                             (setcdr b (cons (car b) (cddr b))))))
               (setq b (cdr b))))
           (setq i (1- i)))
         list))

(byte-compile 'mysort)

(native-compile 'mysort)

(benchmark-run (mysort 2000))

一度mysortを登録しておいてから、naitive-compilel(byte-compile)する。両 者は、二者択一である。

(17.324312703 8 1.1734181080000003)        ;; original
(3.1288189 8 1.168163565)                  ;; byte-compile
(2.0103002869999997 7 1.0635132880000002)  ;; native-compile

結果は、それぞれなんだろうね。

WezTerm