summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2024-04-22 16:27:30 +0800
committerPo Lu <luangruo@yahoo.com>2024-04-22 16:30:15 +0800
commit3bcdf010a9f2576bac0d7f23af70fa9dff81ef95 (patch)
tree600a98286f5469579cf87dac7c920590b5af4082
parent4d9629b087fe6df941b553c6931b2f8996901e21 (diff)
downloademacs-3bcdf010a9f2576bac0d7f23af70fa9dff81ef95.tar.gz
Generate Android shared library list automatically
* .gitignore: Ignore new generated files. * cross/Makefile.in (src/Makefile): Remove leftover specification of the source Gnulib directory. * cross/ndk-build/ndk-build.mk.in (NDK_BUILD_READELF): New variable. * java/Makefile.in (CONFIG_FILE, ALL_DEPENDENCIES, READELF) (cf-stamp-1, cf-stamp): New variables and rules; compute the set of library files in the order of loading and generate a file with this information. (ALL_CLASS_FILES): New variable; if builddir is not srcdir, $($(CONFIG_FILE), $(CLASS_FILES)): Depend on EmacsConfig.java. add generated files in the build directory. (classes.dex): Adjust to match. * java/org/gnu/emacs/EmacsNative.java (EmacsNative) <static initializer>: Load shared libraries from EMACS_SHARED_LIBRARIES rather than a hard-coded list. * m4/ndk-build.m4 (ndk_INIT): Search for readelf... (ndk_CHECK_MODULES): ...and substitute its path as NDK_BUILD_READELF.
-rw-r--r--.gitignore4
-rw-r--r--cross/Makefile.in2
-rw-r--r--cross/ndk-build/ndk-build.mk.in1
-rw-r--r--java/Makefile.in91
-rw-r--r--java/org/gnu/emacs/EmacsNative.java52
-rw-r--r--m4/ndk-build.m411
6 files changed, 126 insertions, 35 deletions
diff --git a/.gitignore b/.gitignore
index 29c571a3dcb..4098e2210b5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,6 +66,10 @@ java/org/gnu/emacs/*.class
# Built by `aapt'.
java/org/gnu/emacs/R.java
+# Built by `make'.
+java/org/gnu/emacs/EmacsConfig.java
+java/org/gnu/emacs/cf-stamp
+
# Built by `config.status'.
java/AndroidManifest.xml
diff --git a/cross/Makefile.in b/cross/Makefile.in
index 1e8daea6f91..575c6c4cb29 100644
--- a/cross/Makefile.in
+++ b/cross/Makefile.in
@@ -140,7 +140,7 @@ src/Makefile: $(top_builddir)/src/Makefile.android
-e 's/\.\.\/admin\/charsets/..\/..\/admin\/charsets/g' \
-e 's/^libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' \
-e 's/libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' \
- -e 's/-I\$$(top_srcdir)\/lib/-I..\/$(subst /,\/,$(srcdir))\/lib/g' \
+ -e 's/-I\$$(top_srcdir)\/lib//g' \
< $(top_builddir)/src/Makefile.android > $@
src/epaths.h: $(top_builddir)/src/epaths.h
diff --git a/cross/ndk-build/ndk-build.mk.in b/cross/ndk-build/ndk-build.mk.in
index ea1be5af6f1..9948e019e3b 100644
--- a/cross/ndk-build/ndk-build.mk.in
+++ b/cross/ndk-build/ndk-build.mk.in
@@ -27,6 +27,7 @@ NDK_BUILD_CXX_LDFLAGS = @NDK_BUILD_CXX_LDFLAGS@
NDK_BUILD_ANY_CXX_MODULE = @NDK_BUILD_ANY_CXX_MODULE@
NDK_BUILD_SHARED =
NDK_BUILD_STATIC =
+NDK_BUILD_READELF = @NDK_BUILD_READELF@
define uniqify
$(if $1,$(firstword $1) $(call uniqify,$(filter-out $(firstword $1),$1)))
diff --git a/java/Makefile.in b/java/Makefile.in
index 7d732be8f91..bd1938689d5 100644
--- a/java/Makefile.in
+++ b/java/Makefile.in
@@ -83,6 +83,10 @@ RESOURCE_FILES := $(foreach file,$(wildcard $(srcdir)/res/*), \
# code. Instead, it is automatically included by the Java compiler.
RESOURCE_FILE := $(srcdir)/org/gnu/emacs/R.java
+# EmacsConfig.java is a file that holds information regarding the set of
+# shared libraries this binary links to, and similar build variables.
+CONFIG_FILE := $(builddir)/org/gnu/emacs/EmacsConfig.java
+
# CLASS_FILES is what should actually be built and included in the
# resulting Emacs executable. The Java compiler might generate more
# than one class file for each source file, so this only serves as a
@@ -294,8 +298,72 @@ $(RESOURCE_FILE): $(RESOURCE_FILES)
-J $(dir $@) -M AndroidManifest.xml \
-S $(top_srcdir)/java/res
-# Make all class files depend on R.java being built.
-$(CLASS_FILES): $(RESOURCE_FILE)
+# Generate a list of libemacs's dependencies with each item ordered
+# before its dependents for the startup process to load in advance, as
+# older versions of the dynamic linker do not consider these libraries
+# when resolving its imports. The several following statements are
+# executed from a recursive `make' run after shared libraries are
+# generated.
+
+ALL_DEPENDENCIES :=
+
+ifneq (,$(filter cf-stamp-1,$(MAKECMDGOALS)))
+# Don't be sidetracked by dependencies of shared libraries outside the
+# ndk-build directory.
+define get-dependencies
+$(foreach x, \
+$(and $(wildcard $(top_builddir)/cross/ndk-build/$1.so), \
+ $(shell $(NDK_BUILD_READELF) -d \
+ $(wildcard $(top_builddir)/cross/ndk-build/$1.so) \
+ | sed -n 's/.*(NEEDED).*\[\(.*\.so\)\].*/\1/p')), \
+$(basename $(notdir $(x))))
+endef #get-dependencies
+define resolve-one-dependency
+$(foreach dependency,$(call get-dependencies,$1),\
+ $(if $(findstring "$(dependency)",$(ALL_DEPENDENCIES)),,\
+ $(call resolve-one-dependency,$(basename $(notdir $(dependency)))) \
+ $(eval ALL_DEPENDENCIES := $(ALL_DEPENDENCIES) "$(dependency)",)))
+endef #resolve-one-dependency
+DEPENDENCIES := $(foreach file,$(NDK_BUILD_SHARED),\
+ $(basename $(notdir $(file))))
+$(foreach file,$(DEPENDENCIES),\
+ $(if $(findstring "$(file)",$(ALL_DEPENDENCIES)),,\
+ $(call resolve-one-dependency,$(file)) \
+ $(eval ALL_DEPENDENCIES := $(ALL_DEPENDENCIES) "$(file)",)))
+endif
+
+# EmacsConfig.java:
+ifeq (${V},1)
+AM_V_EMACSCONFIG =
+else
+AM_V_EMACSCONFIG = @$(info $. GEN org/gnu/emacs/EmacsConfig.java)
+endif
+
+.PHONY: cf-stamp-1
+cf-stamp-1:
+ $(AM_V_at) echo 'package org.gnu.emacs;\
+public class EmacsConfig\
+{\
+/* This is a generated file. Do not edit! */\
+public static final String[] EMACS_SHARED_LIBRARIES\
+= {$(ALL_DEPENDENCIES)};\
+}' | sed 's/\\//g' > globals.tmp
+ $(AM_V_at) mkdir -p org/gnu/emacs
+ $(AM_V_at) $(top_srcdir)/build-aux/move-if-change \
+ globals.tmp org/gnu/emacs/EmacsConfig.java
+
+# cf-stamp-1 is a phony target invoked in a second `make' instance after
+# all shared libraries are compiled, because the computation of
+# ALL_DEPENDENCIES cannot be postponed until that stage in this instance
+# of Make.
+cf-stamp: $(NDK_BUILD_SHARED) $(CROSS_LIBS)
+ $(AM_V_EMACSCONFIG) $(MAKE) cf-stamp-1
+ $(AM_V_at) touch $@
+$(CONFIG_FILE): cf-stamp; @true
+
+# Make all class files depend on R.java and EmacsConfig.java being
+# built.
+$(CLASS_FILES): $(RESOURCE_FILE) $(CONFIG_FILE)
.SUFFIXES: .java .class
$(CLASS_FILES) &: $(JAVA_FILES)
@@ -305,13 +373,23 @@ $(CLASS_FILES) &: $(JAVA_FILES)
# N.B. that find must be called all over again in case javac generated
# nested classes.
+ALL_CLASS_FILES = \
+ $(subst $$,\$$,$(shell find $(srcdir) -type f -name *.class))
+
+ifneq ($(builddir),$(srcdir))
+# If the build directory is distinct from the source directory, also
+# include generated class files located there.
+ALL_CLASS_FILES = $(ALL_CLASS_FILES) \
+ $(subst $$,\$$,$(shell find $(builddir) -type f -name *.class))
+endif
+
classes.dex: $(CLASS_FILES) $(if $(IS_D8_R8), $(srcdir)/proguard.conf)
$(AM_V_D8) $(D8) --classpath $(ANDROID_JAR) \
- $(subst $$,\$$,$(shell find $(srcdir) -type f \
- -name *.class)) --output $(builddir) \
+ $(ALL_CLASS_FILES) \
+ --output $(builddir) \
--min-api $(ANDROID_MIN_SDK) \
$(if $(filter false,$(ANDROID_DEBUGGABLE)),--release, \
- --debug) \
+ --debug) \
$(if $(IS_D8_R8),--pg-conf $(srcdir)/proguard.conf)
# When emacs.keystore expires, regenerate it with:
@@ -345,7 +423,8 @@ TAGS: $(ETAGS) $(tagsfiles)
$(AM_V_GEN) $(ETAGS) $(tagsfiles)
clean:
- rm -f *.apk emacs.apk-in *.dex *.unaligned *.class *.idsig
+ rm -f *.apk emacs.apk-in *.dex *.unaligned *.class *.idsig \
+ cf-stamp $(CONFIG_FILE)
rm -rf install-temp $(RESOURCE_FILE) TAGS
find . -name '*.class' $(FIND_DELETE)
diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java
index 567242f2ec3..9b3e60e1a84 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -321,39 +321,35 @@ public final class EmacsNative
static
{
- /* Older versions of Android cannot link correctly with shared
- libraries that link with other shared libraries built along
- Emacs unless all requisite shared libraries are explicitly
- loaded from Java.
-
- Every time you add a new shared library dependency to Emacs,
- please insert it here as well, before other shared libraries of
- which it might be a dependency. */
-
- libraryDeps = new String[] { "c++_shared", "gnustl_shared",
- "stlport_shared", "gabi++_shared",
- "png_emacs", "pcre_emacs",
- "selinux_emacs", "crypto_emacs",
- "packagelistparser_emacs",
- "gmp_emacs", "nettle_emacs",
- "p11-kit_emacs", "tasn1_emacs",
- "hogweed_emacs", "gnutls_emacs",
- "jpeg_emacs", "tiff_emacs",
- "icuuc_emacs", "xml2_emacs",
- "harfbuzz_emacs", "tree-sitter_emacs", };
+ /* A library search path misconfiguration prevents older versions of
+ Android from successfully loading application shared libraries
+ unless all requisite shared libraries provided by the application
+ are explicitly loaded from Java. The build process arranges that
+ EmacsConfig.EMACS_SHARED_LIBRARIES hold the names of each of
+ these libraries in the correct order, so load them now. */
+
+ libraryDeps = EmacsConfig.EMACS_SHARED_LIBRARIES;
for (String dependency : libraryDeps)
{
- try
- {
- System.loadLibrary (dependency);
- }
- catch (UnsatisfiedLinkError exception)
- {
- /* Ignore this exception. */
- }
+ /* Remove the "lib" prefix, if any. */
+ if (dependency.startsWith ("lib"))
+ dependency = dependency.substring (3);
+
+ /* If this library is provided by the operating system, don't
+ link to it. */
+ if (dependency.equals ("z")
+ || dependency.equals ("c")
+ || dependency.equals ("m")
+ || dependency.equals ("dl")
+ || dependency.equals ("log")
+ || dependency.equals ("android"))
+ continue;
+
+ System.loadLibrary (dependency);
}
+ /* At this point, it should be alright to load Emacs. */
System.loadLibrary ("emacs");
};
};
diff --git a/m4/ndk-build.m4 b/m4/ndk-build.m4
index abe06063ab0..2689ee34287 100644
--- a/m4/ndk-build.m4
+++ b/m4/ndk-build.m4
@@ -339,6 +339,16 @@ NDK_BUILD_NASM=
AS_IF([test "$ndk_ARCH" = "x86" || test "$ndk_ARCH" = "x86_64"],
[AC_CHECK_PROGS([NDK_BUILD_NASM], [nasm])])
+# Search for a suitable readelf binary, which is required to generate
+# the shared library list loaded on old Android systems.
+AC_PATH_PROGS([READELF], [readelf llvm-readelf $host_alias-readelf],
+ [], [$ndk_ranlib_search_path:$PATH])
+AS_IF([test -z "$READELF"],
+ [AC_MSG_ERROR([A suitable `readelf' utility cannot be located.
+Please verify that the Android NDK has been installed correctly,
+or install a functioning `readelf' yourself.])])
+NDK_BUILD_READELF="$READELF"
+
# Search for a C++ compiler. Upon failure, pretend the C compiler is a
# C++ compiler and use that instead.
@@ -644,6 +654,7 @@ AC_DEFUN_ONCE([ndk_CONFIG_FILES],
AC_SUBST([NDK_BUILD_CXX_LDFLAGS])
AC_SUBST([NDK_BUILD_ANY_CXX_MODULE])
AC_SUBST([NDK_BUILD_CFLAGS])
+ AC_SUBST([NDK_BUILD_READELF])
AC_CONFIG_FILES([$ndk_DIR/Makefile])
AC_CONFIG_FILES([$ndk_DIR/ndk-build.mk])