13
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

デバイス上でパッケージをコンパイル(Termux on Android)

Last updated at Posted at 2018-01-22

わたしはパッケージのビルドとかAndroid開発にはあまり詳しくなく、aptyumなどでインストールできるパッケージは素直にパッケージマネージャーでインストールする派です。しかし今回、Termux上でパッケージが見つからなかったアプリをインストールしたくなり、しょうがないのでやってみました(実はこれは辞書サーバーによる辞書環境の構築の後日譚であります。そのため、dictdという世間ではあまり馴染みのないソフトがお題になってます。)

GNU autotoolsパッケージのコンパイル手順 on Termux

Termuxのマニュアルを参照しました。以下にGNU autotoolsアプリ(./configure && make &&make installでインストールできるやつ)をビルドする手順を要約します。

環境の準備

必要なアプリのインストール

ビルドに必要なツールをインストールします。インストールする前に、pkg updatepkg upgradeをしといたほうが良いです。

$ 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
.....

環境変数の設定

以下の2行を.bashrcに追加します。

.bashrc
export PREFIX=/data/data/com.termux/files/usr
export LD_PRELOAD=${PREFIX}/lib/libtermux-exec.so

依存関係の把握

マニュアルにはArchLinux package databaseで依存するパッケージを特定して
それからインストールするようにと記述されてます。身近にlinux機のある方はapt-get build-depの類いのコマンドで調べたほうが簡単でしょう。依存するパッケージがTermuxのパッケージリポジトリで提供されていればそれをpkg installでインストールするだけですが、提供されていなければそれもソースからビルドする必要があります。

ソースコードなどの修正

ソース内で
/bin
/etc
/sbin
/tmp
/usr
/var
のような、ハードコーディングされがちな標準パスの前に、先程の$PREFIXの値を付け加えます。「findgrepかけて...」とか、「emacsDiredから...」とか、好きな方法で修正対象のファイルを見つけて修正します。マニュアルではag(先の必要アプリインストールの中のsilversearcher-agのこと)が推奨されてます。

ビルド

config.subconfig.guessの置き換え

この2つを置き換えないとターゲットシステムを判断できないそうです。以下のコマンドで置き換えます。

find . -name 'config.sub' -exec chmod u+w '{}' \; -exec cp -f "${PREFIX}/share/libtool/build-aux/config.sub" '{}' \;
find . -name 'config.guess' -exec chmod u+w '{}' \; -exec cp -f "${PREFIX}/share/libtool/build-aux/config.guess" '{}' \;

configureに必要なオプションを指定

Termuxでビルド、インストールできるように以下のオプションを指定します。

$ ./configure --prefix="${PREFIX}" CC=clang CXX=clang++

makeしてインストール

特に問題がなければ普通にmake -j4 && make install

ソースtarball入手

dictdlibmaaに依存しており、どちらも同じ開発元が提供しています。こちらからソースを入手しました。最新版はdictd-1.12.1.tar.gzlibmaa-1.3.2.tar.gzです。

libmaaのビルドとインストール

$ tar xvfz libmaa-1.3.2.tar.gz #解凍
libmaa-1.3.2
libmaa-1.3.2/doc
libmaa-1.3.2/doc/Makefile.in
libmaa-1.3.2/doc/extract.pl
libmaa-1.3.2/doc/lgrindefs
.....

$ cd libmaa-1.3.2 #ソースディレクトリへcd

ソース内でハードコードされているディレクトリがないか調べる。libmaaは修正必要なしでした。

$ ag --cc /bin 
$ ag --cc /etc 
$ ag --cc /sbin
$ ag --cc /tmp
$ ag --cc /usr
$ ag --cc /var

configureします。マニュアルに記載のないLIBS=-llogは、これを指定しなくてもlibmaaはビルドとインストールはできたんですけど、dictdconfigure時に「libmaaが未定義の__android_log_printなどを参照している」→「libmaaがインストールされてない(誤解)」とかでコケるので、調べた結果これを追加しました。ちなみにこのliblog.soはTermuxではなくAndroidシステムの/system/lib/liblog.soらしいです。

