# Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Compile a protocol buffer. # # Protobuf parameters: # # proto_in_dir (optional) # Specifies the path relative to the current BUILD.gn file where # proto files are located and the directory structure of # this proto library starts. # # This option can be calculated automatically but it will raise an # assertion error if any nested directories are found. # # proto_out_dir (optional) # Specifies the path suffix that output files are generated under. # This path will be appended to |root_gen_dir|, but for python stubs # it will be appended to |root_build_dir|/pyproto. # # generate_python (optional, default true) # Generate Python protobuf stubs. # # generate_cc (optional, default true) # Generate C++ protobuf stubs. # # generate_javascript (optional, default false) # Generate Javascript protobuf stubs. # # generate_library (optional, default true) # Generate a "static_library" target for linking with the generated code. # # generate_py_runtime (optional, default false) # Generates a "_py_runtime"-suffixed target for test targets that need the # Python stubs available at runtime. # # TODO(crbug.com/1237958): Remove allow_optional when proto rolls to 3.15. # allow_optional (optional, default false) # Enables experimental_allow_proto3_optional. # # cc_generator_options (optional) # List of extra flags passed to the protocol compiler. If you need to # add an EXPORT macro to a protobuf's C++ header, set the # 'cc_generator_options' variable with the value: # 'dllexport_decl=FOO_EXPORT:' (note trailing colon). # # It is likely you also need to #include a file for the above EXPORT # macro to work (see cc_include) and set # component_build_force_source_set = true. # # cc_include (optional) # String listing an extra include that should be passed. # Example: cc_include = "foo/bar.h" # # generator_plugin_label (optional) # GN label for plugin executable which generates custom cc stubs. # Don't specify a toolchain, host toolchain is assumed. # # generator_plugin_script (optional) # Path to plugin script. Mutually exclusive with |generator_plugin_label|. # # generator_plugin_script_deps (optional) # List of additional files required for generator plugin script. # # generator_plugin_suffix[es] (required if using a plugin) # Suffix (before extension) for generated .cc and .h files # or list of suffixes for all files (with extensions). # # generator_plugin_options (optional) # Extra flags passed to the plugin. See cc_generator_options. # # deps (optional) # DEPRECATED: use proto_deps or link_deps instead (crbug.com/938011). # Additional dependencies. # # link_deps (optional) # Additional dependencies linked to library. # # proto_deps (optional) # Additional dependencies required before running protoc. # e.g. proto file generating action. # # use_protobuf_full (optional) # If adding protobuf library would be required, adds protobuf_full to deps # instead of protobuf_lite. # # import_dirs (optional) # A list of extra import directories to be passed to protoc compiler. # WARNING: This circumvents proto checkdeps, and should only be used when # needed, typically when proto files cannot cleanly import through # absolute paths, such as for third_party or generated .proto files. # http://crbug.com/691451 tracks fixing this. # # Parameters for compiling the generated code: # # force_source_set (Default=false) # When set true the generated code will be compiled as a source set. # This can be useful if you need to export the generated symbols from a # shared library. You should use this carefully, as you probably only # want this if your dependencies are *always* shared libraries. Most # of the time, you probably want `component_build_force_source_set` # instead (see the next option). # component_build_force_source_set (Default=false) # When set true the generated code will be compiled as a source set in # the component build. This does not affect static builds. If you are # exporting symbols from a component, this is required to prevent those # symbols from being stripped. If you're not using dllexports in # cc_generator_options, it's usually best to leave this false. # # defines (optional) # Defines to supply to the source set that compiles the generated source # code. # # extra_configs (optional) # A list of config labels that will be appended to the configs applying # to the source set. # # remove_configs (optional) # A list of config labels that will be removed from the configs apllying # to the source set. # # propagate_imports_configs (optional) # A boolean value (defaults to true) that specifies whether the config # generated for the library's import directories will be propagated to # dependents as one of the library target's public_configs. See # crbug.com/1043279#c11 and crbug.com/gn/142 for why this option exists. # WARNING: If set to false, the embedder target is responsible for # propagating a suitable config, so that any dependent headers can resolve # includes generated by proto imports. # # Example: # proto_library("mylib") { # sources = [ # "foo.proto", # ] # } import("//build/config/sanitizers/sanitizers.gni") import("//build/toolchain/kythe.gni") declare_args() { # Allows subprojects to omit javascript dependencies (e.g.) closure_compiler # and google-closure-library. enable_js_protobuf = true } if (enable_js_protobuf) { import("//third_party/closure_compiler/compile_js.gni") } if (host_os == "win") { _host_executable_suffix = ".exe" } else { _host_executable_suffix = "" } _protoc_label = "//third_party/protobuf:protoc($host_toolchain)" _protoc_path = get_label_info(_protoc_label, "root_out_dir") + "/protoc" + _host_executable_suffix template("proto_library") { assert(defined(invoker.sources), "Need sources for proto_library") proto_sources = invoker.sources if (defined(invoker.generate_cc)) { generate_cc = invoker.generate_cc } else { generate_cc = true } if (defined(invoker.generate_python)) { generate_python = invoker.generate_python } else { generate_python = true } if (defined(invoker.generate_javascript)) { generate_javascript = invoker.generate_javascript } else { generate_javascript = false } if (defined(invoker.generate_descriptor)) { generate_descriptor = invoker.generate_descriptor } else { generate_descriptor = "" } if (defined(invoker.generate_py_runtime)) { generate_py_runtime = invoker.generate_py_runtime } else { generate_py_runtime = false } if (generate_py_runtime) { generate_python = true } # exclude_imports is only used for generating the descriptor. Therefore, the # check needs to be here to avoid complaints from GN about the unused # variable. if (generate_descriptor != "") { if (defined(invoker.exclude_imports)) { exclude_imports = invoker.exclude_imports } else { exclude_imports = false } } if (defined(invoker.generator_plugin_label)) { # Straightforward way to get the name of executable doesn't work because # |root_out_dir| and |root_build_dir| may differ in cross-compilation and # also Windows executables have .exe at the end. plugin_host_label = invoker.generator_plugin_label + "($host_toolchain)" plugin_path = get_label_info(plugin_host_label, "root_out_dir") + "/" + get_label_info(plugin_host_label, "name") + _host_executable_suffix generate_with_plugin = true } else if (defined(invoker.generator_plugin_script)) { plugin_path = invoker.generator_plugin_script generate_with_plugin = true } else { generate_with_plugin = false } if (generate_with_plugin) { if (defined(invoker.generator_plugin_suffix)) { generator_plugin_suffixes = [ "${invoker.generator_plugin_suffix}.h", "${invoker.generator_plugin_suffix}.cc", ] } else { generator_plugin_suffixes = invoker.generator_plugin_suffixes } } if (defined(invoker.proto_in_dir)) { proto_in_dir = invoker.proto_in_dir has_nested_dirs = false foreach(proto_source, proto_sources) { if (get_path_info(proto_source, "dir") != proto_in_dir) { has_nested_dirs = true } } } else { proto_in_dir = get_path_info(proto_sources[0], "dir") has_nested_dirs = false # Sanity check, |proto_in_dir| should be defined to allow sub-directories. foreach(proto_source, proto_sources) { assert(get_path_info(proto_source, "dir") == proto_in_dir, "Please define |proto_in_dir| to allow nested directories.") } } # Avoid absolute path because of the assumption that |proto_in_dir| is # relative to the directory of current BUILD.gn file. proto_in_dir = rebase_path(proto_in_dir, ".") if (defined(invoker.proto_out_dir)) { proto_out_dir = invoker.proto_out_dir } else { # Absolute path to the directory of current BUILD.gn file excluding "//". proto_out_dir = rebase_path(".", "//") if (proto_in_dir != ".") { proto_out_dir += "/$proto_in_dir" } } # We need both absolute path to use in GN statements and a relative one # to pass to external script. if (generate_cc || generate_with_plugin) { cc_out_dir = "$root_gen_dir/" + proto_out_dir rel_cc_out_dir = rebase_path(cc_out_dir, root_build_dir) } if (generate_python) { py_out_dir = "$root_out_dir/pyproto/" + proto_out_dir rel_py_out_dir = rebase_path(py_out_dir, root_build_dir) } if (generate_javascript) { js_out_dir = "$root_out_dir/jsproto/" + proto_out_dir rel_js_out_dir = rebase_path(js_out_dir, root_build_dir) } if (generate_descriptor != "") { descriptor_out = "$root_gen_dir/" + proto_out_dir + "/" + generate_descriptor rel_descriptor_out = rebase_path(descriptor_out, root_build_dir) } protos = rebase_path(invoker.sources, proto_in_dir) protogens = [] protogens_py = [] protogens_cc = [] protogens_js = [] # Whether source code bindings should be generated. generate_sources = generate_cc || generate_python || generate_with_plugin || generate_javascript # Whether library should be generated. # Library is not needed when proto_library is used to generate binary descriptor, in which case # corresponding library target should be omitted entirely. if (defined(invoker.generate_library)) { generate_library = invoker.generate_library } else { generate_library = generate_sources } # List output files. if (generate_sources) { foreach(proto, protos) { proto_dir = get_path_info(proto, "dir") proto_name = get_path_info(proto, "name") proto_path = proto_dir + "/" + proto_name if (generate_cc) { protogens_cc += [ "$cc_out_dir/$proto_path.pb.h", "$cc_out_dir/$proto_path.pb.cc", ] } if (generate_python) { protogens_py += [ "$py_out_dir/${proto_path}_pb2.py" ] } if (generate_with_plugin) { foreach(suffix, generator_plugin_suffixes) { protogens_cc += [ "$cc_out_dir/${proto_path}${suffix}" ] } } if (generate_javascript) { protogens_js += [ "$js_out_dir/${proto_path}.js" ] } } } # If descriptor needs to be generated, it should be added to list of outputs once. if (generate_descriptor != "") { protogens += [ descriptor_out ] } action_name = "${target_name}_gen" source_set_name = target_name javascript_name = "${target_name}_js" py_runtime_name = "${target_name}_py_runtime" # Generate protobuf stubs. action(action_name) { visibility = [ ":$javascript_name", ":$py_runtime_name", ":$source_set_name", ] script = "//tools/protoc_wrapper/protoc_wrapper.py" args = protos sources = proto_sources outputs = get_path_info(protogens + protogens_cc + protogens_js + protogens_py, "abspath") if (defined(invoker.testonly)) { testonly = invoker.testonly } args += [ # Wrapper should never pick a system protoc. # Path should be rebased because |root_build_dir| for current toolchain # may be different from |root_out_dir| of protoc built on host toolchain. "--protoc", "./" + rebase_path(_protoc_path, root_build_dir), "--proto-in-dir", rebase_path(proto_in_dir, root_build_dir), ] if (generate_cc) { args += [ "--cc-out-dir", rel_cc_out_dir, ] if (enable_kythe_annotations) { args += [ "--enable-kythe-annotation" ] } if (defined(invoker.cc_generator_options)) { args += [ "--cc-options", invoker.cc_generator_options, ] } if (defined(invoker.allow_optional) && invoker.allow_optional == true) { args += [ "--allow-optional" ] } if (defined(invoker.cc_include)) { args += [ "--include", invoker.cc_include, ] } } if (generate_python) { args += [ "--py-out-dir", rel_py_out_dir, ] } if (generate_javascript) { args += [ "--js-out-dir", rel_js_out_dir, ] } if (generate_with_plugin) { args += [ "--plugin", rebase_path(plugin_path, root_build_dir), "--plugin-out-dir", rel_cc_out_dir, ] if (defined(invoker.generator_plugin_options)) { args += [ "--plugin-options", invoker.generator_plugin_options, ] } } if (generate_descriptor != "") { depfile = "$root_gen_dir/" + proto_out_dir + "/" + generate_descriptor + ".d" rel_depfile = rebase_path(depfile, root_build_dir) if (exclude_imports) { args += [ "--exclude-imports" ] } args += [ "--descriptor-set-out", rel_descriptor_out, "--descriptor-set-dependency-file", rel_depfile, ] } if (defined(invoker.import_dirs)) { foreach(path, invoker.import_dirs) { args += [ "--import-dir=" + rebase_path(path, root_build_dir) ] } } # System protoc is not used so it's necessary to build a chromium one. inputs = [ _protoc_path ] deps = [ _protoc_label ] if (generate_with_plugin) { inputs += [ plugin_path ] if (defined(invoker.generator_plugin_script_deps)) { # Additional scripts for plugin. inputs += invoker.generator_plugin_script_deps } if (defined(plugin_host_label)) { # Action depends on native generator plugin but for host toolchain only. deps += [ plugin_host_label ] } } # The deps may have steps that have to run before running protoc. if (defined(invoker.proto_deps)) { deps += invoker.proto_deps } if (defined(invoker.deps)) { deps += invoker.deps } } if (!generate_library) { # If only descriptor is required, just generate a group wrapper for action output. link_target_type = "group" } else if ((defined(invoker.force_source_set) && invoker.force_source_set) || (defined(invoker.component_build_force_source_set) && invoker.component_build_force_source_set && is_component_build)) { # Option to disable building a library in component build. link_target_type = "source_set" } else { link_target_type = "static_library" } # Generated files may include other generated headers. These includes always # use relative paths starting at |cc_out_dir|. # However there is no necessity to add an additional directory, if all protos # are located in the same directory which is in the search path by default. config_name = "${target_name}_config" config(config_name) { include_dirs = [] if (has_nested_dirs && generate_cc) { include_dirs += [ cc_out_dir ] } if (defined(invoker.import_dirs)) { foreach(path, invoker.import_dirs) { include_dirs += [ "$root_gen_dir/" + rebase_path(path, "//") ] } } } # Build generated javascript stubs. if (generate_javascript) { js_library(javascript_name) { forward_variables_from(invoker, [ "testonly", "visibility", ]) sources = protogens_js deps = [ "//third_party/protobuf:js_proto" ] extra_deps = [ ":$action_name" ] } } # Build generated protobuf stubs as libary or source set. target(link_target_type, target_name) { forward_variables_from(invoker, [ "defines", "testonly", "visibility", ]) if (generate_library) { sources = get_path_info(protogens_cc, "abspath") if (defined(invoker.remove_configs)) { configs -= invoker.remove_configs } if (defined(invoker.extra_configs)) { configs += invoker.extra_configs } # Remove Sanitizer and coverage instrumentation for a performance boost when # fuzzing, since the only fuzzers that use protobuf are libprotobuf-mutator # based fuzzers, and they don't actually target protobuf code. configs -= not_fuzzed_remove_configs configs += [ "//build/config/sanitizers:not_fuzzed" ] } public_configs = [ "//third_party/protobuf:using_proto", "//third_party/protobuf:allow_deprecated_proto_fields", ] public_deps = [] if (generate_cc || generate_with_plugin) { # Not necessary if all protos are located in the same directory. if (has_nested_dirs || defined(invoker.import_dirs)) { # By default, propagate the config for |include_dirs| to dependent # targets, so that public imports can be resolved to corresponding # header files. In some cases, the embedder target handles include # directory propagation itself, e.g. via a common config. propagate_imports_configs = !defined(invoker.propagate_imports_configs) || invoker.propagate_imports_configs if (propagate_imports_configs) { public_configs += [ ":$config_name" ] } else { # Embedder handles include directory propagation to dependents. configs += [ ":$config_name" ] } } # If using built-in cc generator, the resulting headers reference headers # within protobuf_lite. Hence, dependencies require those headers too. # If using generator plugin, extra deps should be resolved by the invoker. if (generate_cc) { if (defined(invoker.use_protobuf_full) && invoker.use_protobuf_full == true) { public_deps += [ "//third_party/protobuf:protobuf_full" ] } else { public_deps += [ "//third_party/protobuf:protobuf_lite" ] } if (is_win) { cflags = [ # disable: C4125 decimal digit terminates octal escape sequence # Protoc generates such sequences frequently, there's no obvious # superior replacement behavior. Since this code is autogenerated, # the warning would never catch a legitimate bug. "/wd4125", ] } } } public_deps += [ ":$action_name" ] deps = [] # This will link any libraries in the deps (the use of invoker.deps in the # action won't link it). if (defined(invoker.deps)) { deps += invoker.deps } if (defined(invoker.link_deps)) { deps += invoker.link_deps } } if (generate_py_runtime) { group(py_runtime_name) { data = protogens_py deps = [ ":$action_name", "//third_party/protobuf:py_proto_runtime", ] } } } # Convert a protocol buffer between text and binary formats. # This can be used to run protoc with the --encode or --decode options. # Parameters: # # sources: list of string # The sources to loop over and run protoc on # # inputs: list of string # The file dependencies for the action. This should be the list of .proto # files involved in the conversion operation. # # output_pattern: string # A path pattern with source expansion variables (like source_name_part) # for where the result of conversion should be placed. # # deps: (optional) list of label # Additional dependencies for the target. # # args: list of string # Arguments to pass to the protoc tool. This could include -I for include # paths, as well as the name of the proto file. # # # Example to convert a .textproto to a .binarybp: # protoc_convert("convert_foo") { # sources = [ # "test/data/example1.textproto", # "test/data/example2.textproto", # ] # inputs = [ # "//component/core/foo.proto", # ] # output_pattern = "$target_gen_dir/foo_data/{{source_name_part}}.binarypb" # args = [ # "--encode=foo.FooMessage", # "-I", # rebase_path("//"), # "component/core/foo.proto", # ] # } template("protoc_convert") { action_foreach(target_name) { script = "//tools/protoc_wrapper/protoc_convert.py" sources = invoker.sources inputs = invoker.inputs deps = [ _protoc_label ] if (defined(invoker.deps)) { deps += invoker.deps } if (defined(invoker.testonly)) { testonly = invoker.testonly } outputs = [ invoker.output_pattern ] args = [ "--protoc", "./" + rebase_path(_protoc_path, root_build_dir), "--infile", "{{source}}", "--outfile", rebase_path(invoker.output_pattern), ] + invoker.args } }