From 34a1d86372796f414fb5923d83a7e369ee1acf43 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 22 Apr 2024 16:27:30 +0800 Subject: [PATCH] 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) : 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 | 4 ++ cross/Makefile.in | 2 +- cross/ndk-build/ndk-build.mk.in | 1 + java/Makefile.in | 91 +++++++++++++++++++++++++++-- java/org/gnu/emacs/EmacsNative.java | 52 ++++++++--------- m4/ndk-build.m4 | 11 ++++ 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]) -- 2.39.5