为 Android 编译 MuPDF 查看器

先决条件

你需要一个 工作的 Android 开发环境,它由 Android SDK 和 Android NDK 组成。建立这一环境最简单的方式就是使用 Android Studio 下载并安装 SDK 和 NDK。确保 Android/Sdk/tools 和 Android/Sdk/ndk-bundle 目录在你的 PATH 上。

你也需要 Oracle 的 Java JDK(OpenJDK 与 Android 不兼容)。你也需要 Apache Ant 构建系统。你也需要 Git,GNU Make,和一个 C 编译器。

如果一切正常,你应该也能够在命令中运行如下这些命令:

  • Android SDK tools: android, emulator, and adb.
  • Android NDK tools: ndk-build.
  • Oracle Java JDK 8: java, and javac.
  • Apache Ant: ant.
  • Git: git.
  • GNU Make: make, or gmake.
  • C compiler: cc, gcc, or clang.

构建

使用 Git 下载工程(不要忘了加上 –recursive 标记):

1
$ git clone --recursive git://git.ghostscript.com/mupdf-android-viewer-mini.git

加上 –recursive 标记是为了让 Git 可以递归地下载 mupdf-android-viewer-mini 工程及其依赖的所有工程。MuPDF 由于牵涉到多个模块,因而采用了 Git 的 submodule 机制来管理这些模块。在 mupdf-android-viewer-mini 工程的根目录下,有一个名为 .gitmodules 描述了它依赖的子模块:

1
2
3
[submodule "jni"]
path = jni
url = ../mupdf-android-fitz.git

在下载代码时,加了 –recursive 标记,Git 在下载完 mupdf-android-viewer-mini 工程之后,就会下载 mupdf-android-fitz 工程,并把它放在 mupdf-android-viewer-mini 工程的 jni 子目录下。而在 mupdf-android-fitz 工程的根目录下,同样有一个 .gitmodules 文件,描述 mupdf-android-fitz 工程依赖的模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[submodule "thirdparty/jbig2dec"]
path = thirdparty/jbig2dec
url = ../jbig2dec.git
[submodule "thirdparty/mujs"]
path = thirdparty/mujs
url = ../mujs.git
[submodule "thirdparty/freetype"]
path = thirdparty/freetype
url = ../thirdparty-freetype2.git
[submodule "thirdparty/harfbuzz"]
path = thirdparty/harfbuzz
url = ../thirdparty-harfbuzz.git
[submodule "thirdparty/jpeg"]
path = thirdparty/libjpeg
url = ../thirdparty-libjpeg.git
[submodule "thirdparty/lcms2"]
path = thirdparty/lcms2
url = ../thirdparty-lcms2.git
[submodule "thirdparty/openjpeg"]
path = thirdparty/openjpeg
url = ../thirdparty-openjpeg.git
[submodule "thirdparty/zlib"]
path = thirdparty/zlib
url = ../thirdparty-zlib.git
[submodule "thirdparty/curl"]
path = thirdparty/curl
url = ../thirdparty-curl.git
[submodule "thirdparty/freeglut"]
path = thirdparty/freeglut
url = ../thirdparty-freeglut.git

Git 在下载完 mupdf-android-fitz 工程之后,还会下载这些模块,并放在 mupdf-android-fitz 工程目录的 thirdparty 目录下,即 mupdf-android-viewer-mini/jni/libmupdf 目录下。

直接使用 Git 的 –recursive 标记下载,与如下的命令序列是等价的:

1
2
3
4
5
6
7
$ git clone git://git.ghostscript.com/mupdf-android-viewer-mini.git
$ cd mupdf-android-viewer-mini
$ git submodule update --init
$ cd jni
$ git submodule update --init
$ cd libmupdf
$ git submodule update --init

在开始构建之前,还需要在 mupdf-android-viewer-mini 工程的根目录下创建 local.properties 文件,配置 Android SDK 和 NDK 的路径:

1
2
3
4
5
6
7
8
9
10
11
12
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Tue Jun 05 19:57:20 CST 2018
sdk.dir=/home/hanpfei0306/data/dev_tools/Android/Sdk
ndk.dir=/home/hanpfei0306/data/dev_tools/Android/android-ndk-r12b

此外,还需要在 mupdf-android-viewer-mini/jni/libmupdf 目录下执行 make generate 命令生成必要的文件:

1
mupdf-android-viewer-mini/jni/libmupdf $ make generate

否则,如果直接执行 make 进行构建,将很快报出找不到某些文件的错误:

