インストールしたところ、早速不具合が出てしまいました。原因はtermux-packagesのemacsパッケージ(emacs-25.3)のパッチを取り込む際に拡張子*.patch
でないパッチを取り込み忘れたため、つまり自分のせいでした、すいません。本文のパッチは2018/02/21に修正しました。
序
Emacs26から導入される、一部で待望されていた行番号表示(実装者によると結構高速らしいです。ホントに不承不承といった感じが微笑ましいです。アンチニュースの執筆スタンスは100%シャレではないんだぜ?ということでしょう。かくいう自分は、実は待ってました。別に行番号が必要とかじゃなく、なんか見た目がカッコイーから。BasicやCOBOLの世代なんですよ、わたし)を試したくてTermuxでビルドしました。
基礎知識
わたしは特にAndroidとかに詳しくないので、Teruxマニュアルのパッケージ管理の手順が基本になります。つまり、
- ビルドツールのインストール
- ソースの入手と修正
- configureのための環境準備
- configureしてmake
...という流れになります。
なお、マニュアルには特に載ってませんがTermuxパッケージの公式リポジトリには、(多分)リポジトリのアプリをDocker上でビルドするために必要なpatchやスクリプトがあり、これらはTermuxでそのアプリをソースからビルドするとき、とても役にたちます。
前準備
ビルドツールのインストール
マニュアルにしたがい、ビルドツールをインストールします。
[~/src/emacs] pkg install autoconf automake bison bzip2 \
clang cmake coreutils diffutils \
flex gawk git grep gzip libtool \
make patch perl sed silversearcher-ag \
tar termux-exec wget
Hit:1 https://termux.net stable InRelease
Reading package lists... Done
Building dependency tree
Reading state information... Done
All packages are up to date.
Reading package lists... Done
Building dependency tree
Reading state information... Done
.....
依存パッケージのインストール
公式リポジトリにはEmacs-25.3ビルド用のスクリプトがあります。それをダウンロードします。中を覗いてみるとncurses
、gnutls
、libxml2
というパッケージが必要らしいです。バージョンが違うので必要十分ではないでしょうが、とりあえずインストールしておきます。ただ、バイナリの実行だけでなくビルドから行いたいので、-devつきのパッケージ、つまりncurses-dev
,gnutls-dev
, libxml2-dev
をインストールします。
[~/src/emacs]$ pkg install ncurses-dev gnutls-dev libxml2-dev
Hit:1 https://termux.net stable InRelease
Reading package lists... Done
Building dependency tree
Reading state information... Done
.....
他にも、わたしの環境にはインストールしてあって、素のTermuxに入ってないパッケージが必要な場合にはconfigureやmakeでエラーが出るので、その場合は、そのパッケージをpkg install
インストールして、configureからやり直せば大丈夫です。
ビルド
いよいよビルドしていきます。
ソースの取得
ソースのemacs-26.0.91.tar.gzをダウンロードして解凍し、そのフォルダに移動します。
[~/src/emacs]$ tar xvfz emacs-26.0.91.tar.gz 2>&1|head -20 emacs-26.0.91/ emacs-26.0.91/.dir-locals.el emacs-26.0.91/.gitattributes emacs-26.0.91/.gitignore emacs-26.0.91/.gitlab-ci.yml emacs-26.0.91/BUGS emacs-26.0.91/CONTRIBUTE emacs-26.0.91/COPYING emacs-26.0.91/ChangeLog.1 emacs-26.0.91/ChangeLog.2 emacs-26.0.91/ChangeLog.3 emacs-26.0.91/GNUmakefile
.....
[~/src/emacs]$ cd emacs-26.0.91
[~/src/emacs/emacs-26.0.91]
Termuxビルド環境の準備
Termuxでビルドするために、以下を行います。本来ならインストール先の--prefix
には$PREFIX
を指定するのですが、流石にそれは怖いので--prefix=$HOME
で行いました。そのためインストールしたEmacsを実行するためにPATH
に~/bin
も追加してます。
[~/src/emacs/emacs-26.0.91]$ export PREFIX=/data/data/com.termux/files/usr
[~/src/emacs/emacs-26.0.91]$ export LD_PRELOAD=${PREFIX}/lib/libtermux-exec.so
[~/src/emacs/emacs-26.0.91]$ export PATH=$HOME/bin:$PATH
ソース修正
patchによる修正を行います。以下のpatchファイルの内容の99%は公式リポジトリEmacsパッケージの*.patch
から頂きました。
diff -u -r ../emacs-26.0.91.orig/lib-src/emacsclient.c ./lib-src/emacsclient.c
--- ../emacs-26.0.91.orig/lib-src/emacsclient.c 2018-01-14 06:07:12.000000000 +0900
+++ ./lib-src/emacsclient.c 2018-02-20 14:00:42.209932042 +0900
@@ -1287,7 +1287,7 @@
}
else
#endif
- tmpdir = "/tmp";
+ tmpdir = "@TERMUX_PREFIX@/tmp";
}
socket_name_storage =
xmalloc (strlen (tmpdir) + strlen (server_name) + EXTRA_SPACE);
diff -u -r ../emacs-26.0.91.orig/lib-src/pop.c ./lib-src/pop.c
--- ../emacs-26.0.91.orig/lib-src/pop.c 2018-01-14 06:07:12.000000000 +0900
+++ ./lib-src/pop.c 2018-02-20 14:00:42.209932042 +0900
@@ -71,6 +71,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <stdbool.h>
#ifdef KERBEROS
# ifdef HAVE_KRB5_H
@@ -129,6 +130,23 @@
char pop_error[ERROR_MAX];
bool pop_debug = false;
+#ifdef __ANDROID__
+static char* getpass(const char* prompt) {
+ printf("%s\n", prompt);
+ static char chars[128];
+ int len = 0;
+ while (true) {
+ char c = fgetc(stdin);
+ if (c == '\r' || c == '\n' || c == 0) break;
+ chars[len++] = c;
+ if (len == sizeof(chars)-1) break;
+ }
+ chars[len] = 0;
+ return chars;
+}
+#endif
+
+
/*
* Function: pop_open (char *host, char *username, char *password,
* int flags)
diff -u -r ../emacs-26.0.91.orig/lisp/cus-start.el ./lisp/cus-start.el
--- ../emacs-26.0.91.orig/lisp/cus-start.el 2018-01-14 06:07:12.000000000 +0900
+++ ./lisp/cus-start.el 2018-02-20 14:23:37.279931517 +0900
@@ -284,12 +284,12 @@
(setq tmp (replace-regexp-in-string
"\n\\'" "" tmp))
;; Handles "getconf: Unrecognized variable..."
- (file-directory-p tmp)
+ (File-directory-p tmp)
tmp))
"/tmp"))
(t
(or (getenv "TMPDIR") (getenv "TMP") (getenv "TEMP")
- "/tmp"))))
+ "@TERMUX_PREFIX@/tmp"))))
:initialize custom-initialize-delay)
;; fns.c
(use-dialog-box menu boolean "21.1")
diff -u -r ../emacs-26.0.91.orig/lisp/international/ucs-normalize.el ./lisp/international/ucs-normalize.el
--- ../emacs-26.0.91.orig/lisp/international/ucs-normalize.el 2018-01-14 06:07:12.000000000 +0900
+++ ./lisp/international/ucs-normalize.el 2018-02-20 14:00:42.219932042 +0900
@@ -111,6 +111,9 @@
(eval-when-compile (require 'cl-lib))
+;;; add for regexp-opt-charset
+(eval-when-compile (require 'regexp-opt))
+
(declare-function nfd "ucs-normalize" (char))
(eval-when-compile
diff -u -r ../emacs-26.0.91.orig/lisp/loadup.el ./lisp/loadup.el
--- ../emacs-26.0.91.orig/lisp/loadup.el 2018-01-14 06:07:12.000000000 +0900
+++ ./lisp/loadup.el 2018-02-20 14:30:30.789931359 +0900
@@ -393,7 +393,12 @@
(message "Warning: Change in load-path due to site-init will be \
lost after dumping")))
-(setq current-load-list nil)
+;; On an emacs that runs undumped, i.e. Android, this causes bogus
+;; entries to appear in load-history. Commenting this out causes an entry
+;; for loadup.el with a bogus filename to appear instead, but since it
+;; neither provides nor requires anything, it appears to be safe.
+;;(setq current-load-list nil)
+
;; Avoid storing references to build directory in the binary.
(setq custom-current-group-alist nil)
@@ -453,7 +458,10 @@
;; Mainly cosmetic, but helpful for Guix. (Bug#20330)
;; Do this here, rather than earlier, so that the above code
;; can invoke Git commands and the like.
- (setq exec-path nil)
+
+ ;; Termux patch: Keep exec-path when running undumped:
+ ;; (setq exec-path nil)
+
(message "Dumping under the name emacs")
(condition-case ()
(delete-file "emacs")
diff -u -r ../emacs-26.0.91.orig/lisp/net/tramp.el ./lisp/net/tramp.el
--- ../emacs-26.0.91.orig/lisp/net/tramp.el 2018-01-14 06:07:12.000000000 +0900
+++ ./lisp/net/tramp.el 2018-02-20 14:32:40.759931310 +0900
@@ -127,7 +127,7 @@
:require 'tramp)
(defcustom tramp-encoding-shell
- (or (tramp-compat-funcall 'w32-shell-name) "/bin/sh")
+ (or (tramp-compat-funcall 'w32-shell-name) "@TERMUX_PREFIX@/bin/sh")
"Use this program for encoding and decoding commands on the local host.
This shell is used to execute the encoding and decoding command on the
local host, so if you want to use `~' in those commands, you should
diff -u -r ../emacs-26.0.91.orig/lisp/server.el ./lisp/server.el
--- ../emacs-26.0.91.orig/lisp/server.el 2018-01-14 06:07:12.000000000 +0900
+++ ./lisp/server.el 2018-02-20 14:35:42.759931240 +0900
@@ -226,7 +226,7 @@
(function-item :tag "Use pop-to-buffer" pop-to-buffer)
(function :tag "Other function")))
-(defcustom server-temp-file-regexp "^/tmp/Re\\|/draft$"
+(defcustom server-temp-file-regexp "^@TERMUX_PREFIX@/tmp/Re\\|@TERMUX_PREFIX@/draft$"
"Regexp matching names of temporary files.
These are deleted and reused after each edit by the programs that
invoke the Emacs server."
@@ -264,7 +264,7 @@
;; does not read the init file.
(defvar server-socket-dir
(and (featurep 'make-network-process '(:family local))
- (format "%s/emacs%d" (or (getenv "TMPDIR") "/tmp") (user-uid)))
+ (format "%s/emacs%d" (or (getenv "TMPDIR") "@TERMUX_PREFIX@/tmp") (user-uid)))
"The directory in which to place the server socket.
If local sockets are not supported, this is nil.")
diff -u -r ../emacs-26.0.91.orig/lisp/subr.el ./lisp/subr.el
--- ../emacs-26.0.91.orig/lisp/subr.el 2018-01-14 06:07:12.000000000 +0900
+++ ./lisp/subr.el 2018-02-20 14:37:40.379931196 +0900
@@ -3142,7 +3142,7 @@
(declare (advertised-calling-convention (name buffer command) "23.1"))
(start-file-process
name buffer
- (if (file-remote-p default-directory) "/bin/sh" shell-file-name)
+ (if (file-remote-p default-directory) "@TERMUX_PREFIX@/bin/sh" shell-file-name)
(if (file-remote-p default-directory) "-c" shell-command-switch)
(mapconcat 'identity args " ")))
@@ -3186,7 +3186,7 @@
(declare (advertised-calling-convention
(command &optional infile buffer display) "24.5"))
(process-file
- (if (file-remote-p default-directory) "/bin/sh" shell-file-name)
+ (if (file-remote-p default-directory) "@TERMUX_PREFIX@/bin/sh" shell-file-name)
infile buffer display
(if (file-remote-p default-directory) "-c" shell-command-switch)
(mapconcat 'identity (cons command args) " ")))
diff -u -r ../emacs-26.0.91.orig/lisp/term.el ./lisp/term.el
--- ../emacs-26.0.91.orig/lisp/term.el 2018-01-14 06:07:12.000000000 +0900
+++ ./lisp/term.el 2018-02-20 14:40:45.919931125 +0900
@@ -1538,7 +1538,7 @@
;; do the decoding by hand on the parts that are made of chars.
(coding-system-for-read 'binary))
(apply 'start-process name buffer
- "/bin/sh" "-c"
+ "@TERMUX_PREFIX@/bin/sh" "-c"
(format "stty -nl echo rows %d columns %d sane 2>/dev/null;\
if [ $1 = .. ]; then shift; fi; exec \"$@\""
term-height term-width)
diff -u -r ../emacs-26.0.91.orig/src/Makefile.in ./src/Makefile.in
--- ../emacs-26.0.91.orig/src/Makefile.in 2018-01-14 06:07:12.000000000 +0900
+++ ./src/Makefile.in 2018-02-20 14:00:42.219932042 +0900
@@ -532,7 +532,8 @@
lisp.mk $(etc)/DOC $(lisp) \
$(lispsource)/international/charprop.el ${charsets}
ifeq ($(CANNOT_DUMP),yes)
- ln -f temacs$(EXEEXT) $@
+ rm -f $@
+ ln -s temacs$(EXEEXT) $@
else
LC_ALL=C $(RUN_TEMACS) -batch $(BUILD_DETAILS) -l loadup dump
ifneq ($(PAXCTL_dumped),)
@@ -736,7 +737,8 @@
bootstrap-emacs$(EXEEXT): temacs$(EXEEXT)
$(MAKE) -C ../lisp update-subdirs
ifeq ($(CANNOT_DUMP),yes)
- ln -f temacs$(EXEEXT) $@
+ rm -f $@
+ ln -s temacs$(EXEEXT) $@
else
$(RUN_TEMACS) --batch $(BUILD_DETAILS) --load loadup bootstrap
ifneq ($(PAXCTL_dumped),)
diff -u -r ../emacs-26.0.91.orig/src/callproc.c ./src/callproc.c
--- ../emacs-26.0.91.orig/src/callproc.c 2018-01-14 06:07:12.000000000 +0900
+++ ./src/callproc.c 2018-02-20 14:00:42.229932042 +0900
@@ -1612,7 +1612,7 @@
dir_warning ("arch-independent data dir", Vdata_directory);
sh = getenv ("SHELL");
- Vshell_file_name = build_string (sh ? sh : "/bin/sh");
+ Vshell_file_name = build_string (sh ? sh : "@TERMUX_PREFIX@/bin/sh");
Lisp_Object gamedir = Qnil;
if (PATH_GAME)
diff -u -r ../emacs-26.0.91.orig/src/editfns.c ./src/editfns.c
--- ../emacs-26.0.91.orig/src/editfns.c 2018-01-14 06:07:12.000000000 +0900
+++ ./src/editfns.c 2018-02-20 14:00:42.229932042 +0900
@@ -1440,7 +1440,7 @@
if (!pw)
return Qnil;
- p = USER_FULL_NAME;
+ p = "unknown";
/* Chop off everything after the first comma. */
q = strchr (p, ',');
full = make_string (p, q ? q - p : strlen (p));
diff -u -r ../emacs-26.0.91.orig/src/filelock.c ./src/filelock.c
--- ../emacs-26.0.91.orig/src/filelock.c 2018-01-14 06:07:12.000000000 +0900
+++ ./src/filelock.c 2018-02-20 14:00:42.229932042 +0900
@@ -127,14 +127,14 @@
static time_t boot_time;
static bool boot_time_initialized;
-#ifdef BOOT_TIME
+#if defined(BOOT_TIME) && !defined(__ANDROID__)
static void get_boot_time_1 (const char *, bool);
#endif
static time_t
get_boot_time (void)
{
-#if defined (BOOT_TIME)
+#if defined (BOOT_TIME) && !defined(__ANDROID__)
int counter;
#endif
@@ -170,7 +170,7 @@
}
}
-#if defined (BOOT_TIME)
+#if defined (BOOT_TIME) && !defined(__ANDROID__)
#ifndef CANNOT_DUMP
/* The utmp routines maintain static state.
Don't touch that state unless we are initialized,
@@ -232,7 +232,7 @@
#endif
}
-#ifdef BOOT_TIME
+#if defined(BOOT_TIME) && !defined(__ANDROID__)
/* Try to get the boot time from wtmp file FILENAME.
This succeeds if that file contains a reboot record.
diff -u -r ../emacs-26.0.91.orig/src/keyboard.c ./src/keyboard.c
--- ../emacs-26.0.91.orig/src/keyboard.c 2018-01-14 06:07:12.000000000 +0900
+++ ./src/keyboard.c 2018-02-20 14:00:42.239932042 +0900
@@ -8916,7 +8916,6 @@
ptrdiff_t keys_start;
Lisp_Object current_binding = Qnil;
- Lisp_Object first_event = Qnil;
/* Index of the first key that has no binding.
It is useless to try fkey.start larger than that. */
@@ -9026,6 +9025,7 @@
starting_buffer = current_buffer;
first_unbound = bufsize + 1;
+ Lisp_Object first_event = mock_input > 0 ? keybuf[0] : Qnil;
/* Build our list of keymaps.
If we recognize a function key and replace its escape sequence in
このpatchファイルをどこかに作成して、ソースフォルダからpatchを適用します。
[~/src/emacs/emacs-26.0.91]$ perl -pe "s{\@TERMUX_PREFIX\@}{$PREFIX}g" ../emacs-26.0.91.patch | patch -p1
patching file lib-src/emacsclient.c
patching file lib-src/pop.c
patching file lisp/cus-start.el
patching file lisp/international/ucs-normalize.el
patching file lisp/loadup.el patching file lisp/net/tramp.el
patching file lisp/server.el
patching file lisp/subr.el
patching file lisp/term.el
patching file src/Makefile.in
patching file src/callproc.c
patching file src/editfns.c
patching file src/filelock.c
patching file src/keyboard.c
build.shからの流用
次はconfigureのオプションを頂きます。先程のbuild.sh
をsourceで取り込みます。
[~/src/emacs/emacs-26.0.91]$ source /path/to/file/build.sh
[~/src/emacs/emacs-26.0.91]$ echo $TERMUX_PKG_EXTRA_CONFIGURE_ARGS
--without-x --with-xpm=no --with-jpeg=no --with-png=no --with-gif=no --with-tiff=no --without-gconf --without-gsettings --with-gnutls --with-xml2 emacs_cv_sanitize_address=yes emacs_cv_prog_cc_nopie=no ac_cv_lib_elf_elf_begin=no gl_cv_func_dup2_works=no
このスクリプトから頂きたいのは、まずTERMUX_PKG_EXTRA_CONFIGURE_ARGS
です。ただ、このスクリプトはHOSTでビルドするためのものなのでclangを使わずgccを使う設定になってます。しかしTermux上ではclangを使用するので、そのためのconfigure引数を追加します。
[~/src/emacs/emacs-26.0.91]$ TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" emacs_cv_prog_cc_no_pie=no"
[~/src/emacs/emacs-26.0.91]$ export TERMUX_PKG_EXTRA_CONFIGURE_ARGS
[~/src/emacs/emacs-26.0.91]$ echo $TERMUX_PKG_EXTRA_CONFIGURE_ARGS
--without-x --with-xpm=no --with-jpeg=no --with-png=no --with-gif=no --with-tiff=no --without-gconf --without-gsettings --with-gnutls --with-xml2 emacs_cv_sanitize_address=yes emacs_cv_prog_cc_nopie=no ac_cv_lib_elf_elf_begin=no gl_cv_func_dup2_works=no emacs_cv_prog_cc_no_pie=no
あと必要なのはexport CANNOT_DUMP=yes
です。これは端末に貼り付け適用します。
[~/src/emacs/emacs-26.0.91]$ export CANNOT_DUMP=yes
[~/src/emacs/emacs-26.0.91]$ echo $CANNOT_DUMP
yes
さらにマニュアルにしたがいTermux版Autoconfのファイルをソースツリーにコピーします。
[~/src/emacs/emacs-26.0.91]$ find . -name 'config.sub' -exec chmod u+w '{}' \; -exec cp -f "${PREFIX}/share/libtool/build-aux/config.sub" '{}' \;
[~/src/emacs/emacs-26.0.91]$ find . -name 'config.guess' -exec chmod u+w '{}' \; -exec cp -f "${PREFIX}/share/libtool/build-aux/config.guess" '{}' \;
configure
開発版ソースにはconfigureスクリプト、その他必要なファイルがありません。以下のコマンドで作成します。
[~/src/emacs/emacs-26.0.91]$ autoreconf -ivf
autoreconf: Entering directory `.'
autoreconf: configure.ac: not using Gettext
autoreconf: running: aclocal --force
autoreconf: configure.ac: tracing
autoreconf: configure.ac: not using Libtool
autoreconf: running: /data/data/com.termux/files/usr/bin/autoconf --force
autoreconf: running: /data/data/com.termux/files/usr/bin/autoheader --force
autoreconf: configure.ac: not using Automake
autoreconf: Leaving directory `.'
終わったらconfigureします。
ちなみにEmacs26の売りの1つはEmacs版mailutilsではなくGNU mailutilsがデフォルトになったことなんですが、GNU mailutilsはTermuxでパッケージ提供されておらず、ビルドも大変でした。しかもビルドは通ったものの恐らく正常に動かない(少なくとも/etc/passwd
絡みの部分は直す必要がある)ので、無視します。わたしは新機能の行番号が見たいんです。
[~/src/emacs/emacs-26.0.91]$ ./configure --prefix="${HOME}" CC=clang CXX=clang++ ${TERMUX_PKG_EXTRA_CONFIGURE_ARGS} --without-pop --without-lcms2
checking for xcrun... no
checking for GNU Make... make
checking build system type... aarch64-unknown-linux-gnu
checking host system type... aarch64-unknown-linux-gnu
Aborted
checking whether the C compiler works... yes
.....
なお、デバッグ機能を有効にするためCFLAGS
にCFLAGS='-O0 -g3' ./configure --enable-checking='yes,glyphs' --enable-check-lisp-object-type
等を指定したい人は、まずCFLAGSを指定しないでmakeして、elispファイルのコンパイルを終わらせてから、改めてCFLAGSを指定してconfigureとmakeをやり直したほうが俄然早いです。デバッグ用のemacsではelispコンパイルステップにとんでもなく時間がかかりました。
make & install
makeします。
[~/src/emacs/emacs-26.0.91]$ make -j4
make -C lib all
make info-real info-dir
make[1]: Entering directory '/data/data/com.termux/files/home/src/emacs/emacs-26.0.91/lib'
GEN alloca.h
make[1]: Entering directory '/data/data/com.termux/files/home/src/emacs/emacs-26.0.91'
make -C doc/lispref info
GEN dirent.h
make[2]: Entering directory '/data/data/com.termux/files/home/src/emacs/emacs-26.0.91/doc/lispref'
.....
makeが通ったらインストールします。
[~/src/emacs/emacs-26.0.91]$ make install
make -C lib all
make[1]: Entering directory '/data/data/com.termux/files/home/src/emacs/emacs-26.0.91/lib'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/data/data/com.termux/files/home/src/emacs/emacs-26.0.91/lib'
make -C lib-src all
.....
あと、TermuxでEmacsを便利に使用するためにパッケージ版のEmacsにはsite-lisp.elというファイルが含まれてるので、同じものを$HOME/share/emacs/25.3/lisp/emacs-lisp/site-init.el
に作成しておけば画面のタップやピンチイン/アウトでコンソールマウスと同等なことが行えるので、作成しておくと便利でしょう。
; Enable terminal mouse events:
(xterm-mouse-mode 1)
(global-set-key [mouse-4] 'scroll-down-line)
(global-set-key [mouse-5] 'scroll-up-line)
インストールしたので実行してみます。以下は前からあるglobal-hl-line-mode
と、新機能のdisplay-line-numbers-mode
を実行した画面です。
結び
わたしはTermux公式リポジトリにない俺ビルドのアプリを何個か使ってますが、やはり不具合調査用にデバッグオプションでビルドしておかないと、おっかないです。今回も常用アプリなので速度も大切なんですけど、デバッグオプション指定でビルドしました。これでしばらく使ってみます。おわり