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