1
2
3
4
5
6
7
8
9
10
11
[mips64] Compile : mupdf_core <= bbox-device.c
[mips64] Compile : mupdf_core <= draw-mesh.c
In file included from /media/data/osprojects/mupdf-android-viewer-mini/jni/libmupdf/platform/java/mupdf_native.c:10:0:
/media/data/osprojects/mupdf-android-viewer-mini/jni/libmupdf/include/mupdf/pdf.h:10:34: fatal error: mupdf/pdf/name-table.h: No such file or directory
#include "mupdf/pdf/name-table.h"
^
compilation terminated.
/mmedia/data/dev_tools/Android/android-ndk-r12b/build/core/build-bake[1]: *** [/media/data/osprojects/mupdf-androiid-viewer-mini/jni/build/intenary.mk:472: recirmediates/ndkBuild/release/obj/local/mips64p/objs/mupdf_java//media/data/osprojecets/mupdf-android-viewer-mini/jni/libmupdf/platform/java/mupdf_nati vfeor .o] Error 1
target '/media/data/osprojects/mupdf-android-viewer-mini/jni/build/intermediates/ndkBuild/release/obj/local/mips64/objs/mupdf_java//media/data/osprojects/mupdf-android-viewer-mini/jni/libmupdf/platform/java/mupdf_native.o' failed
make[1]: *** 正在等待未完成的任务....
make[1]: Leaving directory '/media/data/osprojects/mupdf-android-viewer-mini/jni

之后就可以在 mupdf-android-viewer-mini 工程的根目录下执行如下命令来构建了:

1
$ make

查看 mupdf-android-viewer-mini 工程的 Makefile 文件的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# This is a very simple Makefile that calls 'gradlew' to do the heavy lifting.
#
# The tool 'adb' must be on the path, so that we can find the Android SDK.
ANDROID_HOME := $(shell which adb | sed 's,/platform-tools/adb,,')
default: assembleDebug
release: assembleRelease
install: installDebug
assembleDebug:
ANDROID_HOME=$(ANDROID_HOME) ./gradlew assembleDebug
assembleRelease:
ANDROID_HOME=$(ANDROID_HOME) ./gradlew assembleRelease
installDebug:
ANDROID_HOME=$(ANDROID_HOME) ./gradlew installDebug
lint:
ANDROID_HOME=$(ANDROID_HOME) ./gradlew lint
archive:
ANDROID_HOME=$(ANDROID_HOME) ./gradlew uploadArchives
sync: archive
rsync -av MAVEN/com/ ghostscript.com:/var/www/maven.ghostscript.com/com/
run: install
adb shell am start -n com.artifex.mupdf.mini.app/.LibraryActivity
clean:
rm -rf .gradle build
rm -rf jni/.externalNativeBuild jni/.gradle jni/build
rm -rf lib/.gradle lib/build
rm -rf app/.gradle app/build

执行 make 只是调用了 gradle 命令 ./gradlew assembleDebug

执行上面的 make 命令,在执行到为 armeabi ABI 编译动态链接库时将报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
. . . . . .
/tmp/ccrxybCM.s:4815: Error: cannot honor width suffix -- `add r4,r4,r2'
/tmp/ccrxybCM.s:4824: Error: cannot honor width suffix -- `mul r2,r3'
/tmp/ccrxybCM.s:4827: Error: lo register required -- `add r3,r3,#128'
/tmp/ccrxybCM.s:4828: Error: cannot honor width suffix -- `asr r3,r3,#8'
/tmp/ccrxybCM.s:4876: Error: cannot honor width suffix -- `mov r0,#0'
/tmp/ccrxybCM.s:4937: Error: cannot honor width suffix -- `mov r4,#0'
/tmp/ccrxybCM.s:5010: Error: cannot honor width suffix -- `mov r1,#1'
/tmp/ccrxybCM.s:5011: Error: cannot honor width suffix -- `mov r2,#44'
make[1]: *** [/media/data/osprojects/mupdf-android-viewer-mini/jni/build/intermediates/ndkBuild/release/obj/local/armeabi/objs/mupdf_core//media/data/osprojects/mupdf-android-viewer-mini/jni/libmupdf/source/fitz/draw-scale-simple.o] Error 1
make[1]: *** 正在等待未完成的任务....
make[1]: Leaving directory '/media/data/osprojects/mupdf-android-viewer-mini/jni'
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 4 mins 5.65 secs
Makefile:12: recipe for target 'assembleDebug' failed
make: *** [assembleDebug] Error 1

默认情况下,./gradlew assembleDebug 将为 ‘armeabi’,’armeabi-v7a’,’arm64-v8a’,’x86’,’x86_64’,’mips’,’mips64’ 起种 ABI 构建动态链接库,但实际上对某些 ABI 的支持完全没有必要,比如早已过时的 ‘armeabi’,以及非常小众的 ‘mips’,’mips64’。因而修改 mupdf-android-viewer-mini/jni/build.gradle 文件,配置 ndk.abiFilters,过滤掉不需要的 ABI:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
. . . . . .
android {
compileSdkVersion 25
buildToolsVersion '25.0.3'
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
externalNativeBuild.ndkBuild.arguments '-j4'
// Uncomment one of the following lines to limit builds to certain ABIs.
ndk.abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
. . . . . .

无需编译 armeabi 的 abi 之后,mupdf-android-viewer-mini 工程顺利编译通过:

1
2
3
4
5
6
7
8
9
10
. . . . . .
:lib:transformNativeLibsWithStripDebugSymbolForDebug
:lib:transformNativeLibsWithSyncJniLibsForDebug
:lib:bundleDebug
:lib:compileDebugSources
:lib:assembleDebug
BUILD SUCCESSFUL
Total time: 1 mins 58.883 secs

参考文档:
How to build the MuPDF viewer for Android

坚持原创技术分享,您的支持将鼓励我继续创作!