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/compiler/compiler.gni") 7 8_JAVAP_PATH = "//third_party/jdk/current/bin/javap" 9 10declare_args() { 11 # Enables JNI multiplexing to reduce JNI native methods overhead. 12 allow_jni_multiplexing = false 13 14 # Use hashed symbol names to reduce JNI symbol overhead. 15 use_hashed_jni_names = !is_java_debug 16} 17 18# Use a dedicated include dir so that files can #include headers from other 19# toolchains without affecting non-JNI #includes. 20if (target_os == "android") { 21 jni_headers_dir = "$root_build_dir/gen/jni_headers" 22} else { 23 # Chrome OS builds cannot share gen/ directories because is_android=false 24 # within default_toolchain. 25 jni_headers_dir = "$root_gen_dir/jni_headers" 26} 27 28_jni_zero_dir = "//third_party/jni_zero" 29 30template("jni_sources_list") { 31 generated_file(target_name) { 32 forward_variables_from(invoker, 33 TESTONLY_AND_VISIBILITY + [ 34 "deps", 35 "walk_keys", 36 ]) 37 outputs = [ invoker.output ] 38 data_keys = [ "jni_source_files" ] 39 rebase = root_build_dir 40 metadata = { 41 # This target is just collecting source files used - this is not a 42 # legitimate dependency. 43 shared_libraries_barrier = [] 44 } 45 } 46} 47 48template("_invoke_jni_zero") { 49 action(target_name) { 50 forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) 51 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 52 53 script = "//third_party/jni_zero/jni_zero.py" 54 if (!defined(inputs)) { 55 inputs = [] 56 } 57 inputs += rebase_path([ 58 "codegen/placeholder_gen_jni_java.py", 59 "codegen/proxy_impl_java.py", 60 "common.py", 61 "java_lang_classes.py", 62 "java_types.py", 63 "jni_generator.py", 64 "jni_registration_generator.py", 65 "jni_zero.py", 66 "parse.py", 67 "proxy.py", 68 ], 69 ".", 70 _jni_zero_dir) 71 } 72} 73 74# Declare a jni registration target. 75# 76# This target generates a srcjar containing a copy of GEN_JNI.java, which has 77# the native methods of all dependent java files. It can also create a .h file 78# for use with manual JNI registration. 79# 80# The script does not scan any generated sources (those within .srcjars, or 81# within root_build_dir). This could be fixed by adding deps & logic to scan 82# .srcjars, but isn't currently needed. 83# 84# See third_party/jni_zero/jni_registration_generator.py for more info 85# about the format of the header file. 86# 87# Variables 88# java_targets: List of android_* targets that comprise your app. 89# native_deps: List of shared_library targets that comprise your app. 90# manual_jni_registration: Manually do JNI registration - required for feature 91# splits which provide their own native library. (optional) 92# namespace: Registration functions will be wrapped into this. (optional) 93# require_native_mocks: Enforce that any native calls using 94# org.chromium.base.annotations.NativeMethods must have a mock set 95# (optional). 96# enable_native_mocks: Allow native calls using 97# org.chromium.base.annotations.NativeMethods to be mocked in tests 98# (optional). 99# 100# Example 101# generate_jni_registration("chrome_jni_registration") { 102# java_targets = [ ":chrome_public_apk" ] 103# manual_jni_registration = false 104# } 105template("generate_jni_registration") { 106 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 107 if (defined(invoker.native_deps)) { 108 _native_sources_list = "$target_gen_dir/$target_name.nativesources.txt" 109 jni_sources_list("${target_name}__native_sources") { 110 deps = invoker.native_deps 111 output = _native_sources_list 112 } 113 } 114 115 _java_sources_list = "$target_gen_dir/$target_name.javasources.txt" 116 jni_sources_list("${target_name}__java_sources") { 117 deps = invoker.java_targets 118 output = _java_sources_list 119 120 # When apk or bundle module targets are uses, do not pull metadata from 121 # their native library deps. 122 walk_keys = [ "java_walk_keys" ] 123 } 124 125 _invoke_jni_zero(target_name) { 126 # Cannot depend on jni_sources_list targets since they likely depend on 127 # this target via srcjar_deps. Depfiles are used to add the dep instead. 128 deps = [] 129 _srcjar_output = "$target_gen_dir/$target_name.srcjar" 130 outputs = [ _srcjar_output ] 131 depfile = "$target_gen_dir/$target_name.d" 132 133 java_target_deps = [] 134 if (defined(invoker.java_targets)) { 135 foreach(java_targets_dep, invoker.java_targets) { 136 java_target_deps += 137 [ get_label_info(java_targets_dep, "label_no_toolchain") ] 138 } 139 } 140 metadata = { 141 java_deps = java_target_deps 142 } 143 args = [ 144 "generate-final", 145 "--srcjar-path", 146 rebase_path(_srcjar_output, root_build_dir), 147 "--depfile", 148 rebase_path(depfile, root_build_dir), 149 "--java-sources-file", 150 rebase_path(_java_sources_list, root_build_dir), 151 ] 152 153 if (defined(_native_sources_list)) { 154 args += [ 155 "--native-sources-file", 156 rebase_path(_native_sources_list, root_build_dir), 157 ] 158 } 159 160 if (defined(invoker.include_testonly)) { 161 _include_testonly = invoker.include_testonly 162 } else { 163 _include_testonly = defined(testonly) && testonly 164 } 165 if (_include_testonly) { 166 args += [ "--include-test-only" ] 167 } 168 169 if (use_hashed_jni_names) { 170 args += [ "--use-proxy-hash" ] 171 } 172 173 if (defined(invoker.enable_native_mocks) && invoker.enable_native_mocks) { 174 args += [ "--enable-proxy-mocks" ] 175 176 if (defined(invoker.require_native_mocks) && 177 invoker.require_native_mocks) { 178 args += [ "--require-mocks" ] 179 } 180 } 181 182 if (defined(invoker.remove_uncalled_jni) && invoker.remove_uncalled_jni) { 183 args += [ "--remove-uncalled-methods" ] 184 } 185 if (defined(invoker.add_stubs_for_missing_jni) && 186 invoker.add_stubs_for_missing_jni) { 187 args += [ "--add-stubs-for-missing-native" ] 188 } 189 190 _manual_jni_registration = defined(invoker.manual_jni_registration) && 191 invoker.manual_jni_registration 192 _enable_jni_multiplexing = defined(invoker.enable_jni_multiplexing) && 193 invoker.enable_jni_multiplexing 194 if (_manual_jni_registration) { 195 args += [ "--manual-jni-registration" ] 196 } 197 if (_enable_jni_multiplexing) { 198 args += [ "--enable-jni-multiplexing" ] 199 } 200 201 if (_manual_jni_registration || _enable_jni_multiplexing) { 202 _subdir = rebase_path(target_gen_dir, root_gen_dir) 203 _jni_header_output = 204 "$jni_headers_dir/$_subdir/${target_name}_generated.h" 205 outputs += [ _jni_header_output ] 206 args += [ 207 "--header-path", 208 rebase_path(_jni_header_output, root_build_dir), 209 ] 210 211 public_configs = [ 212 # This gives targets depending on this registration access to our generated header. 213 "//third_party/jni_zero:jni_include_dir", 214 215 # For now, we unconditionally use chromium's base. We could move this 216 # to depend on an invoker variable like we do with generate_jni_impl 217 # if we want this to be configurable. 218 "//third_party/jni_zero:use_chromium_base_define", 219 ] 220 } 221 222 if (defined(invoker.namespace)) { 223 args += [ "--namespace=${invoker.namespace}" ] 224 } 225 226 if (defined(invoker.module_name)) { 227 args += [ "--module-name=${invoker.module_name}" ] 228 } 229 } 230} 231 232# JNI target implementation. See generate_jni or generate_jar_jni for usage. 233template("generate_jni_impl") { 234 public_configs = [] 235 if (!defined(invoker.use_chromium_base) || invoker.use_chromium_base) { 236 public_configs += [ "//third_party/jni_zero:use_chromium_base_define" ] 237 } 238 _jni_zero_action_target_name = target_name + "__action" 239 if (current_toolchain != default_toolchain && target_os == "android") { 240 # Rather than regenerating .h files in secondary toolchains, re-use the 241 # ones from the primary toolchain by depending on it and adding the 242 # root gen directory to the include paths. 243 # https://crbug.com/1369398 244 group(target_name) { 245 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 246 not_needed(invoker, "*") 247 public_configs += 248 [ "//third_party/jni_zero:jni_include_dir($default_toolchain)" ] 249 250 # Depending on the action name to avoid cross-toolchain native deps. 251 public_deps = [ ":$_jni_zero_action_target_name($default_toolchain)" ] 252 deps = [ "//third_party/jni_zero:jni_zero_utils" ] 253 metadata = { 254 shared_libraries_barrier = [] 255 } 256 } 257 } else { 258 _final_target_name = target_name 259 _invoke_jni_zero(_jni_zero_action_target_name) { 260 _subdir = rebase_path(target_gen_dir, root_gen_dir) 261 _jni_output_dir = "$jni_headers_dir/$_subdir/$_final_target_name" 262 if (defined(invoker.jni_generator_include)) { 263 _jni_generator_include = invoker.jni_generator_include 264 _jni_generator_include_deps = [] 265 } else { 266 _jni_generator_include = "//third_party/jni_zero/jni_zero_helper.h" 267 _jni_generator_include_deps = [ 268 # Using //base/android/jni_generator/jni_generator_helper.h introduces 269 # a dependency on buildflags targets indirectly through 270 # base/android/jni_android.h, which is part of the //base target. 271 # This can't depend directly on //base without causing a dependency 272 # cycle, though. 273 "//base:debugging_buildflags", 274 "//base:logging_buildflags", 275 "//build:chromeos_buildflags", 276 ] 277 } 278 279 # The sources aren't compiled so don't check their dependencies. 280 check_includes = false 281 forward_variables_from(invoker, 282 [ 283 "deps", 284 "metadata", 285 "public_deps", 286 ]) 287 if (!defined(public_deps)) { 288 public_deps = [] 289 } 290 public_deps += _jni_generator_include_deps 291 292 public_configs += [ "//third_party/jni_zero:jni_include_dir" ] 293 294 inputs = [] 295 outputs = [] 296 args = [] 297 if (defined(invoker.classes)) { 298 args += [ "from-jar" ] 299 } else { 300 args += [ "from-source" ] 301 } 302 args += [ 303 "--output-dir", 304 rebase_path(_jni_output_dir, root_build_dir), 305 "--extra-include", 306 rebase_path(_jni_generator_include, _jni_output_dir), 307 ] 308 309 if (defined(invoker.classes)) { 310 if (is_robolectric) { 311 not_needed(invoker, [ "jar_file" ]) 312 } else { 313 if (defined(invoker.jar_file)) { 314 _jar_file = invoker.jar_file 315 } else { 316 _jar_file = android_sdk_jar 317 } 318 inputs += [ 319 _jar_file, 320 _JAVAP_PATH, 321 ] 322 args += [ 323 "--jar-file", 324 rebase_path(_jar_file, root_build_dir), 325 "--javap", 326 rebase_path(_JAVAP_PATH, root_build_dir), 327 ] 328 } 329 _input_args = invoker.classes 330 _input_names = invoker.classes 331 if (defined(invoker.unchecked_exceptions) && 332 invoker.unchecked_exceptions) { 333 args += [ "--unchecked-exceptions" ] 334 } 335 } else { 336 assert(defined(invoker.sources)) 337 338 # Using final_target_name to make srcjar_deps work. 339 _srcjar_output = "$target_gen_dir/$_final_target_name.srcjar" 340 args += [ 341 "--srcjar-path", 342 rebase_path(_srcjar_output, root_build_dir), 343 ] 344 outputs += [ _srcjar_output ] 345 inputs += invoker.sources 346 _input_args = rebase_path(invoker.sources, root_build_dir) 347 _input_names = invoker.sources 348 if (use_hashed_jni_names) { 349 args += [ "--use-proxy-hash" ] 350 } 351 352 if (defined(invoker.enable_jni_multiplexing) && 353 invoker.enable_jni_multiplexing) { 354 args += [ "--enable-jni-multiplexing" ] 355 } 356 if (defined(invoker.namespace)) { 357 args += [ "--namespace=${invoker.namespace}" ] 358 } 359 } 360 if (defined(invoker.split_name)) { 361 args += [ "--split-name=${invoker.split_name}" ] 362 } 363 364 foreach(_name, _input_names) { 365 _name = get_path_info(_name, "name") + "_jni.h" 366 outputs += [ "$_jni_output_dir/$_name" ] 367 368 # Avoid passing GN lists because not all webrtc embedders use //build. 369 args += [ 370 "--output-name", 371 _name, 372 ] 373 } 374 375 foreach(_input, _input_args) { 376 args += [ "--input-file=$_input" ] 377 } 378 } 379 380 # This group exists to allow for users of generate_jni() to get our object 381 # files included in their executables without explicitly depending on our 382 # targets in jni_zero/BUILD.gn. 383 group(_final_target_name) { 384 deps = [ "//third_party/jni_zero:jni_zero_utils" ] 385 public_deps = [ ":$_jni_zero_action_target_name" ] 386 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 387 if (defined(visibility)) { 388 visibility += [ ":$target_name" ] 389 } 390 } 391 } 392} 393 394# Declare a jni target 395# 396# This target generates the native jni bindings for a set of .java files. 397# 398# See third_party/jni_zero/jni_generator.py for more info about the 399# format of generating JNI bindings. 400# 401# Variables 402# sources: list of .java files to generate jni for 403# namespace: Specify the namespace for the generated header file. 404# deps, public_deps: As normal 405# 406# Example 407# # Target located in base/BUILD.gn. 408# generate_jni("foo_jni") { 409# # Generates gen/base/foo_jni/Foo_jni.h 410# # To use: #include "base/foo_jni/Foo_jni.h" 411# sources = [ 412# "android/java/src/org/chromium/foo/Foo.java", 413# ..., 414# ] 415# } 416template("generate_jni") { 417 generate_jni_impl(target_name) { 418 forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) 419 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 420 metadata = { 421 jni_source_files = sources 422 } 423 } 424} 425 426# Declare a jni target for a prebuilt jar 427# 428# This target generates the native jni bindings for a set of classes in a .jar. 429# 430# See third_party/jni_zero/jni_generator.py for more info about the 431# format of generating JNI bindings. 432# 433# Variables 434# classes: list of .class files in the jar to generate jni for. These should 435# include the full path to the .class file. 436# jar_file: the path to the .jar. If not provided, will default to the sdk's 437# android.jar 438# unchecked_exceptions: Don't CHECK() for exceptions in generated stubs. 439# This behaves as if every method had @CalledByNativeUnchecked. 440# deps, public_deps: As normal 441# 442# Example 443# # Target located in base/BUILD.gn. 444# generate_jar_jni("foo_jni") { 445# # Generates gen/base/foo_jni/Runnable_jni.h 446# # To use: #include "base/foo_jni/Runnable_jni.h" 447# classes = [ 448# "android/view/Foo.class", 449# ] 450# } 451template("generate_jar_jni") { 452 generate_jni_impl(target_name) { 453 forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) 454 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 455 } 456} 457 458# This is a wrapper around an underlying native target which inserts JNI 459# registration. 460# 461# The registration is based on the closure of the native target's generate_jni 462# transitive dependencies. Additionally, we use provided java_targets to assert 463# that our native and Java sides line up. 464# 465# In order to depend on the JNI registration, use 466# <native-target-name>__jni_registration. 467template("native_with_jni") { 468 _needs_native_dep = 469 (defined(invoker.manual_jni_registration) && 470 invoker.manual_jni_registration) || allow_jni_multiplexing 471 if (_needs_native_dep || current_toolchain == default_toolchain) { 472 _jni_registration_target_name = "${target_name}__jni_registration" 473 } 474 475 if (current_toolchain == default_toolchain) { 476 generate_jni_registration(_jni_registration_target_name) { 477 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 478 native_deps = invoker.deps 479 480 if (allow_jni_multiplexing) { 481 enable_jni_multiplexing = true 482 } 483 if (defined(invoker.testonly) && invoker.testonly) { 484 enable_native_mocks = true 485 add_stubs_for_missing_jni = true 486 remove_uncalled_jni = true 487 } 488 forward_variables_from(invoker, 489 [ 490 "add_stubs_for_missing_jni", 491 "java_targets", 492 "manual_jni_registration", 493 "module_name", 494 "namespace", 495 "remove_uncalled_jni", 496 ]) 497 } 498 } else { 499 not_needed(invoker, 500 [ 501 "add_stubs_for_missing_jni", 502 "java_targets", 503 "manual_jni_registration", 504 "module_name", 505 "namespace", 506 "remove_uncalled_jni", 507 ]) 508 } 509 510 if (!defined(invoker.enable_target) || invoker.enable_target) { 511 if (defined(invoker.target_type_import)) { 512 import(invoker.target_type_import) 513 } 514 target(invoker.target_type, target_name) { 515 deps = invoker.deps 516 if (_needs_native_dep) { 517 deps += [ ":$_jni_registration_target_name($default_toolchain)" ] 518 } 519 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 520 forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY + [ "deps" ]) 521 } 522 } else { 523 not_needed(invoker, "*") 524 if (current_toolchain != default_toolchain) { 525 not_needed([ "target_name" ]) 526 } 527 } 528} 529 530# native_with_jni for shared libraries - see native_with_jni for details. 531template("shared_library_with_jni") { 532 native_with_jni(target_name) { 533 forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) 534 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 535 target_type = "shared_library" 536 } 537} 538set_defaults("shared_library_with_jni") { 539 configs = default_shared_library_configs 540} 541 542# native_with_jni for components - see native_with_jni for details. 543template("component_with_jni") { 544 native_with_jni(target_name) { 545 forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY) 546 forward_variables_from(invoker, TESTONLY_AND_VISIBILITY) 547 target_type = "component" 548 } 549} 550set_defaults("component_with_jni") { 551 configs = default_component_configs 552} 553