1# Copyright 2014 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 5# Compile a protocol buffer. 6# 7# Protobuf parameters: 8# 9# proto_in_dir (optional) 10# Specifies the path relative to the current BUILD.gn file where 11# proto files are located and the directory structure of 12# this proto library starts. 13# 14# This option can be calculated automatically but it will raise an 15# assertion error if any nested directories are found. 16# 17# proto_out_dir (optional) 18# Specifies the path suffix that output files are generated under. 19# This path will be appended to |root_gen_dir|, but for python stubs 20# it will be appended to |root_build_dir|/pyproto. 21# 22# generate_python (optional, default true) 23# Generate Python protobuf stubs. 24# 25# generate_cc (optional, default true) 26# Generate C++ protobuf stubs. 27# 28# generate_javascript (optional, default false) 29# Generate Javascript protobuf stubs. 30# 31# generate_library (optional, default true) 32# Generate a "static_library" target for linking with the generated code. 33# 34# generate_py_runtime (optional, default false) 35# Generates a "_py_runtime"-suffixed target for test targets that need the 36# Python stubs available at runtime. 37# 38# cc_generator_options (optional) 39# List of extra flags passed to the protocol compiler. If you need to 40# add an EXPORT macro to a protobuf's C++ header, set the 41# 'cc_generator_options' variable with the value: 42# 'dllexport_decl=FOO_EXPORT:' (note trailing colon). 43# 44# It is likely you also need to #include a file for the above EXPORT 45# macro to work (see cc_include) and set 46# component_build_force_source_set = true. 47# 48# cc_include (optional) 49# String listing an extra include that should be passed. 50# Example: cc_include = "foo/bar.h" 51# 52# generator_plugin_label (optional) 53# GN label for plugin executable which generates custom cc stubs. 54# Don't specify a toolchain, host toolchain is assumed. 55# 56# generator_plugin_script (optional) 57# Path to plugin script. Mutually exclusive with |generator_plugin_label|. 58# 59# generator_plugin_script_deps (optional) 60# List of additional files required for generator plugin script. 61# 62# generator_plugin_suffix[es] (required if using a plugin) 63# Suffix (before extension) for generated .cc and .h files 64# or list of suffixes for all files (with extensions). 65# 66# generator_plugin_options (optional) 67# Extra flags passed to the plugin. See cc_generator_options. 68# 69# deps (optional) 70# DEPRECATED: use proto_deps or link_deps instead (crbug.com/938011). 71# Additional dependencies. 72# 73# link_deps (optional) 74# Additional dependencies linked to library. 75# 76# proto_deps (optional) 77# Additional dependencies required before running protoc. 78# e.g. proto file generating action. 79# 80# proto_data_sources (optional) 81# Additional proto sources that will only be used for references 82# and will not be compiled. 83# This should be used in conjunction with import_dirs. 84# 85# use_protobuf_full (optional) 86# If adding protobuf library would be required, adds protobuf_full to deps 87# instead of protobuf_lite. 88# 89# import_dirs (optional) 90# A list of extra import directories to be passed to protoc compiler. 91# WARNING: This circumvents proto checkdeps, and should only be used when 92# needed, typically when proto files cannot cleanly import through 93# absolute paths, such as for third_party or generated .proto files. 94# http://crbug.com/691451 tracks fixing this. 95# 96# Parameters for compiling the generated code: 97# 98# force_source_set (Default=false) 99# When set true the generated code will be compiled as a source set. 100# This can be useful if you need to export the generated symbols from a 101# shared library. You should use this carefully, as you probably only 102# want this if your dependencies are *always* shared libraries. Most 103# of the time, you probably want `component_build_force_source_set` 104# instead (see the next option). 105# component_build_force_source_set (Default=false) 106# When set true the generated code will be compiled as a source set in 107# the component build. This does not affect static builds. If you are 108# exporting symbols from a component, this is required to prevent those 109# symbols from being stripped. If you're not using dllexports in 110# cc_generator_options, it's usually best to leave this false. 111# 112# defines (optional) 113# Defines to supply to the source set that compiles the generated source 114# code. 115# 116# extra_configs (optional) 117# A list of config labels that will be appended to the configs applying 118# to the source set. 119# 120# remove_configs (optional) 121# A list of config labels that will be removed from the configs apllying 122# to the source set. 123# 124# propagate_imports_configs (optional) 125# A boolean value (defaults to true) that specifies whether the config 126# generated for the library's import directories will be propagated to 127# dependents as one of the library target's public_configs. See 128# crbug.com/1043279#c11 and crbug.com/gn/142 for why this option exists. 129# WARNING: If set to false, the embedder target is responsible for 130# propagating a suitable config, so that any dependent headers can resolve 131# includes generated by proto imports. 132# 133# Example: 134# proto_library("mylib") { 135# sources = [ 136# "foo.proto", 137# ] 138# } 139 140import("//build/config/cronet/config.gni") 141import("//build/config/sanitizers/sanitizers.gni") 142import("//build/toolchain/kythe.gni") 143 144declare_args() { 145 # Allows subprojects to omit javascript dependencies (e.g.) closure_compiler 146 # and google-closure-library. 147 enable_js_protobuf = !is_cronet_build 148} 149 150if (enable_js_protobuf) { 151 import("//third_party/closure_compiler/compile_js.gni") 152} 153 154if (host_os == "win") { 155 _host_executable_suffix = ".exe" 156} else { 157 _host_executable_suffix = "" 158} 159 160_protoc_label = "//third_party/protobuf:protoc($host_toolchain)" 161_protoc_path = get_label_info(_protoc_label, "root_out_dir") + "/protoc" + 162 _host_executable_suffix 163_protoc_gen_js_label = 164 "//third_party/protobuf-javascript:protoc-gen-js($host_toolchain)" 165_protoc_gen_js_path = get_label_info(_protoc_gen_js_label, "root_out_dir") + 166 "/protoc-gen-js" + _host_executable_suffix 167 168template("proto_library") { 169 assert(defined(invoker.sources), "Need sources for proto_library") 170 proto_sources = invoker.sources 171 172 if (defined(invoker.generate_cc)) { 173 generate_cc = invoker.generate_cc 174 } else { 175 generate_cc = true 176 } 177 178 if (defined(invoker.generate_python)) { 179 generate_python = invoker.generate_python 180 } else { 181 generate_python = true 182 } 183 184 if (defined(invoker.generate_javascript)) { 185 generate_javascript = invoker.generate_javascript 186 } else { 187 generate_javascript = false 188 } 189 190 if (defined(invoker.generate_descriptor)) { 191 generate_descriptor = invoker.generate_descriptor 192 } else { 193 generate_descriptor = "" 194 } 195 196 if (defined(invoker.generate_py_runtime)) { 197 generate_py_runtime = invoker.generate_py_runtime 198 } else { 199 generate_py_runtime = false 200 } 201 if (generate_py_runtime) { 202 generate_python = true 203 } 204 205 # exclude_imports is only used for generating the descriptor. Therefore, the 206 # check needs to be here to avoid complaints from GN about the unused 207 # variable. 208 if (generate_descriptor != "") { 209 if (defined(invoker.exclude_imports)) { 210 exclude_imports = invoker.exclude_imports 211 } else { 212 exclude_imports = false 213 } 214 } 215 216 if (defined(invoker.generator_plugin_label)) { 217 # Straightforward way to get the name of executable doesn't work because 218 # |root_out_dir| and |root_build_dir| may differ in cross-compilation and 219 # also Windows executables have .exe at the end. 220 221 plugin_host_label = invoker.generator_plugin_label + "($host_toolchain)" 222 plugin_path = 223 get_label_info(plugin_host_label, "root_out_dir") + "/" + 224 get_label_info(plugin_host_label, "name") + _host_executable_suffix 225 generate_with_plugin = true 226 } else if (defined(invoker.generator_plugin_script)) { 227 plugin_path = invoker.generator_plugin_script 228 generate_with_plugin = true 229 } else { 230 generate_with_plugin = false 231 } 232 233 if (generate_with_plugin) { 234 if (defined(invoker.generator_plugin_suffix)) { 235 generator_plugin_suffixes = [ 236 "${invoker.generator_plugin_suffix}.h", 237 "${invoker.generator_plugin_suffix}.cc", 238 ] 239 } else { 240 generator_plugin_suffixes = invoker.generator_plugin_suffixes 241 } 242 } 243 244 if (defined(invoker.proto_in_dir)) { 245 proto_in_dir = invoker.proto_in_dir 246 has_nested_dirs = false 247 foreach(proto_source, proto_sources) { 248 if (get_path_info(proto_source, "dir") != proto_in_dir) { 249 has_nested_dirs = true 250 } 251 } 252 } else { 253 proto_in_dir = get_path_info(proto_sources[0], "dir") 254 has_nested_dirs = false 255 256 # Sanity check, |proto_in_dir| should be defined to allow sub-directories. 257 foreach(proto_source, proto_sources) { 258 assert(get_path_info(proto_source, "dir") == proto_in_dir, 259 "Please define |proto_in_dir| to allow nested directories.") 260 } 261 } 262 263 # Avoid absolute path because of the assumption that |proto_in_dir| is 264 # relative to the directory of current BUILD.gn file. 265 proto_in_dir = rebase_path(proto_in_dir, ".") 266 267 if (defined(invoker.proto_out_dir)) { 268 proto_out_dir = invoker.proto_out_dir 269 } else { 270 # Absolute path to the directory of current BUILD.gn file excluding "//". 271 proto_out_dir = rebase_path(".", "//") 272 if (proto_in_dir != ".") { 273 proto_out_dir += "/$proto_in_dir" 274 } 275 } 276 277 # We need both absolute path to use in GN statements and a relative one 278 # to pass to external script. 279 if (generate_cc || generate_with_plugin) { 280 cc_out_dir = "$root_gen_dir/" + proto_out_dir 281 rel_cc_out_dir = rebase_path(cc_out_dir, root_build_dir) 282 } 283 if (generate_python) { 284 py_out_dir = "$root_out_dir/pyproto/" + proto_out_dir 285 rel_py_out_dir = rebase_path(py_out_dir, root_build_dir) 286 } 287 if (generate_javascript) { 288 js_out_dir = "$root_out_dir/jsproto/" + proto_out_dir 289 rel_js_out_dir = rebase_path(js_out_dir, root_build_dir) 290 } 291 if (generate_descriptor != "") { 292 descriptor_out = 293 "$root_gen_dir/" + proto_out_dir + "/" + generate_descriptor 294 rel_descriptor_out = rebase_path(descriptor_out, root_build_dir) 295 } 296 297 protos = rebase_path(invoker.sources, proto_in_dir) 298 protogens = [] 299 protogens_py = [] 300 protogens_cc = [] 301 protogens_js = [] 302 303 # Whether source code bindings should be generated. 304 generate_sources = generate_cc || generate_python || generate_with_plugin || 305 generate_javascript 306 307 # Whether library should be generated. 308 # Library is not needed when proto_library is used to generate binary descriptor, in which case 309 # corresponding library target should be omitted entirely. 310 if (defined(invoker.generate_library)) { 311 generate_library = invoker.generate_library 312 } else { 313 generate_library = generate_sources 314 } 315 316 # List output files. 317 if (generate_sources) { 318 foreach(proto, protos) { 319 proto_dir = get_path_info(proto, "dir") 320 proto_name = get_path_info(proto, "name") 321 proto_path = proto_dir + "/" + proto_name 322 323 if (generate_cc) { 324 protogens_cc += [ 325 "$cc_out_dir/$proto_path.pb.h", 326 "$cc_out_dir/$proto_path.pb.cc", 327 ] 328 } 329 if (generate_python) { 330 protogens_py += [ "$py_out_dir/${proto_path}_pb2.py" ] 331 } 332 if (generate_with_plugin) { 333 foreach(suffix, generator_plugin_suffixes) { 334 protogens_cc += [ "$cc_out_dir/${proto_path}${suffix}" ] 335 } 336 } 337 if (generate_javascript) { 338 protogens_js += [ "$js_out_dir/${proto_path}.js" ] 339 } 340 } 341 } 342 343 # If descriptor needs to be generated, it should be added to list of outputs once. 344 if (generate_descriptor != "") { 345 protogens += [ descriptor_out ] 346 } 347 348 action_name = "${target_name}_gen" 349 source_set_name = target_name 350 javascript_name = "${target_name}_js" 351 py_runtime_name = "${target_name}_py_runtime" 352 353 # Generate protobuf stubs. 354 action(action_name) { 355 visibility = [ 356 ":$javascript_name", 357 ":$py_runtime_name", 358 ":$source_set_name", 359 ] 360 script = "//tools/protoc_wrapper/protoc_wrapper.py" 361 args = protos 362 363 sources = proto_sources 364 outputs = 365 get_path_info(protogens + protogens_cc + protogens_js + protogens_py, 366 "abspath") 367 368 if (defined(invoker.testonly)) { 369 testonly = invoker.testonly 370 } 371 372 args += [ 373 # Wrapper should never pick a system protoc. 374 # Path should be rebased because |root_build_dir| for current toolchain 375 # may be different from |root_out_dir| of protoc built on host toolchain. 376 "--protoc", 377 "./" + rebase_path(_protoc_path, root_build_dir), 378 "--proto-in-dir", 379 rebase_path(proto_in_dir, root_build_dir), 380 ] 381 382 if (generate_cc) { 383 args += [ 384 "--cc-out-dir", 385 rel_cc_out_dir, 386 ] 387 if (enable_kythe_annotations) { 388 args += [ "--enable-kythe-annotation" ] 389 } 390 if (defined(invoker.cc_generator_options)) { 391 args += [ 392 "--cc-options", 393 invoker.cc_generator_options, 394 ] 395 } 396 if (defined(invoker.cc_include)) { 397 args += [ 398 "--include", 399 invoker.cc_include, 400 ] 401 } 402 } 403 404 if (generate_python) { 405 args += [ 406 "--py-out-dir", 407 rel_py_out_dir, 408 ] 409 } 410 411 if (generate_javascript) { 412 args += [ 413 "--js-out-dir", 414 rel_js_out_dir, 415 "--protoc-gen-js", 416 "./" + rebase_path(_protoc_gen_js_path, root_build_dir), 417 ] 418 } 419 420 if (generate_with_plugin) { 421 args += [ 422 "--plugin", 423 rebase_path(plugin_path, root_build_dir), 424 "--plugin-out-dir", 425 rel_cc_out_dir, 426 ] 427 if (defined(invoker.generator_plugin_options)) { 428 args += [ 429 "--plugin-options", 430 invoker.generator_plugin_options, 431 ] 432 } 433 } 434 435 if (generate_descriptor != "") { 436 depfile = 437 "$root_gen_dir/" + proto_out_dir + "/" + generate_descriptor + ".d" 438 rel_depfile = rebase_path(depfile, root_build_dir) 439 440 if (exclude_imports) { 441 args += [ "--exclude-imports" ] 442 } 443 444 args += [ 445 "--descriptor-set-out", 446 rel_descriptor_out, 447 "--descriptor-set-dependency-file", 448 rel_depfile, 449 ] 450 } 451 inputs = [] 452 if (defined(invoker.proto_data_sources)) { 453 inputs += invoker.proto_data_sources 454 } 455 456 if (defined(invoker.import_dirs)) { 457 foreach(path, invoker.import_dirs) { 458 args += [ "--import-dir=" + rebase_path(path, root_build_dir) ] 459 } 460 } 461 462 # System protoc is not used so it's necessary to build a chromium one. 463 inputs += [ _protoc_path ] 464 deps = [ _protoc_label ] 465 466 if (enable_js_protobuf) { 467 inputs += [ _protoc_gen_js_path ] 468 deps += [ _protoc_gen_js_label ] 469 } 470 471 if (generate_with_plugin) { 472 inputs += [ plugin_path ] 473 if (defined(invoker.generator_plugin_script_deps)) { 474 # Additional scripts for plugin. 475 inputs += invoker.generator_plugin_script_deps 476 } 477 if (defined(plugin_host_label)) { 478 # Action depends on native generator plugin but for host toolchain only. 479 deps += [ plugin_host_label ] 480 } 481 } 482 483 # The deps may have steps that have to run before running protoc. 484 if (defined(invoker.proto_deps)) { 485 deps += invoker.proto_deps 486 } 487 if (defined(invoker.deps)) { 488 deps += invoker.deps 489 } 490 } 491 492 if (!generate_library) { 493 # If only descriptor is required, just generate a group wrapper for action output. 494 link_target_type = "group" 495 } else if ((defined(invoker.force_source_set) && invoker.force_source_set) || 496 (defined(invoker.component_build_force_source_set) && 497 invoker.component_build_force_source_set && is_component_build)) { 498 # Option to disable building a library in component build. 499 link_target_type = "source_set" 500 } else { 501 link_target_type = "static_library" 502 } 503 504 # Generated files may include other generated headers. These includes always 505 # use relative paths starting at |cc_out_dir|. 506 # However there is no necessity to add an additional directory, if all protos 507 # are located in the same directory which is in the search path by default. 508 config_name = "${target_name}_config" 509 config(config_name) { 510 include_dirs = [] 511 if (has_nested_dirs && generate_cc) { 512 include_dirs += [ cc_out_dir ] 513 } 514 if (defined(invoker.import_dirs)) { 515 foreach(path, invoker.import_dirs) { 516 include_dirs += [ "$root_gen_dir/" + rebase_path(path, "//") ] 517 } 518 } 519 } 520 521 # Build generated javascript stubs. 522 if (generate_javascript) { 523 js_library(javascript_name) { 524 forward_variables_from(invoker, 525 [ 526 "testonly", 527 "visibility", 528 ]) 529 530 sources = protogens_js 531 532 deps = [ "//third_party/protobuf:js_proto" ] 533 534 extra_deps = [ ":$action_name" ] 535 } 536 } 537 538 # Build generated protobuf stubs as libary or source set. 539 target(link_target_type, target_name) { 540 forward_variables_from(invoker, 541 [ 542 "defines", 543 "testonly", 544 "visibility", 545 ]) 546 547 if (generate_library) { 548 sources = get_path_info(protogens_cc, "abspath") 549 550 if (defined(invoker.remove_configs)) { 551 configs -= invoker.remove_configs 552 } 553 554 if (defined(invoker.extra_configs)) { 555 configs += invoker.extra_configs 556 } 557 558 # Remove Sanitizer and coverage instrumentation for a performance boost when 559 # fuzzing, since the only fuzzers that use protobuf are libprotobuf-mutator 560 # based fuzzers, and they don't actually target protobuf code. 561 configs -= not_fuzzed_remove_configs 562 configs += [ "//build/config/sanitizers:not_fuzzed" ] 563 } 564 565 public_configs = [ 566 "//third_party/protobuf:using_proto", 567 "//third_party/protobuf:allow_deprecated_proto_fields", 568 ] 569 public_deps = [] 570 571 if (generate_cc || generate_with_plugin) { 572 # Not necessary if all protos are located in the same directory. 573 if (has_nested_dirs || defined(invoker.import_dirs)) { 574 # By default, propagate the config for |include_dirs| to dependent 575 # targets, so that public imports can be resolved to corresponding 576 # header files. In some cases, the embedder target handles include 577 # directory propagation itself, e.g. via a common config. 578 propagate_imports_configs = 579 !defined(invoker.propagate_imports_configs) || 580 invoker.propagate_imports_configs 581 if (propagate_imports_configs) { 582 public_configs += [ ":$config_name" ] 583 } else { 584 # Embedder handles include directory propagation to dependents. 585 configs += [ ":$config_name" ] 586 } 587 } 588 589 # If using built-in cc generator, the resulting headers reference headers 590 # within protobuf_lite. Hence, dependencies require those headers too. 591 # If using generator plugin, extra deps should be resolved by the invoker. 592 if (generate_cc) { 593 if (defined(invoker.use_protobuf_full) && 594 invoker.use_protobuf_full == true) { 595 public_deps += [ "//third_party/protobuf:protobuf_full" ] 596 } else { 597 public_deps += [ "//third_party/protobuf:protobuf_lite" ] 598 } 599 600 if (is_win) { 601 cflags = [ 602 # disable: C4125 decimal digit terminates octal escape sequence 603 # Protoc generates such sequences frequently, there's no obvious 604 # superior replacement behavior. Since this code is autogenerated, 605 # the warning would never catch a legitimate bug. 606 "/wd4125", 607 ] 608 } 609 } 610 } 611 612 public_deps += [ ":$action_name" ] 613 deps = [] 614 615 # This will link any libraries in the deps (the use of invoker.deps in the 616 # action won't link it). 617 if (defined(invoker.deps)) { 618 deps += invoker.deps 619 } 620 if (defined(invoker.link_deps)) { 621 deps += invoker.link_deps 622 } 623 } 624 625 if (generate_py_runtime) { 626 group(py_runtime_name) { 627 data = protogens_py 628 deps = [ 629 ":$action_name", 630 "//third_party/protobuf:py_proto_runtime", 631 ] 632 } 633 } 634} 635 636# Convert a protocol buffer between text and binary formats. 637# This can be used to run protoc with the --encode or --decode options. 638# Parameters: 639# 640# sources: list of string 641# The sources to loop over and run protoc on 642# 643# inputs: list of string 644# The file dependencies for the action. This should be the list of .proto 645# files involved in the conversion operation. 646# 647# output_pattern: string 648# A path pattern with source expansion variables (like source_name_part) 649# for where the result of conversion should be placed. 650# 651# deps: (optional) list of label 652# Additional dependencies for the target. 653# 654# args: list of string 655# Arguments to pass to the protoc tool. This could include -I for include 656# paths, as well as the name of the proto file. 657# 658# 659# Example to convert a .textproto to a .binarybp: 660# protoc_convert("convert_foo") { 661# sources = [ 662# "test/data/example1.textproto", 663# "test/data/example2.textproto", 664# ] 665# inputs = [ 666# "//component/core/foo.proto", 667# ] 668# output_pattern = "$target_gen_dir/foo_data/{{source_name_part}}.binarypb" 669# args = [ 670# "--encode=foo.FooMessage", 671# "-I", 672# rebase_path("//"), 673# "component/core/foo.proto", 674# ] 675# } 676template("protoc_convert") { 677 action_foreach(target_name) { 678 script = "//tools/protoc_wrapper/protoc_convert.py" 679 680 sources = invoker.sources 681 682 inputs = invoker.inputs 683 684 deps = [ _protoc_label ] 685 if (defined(invoker.deps)) { 686 deps += invoker.deps 687 } 688 689 if (defined(invoker.testonly)) { 690 testonly = invoker.testonly 691 } 692 693 outputs = [ invoker.output_pattern ] 694 695 args = [ 696 "--protoc", 697 "./" + rebase_path(_protoc_path, root_build_dir), 698 "--infile", 699 "{{source}}", 700 "--outfile", 701 rebase_path(invoker.output_pattern), 702 ] + invoker.args 703 } 704} 705