2024-04-07追記
Github Actions上でのビルドに挑戦してきて、やっと最近どうにか満足できるapkをビルドできるようになりました。詳細についてはレポジトリのREADME.md
に記載してあります。
EmacsのAndroidポートのビルド
1. 手順: ビルド用レポジトリを用意手順をさっくり説明
1.1 レポジトリの準備
1.2 forkする
1.3 ローカルにclone
1.4 作業用ブランチのcut
1.5 リモートにpush
1.6 デフォルトブランチの設定
2. ワークフローファイル
2.1 ワークフローファイルのadd
2.2 リモートにpush
3. ビルド: ビルドする手順(詳細略)
3.1 ワークフローをrun
4. ワークフローで行っていることの説明
4.1 ワークフローの定義
4.2 サードパーティライブラリの取得とかpatch適用
4.2.1 tiff
4.2.2 webp
4.2.3 icu
4.2.4 libxml2
4.2.5 gnutls関連
4.2.6 treesitter
4.2.7 harfbuzz
4.2.8 sqlite3
4.2.9 giflib
4.2.10 json
4.2.11 libjpeg
4.2.12 ImageMagick
4.2.13 selinux関連
4.3 ビルドする
4.3.1 レポジトリの作成とモジュールのダウンロード
4.3.2 環境のセットアップとautogen.shの実行
4.3.3 configureの実行
4.3.4 makeとか
0. 前書き
2022年の大晦日にEmacsメンテナのPo LuさんによりAndroidネイティブアプリとしてEmacsを配布するためのブランチfeature/androidが立ち上がりました(過去記事)。その後、emacs-develでの幾ばくかの議論の末に、feature/androidブランチはめでたくmasterブランチへとマージされました。これはどういうことかというと、ユーザー視点に限って言えば今まではfeature/androidブランチを取得してビルドする必要があったのが、今後はmasterブランチ(いずれemacs-30、emacs-31、…となる)を取得してconfigureのオプションで指定すればAndroidポートをビルドできるようになったことを意味しています。さて、このAndroidポートですが主要開発者のPo Luさんによって当初からドキュメントが非常に充実しています。この記事ではそれらのドキュメントにしたがって、2023年10月時点におけるすべての機能を取り込んだEmacs for Androidのビルド手順を説明します。
-
ビルド環境
Emacsに含まれているAndroidポート用ドキュメントの核となるjava/INSTALL (適当訳、ちょっと訳が古く現時点ではAPI 34が必要です)でも触れられている通り、Android用のソフトウェアはインストールおよび実行されるデバイスではなく別のOS(LinuxとかMacとかWindowsとか)を実行する別のデバイスでビルドされるという特徴があるようです。わたしが所有するAndroid以外の環境としてLinuxはあるものの、これは色々な事をやっている汚れきった環境(その癖仮想化はHW的に不可)なので、例によってGithub Actionsを用いました。 -
ターゲット
このAndroidポートですが可能な限り古いAndroidバージョンにも対応したいというPo Luさんの意向により、Android 2.2以降であれば対応しているようです(SourceForgeで配布されてます)。当初はGithub Actionsのmatrixを用いて色々なバージョンを作ろうかとも思ったんですが多重度にも制限(256だったか?)があること、自分が所有する実機のバージョンでなければ検証できない、ということでターゲットAPIレベルは22と33に絞りました(33とか持ってませんが最新版くらいはビルドしておこうかなー、と)。 -
サードパーティライブラリ
java/INSTALLにはjson(Emacs 28より)、treesitterやsqlite(Emacs 29より)といった外部ライブラリをリンクしたバージョンのビルド手順が記載されているので、これらはすべて取り込むことにします(正直自分はselinuxとか「見かけたらすぐに無効化しなさい!」と教えられてきたのでいらないんですけどね)。 -
共有ユーザーID
Emacsで著名なmagitといったパッケージを使うには背後にLinux環境が必要になりますが、root化端末でない限りAndroid自体のLinux基盤は使えません。そのためこのAndroidポートではAndroidにおけるLinuxエミュレーションアプリとして実績があるtermuxとの併用についても考慮されています(SourceForgeのREADMEやEmacs 30になる予定のマニュアルを参照)。この記事で説明する手順でもTermuxと共存できるように共有ユーザーIDはcom.termux
としましたが、署名キーが異なるためF-DroidやGoogle PlayからインストールしたTermuxとは共存できません。前述のSourceForge版Termuxをインストールする必要があります(もちろん自分でビルドしてjava/emacs.keystoreで署名したバージョンでも可)。 -
Githubのレポジトリについて
以降で説明していくわたしのレポジトリにはビルドした結果がartifactsとしてアップロードしてあるのでそれをダウンロード、インストールすればよいのですが、実際にわたしが行っているのは
- emacs-mirrorをfork
- masterブランチから自分用のブランチ(my/master)をcutしてGithubのデフォルトブランチに設定(わたしはmasterとconflictするような変更をforkで行った際(たとえばconfigure.acにprintf デバッグ仕込むとか)のconflict解決をGithubのweb UIで行うのは余りにもキツいので別ブランチ切ってます)
-
.github/workflows
にビルド用のYAMLファイルをaddしてGithubのweb UIよりrun
だけなので、上記1.と2.をご自身のgithubアカウントで行い3.の.github/workflows
を持っていけば同じことができます(Githubのweb UIでemacs-mirrorのmasterと自分のforkのmasterをsync、ローカルにmasterをpullしてmy/masterにmerge、pushしてワークフローをrunすれば新鮮なEmacsが随時ビルドできます)。そもそもわたしのレポジトリのActionsはくだらないミスや適当なrunで履歴が汚なすぎるのでおすすめできませぬ。
- Github
上述してきた通り、Githubのアカウントお持ちであることを前提とします。ただ、Github Actionsのワークフローファイルで行っているのは、ご覧になれば判ると思いますがLinuxのコマンドプロンプトで行う処理ばかりなので流用は容易いのではないか、と願います(他の環境はよく判りませぬ故)。
1. 手順
1.1 レポジトリの準備
Emacsの本家レポジトリはsavannahのemacs.gitですが、Github外部から直接fork/mirrorする手段は用意されていない(ローカルにcloneしてgithubにpushとかcron組む必要がある)ので、非公式ミラーであるemacs-mirror/emacsをfork元にします。
1.2 forkする
Githubにログインしてemacs-mirror/emacsをforkします(わたしはmy-emacsという名前でforkしましたが、名前は何でも良いと思います)。
1.3 ローカルにclone
gitコマンドでローカルにcloneしてください。
1.4 作業用ブランチのcut
デフォルトブランチはmaster
になってる筈なので、そのままgit checkout -b my/master
(わたしの場合はmy/master
ですが、以下これを例とします)のように自分用master追随ブランチをcutしましょう。
1.5 リモートにpush
ブランチはmy/master
になっていると思いますので、そのブランチをリモートにpushします。git push
するとgit push --set-upstream origin my/master
しろと怒られるのでしたがいましょう。Githubにmy/master
ブランチが登録される筈です。
1.6 デフォルトブランチの設定
Githubのワークフローはデフォルトブランチのワークフローを対象とするので、必ず設定してください。
たとえばデフォルトのmaster
ブランチがデフォルトのままmy/master
ブランチにワークフローを追加しても認識してくれない→実行できません。
Githubのweb UIからSettings
でDefault Branch
をmy/emacs
に変更します。
2. ワークフローファイル
ローカルのcloneにAndroid版のEmacsをビルドするためのワークフローファイルを追加、commit、pushします。
2.1 ワークフローファイルのadd
cloneしたレポジトリに.github/workflows
というディレクトリーを作成して全部のせビルドワークフローをコピー、gitでaddしてcommit(行末スペースを拒絶するGithubと相性が悪いpatchコードが含まれているので--no-verify
オプションが必要かもしれません)。
2.2 リモートにpush
普通にpushしてください。
3. ビルド
ちょっと記憶が曖昧ですが、もしかしたらGithubのSettings
でActionsを有効にする必要があるかもしれません。
3.1 ワークフローをrun
GithubのActions
にrun configure and make Emacs
というワークフローがある筈なので、それを選択してRun workflow
(ブランチはデフォルトのmy/master
でおk)を実行します。正常に終了すればartifactsとしてEmacs.apk.zipができる筈です(120MBくらい)
4. ワークフローで行っていることの説明
基本的にはjava/INSTALL
に記載されたpatchをあてているだけですが、Po Luさんの環境も開発中ということもあり汚れている(素のLinuxではインストールされていないライブラリとかがインストール済みだったり)と思われ、若干の手直しが必要でした。以下順を追って説明します。
4.1 ワークフローの定義
冒頭では名前と起動方法を定義します。重要なのはworkflow_dispatch
で、これはこのワークフローがpush等レポジトリに発生するイベントによって自動的に走行するのではなく、GithubのActionsでこのワークフローを選択してRun workflow
を選択することにより手動で実行する必要があることを意味しています。
name: run configure and make Emacs
on:
workflow_dispatch:
4.2 サードパーティライブラリの取得とかpatch適用
以下、java/INSTALL
のpatchと比較しつつ説明していきます(わたしの全部のせビルドワークフローでは試行錯誤しつつトライしてるのでjava/INSTALL
の順とは異なりますがget-tiff
からget-boringssl
までのjob、download tiff
からdownload boringssl
のstepとそれぞれの直後にあるexpand archives
のstepの対にたいして、job単位/step対単位であれば並べ替えできます(気持ち悪い人は並べ替えられます; ちなみにわたしも気持ち悪いです)。あとビルドするためにサードパーティライブラリのAndroid.mkを利用しています。このAndroid.mkを使う仕組みはAndroid開発においては古い手法らしく、現在ではAndroid.bpが主流のようです。ということでjava/INSTALL
に記載されているように、Android.mkが存在する古いブランチ(nougat-release近辺の模様)を取得しています。
4.2.1 tiff
tiffについてはEmacs for Androidとビルド可能なバージョンがすでに用意されています。
libtiff - https://sourceforge.net/projects/android-ports-for-gnu-emacs
(Extract and point ``--with-ndk-path'' to tiff-4.5.0-emacs.tar.gz.)
ダウンロードしてartifactsとしてアップロード、
get-tiff:
#
# get TIFF from Android ports for GNU Emacs
runs-on: ubuntu-latest
steps:
- name: get tiff from Android ports for GNU Emacs
run: |
wget https://sourceforge.net/projects/android-ports-for-gnu-emacs/files/tiff-4.5.0-emacs.tar.gz
# upload as artifacts
- name: upload source archives
uses: actions/upload-artifact@v3
with:
name: tiff
path: |
tiff-4.5.0-emacs.tar.gz
次ジョブでダウンロードしてmy_sub_modules
というディレクトリー配下に解凍しているだけです。
# download tiff
- name: download tiff
uses: actions/download-artifact@v3
with:
name: tiff
path: my_sub_modules
- name: expand archives
run: |
cd my_sub_modules
tar xvfz tiff-4.5.0-emacs.tar.gz
rm -f *gz
cd -
4.2.2 webp
webpについては本来ならarmv7というデバイス以外はpatchをあてる必要はありません。ただ全部のせする場合にはImageMagickをビルドする際に静的ライブラリが衝突してしまい、ビルドには成功するもののデバイスにインストールするとクラッシュしてしまう(起動できない)ので、モジュール名を弄りました。尚サードパーティライブラリのバージョンについてはImageMagick版webpのv1.0.3の最終コミットを採用しました(ライブラリ衝突の際に「同じバージョン使えばどっちのライブラリでも大丈夫では!?」と思ってこのコミットを選んだんですが、結局駄目で名前変更したのでnougat-releaseでも問題ないと思います)。
libwebp - https://android.googlesource.com/platform/external/webp
(You must apply the patch at the end of this file for the resulting
binary to work on armv7 devices.)
cloneしてお目当てのコミットをcheckout
##########################
# Get webp, then upload it
get-webp:
runs-on: ubuntu-latest
steps:
- name: get webp, patch and upload
run: |
git clone https://android.googlesource.com/platform/external/webp
# use v1.0.3 latest(just before v1.1.0) same as imagemagick version.
git -C webp checkout a76694a1
以下、ライブラリ名を変えるパッチをあてます。
diff --git a/Android.mk b/Android.mk
index 8f0cb756..e6268645 100644
--- a/Android.mk
+++ b/Android.mk
@@ -183,16 +183,16 @@ ifeq ($(USE_CPUFEATURES),yes)
LOCAL_STATIC_LIBRARIES := cpufeatures
endif
-LOCAL_MODULE := webpdecoder_static
+LOCAL_MODULE := webpdecoder_static_webp
include $(BUILD_STATIC_LIBRARY)
ifeq ($(ENABLE_SHARED),1)
include $(CLEAR_VARS)
-LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static
+LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static_webp
-LOCAL_MODULE := webpdecoder
+LOCAL_MODULE := webpdecoder_webp
include $(BUILD_SHARED_LIBRARY)
endif # ENABLE_SHARED=1
@@ -213,7 +213,7 @@ LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src
# prefer arm over thumb mode for performance gains
LOCAL_ARM_MODE := arm
-LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static
+LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static_webp
LOCAL_MODULE := webp
そしたらartifactsとしてアップロード(委細略)。
tar cvfz webp.tar.gz ./webp
# upload as artifacts
- name: upload source archives
my_sub_modules
というディレクトリー配下に解凍するだけです(委細略)。
# download webp
- name: download webp
4.2.3 icu
パッチを当てる必要があるとのこと。
icu4c - https://android.googlesource.com/platform/external/icu/
(You must apply the patch at the end of this file.)
ダウンロードします(当初は何処らへんのバージョンを使えばよいか探りつつだったのでcloneして探ってましたが、段々判ってきた時点で該当するブランチであろうnougat-releaseのアーカイブをダウンロードしたりしてます)。
# Get icu4c, then upload it
get-icu4c:
runs-on: ubuntu-latest
steps:
- name: get icu4c and upload it
run: |
wget https://android.googlesource.com/platform/external/icu/+archive/refs/heads/nougat-release.tar.gz
java/INSTALL
のpatchをそのままあてます。ライブラリの名前はそのまま(imagemagick側のicuの名前を変えます)。あてたらアーカイブしてartifactsとしてアップロード、次ジョブでダウンロードして解凍とかは他と同じです(コード略)。
diff --git a/icu4j/Android.mk b/icu4j/Android.mk
index d1ab3d5..69eff81 100644
--- a/icu4j/Android.mk
+++ b/icu4j/Android.mk
@@ -69,7 +69,7 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
# Path to the ICU4C data files in the Android device file system:
icu4c_data := /system/usr/icu
icu4j_config_root := $(LOCAL_PATH)/main/classes/core/src
-include external/icu/icu4j/adjust_icudt_path.mk
+include $(LOCAL_PATH)/adjust_icudt_path.mk
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(icu4j_src_files)
diff --git a/icu4c/source/common/Android.mk b/icu4c/source/common/Android.mk
index 8e5f757..44bb130 100644
--- a/icu4c/source/common/Android.mk
+++ b/icu4c/source/common/Android.mk
@@ -231,7 +231,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES += $(src_files)
LOCAL_C_INCLUDES += $(c_includes) $(optional_android_logging_includes)
LOCAL_CFLAGS += $(local_cflags) -DPIC -fPIC
-LOCAL_SHARED_LIBRARIES += libdl $(optional_android_logging_libraries)
+LOCAL_SHARED_LIBRARIES += libdl libstdc++ $(optional_android_logging_libraries)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libicuuc
LOCAL_RTTI_FLAG := -frtti
4.2.4 libxml2
patchをあてる前にコミットedb5870767fed8712a9b77ef34097209b61ab2db'
をrevertするよう記されています。
libxml2 - https://android.googlesource.com/platform/external/libxml2/
(You must also place the dependency icu4c in ``--with-ndk-path'',
and apply the patch at the end of this file.)
...
This patch must be applied to the Android.mk in Google's version of
libxml2 before it can be built for Emacs. In addition, you must also
revert the commit `edb5870767fed8712a9b77ef34097209b61ab2db'.
cloneしてrevertします。ちなみにrevertするのはHTMLサポートを無効にしたコミットとのことです。
# Get libxml2 and upload it
get-libxml2:
runs-on: ubuntu-latest
steps:
# Clone libxml2 and upload it
- name: clone libxml2 and upload as tar.gz
run: |
git clone https://android.googlesource.com/platform/external/libxml2/
git -C libxml2 checkout nougat-release
git -C libxml2 config user.email "you@example.com"
git -C libxml2 config user.name "Your Name"
git -C libxml2 revert edb5870767fed8712a9b77ef34097209b61ab2db
ここではjava/INSTALL
のpatchに加えて、更にLOCAL_EXPORT_CFLAGS := -isystem $(LOCAL_PATH)/include
という行を追加しています。C言語におけるヘッダのincludeでは#include <aaa.h>
と#include "bbb.h"
という書き方があります。この<aaa.h>
で指定されているヘッダファイルはシステムのインクルードファイル用ディレクトリーからヘッダを読み込むよう指定する書き方で、元々ソースに記述されているLOCAL_C_INCLUDES
では指定できないようです。java/INSTALL
を手がかりに、どうやら-isystem path/to/system_include_dir
という情報をコンパイラに渡せばヘッダを見つけてくれる事が判りましたが、ここでコンパイルしているlibxml2はicu4cの56、imagemagickのlibxml2は64というバージョンのicu4cを使う別バージョンだったため、configer ANDROID_CFLAGS=-isystem path/to/system_include_dir
としてしまうと、どちらかで実行時エラーを起こしてクラッシュするバージョンがビルドされてしまいます。結局cross/ndk-build/READMEからcross/ndk-build/ndk-build-static-library.mkと辿って、モジュールのAndroid.mkでLOCAL_EXPORT_CFLAGS
を設定すれば、モジュール毎にコンパイラにCFLAGを渡せる事が判ったので、正にそれを行っています。
diff --git a/Android.mk b/Android.mk
index 6ec80122..3c0c2e13 100644
--- a/Android.mk
+++ b/Android.mk
@@ -63,6 +63,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(common_SRC_FILES)
LOCAL_C_INCLUDES += $(common_C_INCLUDES)
LOCAL_CFLAGS += $(common_CFLAGS) -fvisibility=hidden
+LOCAL_EXPORT_CFLAGS := -isystem $(LOCAL_PATH)/include
LOCAL_SHARED_LIBRARIES += libicuuc
LOCAL_MODULE := libxml2
LOCAL_CLANG := true
@@ -76,10 +77,12 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(common_SRC_FILES)
LOCAL_C_INCLUDES := $(common_C_INCLUDES)
LOCAL_CFLAGS += $(common_CFLAGS)
+LOCAL_EXPORT_CFLAGS := -isystem $(LOCAL_PATH)/include
LOCAL_SHARED_LIBRARIES := libicuuc
LOCAL_MODULE:= libxml2
LOCAL_CLANG := true
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)
include $(BUILD_SHARED_LIBRARY)
# For the host
@@ -89,8 +92,11 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(common_SRC_FILES)
LOCAL_C_INCLUDES += $(common_C_INCLUDES)
LOCAL_CFLAGS += $(common_CFLAGS) -fvisibility=hidden
+LOCAL_EXPORT_CFLAGS := -isystem $(LOCAL_PATH)/include
LOCAL_SHARED_LIBRARIES += libicuuc-host
LOCAL_MODULE := libxml2
LOCAL_CLANG := true
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_STATIC_LIBRARY)
+
+$(call import-module,libicuuc)
アーカイブしてartifactsとしてアップロード、次ジョブでダウンロードと解凍は他と一緒なので割愛します。
4.2.5 gnutls関連
java/INSTALL
の以下の部分です。バージョンは限定されるようですが、わたしの実機であるAndroid 8.0のarm(aarch64とも呼ぶらしい)については依存関係も含めてすべてEmacs for Androidとビルド可能なバージョンがすでに用意されています。get-gnutls-and-dependencies
というジョブで取得してます。コードは割愛。
Modified copies of GnuTLS and its dependencies (such as libgmp,
libtasn1, p11-kit) which can be built with the ndk-build system can be
found at https://sourceforge.net/projects/android-ports-for-gnu-emacs.
They have only been tested on arm64 Android systems running Android
5.0 or later, and armv7l systems running Android 13 or later, so your
mileage may vary, especially if you are trying to build Emacs for
another kind of machine.
To build Emacs with GnuTLS, you must unpack each of the following tar
archives in that site:
gmp-6.2.1-emacs.tgz
gnutls-3.7.8-emacs.tar.gz
libtasn1-4.19.0-emacs.tar.gz
p11-kit-0.24.1-emacs.tar.gz
nettle-3.8-emacs.tar.gz
4.2.6 treesitter
これも対応版が用意されてました。ダウンロードとかの処理はジョブget-treesiter
で行っています
。
A copy of tree-sitter modified to build with the ndk-build system can
also be found that URL. To build Emacs with tree-sitter, you must
unpack the following tar archive in that site:
tree-sitter-0.20.7-emacs.tar.gz
4.2.7 harfbuzz
。これも対応版が用意されてました。ジョブはget-harfbuzz
です。
A copy of HarfBuzz modified to build with the ndk-build system can
also be found at that URL. To build Emacs with HarfBuzz, you must
unpack the following tar archive in that site:
harfbuzz-7.1.0-emacs.tar.gz
4.2.8 sqlite3
モジュールのサブディレクトリーdistをconfigureのオプションで指定せよとのこと。これはダウンロード、artifacts経由で渡した次ジョブで行います。
sqlite3 - https://android.googlesource.com/platform/external/sqlite/
(You must apply the patch at the end of this file, and add the `dist'
directory to ``--with-ndk-path''.)
cloneしてコミットハッシュf63e8d96e298783c310c08030d4c51a875dae4cd
をチェックアウトします。コミットはパッチにあったLOCAL_SDK_VERSION := 23
という記述から探しました(多分nougat-releaseでも大丈夫かなあ)。
# Get sqlite3 and upload it
get-sqlite3:
runs-on: ubuntu-latest
steps:
- name: clone sqlite3 and upload as tar.gz
run: |
git clone https://android.googlesource.com/platform/external/sqlite
# commit hash had guessed by `LOCAL_SDK_VERSION := 23'
git -C sqlite checkout f63e8d96e298783c310c08030d4c51a875dae4cd
java/INSTALL
のパッチをそのままあてます。
diff --git a/dist/Android.mk b/dist/Android.mk
index bf277d2..36734d9 100644
--- a/dist/Android.mk
+++ b/dist/Android.mk
@@ -141,6 +141,7 @@ include $(BUILD_HOST_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(common_src_files)
LOCAL_CFLAGS += $(minimal_sqlite_flags)
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)
LOCAL_MODULE:= libsqlite_static_minimal
LOCAL_SDK_VERSION := 23
include $(BUILD_STATIC_LIBRARY)
diff --git a/dist/sqlite3.c b/dist/sqlite3.c
index b0536a4..8fa1ee9 100644
--- a/dist/sqlite3.c
+++ b/dist/sqlite3.c
@@ -26474,7 +26474,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
*/
#if !defined(HAVE_POSIX_FALLOCATE) \
&& (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L)
-# define HAVE_POSIX_FALLOCATE 1
+/* # define HAVE_POSIX_FALLOCATE 1 */
#endif
/*
4.2.9 giflib
ジョブはget-giflib
です。java/INSTALL
にはAndroid.mkにLOCAL_EXPORT_CFLAGS := -I$(LOCAL_PATH)
を追加するよう記述されています。
giflib - https://android.googlesource.com/platform/external/giflib
(You must add LOCAL_EXPORT_CFLAGS := -I$(LOCAL_PATH) before
its Android.mk includes $(BUILD_STATIC_LIBRARY))
これもパッチで行いました。
diff --git a/Android.mk b/Android.mk
index 03a9355..13a93d0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,4 +14,5 @@ LOCAL_SRC_FILES := \
LOCAL_CFLAGS += -Wno-format -Wno-sign-compare -Wno-unused-parameter -DHAVE_CONFIG_H
LOCAL_MODULE:= libgif
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
4.2.10 json
get-jansson
というジョブです。java/INSTALL
によるとAndroid.mkにLOCAL_EXPORT_INCLUDES := $(LOCAL_C_INCLUDES)
を追加して、android/jansson_config.h
をandroid/jansson_private_config.h
にコピーするとあります。
libjansson - https://github.com/akheron/jansson
(You must add LOCAL_EXPORT_INCLUDES := $(LOCAL_C_INCLUDES) before
its Android.mk includes $(BUILD_SHARED_LIBRARY), then copy
android/jansson_config.h to android/jansson_private_config.h.)
今回もコピーを含めてパッチで処理しましたが、それだけだとヘッダが見つからずmake
でこけてしまったので、ヘッダのディレクトリー指定を追加しました(LOCAL_EXPORT_INCLUDES
とLOCAL_EXPORT_CFLAGS
)。
diff --git a/Android.mk b/Android.mk
index e3b09e7..c0aa047 100644
--- a/Android.mk
+++ b/Android.mk
@@ -27,4 +27,6 @@ LOCAL_CFLAGS += -O3 -DHAVE_STDINT_H=1
LOCAL_MODULE:= libjansson
+LOCAL_EXPORT_INCLUDES := $(LOCAL_C_INCLUDES)
+LOCAL_EXPORT_CFLAGS := -isystem $(LOCAL_PATH)/src -I $(LOCAL_PATH)/android
include $(BUILD_SHARED_LIBRARY)
diff --git a/android/jansson_private_config.h b/android/jansson_private_config.h
new file mode 100644
index 0000000..618a0da
--- /dev/null
+++ b/android/jansson_private_config.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ *
+ *
+ * This file specifies a part of the site-specific configuration for
+ * Jansson, namely those things that affect the public API in
+ * jansson.h.
+ *
+ * The configure script copies this file to jansson_config.h and
+ * replaces @var@ substitutions by values that fit your system. If you
+ * cannot run the configure script, you can do the value substitution
+ * by hand.
+ */
+
+#ifndef JANSSON_CONFIG_H
+#define JANSSON_CONFIG_H
+
+/* If your compiler supports the inline keyword in C, JSON_INLINE is
+ defined to `inline', otherwise empty. In C++, the inline is always
+ supported. */
+#ifdef __cplusplus
+#define JSON_INLINE inline
+#else
+#define JSON_INLINE inline
+#endif
+
+/* If your compiler supports the `long long` type and the strtoll()
+ library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
+ otherwise to 0. */
+#define JSON_INTEGER_IS_LONG_LONG 1
+
+/* If locale.h and localeconv() are available, define to 1,
+ otherwise to 0. */
+#define JSON_HAVE_LOCALECONV 0
+
+/* Maximum recursion depth for parsing JSON input.
+ This limits the depth of e.g. array-within-array constructions. */
+#define JSON_PARSER_MAX_DEPTH 2048
+
+#endif
4.2.11 libjpeg
ジョブはget-libjpeg
です。java/INSTALL
にはLOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
を追加するよう記載されています。
libjpeg-turbo - https://android.googlesource.com/platform/external/libjpeg-turbo
(You must add LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) before
its Android.mk includes $(BUILD_SHARED_LIBRARY))
その通りパッチを当てました。
get-libjpeg:
runs-on: ubuntu-latest
steps:
- name: get libjpeg-turbo and patch it
run: |
git clone https://android.googlesource.com/platform/external/libjpeg-turbo
git -C libjpeg-turbo checkout nougat-release
patch --directory=libjpeg-turbo -p1<<'EOS'
diff --git a/Android.mk b/Android.mk
index 2801805f..ab94028e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -75,6 +75,9 @@ ifneq (,$(TARGET_BUILD_APPS))
LOCAL_SDK_VERSION := 17
endif
+# Added to build with GNU Emacs on Android
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+
# Build as a static library.
LOCAL_MODULE := libjpeg_static
include $(BUILD_STATIC_LIBRARY)
EOS
tar cvfz libjpeg-turbo.tar.gz ./libjpeg-turbo
4.2.12 ImageMagick
次はImageMagickです。今まで追加してきたライブラリの独自バージョンがいくつか含まれている大物です。まずはjava/INSTALL
の記載より。
There is a third party port of ImageMagick to Android. Unfortunately,
the port also uses its own patched versions of libpng, libjpeg,
libtiff and libwebp, which conflict with those used by Emacs. Its
Makefiles were also written for MS Windows, so you must also apply the
patch at the end of this file.
つまりパッチを沢山当てる必要があるとのこと。更にlibxml2とともにビルドする場合には異なるバージョンで同じファイル名をもつ静的ライブラリの衝突を回避するためのパッチの追加も必要でした。でかいので全部は載せません。java/INSTALL
のパッチに加えて、以下のように名前を弄っています。ジョブはget-imagemagick
です。
-LOCAL_STATIC_LIBRARIES += libicuuc
+LOCAL_STATIC_LIBRARIES += libicuucmagick
4.2.13 selinux関連
最後はselinuxとその依存関係です。
libselinux - https://android.googlesource.com/platform/external/libselinux
(You must apply the patches at the end of the file, and obtain
the following three dependencies.)
libpackagelistparser
https://android.googlesource.com/platform/system/core/+/refs/heads/nougat-mr1-dev/libpackagelistparser/
libpcre - https://android.googlesource.com/platform/external/pcre
libcrypto - https://android.googlesource.com/platform/external/boringssl
(You must apply the patch at the end of this file when building for
ARM systems.)
まずはselinuxから。
get-libselinux:
runs-on: ubuntu-latest
steps:
- name: get selinux and archive it
run: |
git clone https://android.googlesource.com/platform/external/libselinux
git -C libselinux checkout nougat-release
patch --directory libselinux -p1 << 'EOS'
diff --git a/Android.mk b/Android.mk
# ...
# 以下 java/INSTALLのパッチをそのまま当ててます
次にAndroidのcoreライブラリに含まれているpackagelistparserです。ブランチは指定通りnougat-mr1-dev
をチェックアウトします。
get-core:
runs-on: ubuntu-latest
steps:
- name: get core and archive it
run: |
git clone https://android.googlesource.com/platform/system/core
git -C core checkout nougat-mr1-dev
システムヘッダファイルをmake
が見つけられなかったので、LOCAL_EXPORT_CFLAGS := -isystem $(LOCAL_PATH)/../include
を追加するパッチを当てます。
diff --git a/libpackagelistparser/Android.mk b/libpackagelistparser/Android.mk
index c8be050e0..563fdc66a 100644
--- a/libpackagelistparser/Android.mk
+++ b/libpackagelistparser/Android.mk
@@ -13,6 +13,7 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_CLANG := true
LOCAL_SANITIZE := integer
+LOCAL_EXPORT_CFLAGS := -isystem $(LOCAL_PATH)/../include
include $(BUILD_SHARED_LIBRARY)
#########################
@@ -29,4 +30,5 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_CLANG := true
LOCAL_SANITIZE := integer
+LOCAL_EXPORT_CFLAGS := -isystem $(LOCAL_PATH)/../include
include $(BUILD_STATIC_LIBRARY)
次はpcreです。cloneしてnougat-releaseをチェックアウトするだけです。
# get pcre from google
get-pcre:
runs-on: ubuntu-latest
steps:
- name: get pcre and archive it
run: |
git clone https://android.googlesource.com/platform/external/pcre
git -C pcre checkout nougat-release
最後はboringsslです。cloneしてnougat-releaseをチェックアウト、artifacts経由で次ジョブに渡して
# get boringssl from google
get-boringssl:
runs-on: ubuntu-latest
steps:
- name: get boringssl and archive it
run: |
git clone https://android.googlesource.com/platform/external/boringssl
git -C boringssl checkout nougat-release
次ジョブにてターゲットがarm
ならjava/INSTALL
のパッチをそのまま当てます。
# download boringssl
- name: download boringssl
uses: actions/download-artifact@v3
with:
name: boringssl
path: my_sub_modules
- name: expand archives
run: |
cd my_sub_modules
tar xvfz boringssl.tar.gz
# patch if ARM
if [ ${{ matrix.abi }} == "arm" ]
then
patch --directory boringssl -p1 << 'EOS'
# ...
# コードは省略します
4.3 ビルドする
4.2 サードパーティライブラリの取得とかpatch適用が終わったらそれらと一緒にEmacsをビルドします。needs:
に記した前提条件は、正常終了が実行の条件となるジョブであり、それらのジョブというのがすなわち4.2 サードパーティライブラリの取得とかpatch適用ということです。
strategy
のmatrix
ですが、これらはビルドする環境や配布先のデバイスを指定します。当初は「一片にビルド走って壮観だろうなあ」とか思って書きましたが、200以上のジョブの赤い異常終了ステータスを目にするのはあまり面白くなく、自分がもっている環境じゃないとテストもできないので、実績2つのmatrixだけです。
job1:
runs-on: ubuntu-latest
needs: [get-libselinux, get-core, get-pcre, get-boringssl, get-tiff, get-webp, get-libxml2, get-icu4c, get-gnutls-and-dependencies, get-treesiter, get-harfbuzz, get-sqlite3, get-giflib, get-jansson, get-libjpeg, get-imagemagick]
strategy:
matrix:
api-version: [34]
# ndk-version: [23.2.8568313, 24.0.8215888, 25.2.9519653]
ndk-version: [25.2.9519653]
# abi: [i686, x86_64, aarch64, armv7a, mips64, mips, arm]
abi: [aarch64]
# minsdk: [22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33]
minsdk: [22, 33]
-
api-version:Android SDK同梱の`android.jar'のパス。api levelとも呼ばれるもので、最新バージョンの指定が必須。先頃33から34に更新されました。
-
ndk-version: ビルドしたEmacsを実行するCPUに応じたAndroid NDKのCコンパイラーのパス。古かったりバージョンが違ったりで色々問題が発生するらしいです。
-
abi: ビルドしてインストール先となるCPU。configureとかパッチではaarch64がarm64-v8aとかになってます。対応は
configure.ac
に書いてあります。
AC_MSG_CHECKING([for the kind of Android system Emacs is being built for])
cc_target=`${ANDROID_CC} -v 2>&1 | sed -n 's/Target: //p'`
case "$cc_target" in
[
*i[3-6]86*) android_abi=x86
;;
*x86_64*) android_abi=x86_64
;;
*aarch64*) android_abi=arm64-v8a
;;
*arm*v7a*) android_abi=armeabi-v7a
;;
*mips64*) android_abi=mips64
;;
*mips*) android_abi=mips
;;
*arm*) android_abi=armeabi
;;
]
- minsdk: アプリが実行できるAndroidの最低バージョン。ユーザーにとってはいちばん気のなるところだと思います(たとえばわたしのAndroid 8.0端末では先だってのslackのバージョンアップにより対象バージョンから漏れて、アプリによるアクセスができなくなりました)。
4.3.1 レポジトリの作成とモジュールのダウンロード
レポジトリ自体をチェックアウトしてからartifactsとしてモジュールをダウンロードするためのディレクトリーを作成します。最初はレポジトリのルートにそのままダウンロードしてたんですが、core/libpackagelistparserのビルドが何をどーやって指定しても上手くゆかず、ログをひっくり返してやっとEmacs自体のビルド処理がレポジトリルートのcoreというファイルを削除しているらしい事がわかったので、別途フォルダを作成するよう変更しました。このフォルダに4.2 サードパーティライブラリの取得とかpatch適用のモジュール達をダウンロードして展開します。
steps:
#
# Checkout source.
- name: Checkout source
uses: actions/checkout@v3
#
# make directory for sub modules
- name: make directory for sub modules
run: |
mkdir my_sub_modules
4.3.2 環境のセットアップとautogen.shの実行
どこかで見つけた例の通りにjavaとandroidの環境をセットアップして、Emacsをレポジトリから直接ビルドする際に通常実行するautogen.sh
を実行します(これによってconfigure
スクリプトが作成される)。
# Setup java/jdk.
- name: Setup java environment
uses: actions/setup-java@v3
with:
distribution: zulu
java-version: 11
#
# Setup android environment.
- name: Setup android
uses: android-actions/setup-android@v2
#
# Run autogen.sh
- name: run autogen.sh
run: |
./autogen.sh
4.3.3 configureの実行
やっとconfigure
を実行します。モジュールを指定したり通常のEmacsのconfigure時のように有効にするライブラリとかを指定してます。configureの前後にあるsh -x
と2>&1 | tee configure.out
は上手く行かなかったとき用のログ収集のため。sh -x
を削除すれば出力はぐっと減ります。
指定しているオプションについて説明すると
- --with-android: matrixのapi-version、すなわち34です
- ANDROID_CC: matrixのndk-versionからCPUがmatrixのabi、対象とする最低バージョンのAndroid APIレベルがmatrixのminsdkであることを指定しています。
- SDK_BUILD_TOOLS: これはapi-versionと同じにしてます(定数になってますが意味はありません)。
- --with-ndk-path: artifacts経由でダウンロード、解凍したモジュールのAndroid.mkがあるディレクトリーです。
- --with-shared-user: Termuxと互いにディレクトリーを参照できるように
com.termux
を指定しています。
Google PlayやF-DroidのTermuxはそれぞれその配布元のキーで署名されているので、それらがインストールされている場合には、共有ユーザーIDにcom.termux
を指定するとインストールができません(不明なキーで署名された同一ユーザー名を騙るアプリとして認識されるからです)。ここで共存先として想定しているのは、Emacs for Androidで配布されているバージョンのTermuxです。Termuxもオープンソースであり、Githubにレポジトリもあるのでforkして署名だけjava/emacs.keystore
を使って自分でビルドすることもできます。他のプラグイン(Termux:bootとか)も使いたい人は自前でビルドする方がよいかもしれません。
- その他の--with-XXX: Emacsのいつものビルドで指定するオプションと同じ。
--with-ndk-path
で指定したモジュールについてはyes
を指定しましょう(--with-x-toolkit=no
は不要かもしれません; 最小構成で実験的にビルドしてたときの名残りです)。
MODULE_ROOT=$(pwd)/my_sub_modules
sh -x ./configure \
--with-android=$ANDROID_HOME/platforms/android-${{ matrix.api-version }}/android.jar \
ANDROID_CC="$ANDROID_HOME/ndk/${{ matrix.ndk-version }}/toolchains/llvm/prebuilt/linux-x86_64/bin/${{ matrix.abi }}-linux-android${{ matrix.minsdk }}-clang" \
SDK_BUILD_TOOLS=$ANDROID_HOME/build-tools/34.0.0 \
"--with-ndk-path=$MODULE_ROOT/libselinux $MODULE_ROOT/core/libpackagelistparser $MODULE_ROOT/pcre $MODULE_ROOT/boringssl $MODULE_ROOT/tiff-4.5.0 $MODULE_ROOT/webp $MODULE_ROOT/libxml2 $MODULE_ROOT/icu $MODULE_ROOT/gmp-6.2.1 $MODULE_ROOT/gnutls-3.7.8 $MODULE_ROOT/libtasn1-4.19.0 $MODULE_ROOT/nettle-3.8 $MODULE_ROOT/p11-kit-0.24.1 $MODULE_ROOT/tree-sitter-0.20.7 $MODULE_ROOT/harfbuzz-7.1.0 $MODULE_ROOT/sqlite/dist $MODULE_ROOT/giflib $MODULE_ROOT/jansson $MODULE_ROOT/libjpeg-turbo $MODULE_ROOT/Android-ImageMagick7" \
--with-x-toolkit=no \
--with-shared-user-id=com.termux \
--with-gnutls=yes \
--with-tree-sitter=yes \
--with-harfbuzz=yes \
--with-sqlite3=yes \
--with-xml2=yes \
--with-gif=yes \
--with-json=yes \
--with-tiff=yes \
--with-jpeg=yes \
--with-webp=yes \
--with-png=yes \
--with-imagemagick=yes \
--with-selinux=yes 2>&1 | tee configure.out
4.3.4 makeとか
makeして成功したら*.apk
をartifactsとしてアップロードします。makeのオプションV=1
はデバッグ用に沢山出力させるたねのオプションかな? -j$(nproc)
は多重度を上げて高速化するためのオプション。とるとスピードは遅くなるが出力は読みやすくなります。
#
# Run make to build apk.
- name: run make
run: |
make V=1 -j$(nproc) ||
for F in cross/ndk-build/*.a
do
echo "ayatakesi_debug: nm $F"
nm $F
echo
done
#
# Upload apk as artifacts.
- name: upload apk
uses: actions/upload-artifact@v3
with:
name: Emacs.apk
path: ./java/*.apk
5 インストール
ダウンロードして解凍して*.apkをタップすればインストールできる筈です。
実行して確認するとビルドできたみたいです。
ewwも動きます
次記事ではEmacsとTermuxを共存させるために行った環境設定について説明します。
おしまい