| # 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 |
| } |