$ ./configure --prefix="${PREFIX}" CC=clang CXX=clang++ LIBS=-llog                      Configuring for libmaa 1.3.2
.
checking build system type... aarch64-unknown-linux-gnu
checking host system type... aarch64-unknown-linux-gnu
checking for gawk... gawk
checking for gcc... clang
checking whether the C compiler works... yes
.....

終わったらmakeしてインストール

$ make -j4 && make install
libtool --tag=CC --mode=compile clang -o obstack.o -c -DHAVE_CONFIG_H  -g -O2 -DMAA_MAJOR=1 -DMAA_MINOR=3 -DMAA_TEENY=2 -I. -I. obstack.c
libtool --tag=CC --mode=compile clang -o xmalloc.o -c -DHAVE_CONFIG_H  -g -O2 -DMAA_MAJOR=1 -DMAA_MINOR=3 -DMAA_TEENY=2 -I. -I. xmalloc.c
libtool --tag=CC --mode=compile clang -o hash.o -c -DHAVE_CONFIG_H  -g -O2 -DMAA_MAJOR=1 -DMAA_MINOR=3 -DMAA_TEENY=2 -I. -I. hash.c
libtool --tag=CC --mode=compile clang -o set.o -c -DHAVE_CONFIG_H  -g -O2 -DMAA_MAJOR=1 -DMAA_MINOR=3 -DMAA_TEENY=2 -I. -I. set.c
libtool: compile:  clang -c -DHAVE_CONFIG_H -g -O2 -DMAA_MAJOR=1 -DMAA_MINOR=3 -DMAA_TEENY=2 -I. -I. hash.c  -fPIC -DPIC -o .libs/hash.o
.....

dictdのビルドとインストール

$ tar xvfz dictd-1.12.1.tar.gz #解凍
dictd-1.12.1
dictd-1.12.1/doc
dictd-1.12.1/doc/Makefile.in
dictd-1.12.1/doc/dicf.ms
dictd-1.12.1/doc/rfc.ms
.....
$ cd dictd-1.12.1 #ソースディレクトリへcd

ソース内でハードコードされているディレクトリがないか調べる。parse.cdictd.cが要修正です。

$ ag --cc /bin
$ ag --cc /etc
$ ag --cc /sbin
$ ag --cc /tmp
$ ag --cc /usr
parse.c
94:                                 "/usr/lib/cpp",
95:                                 "/usr/ccs/lib/cpp", /*Solaris */
96:                                 "/usr/lang/cpp",
$ ag --cc /var
dictd.c
78:const char *pidFile     = "/var/run/dictd.pid";

parse.cを修正した結果です。

parse.c.patch
--- parse.c~	2008-12-08 01:50:05.000000000 +0900
+++ parse.c	2018-01-23 05:51:09.760006028 +0900
@@ -90,10 +90,10 @@ void prs_file( const char *filename )
    char              *buffer;
    const char        **pt;
    static const char *cpp = NULL;
