# 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
-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)
# 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:
$(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)
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");
};
};
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.
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])