1# Copyright 2023 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import("//build/config/android/config.gni") 6import("//build/config/python.gni") 7import("//build/partitioned_shared_library.gni") 8 9declare_args() { 10 # Enables JNI multiplexing to reduce JNI native methods overhead. 11 allow_jni_multiplexing = false 12 13 # Use hashed symbol names to reduce JNI symbol overhead. 14 use_hashed_jni_names = !is_java_debug 15} 16 17# Use a dedicated include dir so that files can #include headers from other 18# toolchains without affecting non-JNI #includes. 19if (target_os == "android") { 20 jni_headers_dir = "$root_build_dir/gen/jni_headers" 21} else { 22 # Chrome OS builds cannot share gen/ directories because is_android=false 23 # within default_toolchain. 24 jni_headers_dir = "$root_gen_dir/jni_headers" 25} 26 27template("jni_sources_list") { 28 generated_file(target_name) { 29 forward_variables_from(invoker, 30 TESTONLY_AND_VISIBILITY + [ 31 "deps", 32 "walk_keys", 33 ]) 34 outputs = [ invoker.output ] 35 data_keys = [ "jni_source_files" ] 36 rebase = root_build_dir 37 metadata = { 38 # This target is just collecting source files used - this is not a 39 # legitimate dependency. 40 shared_libraries_barrier = [] 41 } 42 } 43} 44 45# Declare a jni registration target. 46# 47# This target generates a srcjar containing a copy of GEN_JNI.java, which has 48# the native methods of all dependent java files. It can also create a .h file 49# for use with manual JNI registration. 50# 51# The script does not scan any generated sources (those within .srcjars, or 52# within root_build_dir). This could be fixed by adding deps & logic to scan 53# .srcjars, but isn't currently needed. 54# 55# See base/android/jni_generator/jni_registration_generator.py for more info 56# about the format of the header file. 57# 58# Variables 59# java_targets: List of android_* targets that comprise your app. 60# native_deps: List of shared_library targets that comprise your app. 61# manual_jni_registration: Manually do JNI registration - required for feature 62# splits which provide their own native library. (optional) 63# namespace: Registration functions will be wrapped into this. (optional) 64# require_native_mocks: Enforce that any native calls using 65# org.chromium.base.annotations.NativeMethods must have a mock set 66# (optional). 67# enable_native_mocks: Allow native calls using 68# org.chromium.base.annotations.NativeMethods to be mocked in tests 69# (optional). 70# 71# Example 72# generate_jni_registration("chrome_jni_registration") { 73# java_targets = [ ":chrome_public_apk" ] 74# manual_jni_registration = false 75# } 76template("generate_jni_registration") { 77 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 78 if (defined(invoker.native_deps)) { 79 _native_sources_list = "$target_gen_dir/$target_name.nativesources.txt" 80 jni_sources_list("${target_name}__native_sources") { 81 deps = invoker.native_deps 82 output = _native_sources_list 83 } 84 } 85 86 _java_sources_list = "$target_gen_dir/$target_name.javasources.txt" 87 jni_sources_list("${target_name}__java_sources") { 88 deps = invoker.java_targets 89 output = _java_sources_list 90 91 # When apk or bundle module targets are uses, do not pull metadata from 92 # their native library deps. 93 walk_keys = [ "java_walk_keys" ] 94 } 95 96 action_with_pydeps(target_name) { 97 script = "//base/android/jni_generator/jni_zero.py" 98 inputs = [] 99 100 # Cannot depend on jni_sources_list targets since they likely depend on 101 # this target via srcjar_deps. Depfiles are used to add the dep instead. 102 deps = [] 103 _srcjar_output = "$target_gen_dir/$target_name.srcjar" 104 outputs = [ _srcjar_output ] 105 depfile = "$target_gen_dir/$target_name.d" 106 107 args = [ 108 "generate-final", 109 "--srcjar-path", 110 rebase_path(_srcjar_output, root_build_dir), 111 "--depfile", 112 rebase_path(depfile, root_build_dir), 113 "--java-sources-file", 114 rebase_path(_java_sources_list, root_build_dir), 115 ] 116 117 if (defined(_native_sources_list)) { 118 args += [ 119 "--native-sources-file", 120 rebase_path(_native_sources_list, root_build_dir), 121 ] 122 } 123 124 if (defined(invoker.include_testonly)) { 125 _include_testonly = invoker.include_testonly 126 } else { 127 _include_testonly = defined(testonly) && testonly 128 } 129 if (_include_testonly) { 130 args += [ "--include-test-only" ] 131 } 132 133 if (use_hashed_jni_names) { 134 args += [ "--use-proxy-hash" ] 135 } 136 137 if (defined(invoker.enable_native_mocks) && invoker.enable_native_mocks) { 138 args += [ "--enable-proxy-mocks" ] 139 140 if (defined(invoker.require_native_mocks) && 141 invoker.require_native_mocks) { 142 args += [ "--require-mocks" ] 143 } 144 } 145 146 if (defined(invoker.remove_uncalled_jni) && invoker.remove_uncalled_jni) { 147 args += [ "--remove-uncalled-methods" ] 148 } 149 if (defined(invoker.add_stubs_for_missing_jni) && 150 invoker.add_stubs_for_missing_jni) { 151 args += [ "--add-stubs-for-missing-native" ] 152 } 153 154 _manual_jni_registration = defined(invoker.manual_jni_registration) && 155 invoker.manual_jni_registration 156 _enable_jni_multiplexing = defined(invoker.enable_jni_multiplexing) && 157 invoker.enable_jni_multiplexing 158 if (_manual_jni_registration) { 159 args += [ "--manual-jni-registration" ] 160 } 161 if (_enable_jni_multiplexing) { 162 args += [ "--enable-jni-multiplexing" ] 163 } 164 165 if (_manual_jni_registration || _enable_jni_multiplexing) { 166 _subdir = rebase_path(target_gen_dir, root_gen_dir) 167 _jni_header_output = 168 "$jni_headers_dir/$_subdir/${target_name}_generated.h" 169 outputs += [ _jni_header_output ] 170 args += [ 171 "--header-path", 172 rebase_path(_jni_header_output, root_build_dir), 173 ] 174 175 # This gives targets depending on this registration access to our generated header. 176 public_configs = [ "//build/config/android:jni_include_dir" ] 177 } 178 179 if (defined(invoker.namespace)) { 180 args += [ "--namespace=${invoker.namespace}" ] 181 } 182 183 if (defined(invoker.module_name)) { 184 args += [ "--module-name=${invoker.module_name}" ] 185 } 186 } 187} 188 189# This is a wrapper around an underlying native target which inserts JNI 190# registration. 191# 192# The registration is based on the closure of the native target's generate_jni 193# transitive dependencies. Additionally, we use provided java_targets to assert 194# that our native and Java sides line up. 195# 196# In order to depend on the JNI registration, use 197# <native-target-name>__jni_registration. 198template("_native_with_jni") { 199 _needs_native_dep = 200 (defined(invoker.manual_jni_registration) && 201 invoker.manual_jni_registration) || allow_jni_multiplexing 202 if (_needs_native_dep || current_toolchain == default_toolchain) { 203 _jni_registration_target_name = "${target_name}__jni_registration" 204 } 205 206 if (current_toolchain == default_toolchain) { 207 generate_jni_registration(_jni_registration_target_name) { 208 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 209 native_deps = invoker.deps 210 211 if (allow_jni_multiplexing) { 212 enable_jni_multiplexing = true 213 } 214 if (defined(invoker.testonly) && invoker.testonly) { 215 enable_native_mocks = true 216 add_stubs_for_missing_jni = true 217 remove_uncalled_jni = true 218 } 219 forward_variables_from(invoker, 220 [ 221 "add_stubs_for_missing_jni", 222 "java_targets", 223 "manual_jni_registration", 224 "module_name", 225 "namespace", 226 "remove_uncalled_jni", 227 ]) 228 } 229 } else { 230 not_needed(invoker, 231 [ 232 "add_stubs_for_missing_jni", 233 "java_targets", 234 "manual_jni_registration", 235 "module_name", 236 "namespace", 237 "remove_uncalled_jni", 238 ]) 239 } 240 241 if (!defined(invoker.enable_target) || invoker.enable_target) { 242 target(invoker.target_type, target_name) { 243 deps = invoker.deps 244 if (_needs_native_dep) { 245 deps += [ ":$_jni_registration_target_name($default_toolchain)" ] 246 } 247 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 248 forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY + [ "deps" ]) 249 } 250 } else { 251 not_needed(invoker, "*") 252 if (current_toolchain != default_toolchain) { 253 not_needed([ "target_name" ]) 254 } 255 } 256} 257 258# native_with_jni for shared libraries - see _native_with_jni for details. 259template("shared_library_with_jni") { 260 _native_with_jni(target_name) { 261 forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) 262 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 263 target_type = "shared_library" 264 } 265} 266set_defaults("shared_library_with_jni") { 267 configs = default_shared_library_configs 268} 269 270# native_with_jni for partitioned shared libraries - see _native_with_jni for 271# details. 272template("partitioned_shared_library_with_jni") { 273 _native_with_jni(target_name) { 274 forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) 275 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 276 target_type = "partitioned_shared_library" 277 } 278} 279set_defaults("partitioned_shared_library_with_jni") { 280 configs = default_shared_library_configs 281} 282 283# native_with_jni for components - see _native_with_jni for details. 284template("component_with_jni") { 285 _native_with_jni(target_name) { 286 forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) 287 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 288 target_type = "component" 289 } 290} 291set_defaults("component_with_jni") { 292 configs = default_component_configs 293} 294