Bug 1378410 - 2. Generate JNI bindings using Python script; r=nalexander
Generate JNI bindings through a Python script using GENERATED_FILES. The
script calls the AnnotationProcessor Java program to generate the
bindings, and then compares the new version against the existing in-tree
version. Comparison is done after preprocessing to accommodate features
hidden by build flags.
MozReview-Commit-ID: CmBdpjEXc0W
--- a/mobile/android/base/generate_build_config.py
+++ b/mobile/android/base/generate_build_config.py
@@ -17,17 +17,19 @@ transition to Gradle.
'''
from __future__ import (
print_function,
unicode_literals,
)
from collections import defaultdict
+from io import BytesIO, StringIO
import os
+import subprocess
import sys
import buildconfig
from mozbuild import preprocessor
from mozbuild.android_version_code import android_version_code
@@ -144,8 +146,73 @@ def generate_java(output_file, input_fil
def generate_android_manifest(output_file, input_filename):
includes = preprocessor.preprocess(includes=[input_filename],
defines=_defines(),
output=output_file,
marker='#')
includes.add(os.path.join(buildconfig.topobjdir, 'buildid.h'))
return includes
+
+def generate_jni_bindings(output_file, ap_jar, *args):
+ # Jar names and flags are separated by '-'.
+ dash = args.index('-')
+ target_jars = args[:dash]
+ classpath, srcpath, prefix, update_cmd = args[dash + 1:]
+
+ # Generate new JNI bindings.
+ CONFIG = buildconfig.substs
+ sdk_jar = os.path.join(CONFIG['ANDROID_SDK'], 'android.jar')
+ subprocess.check_call((
+ CONFIG['JAVA'],
+ '-cp', ':'.join([ap_jar, sdk_jar, classpath]),
+ 'org.mozilla.gecko.annotationProcessors.AnnotationProcessor',
+ prefix,
+ ) + target_jars)
+
+ # Preprocess bindings and compare new version against existing version.
+ DEFINES = _defines()
+ # The generated files go through two preprocessing passes -- the current
+ # pass when the generated files are compared, and another pass when the
+ # generated files are later compiled. Define MOZ_PREPROCESSOR here so we
+ # can distinguish the two passes inside the generated files.
+ DEFINES['MOZ_PREPROCESSOR'] = 1
+
+ def preprocess(files):
+ with StringIO() if isinstance('', str) else BytesIO() as output:
+ preprocessor.preprocess(includes=files,
+ defines=DEFINES,
+ output=output,
+ marker='#')
+ return output.getvalue()
+
+ new_files = [prefix + suffix for suffix in (
+ 'JNIWrappers.cpp',
+ 'JNIWrappers.h',
+ 'JNINatives.h',
+ )]
+ src_files = [os.path.join(srcpath, f) for f in new_files]
+
+ if not all(os.path.exists(f) for f in new_files):
+ raise Exception('Not all files generated')
+
+ try:
+ if preprocess(new_files) == preprocess(src_files):
+ # Preprocessed new version matches preprocessed existing version.
+ return
+ except:
+ pass
+
+ sys.stderr.write(
+ ' \n'
+ '*****************************************************\n'
+ '*** ERROR: The generated JNI code has changed ***\n'
+ '* To update generated code in the tree, please run *\n'
+ ' \n'
+ ' make -C {curdir} {target}\n'
+ ' \n'
+ '* Repeat the build, and check in any changes. *\n'
+ '*****************************************************\n'
+ ' \n'
+ .format(curdir=os.path.abspath(os.path.curdir),
+ target=update_cmd))
+ sys.stderr.flush()
+ raise Exception('JNI binding mismatch')
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -1819,8 +1819,47 @@ if CONFIG['MOZ_ANDROID_HLS_SUPPORT']:
'util/XmlPullParserUtil.java',
'video/AvcConfig.java',
'video/ColorInfo.java',
'video/HevcConfig.java',
'video/MediaCodecVideoRenderer.java',
'video/VideoFrameReleaseTimeHelper.java',
'video/VideoRendererEventListener.java',
]]
+
+# Generate separate JNI bindings for GeckoView and for Fennec.
+def generate_jni_bindings(generated_files, srcpath, prefix, update_cmd, jars):
+ ap_jar = '!/build/annotationProcessors/annotationProcessors.jar'
+ target_jars = ['!' + jar.name + '.jar' for jar in jars]
+
+ # classpath includes the target jars' dependencies.
+ classpath = ':'.join(set().union(*(jar.extra_jars for jar in jars)))
+
+ # Use a dummy file as the "output" file because our model for generating
+ # JNI bindings doesn't exactly match the build system's expectation for
+ # GENERATED_FILES. In particular, if we write to the output file outside of
+ # the Python file object, which is the case when using the annotation
+ # processor, the build system can get confused and end up deleting the
+ # output file. Using a dummy file avoids this issue.
+ output = prefix + '.bindings'
+
+ generated_files += [output]
+ generated_files[output].script = 'generate_build_config.py:generate_jni_bindings'
+ generated_files[output].inputs = [ap_jar] + target_jars
+ # Inputs and flags are passed to the script together, so use '-' as a separator.
+ generated_files[output].flags = ['-', classpath, srcpath, prefix, update_cmd]
+ generated_files[output].tier = 'libs'
+
+# Don't perform binding generation when building under gradle because we don't
+# have the local jar files. Bug 1384312 will add support for binding generation
+# under gradle. In addition, don't perform binding generation in artifact mode,
+# because we never use the generated C++ bindings in artifact mode.
+if not CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE'] and not CONFIG['MOZ_ARTIFACT_BUILDS']:
+ generate_jni_bindings(GENERATED_FILES, TOPSRCDIR + '/widget/android',
+ 'Generated', 'update-generated-wrappers', [
+ gujar, # 'gecko-util'
+ gvjar, # 'gecko-view'
+ ])
+
+ generate_jni_bindings(GENERATED_FILES, TOPSRCDIR + '/widget/android/fennec',
+ 'Fennec', 'update-fennec-wrappers', [
+ gbjar, # 'gecko-browser'
+ ])