Commit 38d5b1de authored by Eric Stevenson's avatar Eric Stevenson Committed by Commit Bot

Generate GEN_JNI.java for every .apk

This is part of a better scheme for calling Java->Native
where all native methods are hidden behind interfaces,
and exist within a single "GEN_JNI.java" generated file.
See bug for more details.

Bug: 898261
Change-Id: I1fd7a9f162cb2853a2dfcd62ffcdebe909cb40e1
Reviewed-on: https://chromium-review.googlesource.com/c/1315808
Commit-Queue: Eric Stevenson <estevenson@chromium.org>
Reviewed-by: default avatarMisha Efimov <mef@chromium.org>
Reviewed-by: default avatarTibor Goldschwendt <tiborg@chromium.org>
Reviewed-by: default avatarEric Stevenson <estevenson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608614}
parent 475eac41
......@@ -5,6 +5,8 @@
import("//build/config/android/rules.gni")
import("//testing/test.gni")
_registration_header = "$target_gen_dir/sample_jni_registration.h"
generate_jni("jni_sample_header") {
sources = [
"java/src/org/chromium/example/jni_generator/SampleForAnnotationProcessor.java",
......@@ -49,11 +51,12 @@ source_set("jni_sample_native_side") {
shared_library("jni_sample_lib") {
sources = [
"sample_entry_point.cc",
_registration_header,
]
deps = [
":jni_sample_native_side",
":sample_jni_registration",
":sample_jni_apk__final_jni", # For registration_header
"//base",
]
}
......@@ -66,11 +69,7 @@ android_apk("sample_jni_apk") {
"//base:base_java",
]
shared_libraries = [ ":jni_sample_lib" ]
}
generate_jni_registration("sample_jni_registration") {
target = ":sample_jni_apk"
output = "$target_gen_dir/${target_name}.h"
jni_registration_header = _registration_header
}
# Serves to test that generated bindings compile properly.
......
......@@ -1437,21 +1437,12 @@ def GenerateJNIHeader(input_file, output_file, options):
print e
sys.exit(1)
if output_file:
WriteOutput(output_file, content)
with build_utils.AtomicOutput(output_file) as f:
f.write(content)
else:
print content
def WriteOutput(output_file, content):
if os.path.exists(output_file):
with open(output_file) as f:
existing_content = f.read()
if existing_content == content:
return
with open(output_file, 'w') as f:
f.write(content)
def GetScriptName():
script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
base_index = 0
......
......@@ -3,18 +3,20 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generate JNI registration entry points
"""Generates GEN_JNI.java and helper for manual JNI registration.
Creates a header file with two static functions: RegisterMainDexNatives() and
RegisterNonMainDexNatives(). Together, these will use manual JNI registration
to register all native methods that exist within an application."""
import argparse
import jni_generator
import multiprocessing
import os
import string
import sys
import zipfile
import jni_generator
from util import build_utils
......@@ -31,41 +33,50 @@ MERGEABLE_KEYS = [
]
def GenerateJNIHeader(java_file_paths, output_file, args):
"""Generate a header file including two registration functions.
def _Generate(java_file_paths, srcjar_path, header_path=None, namespace=''):
"""Generates files required to perform JNI registration.
Generates a srcjar containing a single class, GEN_JNI, that contains all
native method declarations.
Forward declares all JNI registration functions created by jni_generator.py.
Calls the functions in RegisterMainDexNatives() if they are main dex. And
calls them in RegisterNonMainDexNatives() if they are non-main dex.
Optionally generates a header file that provides functions
(RegisterMainDexNatives and RegisterNonMainDexNatives) to perform
JNI registration.
Args:
java_file_paths: A list of java file paths.
output_file: A relative path to output file.
args: All input arguments.
java_file_paths: A list of java file paths.
srcjar_path: Path to the GEN_JNI srcjar.
header_path: If specified, generates a header file in this location.
namespace: If specified, sets the namespace for the generated header file.
"""
# Without multiprocessing, script takes ~13 seconds for chrome_public_apk
# on a z620. With multiprocessing, takes ~2 seconds.
pool = multiprocessing.Pool()
paths = (p for p in java_file_paths if p not in args.no_register_java)
results = [d for d in pool.imap_unordered(_DictForPath, paths) if d]
results = [d for d in pool.imap_unordered(_DictForPath, java_file_paths) if d]
pool.close()
# Sort to make output deterministic.
results.sort(key=lambda d: d['FULL_CLASS_NAME'])
combined_dict = {}
for key in MERGEABLE_KEYS:
combined_dict[key] = ''.join(d.get(key, '') for d in results)
if header_path:
combined_dict = {}
for key in MERGEABLE_KEYS:
combined_dict[key] = ''.join(d.get(key, '') for d in results)
combined_dict['HEADER_GUARD'] = \
os.path.splitext(output_file)[0].replace('/', '_').upper() + '_'
combined_dict['NAMESPACE'] = args.namespace
combined_dict['HEADER_GUARD'] = \
os.path.splitext(header_path)[0].replace('/', '_').upper() + '_'
combined_dict['NAMESPACE'] = namespace
header_content = CreateFromDict(combined_dict)
if output_file:
jni_generator.WriteOutput(output_file, header_content)
else:
print header_content
header_content = CreateFromDict(combined_dict)
with build_utils.AtomicOutput(header_path) as f:
f.write(header_content)
with build_utils.AtomicOutput(srcjar_path) as f:
with zipfile.ZipFile(f, 'w') as srcjar:
# TODO(abenner): Write GEN_JNI.java here.
# build_utils.AddToZipHermetic(srcjar, 'org/chromium/base/GEN_JNI.java',
# data='$CONTENT')
pass
def _DictForPath(path):
......@@ -439,15 +450,22 @@ def main(argv):
arg_parser = argparse.ArgumentParser()
build_utils.AddDepfileOption(arg_parser)
arg_parser.add_argument('--sources_files',
help='A list of .sources files which contain Java '
'file paths. Must be used with --output.')
arg_parser.add_argument('--output',
help='The output file path.')
arg_parser.add_argument('--no_register_java',
default=[],
help='A list of Java files which should be ignored '
'by the parser.')
arg_parser.add_argument(
'--sources-files',
required=True,
help='A list of .sources files which contain Java '
'file paths.')
arg_parser.add_argument(
'--header-path', help='Path to output header file (optional).')
arg_parser.add_argument(
'--srcjar-path',
required=True,
help='Path to output srcjar for GEN_JNI.java.')
arg_parser.add_argument(
'--sources-blacklist',
default=[],
help='A list of Java files which should be ignored '
'by the parser.')
arg_parser.add_argument('--namespace',
default='',
help='Namespace to wrap the registration functions '
......@@ -455,21 +473,25 @@ def main(argv):
args = arg_parser.parse_args(build_utils.ExpandFileArgs(argv[1:]))
args.sources_files = build_utils.ParseGnList(args.sources_files)
if not args.sources_files:
print '\nError: Must specify --sources_files.'
return 1
java_file_paths = []
for f in args.sources_files:
# java_file_paths stores each Java file path as a string.
java_file_paths += build_utils.ReadSourcesList(f)
output_file = args.output
GenerateJNIHeader(java_file_paths, output_file, args)
java_file_paths += [
p for p in build_utils.ReadSourcesList(f)
if p not in args.sources_blacklist
]
_Generate(
java_file_paths,
args.srcjar_path,
header_path=args.header_path,
namespace=args.namespace)
if args.depfile:
build_utils.WriteDepfile(args.depfile, output_file,
args.sources_files + java_file_paths,
add_pydeps=False)
build_utils.WriteDepfile(
args.depfile,
args.srcjar_path,
args.sources_files + java_file_paths,
add_pydeps=False)
if __name__ == '__main__':
......
......@@ -1032,7 +1032,8 @@ def main(argv):
# TODO(tiborg): Remove creation of JNI info for type group and java_library
# once we can generate the JNI registration based on APK / module targets as
# opposed to groups and libraries.
if is_apk_or_module_target or options.type in ('group', 'java_library'):
if is_apk_or_module_target or options.type in (
'group', 'java_library', 'junit_binary'):
config['jni'] = {}
all_java_sources = [c['java_sources_file'] for c in all_library_deps
if 'java_sources_file' in c]
......
......@@ -386,25 +386,25 @@ if (enable_java_templates) {
# Declare a jni registration target.
#
# This target generates a header file calling JNI registration functions
# created by generate_jni and generate_jar_jni.
# This target generates a srcjar containing a copy of GEN_JNI.java, which has
# the native methods of all dependent java files. It can also create a .h file
# for use with manual JNI registration.
#
# See base/android/jni_generator/jni_registration_generator.py for more info
# about the format of the header file.
#
# Variables
# target: The Apk target to generate registrations for.
# output: Path to the generated .h file.
# exception_files: List of .java files that should be ignored when searching
# for native methods. (optional)
# target: The Apk target to use for the java sources list.
# header_output: Path to the generated .h file (optional).
# sources_blacklist: List of .java files that should be skipped. (optional)
# namespace: Registration functions will be wrapped into this. (optional)
#
# Example
# generate_jni_registration("chrome_jni_registration") {
# target = ":chrome_public_apk"
# output = "$root_gen_dir/chrome/browser/android/${target_name}.h"
# exception_files = [
# "//base/android/java/src/org/chromium/base/library_loader/Linker.java",
# header_output = "$target_gen_dir/$target_name.h"
# sources_blacklist = [
# "//path/to/Exception.java",
# ]
# }
template("generate_jni_registration") {
......@@ -413,6 +413,7 @@ if (enable_java_templates) {
_build_config = get_label_info(invoker.target, "target_gen_dir") + "/" +
get_label_info(invoker.target, "name") + ".build_config"
_rebased_build_config = rebase_path(_build_config, root_build_dir)
_srcjar_output = "$target_gen_dir/$target_name.srcjar"
script = "//base/android/jni_generator/jni_registration_generator.py"
deps = [
......@@ -422,22 +423,29 @@ if (enable_java_templates) {
_build_config,
]
outputs = [
invoker.output,
_srcjar_output,
]
depfile = "$target_gen_dir/$target_name.d"
args = [
# This is a list of .sources files.
"--sources_files=@FileArg($_rebased_build_config:jni:all_source)",
"--output",
rebase_path(invoker.output, root_build_dir),
"--sources-files=@FileArg($_rebased_build_config:jni:all_source)",
"--srcjar-path",
rebase_path(_srcjar_output, root_build_dir),
"--depfile",
rebase_path(depfile, root_build_dir),
]
if (defined(invoker.exception_files)) {
_rebase_exception_java_files =
rebase_path(invoker.exception_files, root_build_dir)
args += [ "--no_register_java=$_rebase_exception_java_files" ]
if (defined(invoker.header_output)) {
outputs += [ invoker.header_output ]
args += [
"--header-path",
rebase_path(invoker.header_output, root_build_dir),
]
}
if (defined(invoker.sources_blacklist)) {
_rebase_sources_blacklist =
rebase_path(invoker.sources_blacklist, root_build_dir)
args += [ "--sources-blacklist=$_rebase_sources_blacklist" ]
}
if (defined(invoker.namespace)) {
args += [ "--namespace=${invoker.namespace}" ]
......@@ -1266,6 +1274,12 @@ if (enable_java_templates) {
}
}
_jni_srcjar_target = "${target_name}__final_jni"
_outer_target_name = target_name
generate_jni_registration(_jni_srcjar_target) {
target = ":$_outer_target_name"
}
java_library_impl(_java_binary_target_name) {
forward_variables_from(invoker, "*", [ "deps" ])
type = "junit_binary"
......@@ -1285,6 +1299,7 @@ if (enable_java_templates) {
srcjar_deps = []
}
srcjar_deps += [
":$_jni_srcjar_target",
":$_prepare_resources_target",
# This dep is required for any targets that depend on //base:base_java.
......@@ -1730,6 +1745,7 @@ if (enable_java_templates) {
jar_excluded_patterns = []
}
jar_excluded_patterns += [
"*/GEN_JNI.class",
"*/R.class",
"*/R\$*.class",
"*/Manifest.class",
......@@ -1866,6 +1882,13 @@ if (enable_java_templates) {
# generate_buildconfig_java: If defined and false, skip generating the
# BuildConfig java class describing the build configuration. The default
# is true for non-test APKs.
# generate_final_jni: If defined and false, skip generating the
# GEN_JNI srcjar.
# jni_registration_header: If specified, causes the
# ${target_name}__final_jni target to additionally output a
# header file to this path for use with manual JNI registration.
# jni_sources_blacklist: List of source path to exclude from the
# final_jni step.
# firebase_app_id: The value for BuildConfig.FIREBASE_APP_ID (optional).
# Identifier is sent with crash reports to enable Java stack deobfuscation.
# aapt_locale_whitelist: If set, all locales not in this list will be
......@@ -2089,6 +2112,12 @@ if (enable_java_templates) {
_generate_buildconfig_java = invoker.generate_buildconfig_java
}
# JNI generation usually goes hand-in-hand with buildconfig generation.
_generate_final_jni = _generate_buildconfig_java
if (defined(invoker.generate_final_jni)) {
_generate_final_jni = invoker.generate_final_jni
}
_proguard_enabled =
defined(invoker.proguard_enabled) && invoker.proguard_enabled
if (_proguard_enabled) {
......@@ -2305,7 +2334,7 @@ if (enable_java_templates) {
}
}
java_cpp_template("${_template_name}__native_libraries_java") {
java_cpp_template("${_template_name}__native_libraries_srcjar") {
package_path = "org/chromium/base/library_loader"
sources = [
"//base/android/java/templates/NativeLibraries.template",
......@@ -2344,7 +2373,7 @@ if (enable_java_templates) {
defines += [ "ENABLE_CHROMIUM_LINKER_TESTS" ]
}
}
_srcjar_deps += [ ":${_template_name}__native_libraries_java" ]
_srcjar_deps += [ ":${_template_name}__native_libraries_srcjar" ]
}
_extra_native_libs = []
......@@ -2361,7 +2390,7 @@ if (enable_java_templates) {
}
if (_generate_buildconfig_java) {
generate_build_config_srcjar("${_template_name}__build_config_java") {
generate_build_config_srcjar("${_template_name}__build_config_srcjar") {
forward_variables_from(invoker, [ "firebase_app_id" ])
use_final_fields = true
build_config = _build_config
......@@ -2374,7 +2403,20 @@ if (enable_java_templates) {
":$_build_config_target",
]
}
_srcjar_deps += [ ":${_template_name}__build_config_java" ]
_srcjar_deps += [ ":${_template_name}__build_config_srcjar" ]
}
if (_generate_final_jni) {
generate_jni_registration("${_template_name}__final_jni") {
target = ":$_template_name"
if (defined(invoker.jni_registration_header)) {
header_output = invoker.jni_registration_header
}
if (defined(invoker.jni_sources_blacklist)) {
sources_blacklist = invoker.jni_sources_blacklist
}
}
_srcjar_deps += [ ":${_template_name}__final_jni" ]
}
_java_target = "${_template_name}__java"
......@@ -2870,9 +2912,12 @@ if (enable_java_templates) {
"final_apk_path",
"firebase_app_id",
"generate_buildconfig_java",
"generate_final_jni",
"input_jars_paths",
"java_files",
"javac_args",
"jni_registration_header",
"jni_sources_blacklist",
"keystore_name",
"keystore_password",
"keystore_path",
......@@ -2974,10 +3019,14 @@ if (enable_java_templates) {
"enable_chromium_linker_tests",
"enable_multidex",
"firebase_app_id",
"generate_buildconfig_java",
"generate_final_jni",
"input_jars_paths",
"is_base_module",
"java_files",
"javac_args",
"jni_registration_header",
"jni_sources_blacklist",
"load_library_from_apk",
"loadable_modules",
"min_sdk_version",
......
......@@ -49,14 +49,13 @@ trichrome_chrome_android_manifest =
app_hooks_impl = "java/src/org/chromium/chrome/browser/AppHooksImpl.java"
# Exclude it from JNI registration if VR is not enabled.
jni_exception_files = []
chrome_jni_sources_blacklist = []
if (!enable_vr) {
jni_exception_files += [ "//chrome/android/java/src/org/chromium/chrome/browser/vr/VrModuleProvider.java" ]
chrome_jni_sources_blacklist += [ "//chrome/android/java/src/org/chromium/chrome/browser/vr/VrModuleProvider.java" ]
}
chrome_jni_registration_header =
"$root_build_dir/gen/chrome/browser/android/chrome_jni_registration.h"
chrome_jni_for_test_registration_header = "$root_build_dir/gen/chrome/browser/android/chrome_jni_for_test_registration.h"
chrome_sync_shell_jni_registration_header = "$root_build_dir/gen/chrome/browser/android/chrome_sync_shell_jni_registration.h"
jinja_template("chrome_public_android_manifest") {
input = "java/AndroidManifest.xml"
......@@ -1093,8 +1092,8 @@ chrome_shared_library("libchromefortest") {
if (current_toolchain == default_toolchain) {
generate_jni_registration("chrome_jni_registration") {
target = ":chrome_public_base_module_java"
output = chrome_jni_registration_header
exception_files = jni_exception_files
header_output = chrome_jni_registration_header
sources_blacklist = chrome_jni_sources_blacklist
}
# The test apks do not use chromium linker, but using manual JNI registration
......@@ -1102,8 +1101,8 @@ if (current_toolchain == default_toolchain) {
generate_jni_registration("chrome_jni_for_test_registration") {
testonly = true
target = ":chrome_public_base_module_java_for_test"
output = chrome_jni_for_test_registration_header
exception_files = jni_exception_files
header_output = chrome_jni_for_test_registration_header
sources_blacklist = chrome_jni_sources_blacklist
}
# This template instantiates targets responsible for generating pak
......@@ -1426,6 +1425,7 @@ template("chrome_public_apk_or_module_tmpl") {
[
"apk_name",
"is_base_module",
"jni_registration_header",
"is_modern",
"module_name",
"target_type",
......
......@@ -213,7 +213,7 @@ if (enable_arcore) {
if (current_toolchain == default_toolchain) {
generate_jni_registration("jni_registration") {
target = ":java"
output = "$target_gen_dir/${target_name}.h"
header_output = "$target_gen_dir/${target_name}.h"
namespace = "vr"
}
}
......
......@@ -13,6 +13,8 @@ import("//third_party/netty4/netty4.gni")
import("//third_party/protobuf/proto_library.gni")
import("//url/features.gni")
_jni_registration_header = "$target_gen_dir/cronet_jni_registration.h"
declare_args() {
# In integrated mode, CronetEngine will use the shared network task runner by
# other Chromium-based clients like webview without self-initialization.
......@@ -38,16 +40,19 @@ generate_jni("cronet_jni_headers") {
}
generate_jni_registration("cronet_jni_registration") {
target = ":cronet_jni_apk"
output = "$root_gen_dir/components/cronet/android/${target_name}.h"
exception_files = [
target = ":cronet_impl_all_java"
header_output = _jni_registration_header
sources_blacklist = [
"//base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
"//base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
"//base/android/java/src/org/chromium/base/SysUtils.java",
]
}
java_library("cronet_jni_registration_java") {
srcjar_deps = [ ":cronet_jni_registration" ]
}
java_cpp_enum("effective_connection_type_java") {
sources = [
"//net/nqe/effective_connection_type.h",
......@@ -199,6 +204,7 @@ source_set("cronet_static") {
"//components/cronet/android/metrics_util.h",
"//components/cronet/android/url_request_error.cc",
"//components/cronet/android/url_request_error.h",
_jni_registration_header,
]
if (integrated_mode) {
......@@ -411,17 +417,6 @@ java_group("cronet_impl_all_java") {
]
}
# This target exists only to provide input to the generate_jni_registration
# target previously declared. It contains all Java with JNI declarations.
android_apk("cronet_jni_apk") {
apk_name = "CronetJni"
android_manifest = "sample/AndroidManifest.xml"
deps = [
":cronet_impl_all_java",
"//third_party/android_deps:android_support_v7_appcompat_java",
]
}
android_resources("cronet_sample_apk_resources") {
resource_dirs = [ "sample/res" ]
android_manifest = "sample/AndroidManifest.xml"
......@@ -686,6 +681,7 @@ action("extract_cronet_native_jars") {
deps = [
":cronet_impl_native_base_java",
":cronet_jni_registration_java",
]
deps += cronet_impl_native_java_deps_to_package
......@@ -1375,6 +1371,7 @@ if (!is_component_build) {
"//url:url_java",
]
srcjar_deps = cronet_impl_native_java_srcjar_deps + [
":cronet_jni_registration",
"//base:base_android_java_enums_srcjar",
"//net/android:net_android_java_enums_srcjar",
"//net/android:net_errors_java",
......
......@@ -244,6 +244,8 @@ if (current_cpu != "x64") {
_linker_test_apk_target_name = "chromium_linker_test_apk__apk"
_linker_test_apk_test_runner_target_name =
"chromium_linker_test_apk__test_runner_script"
_linker_test_jni_registration_header =
"$target_gen_dir/linker_test_apk/linker_test_jni_registration.h"
android_apk(_linker_test_apk_target_name) {
testonly = true
......@@ -267,6 +269,7 @@ if (current_cpu != "x64") {
shared_libraries = [ ":linker_test" ]
use_chromium_linker = true
enable_chromium_linker_tests = true
jni_registration_header = _linker_test_jni_registration_header
}
test_runner_script(_linker_test_apk_test_runner_target_name) {
......@@ -289,11 +292,12 @@ if (current_cpu != "x64") {
sources = [
"linker_test_apk/chromium_linker_test_android.cc",
"linker_test_apk/chromium_linker_test_linker_tests.cc",
_linker_test_jni_registration_header,
]
deps = [
":${_linker_test_apk_target_name}__final_jni",
":linker_test_jni_headers",
":linker_test_jni_registration",
"//content/shell:content_shell_lib",
# Required to include "content/public/browser/android/compositor.h"
......@@ -319,13 +323,6 @@ if (current_cpu != "x64") {
"linker_test_apk/src/org/chromium/chromium_linker_test_apk/LinkerTests.java",
]
}
generate_jni_registration("linker_test_jni_registration") {
testonly = true
target = ":chromium_linker_test_apk__apk"
output =
"$root_gen_dir/content/shell/android/linker_test_apk/${target_name}.h"
}
}
android_library("content_shell_browsertests_java") {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment