354 lines
17 KiB
Plaintext
354 lines
17 KiB
Plaintext
NDK BUILD SYSTEM IMPLEMENTATION
|
||
|
||
Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||
See the end of the file for license conditions.
|
||
|
||
Emacs implements ndk-build itself, because the version that comes with
|
||
the Android NDK is not easy to use from another Makefile, and keeps
|
||
accumulating incompatible changes.
|
||
|
||
The Emacs implementation of ndk-build consists of one m4 file:
|
||
|
||
m4/ndk-build.m4
|
||
|
||
four Makefiles in build-aux, run during configure:
|
||
|
||
build-aux/ndk-build-helper-1.mk
|
||
build-aux/ndk-build-helper-2.mk
|
||
build-aux/ndk-build-helper-3.mk
|
||
build-aux/ndk-build-helper.mk
|
||
|
||
one awk script in build-awx, run during configure:
|
||
|
||
build-aux/ndk-module-extract.awk
|
||
|
||
seven Makefiles in cross/ndk-build,
|
||
|
||
cross/ndk-build/ndk-build-shared-library.mk
|
||
cross/ndk-build/ndk-build-static-library.mk
|
||
cross/ndk-build/ndk-build-executable.mk
|
||
cross/ndk-build/ndk-clear-vars.mk
|
||
cross/ndk-build/ndk-prebuilt-shared-library.mk
|
||
cross/ndk-build/ndk-prebuilt-static-library.mk
|
||
cross/ndk-build/ndk-resolve.mk
|
||
|
||
and finally, two more Makefiles in cross/ndk-build, generated by
|
||
configure:
|
||
|
||
cross/ndk-build/Makefile (generated from cross/ndk-build/Makefile.in)
|
||
cross/ndk-build/ndk-build.mk (generated from cross/ndk-build/ndk-build.mk.in)
|
||
|
||
m4/ndk-build.m4 is a collection of macros which are used by the
|
||
configure script to set up the ndk-build system, look for modules, add
|
||
the appropriate options to LIBS and CFLAGS, and generate the Makefiles
|
||
necessary to build the rest of Emacs.
|
||
|
||
Immediately after determining the list of directories in which to look
|
||
for ``Android.mk'' files, the version and type of Android system being
|
||
built for, configure calls:
|
||
|
||
ndk_INIT([$android_abi], [$ANDROID_SDK], [cross/ndk-build])
|
||
|
||
This expands to a sequence of shell script that enumerates all of the
|
||
Android.mk files specified in "$with_ndk_path", sets up some shell
|
||
functions used by the rest of the ndk-build code run by the configure
|
||
script, and teaches the ndk-build system that the Makefiles to be
|
||
generated are found in the directory "cross/ndk-build/Makefile".
|
||
|
||
When configure is cross-compiling for Android, the macro
|
||
EMACS_CHECK_MODULES will expand to the macro ndk_CHECK_MODULES,
|
||
instead of pkg-config.m4's PKG_CHECK_MODULES. Thus, the following
|
||
code:
|
||
|
||
EMACS_CHECK_MODULES([PNG], [libpng >= 1.0.0])
|
||
|
||
will actually expand to:
|
||
|
||
ndk_CHECK_MODULES([PNG], [libpng >= 1.0.0], [HAVE_PNG=yes],
|
||
[HAVE_PNG=no])
|
||
|
||
which in turn expands to a sequence shell script that first invokes:
|
||
|
||
make -f build-aux/ndk-build-helper.mk
|
||
|
||
for each ``Android.mk'' file found by ndk_INIT, with the following
|
||
variables given to Make:
|
||
|
||
EMACS_SRCDIR=. # the source directory (in which configure is running)
|
||
BUILD_AUXDIR=$ndk_AUX_DIR # the build-aux directory
|
||
EMACS_ABI=$ndk_ABI # this is the $android_abi given to ndk_INIT
|
||
ANDROID_MAKEFILE="/opt/android/libpng/Android.mk"
|
||
ANDROID_MODULE_DIRECTORY="/opt/android/libpng"
|
||
NDK_BUILD_DIR="$ndk_DIR" # this is the directory given as to ndk_INIT
|
||
|
||
build-aux/ndk-build-helper.mk will then evaluate the contents
|
||
$(ANDROID_MAKEFILE), the ``Android.mk'' file, for the first time. The
|
||
purpose of this evaluation is to establish a list of packages (or
|
||
modules) provided by the ``Android.mk'' file, and the corresponding
|
||
Makefile targets and compiler and linker flags required to build and
|
||
link to those targets.
|
||
|
||
Before doing so, build-aux/ndk-build-helper.mk will define several
|
||
variables and functions required by all ``Android.mk'' files. The
|
||
most important of these are:
|
||
|
||
my-dir # the directory containing the Android.mk file.
|
||
BUILD_SHARED_LIBRARY # build-aux/ndk-build-helper-1.mk
|
||
BUILD_STATIC_LIBRARY # build-aux/ndk-build-helper-2.mk
|
||
BUILD_EXECUTABLE # build-aux/ndk-build-helper-3.mk
|
||
CLEAR_VARS # build-aux/ndk-build-helper-4.mk
|
||
|
||
Then, ``Android.mk'' will include $(CLEAN_VARS), possibly other
|
||
``Android.mk'' files, (to clear variables previously set), set several
|
||
variables describing each module to the ndk-build system, and include
|
||
one of $(BUILD_SHARED_LIBRARY), $(BUILD_STATIC_LIBRARY) and
|
||
$(BUILD_EXECUTABLE).
|
||
|
||
Each one of those three scripts will then read from the variables set
|
||
by ``Android.mk'', resolve dependencies, and print out some text
|
||
describing the module to Emacs. For example, the shared library
|
||
module "libpng" results in the following text being printed:
|
||
|
||
Building shared
|
||
libpng
|
||
/opt/android/libpng/png.c /opt/android/libpng/pngerror.c /opt/android/libpng/pngget.c /opt/android/libpng/pngmem.c /opt/android/libpng/pngpread.c /opt/android/libpng/pngread.c /opt/android/libpng/pngrio.c /opt/android/libpng/pngrtran.c /opt/android/libpng/pngrutil.c /opt/android/libpng/pngset.c /opt/android/libpng/pngtrans.c /opt/android/libpng/pngwio.c /opt/android/libpng/pngwrite.c /opt/android/libpng/pngwtran.c /opt/android/libpng/pngwutil.c
|
||
-I/opt/android/libpng
|
||
|
||
-L/opt/emacs/cross/ndk-build -l:libpng_emacs.so
|
||
libpng_emacs.so
|
||
End
|
||
|
||
The output is arranged as follows:
|
||
|
||
- The first line consists of the word ``Building'', followed by
|
||
either ``shared'', ``static'', or ``executable'', depending on
|
||
what type of module being built.
|
||
|
||
- The second line consists of the name of the module currently being
|
||
built.
|
||
|
||
- The third line consists of all of the source code files comprising
|
||
the module.
|
||
|
||
- The fourth line consists of the text that has to be added to
|
||
CFLAGS in order to find the includes associated with the module.
|
||
|
||
- The fifth line consists of the text that has to be added to LIBS
|
||
in order to link with this module and all of its dependencies.
|
||
|
||
- The sixth line consists of the Make targets (more on this later)
|
||
that will build the final shared object or library archive of this
|
||
module, along with all of its dependencies.
|
||
|
||
- The seventh line is either empty, or the name of a dependency on
|
||
the C++ standard library. This is used to determine whether or
|
||
not Emacs will include the C++ standard library in the application
|
||
package.
|
||
|
||
The output from Make is given to an awk script,
|
||
build-aux/ndk-module-extract.awk. This is responsible for parsing the
|
||
that output and filtering out modules other than what is being built:
|
||
|
||
awk -f build-aux/ndk-module-extract.awk MODULE=libpng
|
||
|
||
eventually generating this section of shell script:
|
||
|
||
module_name=libpng
|
||
module_kind=shared
|
||
module_src="/opt/android/libpng/png.c /opt/android/libpng/pngerror.c /opt/android/libpng/pngget.c /opt/android/libpng/pngmem.c /opt/android/libpng/pngpread.c /opt/android/libpng/pngread.c /opt/android/libpng/pngrio.c /opt/android/libpng/pngrtran.c /opt/android/libpng/pngrutil.c /opt/android/libpng/pngset.c /opt/android/libpng/pngtrans.c /opt/android/libpng/pngwio.c /opt/android/libpng/pngwrite.c /opt/android/libpng/pngwtran.c /opt/android/libpng/pngwutil.c"
|
||
module_includes="-I/opt/android/libpng"
|
||
module_cflags=""
|
||
module_ldflags=" -L/opt/emacs/cross/ndk-build -l:libpng_emacs.so"
|
||
module_target="libpng_emacs.so"
|
||
module_cxx_deps=""
|
||
module_imports=""
|
||
|
||
which is then evaluated by `configure'. Once the variable
|
||
`module_name' is set, configure appends the remaining
|
||
$(module_includes), $(module_cflags) and $(module_ldflags) to the
|
||
module's CFLAGS and LIBS variables, and appends the list of Makefile
|
||
targets specified to the variable NDK_BUILD_MODULES.
|
||
|
||
In some cases, an ``Android.mk'' file may chose to import a module
|
||
defined in ``--with-ndk-path'', but not defined inside its own
|
||
``Android.mk'' file. build-aux/ndk-build-helper.mk defines the
|
||
`import-module' function to add the modules being imported to a
|
||
variable, which is then printed out after ``ndk-build-helper.mk''
|
||
completes. For example, libxml2 imports the ``libicucc'' module,
|
||
which results in the following text being printed:
|
||
|
||
Building shared
|
||
libxml2
|
||
/home/oldosfan/libxml2/SAX.c /home/oldosfan/libxml2/entities.c /home/oldosfan/libxml2/encoding.c /home/oldosfan/libxml2/error.c /home/oldosfan/libxml2/parserInternals.c /home/oldosfan/libxml2/parser.c /home/oldosfan/libxml2/tree.c /home/oldosfan/libxml2/hash.c /home/oldosfan/libxml2/list.c /home/oldosfan/libxml2/xmlIO.c /home/oldosfan/libxml2/xmlmemory.c /home/oldosfan/libxml2/uri.c /home/oldosfan/libxml2/valid.c /home/oldosfan/libxml2/xlink.c /home/oldosfan/libxml2/debugXML.c /home/oldosfan/libxml2/xpath.c /home/oldosfan/libxml2/xpointer.c /home/oldosfan/libxml2/xinclude.c /home/oldosfan/libxml2/DOCBparser.c /home/oldosfan/libxml2/catalog.c /home/oldosfan/libxml2/globals.c /home/oldosfan/libxml2/threads.c /home/oldosfan/libxml2/c14n.c /home/oldosfan/libxml2/xmlstring.c /home/oldosfan/libxml2/buf.c /home/oldosfan/libxml2/xmlregexp.c /home/oldosfan/libxml2/xmlschemas.c /home/oldosfan/libxml2/xmlschemastypes.c /home/oldosfan/libxml2/xmlunicode.c /home/oldosfan/libxml2/xmlreader.c /home/oldosfan/libxml2/relaxng.c /home/oldosfan/libxml2/dict.c /home/oldosfan/libxml2/SAX2.c /home/oldosfan/libxml2/xmlwriter.c /home/oldosfan/libxml2/legacy.c /home/oldosfan/libxml2/chvalid.c /home/oldosfan/libxml2/pattern.c /home/oldosfan/libxml2/xmlsave.c /home/oldosfan/libxml2/xmlmodule.c /home/oldosfan/libxml2/schematron.c /home/oldosfan/libxml2/SAX.c /home/oldosfan/libxml2/entities.c /home/oldosfan/libxml2/encoding.c /home/oldosfan/libxml2/error.c /home/oldosfan/libxml2/parserInternals.c /home/oldosfan/libxml2/parser.c /home/oldosfan/libxml2/tree.c /home/oldosfan/libxml2/hash.c /home/oldosfan/libxml2/list.c /home/oldosfan/libxml2/xmlIO.c /home/oldosfan/libxml2/xmlmemory.c /home/oldosfan/libxml2/uri.c /home/oldosfan/libxml2/valid.c /home/oldosfan/libxml2/xlink.c /home/oldosfan/libxml2/debugXML.c /home/oldosfan/libxml2/xpath.c /home/oldosfan/libxml2/xpointer.c /home/oldosfan/libxml2/xinclude.c /home/oldosfan/libxml2/DOCBparser.c /home/oldosfan/libxml2/catalog.c /home/oldosfan/libxml2/globals.c /home/oldosfan/libxml2/threads.c /home/oldosfan/libxml2/c14n.c /home/oldosfan/libxml2/xmlstring.c /home/oldosfan/libxml2/buf.c /home/oldosfan/libxml2/xmlregexp.c /home/oldosfan/libxml2/xmlschemas.c /home/oldosfan/libxml2/xmlschemastypes.c /home/oldosfan/libxml2/xmlunicode.c /home/oldosfan/libxml2/xmlreader.c /home/oldosfan/libxml2/relaxng.c /home/oldosfan/libxml2/dict.c /home/oldosfan/libxml2/SAX2.c /home/oldosfan/libxml2/xmlwriter.c /home/oldosfan/libxml2/legacy.c /home/oldosfan/libxml2/chvalid.c /home/oldosfan/libxml2/pattern.c /home/oldosfan/libxml2/xmlsave.c /home/oldosfan/libxml2/xmlmodule.c /home/oldosfan/libxml2/schematron.c
|
||
|
||
|
||
-L/home/oldosfan/emacs-dev/emacs-android/cross/ndk-build -l:libxml2_emacs.so -l:libicuuc_emacs.so
|
||
libxml2_emacs.so libicuuc_emacs.so
|
||
End
|
||
Start Imports
|
||
libicuuc
|
||
End Imports
|
||
|
||
Upon encountering the ``Start Imports'' section,
|
||
build-aux/ndk-module-extract.awk collects all imports until it
|
||
encounters the line ``End Imports'', at which point it prints:
|
||
|
||
module_imports="libicuuc"
|
||
|
||
Then, if the list of imports is not empty, ndk_CHECK_MODULES
|
||
additionally calls itself for each import before appending the
|
||
module's own ``Android.mk'', ensuring that the module's imported
|
||
dependencies are included by $ndk_DIR/Makefile before itself.
|
||
|
||
Finally, immediately before generating src/Makefile.android, configure
|
||
expands:
|
||
|
||
ndk_CONFIG_FILES
|
||
|
||
to generate $ndk_DIR/Makefile and $ndk_DIR/ndk-build.mk.
|
||
|
||
Now, the $ndk_DIR directory is set up to build all modules upon which
|
||
depends, and $ndk_DIR/ndk-build.mk includes a list of files required
|
||
to link Emacs, along with the rules to chdir into $ndk_DIR in order to
|
||
build them.
|
||
|
||
$ndk_DIR/ndk-build.mk is included by cross/src/Makefile
|
||
(Makefile.android) and java/Makefile. It defines three different
|
||
variables:
|
||
|
||
NDK_BUILD_MODULES the file names of all modules to be built.
|
||
NDK_BUILD_STATIC absolute names of all library archives
|
||
to be built.
|
||
NDK_BUILD_SHARED absolute names of all shared libraries to
|
||
be built.
|
||
|
||
and then proceeds to define rules to build each of the modules in
|
||
$(NDK_BUILD_MODULES).
|
||
|
||
cross/src/Makefile arranges to have all dependencies of Emacs not
|
||
already built built before linking ``libemacs.so'' with them.
|
||
|
||
java/Makefile additionally arranges to have all shared object
|
||
dependencies built before the application package is built, which is
|
||
normally redundant because they should have already been built before
|
||
linking ``libemacs.so''.
|
||
|
||
Building the modules is performed through $ndk_DIR/Makefile, which
|
||
contains the actual implementation of the ``ndk-build'' build system.
|
||
First, it defines certain variables constant within the ``ndk-build''
|
||
build system, such as the files included by ``Android.mk'' to build
|
||
shared or static libraries, and CLEAR_VARS. The most important of
|
||
these are:
|
||
|
||
CLEAR_VARS cross/ndk-build/ndk-clear-vars.mk
|
||
BUILD_EXECUTABLE cross/ndk-build/ndk-build-executable.mk
|
||
BUILD_SHARED_LIBRARY cross/ndk-build/ndk-build-shared-library.mk
|
||
BUILD_STATIC_LIBRARY cross/ndk-build/ndk-build-static-library.mk
|
||
PREBUILT_SHARED_LIBRARY cross/ndk-build/ndk-prebuilt-shared-library.mk
|
||
PREBUILT_STATIC_LIBRARY cross/ndk-build/ndk-prebuilt-static-library.mk
|
||
|
||
Then, it loads each Emacs dependency's ``Android.mk'' file. For each
|
||
module defined there, ``Android.mk'' includes $(CLEAR_VARS) to unset
|
||
all variables specific to each module, and then includes
|
||
$(BUILD_SHARED_LIBRARY) or $(BUILD_STATIC_LIBRARY) for each shared or
|
||
static library module.
|
||
|
||
This results in cross/ndk-build/ndk-build-shared-library.mk or
|
||
cross/ndk-build/ndk-build-static-library being included, just like the
|
||
Makefiles in build-aux were inside the configure script.
|
||
|
||
Each one of those two scripts then defines rules to build all of the
|
||
object files associated with the module, and then link or archive
|
||
them. The name under which the module is linked is the same as the
|
||
Make target found on the sixth line of output from
|
||
build-aux/ndk-build-helper.mk.
|
||
|
||
In doing so, they both include the file ndk-resolve.mk.
|
||
ndk-resolve.mk is expected to recursively add all of the exported
|
||
CFLAGS and includes of any dependencies to the compiler and linker
|
||
command lines for the module being built.
|
||
|
||
When building a shared library module, ndk-resolve.mk is also expected
|
||
to define the variables NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) and
|
||
NDK_WHOLE_A_NAMES_$(LOCAL_MODULE), containing all static library
|
||
dependencies' archive files. They are to be linked in to the
|
||
resulting shared object file.
|
||
|
||
This is done by including cross/ndk-build/ndk-resolve.mk each time a
|
||
shared or static library module is going to be built. How is this
|
||
done?
|
||
|
||
First, ndk-resolve.mk saves the LOCAL_PATH, LOCAL_STATIC_LIBRARIES,
|
||
LOCAL_SHARED_LIBRARIES, LOCAL_EXPORT_CFLAGS and
|
||
LOCAL_EXPORT_C_INCLUDES from the module.
|
||
|
||
Next, ndk-resolve loops through the dependencies the module has
|
||
specified, appending its CFLAGS and includes to the command line for
|
||
the current module.
|
||
|
||
Then, that process is repeated for each such dependency which has not
|
||
already been resolved, until all dependencies have been resolved.
|
||
|
||
libpng is a very simple module, providing only a single shared object
|
||
module. This module is named libpng_emacs.so and is eventually built
|
||
and packaged into the library directory of the Emacs application
|
||
package. Now, let us look at a more complex module, libwebp:
|
||
|
||
|
||
|
||
When built with libwebp, Emacs depends on a single library,
|
||
libwebpdemux. This library is named ``libwebpdemux'' on Unix systems,
|
||
and that is the name by which it is found with pkg-config.
|
||
|
||
However, the library's module is only named ``webpdemux'' on Android.
|
||
When ndk_CHECK_MODULES begins to look for a module, it first tries to
|
||
see if its name is found in the variable `ndk_package_map', which was
|
||
set inside ndk_INIT. In this case, it finds the following word:
|
||
|
||
libwebpdemux:webpdemux
|
||
|
||
and immediately replaces ``libwebpdemux'' with ``webpdemux''.
|
||
|
||
Then, it locates the ``Android.mk'' file containing a static library
|
||
module named webpdemux and gives the output from
|
||
build-aux/ndk-build-helper.mk to the awk script, resulting in:
|
||
|
||
module_name=webpdemux
|
||
module_kind=static
|
||
module_src="/opt/android/webp/src/demux/anim_decode.c /opt/android/webp/src/demux/demux.c"
|
||
module_includes="-I/opt/android/webp/src"
|
||
module_cflags=""
|
||
module_ldflags=" cross/ndk-build/libwebpdemux.a cross/ndk-build/libwebp.a cross/ndk-build/libwebpdecoder_static.a "
|
||
module_target="libwebpdemux.a libwebp.a libwebpdecoder_static.a"
|
||
|
||
The attentive reader will notice that in addition to the
|
||
``libwebpdemux.a'' archive associated with the ``webpdemux'' library,
|
||
Emacs has been made to link with two additional libraries. This is
|
||
because the ``webpdemux'' module specifies a dependency on the
|
||
``webp'' module (defined in the same Android.mk).
|
||
build-aux/ndk-build-helper.mk resolved that dependency, noticing that
|
||
it in turn specified another dependency on ``webpdecoder_static'',
|
||
which in turn was added to the linker command line and list of targets
|
||
to build.
|
||
|
||
As a result, all three dependencies will be built and linked to Emacs,
|
||
instead of just the single ``webpdemux'' dependency that was
|
||
specified.
|
||
|
||
|
||
|
||
This file is part of GNU Emacs.
|
||
|
||
GNU Emacs is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
GNU Emacs is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|