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
少し実用って事で、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なんだけど、予備ということで試してみた。
オイラーのパソコンは英語キーボードなんで、半角全角とか変換キーが無い。使えるかなと思ったら何とか使えた。
起動すると、英語入力になってる。コントロール+バックスラッシュすると、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
結果は、それぞれなんだろうね。