-   static const char *cpps[] = { "/lib/cpp",
-                                 "/usr/lib/cpp",
-                                 "/usr/ccs/lib/cpp",	/* Solaris */
-                                 "/usr/lang/cpp",
+   static const char *cpps[] = { "/data/data/com.termux/files/usr/lib/cpp",
+                                 "/data/data/com.termux/files/usr/usr/lib/cpp",
+                                 "/data/data/com.termux/files/usr/usr/ccs/lib/cpp",	/* Solaris */
+                                 "/data/data/com.termux/files/usr/usr/lang/cpp",
                                  0 };
    static const char *extra_options = "";
    FILE              *tmp;

そしてこちらがdictd.cの修正後

dictd.c.patch
--- dictd.c~	2011-01-10 01:53:27.000000000 +0900
+++ dictd.c	2018-01-23 05:51:01.530006030 +0900
@@ -75,7 +75,7 @@ int                logOptions   = 0;
 const char         *logFile     = NULL;
 int logFile_set; /* 1 if set by command line option */
 
-const char *pidFile     = "/var/run/dictd.pid";
+const char *pidFile     = "/data/data/com.termux/files/usr/var/run/dictd.pid";
 int pidFile_set; /* 1 if set by command line option */
 
 const char         *daemon_service     = DICT_DEFAULT_SERVICE;

ここまでは、マニュアルに記載されていたTermuxでのビルド方法を踏襲しました。しかし、これだけではやはり上手くビルド、またはビルドできても正常に動作させることができなかったので、その対応を書きます。

Makefile.inの修正

libtoolというのはmode=compileのときはソースディレクトリに.libsというディレクトリを作成してそこにオブジェクトファイル*.oをコンパイルし、ソースディレクトリのルートには、(多分)それらのオブジェクトファイルを参照する*.loというファイルを作成するようです。そしてmode=linkのときはルートディレクトリの*.loを参照する感じなのでしょうか。自分も他のMakefileとかルールなどと比較して推測してるんで、あやふやで申し訳ないんですが。以下が修正後です。

Makefile.in.patch
diff -u -r ../dictd-1.12.1.orig/Makefile.in ./Makefile.in
--- ../dictd-1.12.1.orig/Makefile.in	2011-03-07 02:52:54.000000000 +0900
+++ ./Makefile.in	2018-02-08 18:50:15.790039388 +0900
@@ -123,7 +123,7 @@
 
 %: %.o
 	$(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ -static \
-		$^ $(OBJS) $(LDFLAGS) -lz ${LIBS}
+		$(^:.o=.lo) $(OBJS) $(LDFLAGS) -lz ${LIBS}
 
 include $(srcdir)/deps

しかし同じコマンドでmode=compileのときは勝手に.libsなるディレクトリを作成して、mode=linkのときはルートの*.loを参照するのはいいとしても、ユーザーが毎回いちいち:.o=.loとか記述しなきゃいけないのは何か釈然としません。こーゆーものなのでしょうか?(だとしたらdictdMakefile.inのバグということになりますが、他のプラットフォームで検証してないので、よく解りません)。

net.cの修正

ここまでの対応で無事にビルド、インストールできたのですが、実行するとCan't get "tcp" protocol entryというエラーで起動してくれません。ググった結果、Androidでは/etc/protocolsの内容をハードコーディングしなくてはならないようです。以下はその対応です。

net.c.patch
--- net.c~	2010-08-22 02:55:40.000000000 +0900
+++ net.c	2018-01-23 07:20:49.070005208 +0900
@@ -65,7 +65,9 @@ int net_connect_tcp( const char *host, c
 {
    struct hostent     *hostEntry;
    struct servent     *serviceEntry;
+   /*
    struct protoent    *protocolEntry;
+   */
    struct sockaddr_in ssin;
    int                s;
    int                hosts = 0;
@@ -79,8 +81,10 @@ int net_connect_tcp( const char *host, c
    } else if (!(ssin.sin_port = htons(atoi(service))))
       return NET_NOSERVICE;
 
+   /*
    if (!(protocolEntry = getprotobyname("tcp")))
       return NET_NOPROTOCOL;
+   */
    
    if ((hostEntry = gethostbyname(host))) {
       ++hosts;
@@ -92,7 +96,7 @@ int net_connect_tcp( const char *host, c
 	 memcpy( &ssin.sin_addr.s_addr, *current, hostEntry->h_length );
 	 PRINTF(DBG_VERBOSE,
 		("Trying %s (%s)\n",host,inet_ntoa(ssin.sin_addr)));
-	 if ((s = socket(PF_INET, SOCK_STREAM, protocolEntry->p_proto)) < 0)
+	 if ((s = socket(PF_INET, SOCK_STREAM, 6)) < 0)
 	    err_fatal_errno( __func__, "Can't open socket on port %d\n",
 			     ntohs(ssin.sin_port) );      
 	 if (connect(s, (struct sockaddr *)&ssin, sizeof(ssin)) >= 0)
@@ -100,7 +104,7 @@ int net_connect_tcp( const char *host, c
 	 close(s);
       }
    } else {
-      if ((s = socket(PF_INET, SOCK_STREAM, protocolEntry->p_proto)) < 0)
+      if ((s = socket(PF_INET, SOCK_STREAM, 6)) < 0)
 	 err_fatal_errno( __func__, "Can't open socket on port %d\n",
 			  ntohs(ssin.sin_port) );
       if (connect(s, (struct sockaddr *)&ssin, sizeof(ssin)) >= 0)
@@ -117,7 +121,10 @@ int net_open_tcp (
    int queueLength)
 {
    struct servent     *serviceEntry;
+   /*
    struct protoent    *protocolEntry;
+   */
+   
    struct sockaddr_in ssin;
    int                s;
    const int          one = 1;
@@ -131,10 +138,12 @@ int net_open_tcp (
    } else if (!(ssin.sin_port = htons(atoi(service))))
       err_fatal( __func__, "Can't get \"%s\" service entry\n", service );
 
+   /*
    if (!(protocolEntry = getprotobyname("tcp")))
       err_fatal( __func__, "Can't get \"tcp\" protocol entry\n" );
+   */
    
-   if ((s = socket(PF_INET, SOCK_STREAM, protocolEntry->p_proto)) < 0)
+   if ((s = socket(PF_INET, SOCK_STREAM, 6)) < 0)
       err_fatal_errno( __func__, "Can't open socket on port %d\n",
 		       ntohs(ssin.sin_port) );

これでソースへの対応は終了です。マニュアルどおりにインストールします。

$ find . -name 'config.sub' -exec chmod u+w '{}' \; -exec cp -f "${PREFIX}/share/libtoo\
l/build-aux/config.sub" '{}' \;
$ find . -name 'config.guess' -exec chmod u+w '{}' \; -exec cp -f "${PREFIX}/share/libt\
ool/build-aux/config.guess" '{}' \;
$ ./configure --prefix="${PREFIX}" CC=clang CXX=clang++
Configuring for dict
.
checking build system type... aarch64-unknown-linux-gnu
checking host system type... aarch64-unknown-linux-gnu
checking for gcc... clang
checking whether the C compiler works... yes
.....

$ make -j4
libtool --tag=CC --mode=compile clang -c -DHAVE_CONFIG_H -g -O2 -DUSE_PLUGIN -DDICT_PLUGIN_PATH=\"/data/data/com.termux/files/usr/libexec/\" -DDICT_DICTIONARY_PATH=\"/data/data/com.termux/files/usr/share/\" -DDICT_VERSION=\"1.12.1\" -DDICT_CONFIG_PATH=\"/data/data/com.termux/files/usr/etc/\" -I. -I. net.c -o net.o
flex  -oclientscan.c clientscan.l
bison -y -dv clientparse.y; \
    cmp -s y.tab.h clientparse.h || mv y.tab.h clientparse.h; \
    cmp -s y.tab.c clientparse.c || mv y.tab.c clientparse.c; \
    rm -f y.tab.h y.tab.c
bison -y -dv clientparse.y; \
    cmp -s y.tab.h clientparse.h || mv y.tab.h clientparse.h; \
    cmp -s y.tab.c clientparse.c || mv y.tab.c clientparse.c; \
    rm -f y.tab.h y.tab.c
libtool --tag=CC --mode=compile clang -c -DHAVE_CONFIG_H -g -O2 -DUSE_PLUGIN -DDICT_PLUGIN_PATH=\"/data/data/com.termux/files/usr/libexec/\" -DDICT_DICTIONARY_PATH=\"/data/data/com.termux/files/usr/share/\" -DDICT_VERSION=\"1.12.1\" -DDICT_CONFIG_PATH=\"/data/data/com.termux/files/usr/etc/\" -I. -I. md5.c -o md5.o
.....

$ make install
libtool --tag=CC --mode=compile clang -c -DHAVE_CONFIG_H -g -O2 -DUSE_PLUGIN -DDICT_PLUGIN_PATH=\"/data/data/com.termux/files/usr/libexec/\" -DDICT_DICTIONARY_PATH=\"/data/data/com.termux/files/usr/share/\" -DDICT_VERSION=\"1.12.1\" -DDICT_CONFIG_PATH=\"/data/data/com.termux/files/usr/etc/\" -I. -I. net.c -o net.o
libtool: compile:  clang -c -DHAVE_CONFIG_H -g -O2 -DUSE_PLUGIN -DDICT_PLUGIN_PATH=\"/data/data/com.termux/files/usr/libexec/\" -DDICT_DICTIONARY_PATH=\"/data/data/com.termux/files/usr/share/\" -DDICT_VERSION=\"1.12.1\" -DDICT_CONFIG_PATH=\"/data/data/com.termux/files/usr/etc/\" -I. -I. net.c  -fPIC -DPIC -o .libs/net.o
libtool --tag=CC --mode=compile clang -c -DHAVE_CONFIG_H -g -O2 -DUSE_PLUGIN -DDICT_PLUGIN_PATH=\"/data/data/com.termux/files/usr/libexec/\" -DDICT_DICTIONARY_PATH=\"/data/data/com.termux/files/usr/share/\" -DDICT_VERSION=\"1.12.1\" -DDICT_CONFIG_PATH=\"/data/data/com.termux/files/usr/etc/\" -I. -I. clientparse.c -o clientparse.o
libtool: compile:  clang -c -DHAVE_CONFIG_H -g -O2 -DUSE_PLUGIN -DDICT_PLUGIN_PATH=\"/data/data/com.termux/files/usr/libexec/\" -DDICT_DICTIONARY_PATH=\"/data/data/com.termux/files/usr/share/\" -DDICT_VERSION=\"1.12.1\" -DDICT_CONFIG_PATH=\"/data/data/com.termux/files/usr/etc/\" -I. -I. clientparse.c  -fPIC -DPIC -o .libs/clientparse.o
.....

辞書や設定ファイルなどの準備

わたしの目的はGNURootからTermuxへの運用移行なので、そこまで書いておきます。一応ここまででTermuxでソースからビルドする、というのは終わりなのでdictdとか興味ない人は読む必要ないです。

辞書ファイルの準備

GNURootで運用してた辞書ファイルを流用します。辞書変換ツールdictfmtdictdと一緒にインストールされているので、多分テキスト形式の辞書ファイルからdictd形式への変換はできるでしょう。後日余力があったら検証します。

$ ls -l $PREFIX/share/dict/
total 87924
-rwxr--r-- 1 u0_a132 u0_a132 62649479 Jan 22 23:54 eijiro.dict
-rwxr--r-- 1 u0_a132 u0_a132 27278900 Jan 22 23:54 eijiro.index

pidファイル用のディレクトリ作成

dictd.cの修正で/var/run/dictd.pid/data/data/com.termux/files/usr/var/run/dictd.pidに修正しましたが、$PREFIX/varrunというディレクトリがないので作成します。

$ mkdir $PREFIX/var/run

設定ファイルの準備

設定ファイルを準備します。コマンドにはフルパスを指定するのでどこにどんな名前で作ってもいいんですが、GNURootと合わせるため、$PREFIX/etcdictdというディレクトリを作って、設定ファイル名もdictd.confにしました。

$PREFIX/etc/dictd/dictd.conf
global {
listen_to 127.0.0.1
# bind to local interfacea only
}

# Access section here:

access {
allow localhost
allow 127.0.0.1
# this allows access only from local host
allow inetd
# this allows access from inetd server
}

# Database section here:

#include /var/lib/dictd/db.list

# User section here:
database eijiro {
data  "/data/data/com.termux/files/usr/share/dict/eijiro.dict"
index "/data/data/com.termux/files/usr/share/dict/eijiro.index" }

起動

起動します。うまく起動しないときログが役に立ったので情報を多く出力するよう指定してます。あとAndroidのsyslogがどこに吐かれるか解らなかったので、出力ファイル名も指定してます。

$ $PREFIX/sbin/dictd -c $PREFIX/etc/dictd/dictd.conf -v -d verbose -L $PREFIX/var/log/dictd.log
$ cat $PREFIX/var/log/dictd.log
:I: 9079 starting dictd 1.12.1/rf on Linux 3.10.94 Tue Jan 23 01:17:18 2018
:I: eijiro            1016642     27278900     62649479     62649479

$ dict -h localhost 'heavy snow'
1 definition found

From eiwa [eijiro]:

  heavy snow
  豪雪、ひどい雪、大雪、まとまった雪

結び

マニュアルを見たときは「こりゃ簡単!」と思ったんですが、結局朝までかかりました。GNU autotools難しいですね。おわり

13
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?