# 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/python.gni") import("//build/partitioned_shared_library.gni") 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" } 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 = [] } } } # 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 base/android/jni_generator/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" ] } action_with_pydeps(target_name) { script = "//base/android/jni_generator/jni_zero.py" inputs = [] # 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" 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), ] # This gives targets depending on this registration access to our generated header. public_configs = [ "//build/config/android:jni_include_dir" ] } if (defined(invoker.namespace)) { args += [ "--namespace=${invoker.namespace}" ] } if (defined(invoker.module_name)) { args += [ "--module-name=${invoker.module_name}" ] } } } # 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 # __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) { 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 partitioned shared libraries - see _native_with_jni for # details. template("partitioned_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 = "partitioned_shared_library" } } set_defaults("partitioned_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 }