blob: aad9d3f4d2e1474fe4091ace323a9641d5a78f8c [file] [log] [blame]
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/android/config.gni")
import("//build/config/compiler/compiler.gni")
_JAVAP_PATH = "//third_party/jdk/current/bin/javap"
declare_args() {
# Enables JNI multiplexing to reduce JNI native methods overhead.
allow_jni_multiplexing = false
# Use hashed symbol names to reduce JNI symbol overhead.
use_hashed_jni_names = !is_java_debug
}
# Use a dedicated include dir so that files can #include headers from other
# toolchains without affecting non-JNI #includes.
if (target_os == "android") {
jni_headers_dir = "$root_build_dir/gen/jni_headers"
} else {
# Chrome OS builds cannot share gen/ directories because is_android=false
# within default_toolchain.
jni_headers_dir = "$root_gen_dir/jni_headers"
}
_jni_zero_dir = "//third_party/jni_zero"
template("jni_sources_list") {
generated_file(target_name) {
forward_variables_from(invoker,
TESTONLY_AND_VISIBILITY + [
"deps",
"walk_keys",
])
outputs = [ invoker.output ]
data_keys = [ "jni_source_files" ]
rebase = root_build_dir
metadata = {
# This target is just collecting source files used - this is not a
# legitimate dependency.
shared_libraries_barrier = []
}
}
}
template("_invoke_jni_zero") {
action(target_name) {
forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
script = "//third_party/jni_zero/jni_zero.py"
if (!defined(inputs)) {
inputs = []
}
inputs += rebase_path([
"codegen/placeholder_gen_jni_java.py",
"codegen/proxy_impl_java.py",
"common.py",
"java_lang_classes.py",
"java_types.py",
"jni_generator.py",
"jni_registration_generator.py",
"jni_zero.py",
"parse.py",
"proxy.py",
],
".",
_jni_zero_dir)
}
}
# Declare a jni registration target.
#
# 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.
#
# The script does not scan any generated sources (those within .srcjars, or
# within root_build_dir). This could be fixed by adding deps & logic to scan
# .srcjars, but isn't currently needed.
#
# See third_party/jni_zero/jni_registration_generator.py for more info
# about the format of the header file.
#
# Variables
# java_targets: List of android_* targets that comprise your app.
# native_deps: List of shared_library targets that comprise your app.
# manual_jni_registration: Manually do JNI registration - required for feature
# splits which provide their own native library. (optional)
# namespace: Registration functions will be wrapped into this. (optional)
# require_native_mocks: Enforce that any native calls using
# org.chromium.base.annotations.NativeMethods must have a mock set
# (optional).
# enable_native_mocks: Allow native calls using
# org.chromium.base.annotations.NativeMethods to be mocked in tests
# (optional).
#
# Example
# generate_jni_registration("chrome_jni_registration") {
# java_targets = [ ":chrome_public_apk" ]
# manual_jni_registration = false
# }
template("generate_jni_registration") {
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
if (defined(invoker.native_deps)) {
_native_sources_list = "$target_gen_dir/$target_name.nativesources.txt"
jni_sources_list("${target_name}__native_sources") {
deps = invoker.native_deps
output = _native_sources_list
}
}
_java_sources_list = "$target_gen_dir/$target_name.javasources.txt"
jni_sources_list("${target_name}__java_sources") {
deps = invoker.java_targets
output = _java_sources_list
# When apk or bundle module targets are uses, do not pull metadata from
# their native library deps.
walk_keys = [ "java_walk_keys" ]
}
_invoke_jni_zero(target_name) {
# Cannot depend on jni_sources_list targets since they likely depend on
# this target via srcjar_deps. Depfiles are used to add the dep instead.
deps = []
_srcjar_output = "$target_gen_dir/$target_name.srcjar"
outputs = [ _srcjar_output ]
depfile = "$target_gen_dir/$target_name.d"
java_target_deps = []
if (defined(invoker.java_targets)) {
foreach(java_targets_dep, invoker.java_targets) {
java_target_deps +=
[ get_label_info(java_targets_dep, "label_no_toolchain") ]
}
}
metadata = {
java_deps = java_target_deps
}
args = [
"generate-final",
"--srcjar-path",
rebase_path(_srcjar_output, root_build_dir),
"--depfile",
rebase_path(depfile, root_build_dir),
"--java-sources-file",
rebase_path(_java_sources_list, root_build_dir),
]
if (defined(_native_sources_list)) {
args += [
"--native-sources-file",
rebase_path(_native_sources_list, root_build_dir),
]
}
if (defined(invoker.include_testonly)) {
_include_testonly = invoker.include_testonly
} else {
_include_testonly = defined(testonly) && testonly
}
if (_include_testonly) {
args += [ "--include-test-only" ]
}
if (use_hashed_jni_names) {
args += [ "--use-proxy-hash" ]
}
if (defined(invoker.enable_native_mocks) && invoker.enable_native_mocks) {
args += [ "--enable-proxy-mocks" ]
if (defined(invoker.require_native_mocks) &&
invoker.require_native_mocks) {
args += [ "--require-mocks" ]
}
}
if (defined(invoker.remove_uncalled_jni) && invoker.remove_uncalled_jni) {
args += [ "--remove-uncalled-methods" ]
}
if (defined(invoker.add_stubs_for_missing_jni) &&
invoker.add_stubs_for_missing_jni) {
args += [ "--add-stubs-for-missing-native" ]
}
_manual_jni_registration = defined(invoker.manual_jni_registration) &&
invoker.manual_jni_registration
_enable_jni_multiplexing = defined(invoker.enable_jni_multiplexing) &&
invoker.enable_jni_multiplexing
if (_manual_jni_registration) {
args += [ "--manual-jni-registration" ]
}
if (_enable_jni_multiplexing) {
args += [ "--enable-jni-multiplexing" ]
}
if (_manual_jni_registration || _enable_jni_multiplexing) {
_subdir = rebase_path(target_gen_dir, root_gen_dir)
_jni_header_output =
"$jni_headers_dir/$_subdir/${target_name}_generated.h"
outputs += [ _jni_header_output ]
args += [
"--header-path",
rebase_path(_jni_header_output, root_build_dir),
]
public_configs = [
# This gives targets depending on this registration access to our generated header.
"//third_party/jni_zero:jni_include_dir",
# For now, we unconditionally use chromium's base. We could move this
# to depend on an invoker variable like we do with generate_jni_impl
# if we want this to be configurable.
"//third_party/jni_zero:use_chromium_base_define",
]
}
if (defined(invoker.namespace)) {
args += [ "--namespace=${invoker.namespace}" ]
}
if (defined(invoker.module_name)) {
args += [ "--module-name=${invoker.module_name}" ]
}
}
}
# JNI target implementation. See generate_jni or generate_jar_jni for usage.
template("generate_jni_impl") {
public_configs = []
if (!defined(invoker.use_chromium_base) || invoker.use_chromium_base) {
public_configs += [ "//third_party/jni_zero:use_chromium_base_define" ]
}
_jni_zero_action_target_name = target_name + "__action"
if (current_toolchain != default_toolchain && target_os == "android") {
# Rather than regenerating .h files in secondary toolchains, re-use the
# ones from the primary toolchain by depending on it and adding the
# root gen directory to the include paths.
# https://crbug.com/1369398
group(target_name) {
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
not_needed(invoker, "*")
public_configs +=
[ "//third_party/jni_zero:jni_include_dir($default_toolchain)" ]
# Depending on the action name to avoid cross-toolchain native deps.
public_deps = [ ":$_jni_zero_action_target_name($default_toolchain)" ]
deps = [ "//third_party/jni_zero:jni_zero_utils" ]
metadata = {
shared_libraries_barrier = []
}
}
} else {
_final_target_name = target_name
_invoke_jni_zero(_jni_zero_action_target_name) {
_subdir = rebase_path(target_gen_dir, root_gen_dir)
_jni_output_dir = "$jni_headers_dir/$_subdir/$_final_target_name"
if (defined(invoker.jni_generator_include)) {
_jni_generator_include = invoker.jni_generator_include
_jni_generator_include_deps = []
} else {
_jni_generator_include = "//third_party/jni_zero/jni_zero_helper.h"
_jni_generator_include_deps = [
# Using //base/android/jni_generator/jni_generator_helper.h introduces
# a dependency on buildflags targets indirectly through
# base/android/jni_android.h, which is part of the //base target.
# This can't depend directly on //base without causing a dependency
# cycle, though.
"//base:debugging_buildflags",
"//base:logging_buildflags",
"//build:chromeos_buildflags",
]
}
# The sources aren't compiled so don't check their dependencies.
check_includes = false
forward_variables_from(invoker,
[
"deps",
"metadata",
"public_deps",
])
if (!defined(public_deps)) {
public_deps = []
}
public_deps += _jni_generator_include_deps
public_configs += [ "//third_party/jni_zero:jni_include_dir" ]
inputs = []
outputs = []
args = []
if (defined(invoker.classes)) {
args += [ "from-jar" ]
} else {
args += [ "from-source" ]
}
args += [
"--output-dir",
rebase_path(_jni_output_dir, root_build_dir),
"--extra-include",
rebase_path(_jni_generator_include, _jni_output_dir),
]
if (defined(invoker.classes)) {
if (is_robolectric) {
not_needed(invoker, [ "jar_file" ])
} else {
if (defined(invoker.jar_file)) {
_jar_file = invoker.jar_file
} else {
_jar_file = android_sdk_jar
}
inputs += [
_jar_file,
_JAVAP_PATH,
]
args += [
"--jar-file",
rebase_path(_jar_file, root_build_dir),
"--javap",
rebase_path(_JAVAP_PATH, root_build_dir),
]
}
_input_args = invoker.classes
_input_names = invoker.classes
if (defined(invoker.unchecked_exceptions) &&
invoker.unchecked_exceptions) {
args += [ "--unchecked-exceptions" ]
}
} else {
assert(defined(invoker.sources))
# Using final_target_name to make srcjar_deps work.
_srcjar_output = "$target_gen_dir/$_final_target_name.srcjar"
args += [
"--srcjar-path",
rebase_path(_srcjar_output, root_build_dir),
]
outputs += [ _srcjar_output ]
inputs += invoker.sources
_input_args = rebase_path(invoker.sources, root_build_dir)
_input_names = invoker.sources
if (use_hashed_jni_names) {
args += [ "--use-proxy-hash" ]
}
if (defined(invoker.enable_jni_multiplexing) &&
invoker.enable_jni_multiplexing) {
args += [ "--enable-jni-multiplexing" ]
}
if (defined(invoker.namespace)) {
args += [ "--namespace=${invoker.namespace}" ]
}
}
if (defined(invoker.split_name)) {
args += [ "--split-name=${invoker.split_name}" ]
}
foreach(_name, _input_names) {
_name = get_path_info(_name, "name") + "_jni.h"
outputs += [ "$_jni_output_dir/$_name" ]
# Avoid passing GN lists because not all webrtc embedders use //build.
args += [
"--output-name",
_name,
]
}
foreach(_input, _input_args) {
args += [ "--input-file=$_input" ]
}
}
# This group exists to allow for users of generate_jni() to get our object
# files included in their executables without explicitly depending on our
# targets in jni_zero/BUILD.gn.
group(_final_target_name) {
deps = [ "//third_party/jni_zero:jni_zero_utils" ]
public_deps = [ ":$_jni_zero_action_target_name" ]
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
if (defined(visibility)) {
visibility += [ ":$target_name" ]
}
}
}
}
# Declare a jni target
#
# This target generates the native jni bindings for a set of .java files.
#
# See third_party/jni_zero/jni_generator.py for more info about the
# format of generating JNI bindings.
#
# Variables
# sources: list of .java files to generate jni for
# namespace: Specify the namespace for the generated header file.
# deps, public_deps: As normal
#
# Example
# # Target located in base/BUILD.gn.
# generate_jni("foo_jni") {
# # Generates gen/base/foo_jni/Foo_jni.h
# # To use: #include "base/foo_jni/Foo_jni.h"
# sources = [
# "android/java/src/org/chromium/foo/Foo.java",
# ...,
# ]
# }
template("generate_jni") {
generate_jni_impl(target_name) {
forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
metadata = {
jni_source_files = sources
}
}
}
# Declare a jni target for a prebuilt jar
#
# This target generates the native jni bindings for a set of classes in a .jar.
#
# See third_party/jni_zero/jni_generator.py for more info about the
# format of generating JNI bindings.
#
# Variables
# classes: list of .class files in the jar to generate jni for. These should
# include the full path to the .class file.
# jar_file: the path to the .jar. If not provided, will default to the sdk's
# android.jar
# unchecked_exceptions: Don't CHECK() for exceptions in generated stubs.
# This behaves as if every method had @CalledByNativeUnchecked.
# deps, public_deps: As normal
#
# Example
# # Target located in base/BUILD.gn.
# generate_jar_jni("foo_jni") {
# # Generates gen/base/foo_jni/Runnable_jni.h
# # To use: #include "base/foo_jni/Runnable_jni.h"
# classes = [
# "android/view/Foo.class",
# ]
# }
template("generate_jar_jni") {
generate_jni_impl(target_name) {
forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
}
}
# This is a wrapper around an underlying native target which inserts JNI
# registration.
#
# The registration is based on the closure of the native target's generate_jni
# transitive dependencies. Additionally, we use provided java_targets to assert
# that our native and Java sides line up.
#
# In order to depend on the JNI registration, use
# <native-target-name>__jni_registration.
template("native_with_jni") {
_needs_native_dep =
(defined(invoker.manual_jni_registration) &&
invoker.manual_jni_registration) || allow_jni_multiplexing
if (_needs_native_dep || current_toolchain == default_toolchain) {
_jni_registration_target_name = "${target_name}__jni_registration"
}
if (current_toolchain == default_toolchain) {
generate_jni_registration(_jni_registration_target_name) {
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
native_deps = invoker.deps
if (allow_jni_multiplexing) {
enable_jni_multiplexing = true
}
if (defined(invoker.testonly) && invoker.testonly) {
enable_native_mocks = true
add_stubs_for_missing_jni = true
remove_uncalled_jni = true
}
forward_variables_from(invoker,
[
"add_stubs_for_missing_jni",
"java_targets",
"manual_jni_registration",
"module_name",
"namespace",
"remove_uncalled_jni",
])
}
} else {
not_needed(invoker,
[
"add_stubs_for_missing_jni",
"java_targets",
"manual_jni_registration",
"module_name",
"namespace",
"remove_uncalled_jni",
])
}
if (!defined(invoker.enable_target) || invoker.enable_target) {
if (defined(invoker.target_type_import)) {
import(invoker.target_type_import)
}
target(invoker.target_type, target_name) {
deps = invoker.deps
if (_needs_native_dep) {
deps += [ ":$_jni_registration_target_name($default_toolchain)" ]
}
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY + [ "deps" ])
}
} else {
not_needed(invoker, "*")
if (current_toolchain != default_toolchain) {
not_needed([ "target_name" ])
}
}
}
# native_with_jni for shared libraries - see native_with_jni for details.
template("shared_library_with_jni") {
native_with_jni(target_name) {
forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
target_type = "shared_library"
}
}
set_defaults("shared_library_with_jni") {
configs = default_shared_library_configs
}
# native_with_jni for components - see native_with_jni for details.
template("component_with_jni") {
native_with_jni(target_name) {
forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
target_type = "component"
}
}
set_defaults("component_with_jni") {
configs = default_component_configs
}