• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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