]> git.eshelyaron.com Git - emacs.git/commitdiff
Generate Android shared library list automatically
authorPo Lu <luangruo@yahoo.com>
Mon, 22 Apr 2024 08:27:30 +0000 (16:27 +0800)
committerEshel Yaron <me@eshelyaron.com>
Mon, 22 Apr 2024 18:38:33 +0000 (20:38 +0200)
* .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.

(cherry picked from commit 3bcdf010a9f2576bac0d7f23af70fa9dff81ef95)

.gitignore
cross/Makefile.in
cross/ndk-build/ndk-build.mk.in
java/Makefile.in
java/org/gnu/emacs/EmacsNative.java
m4/ndk-build.m4

index 29c571a3dcb10e2e72c9160908328c4ba9380af1..4098e2210b5c5fb77c874c1a8885a18870158858 100644 (file)
@@ -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
 
index 1e8daea6f9139d6993117004d82b6474c4ddfc3c..575c6c4cb29569b41f6862464103d8e7c76c72e5 100644 (file)
@@ -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
index ea1be5af6f1e681144e4d67eb9d379eeba2d095b..9948e019e3be0dbef3a35dba18a0824e6cb34a8f 100644 (file)
@@ -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)))
index 7d732be8f91c0119df2a4576cf927fb26bea46d2..bd1938689d5bc1ada0453feb41cefe691edae572 100644 (file)
@@ -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)
 
index 567242f2ec3d6e43813516fa24fadab22ecdcfe1..9b3e60e1a84895b6ac59d917d10a0b793ea00138 100644 (file)
@@ -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");
   };
 };
index abe06063ab01ac9c2216798796047454f62da88f..2689ee3428780c7e8b6f54613baa876ee3b0d40d 100644 (file)
@@ -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])