perfetto: refine proto descriptor support

Add an explicit gn rule which sets generate_descriptor to allow for
decoupling protolite/protozero from the generation of descriptor.

This allows targets to depend on the existence of the descriptor file
without needing to depend on lite or zero (and prevents clashes when
both depending on both lite and zero).

Change-Id: Iade95036c7680913122ec36530cf586149fe70f8
diff --git a/BUILD b/BUILD
index 4cf7624..3b39406 100644
--- a/BUILD
+++ b/BUILD
@@ -556,9 +556,11 @@
         "//third_party/perfetto/protos:common_zero_cc_proto",
         "//third_party/perfetto/protos:config_zero_cc_proto",
         "//third_party/perfetto/protos:metrics_android_zero_cc_proto",
+        "//third_party/perfetto/protos:metrics_descriptor",
         "//third_party/perfetto/protos:metrics_zero_cc_proto",
         "//third_party/perfetto/protos:trace_android_zero_cc_proto",
         "//third_party/perfetto/protos:trace_chrome_zero_cc_proto",
+        "//third_party/perfetto/protos:trace_descriptor",
         "//third_party/perfetto/protos:trace_filesystem_zero_cc_proto",
         "//third_party/perfetto/protos:trace_ftrace_zero_cc_proto",
         "//third_party/perfetto/protos:trace_interned_data_zero_cc_proto",
diff --git a/gn/proto_descriptor.gni b/gn/proto_descriptor.gni
new file mode 100644
index 0000000..a0183f3
--- /dev/null
+++ b/gn/proto_descriptor.gni
@@ -0,0 +1,36 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("proto_library.gni")
+
+# Equivalent to proto_library (generation of .h/.cc from .proto files) but
+# enables also generation using the protozero plugin.
+# The generated files will have the .pbzero.{cc,h} suffix, as opposed to the
+# .pb.{cc,h} of the official proto library.
+template("proto_descriptor") {
+  proto_library(target_name) {
+    generate_cc = false
+    generate_python = false
+
+    forward_variables_from(invoker,
+                           [
+                             "proto_in_dir",
+                             "proto_out_dir",
+                             "sources",
+                             "testonly",
+                             "visibility",
+                             "generate_descriptor",
+                           ])
+  }
+}
diff --git a/gn/standalone/proto_library.gni b/gn/standalone/proto_library.gni
index 2f46e9d..d2ffe54 100644
--- a/gn/standalone/proto_library.gni
+++ b/gn/standalone/proto_library.gni
@@ -69,6 +69,11 @@
   cc_out_dir = "$root_gen_dir/" + proto_out_dir
   rel_cc_out_dir = rebase_path(cc_out_dir, root_build_dir)
 
+  # Prevent unused errors when generating descriptor only.
+  if (generate_descriptor != "") {
+    not_needed([ "rel_cc_out_dir" ])
+  }
+
   protos = rebase_path(proto_sources, proto_in_dir)
   protogens = []
 
@@ -81,6 +86,11 @@
     proto_name = get_path_info(proto, "name")
     proto_path = proto_dir + "/" + proto_name
 
+    # Prevent unused errors when generating descriptor only.
+    if (generate_descriptor != "") {
+      not_needed([ "proto_path" ])
+    }
+
     if (generate_cc) {
       protogens += [
         "$cc_out_dir/$proto_path.pb.h",
diff --git a/protos/BUILD b/protos/BUILD
index 45c19c2..90492bf 100644
--- a/protos/BUILD
+++ b/protos/BUILD
@@ -245,6 +245,22 @@
     ],
 )
 
+# GN target: //protos/perfetto/metrics:descriptor_gen
+transitive_descriptor_set(
+    name = "metrics_descriptor",
+    deps = [
+        "//third_party/perfetto/protos:metrics_descriptor_proto",
+    ],
+)
+
+# GN target: //protos/perfetto/metrics:descriptor_gen
+proto_library(
+    name = "metrics_descriptor_proto",
+    srcs = [
+        "perfetto/metrics/metrics.proto",
+    ],
+)
+
 # GN target: //protos/perfetto/metrics:zero_gen
 proto_library(
     name = "metrics_zero",
@@ -433,6 +449,24 @@
     ],
 )
 
+# GN target: //protos/perfetto/trace:descriptor_gen
+transitive_descriptor_set(
+    name = "trace_descriptor",
+    deps = [
+        "//third_party/perfetto/protos:trace_descriptor_proto",
+    ],
+)
+
+# GN target: //protos/perfetto/trace:descriptor_gen
+proto_library(
+    name = "trace_descriptor_proto",
+    srcs = [
+        "perfetto/trace/test_event.proto",
+        "perfetto/trace/trace.proto",
+        "perfetto/trace/trace_packet.proto",
+    ],
+)
+
 # GN target: //protos/perfetto/trace/filesystem:lite_gen
 proto_library(
     name = "trace_filesystem",
diff --git a/protos/perfetto/config/BUILD.gn b/protos/perfetto/config/BUILD.gn
index 7b8fea9..5761fda 100644
--- a/protos/perfetto/config/BUILD.gn
+++ b/protos/perfetto/config/BUILD.gn
@@ -23,10 +23,6 @@
   generate_python = false
   proto_in_dir = "$perfetto_root_path/protos"
   proto_out_dir = "$perfetto_root_path/protos"
-  if (!build_with_chromium) {
-    generate_descriptor =
-        "$perfetto_root_path/protos/perfetto/trace/config.descriptor"
-  }
   deps = [
     "../common:lite",
   ]
diff --git a/protos/perfetto/metrics/BUILD.gn b/protos/perfetto/metrics/BUILD.gn
index 5cfcd7e..b2d7644 100644
--- a/protos/perfetto/metrics/BUILD.gn
+++ b/protos/perfetto/metrics/BUILD.gn
@@ -15,6 +15,7 @@
 import("../../../gn/perfetto.gni")
 import("../../../gn/proto_library.gni")
 import("../../../gn/protozero_library.gni")
+import("../../../gn/proto_descriptor.gni")
 
 common_sources = [ "metrics.proto" ]
 
@@ -35,9 +36,15 @@
   proto_in_dir = "$perfetto_root_path/protos"
   proto_out_dir = "$perfetto_root_path/protos"
   sources = common_sources
-  if (perfetto_build_standalone) {
+  generator_plugin_options = "wrapper_namespace=pbzero"
+}
+
+if (perfetto_build_standalone) {
+  proto_descriptor("descriptor") {
+    proto_in_dir = "$perfetto_root_path/protos"
+    proto_out_dir = "$perfetto_root_path/protos"
+    sources = common_sources
     generate_descriptor =
         "$perfetto_root_path/protos/perfetto/metrics/metrics.descriptor"
   }
-  generator_plugin_options = "wrapper_namespace=pbzero"
 }
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index 11186ae..f049930 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -14,6 +14,7 @@
 
 import("../../../gn/perfetto.gni")
 import("../../../gn/proto_library.gni")
+import("../../../gn/proto_descriptor.gni")
 import("../../../gn/protozero_library.gni")
 
 # Common protos used by both the ":minimal_lite" target (for the service) and
@@ -70,14 +71,20 @@
     "sys_stats:lite",
     "track_event:lite",
   ]
-  if (perfetto_build_standalone) {
-    generate_descriptor = "$perfetto_root_path/protos/trace/trace.descriptor"
-  }
   sources = proto_sources
   proto_in_dir = "$perfetto_root_path/protos"
   proto_out_dir = "$perfetto_root_path/protos"
 }
 
+if (perfetto_build_standalone) {
+  proto_descriptor("descriptor") {
+    sources = proto_sources
+    generate_descriptor = "$perfetto_root_path/protos/trace/trace.descriptor"
+    proto_in_dir = "$perfetto_root_path/protos"
+    proto_out_dir = "$perfetto_root_path/protos"
+  }
+}
+
 # Contains the subprotos of TracePacket that all targets need.
 proto_library("minimal_lite") {
   generate_python = false
diff --git a/protos/perfetto/trace/ftrace/BUILD.gn b/protos/perfetto/trace/ftrace/BUILD.gn
index 8e24e04..bccb787 100644
--- a/protos/perfetto/trace/ftrace/BUILD.gn
+++ b/protos/perfetto/trace/ftrace/BUILD.gn
@@ -14,14 +14,11 @@
 
 import("../../../../gn/perfetto.gni")
 import("../../../../gn/protozero_library.gni")
+import("../../../../gn/proto_descriptor.gni")
 import("all_protos.gni")
 
 proto_library("lite") {
   generate_python = false
-  if (perfetto_build_standalone) {
-    generate_descriptor =
-        "$perfetto_root_path/protos/perfetto/trace/ftrace.descriptor"
-  }
   sources = ftrace_proto_names
   proto_in_dir = "$perfetto_root_path/protos"
   proto_out_dir = "$perfetto_root_path/protos"
@@ -33,3 +30,11 @@
   proto_out_dir = "$perfetto_root_path/protos"
   generator_plugin_options = "wrapper_namespace=pbzero"
 }
+
+proto_descriptor("descriptor") {
+  generate_descriptor =
+      "$perfetto_root_path/protos/perfetto/trace/ftrace.descriptor"
+  sources = ftrace_proto_names
+  proto_in_dir = "$perfetto_root_path/protos"
+  proto_out_dir = "$perfetto_root_path/protos"
+}
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 6c0e046..a6adc2a 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -205,7 +205,14 @@
         "../../buildtools:linenoise",
         "../../gn/standalone:gen_git_revision",
       ]
+
+      # Build the descriptors as the diff testing framework depends on this.
+      deps += [
+        "../../protos/perfetto/metrics:descriptor",
+        "../../protos/perfetto/trace:descriptor",
+      ]
     }
+
     sources = [
       "trace_processor_shell.cc",
     ]
diff --git a/tools/diff_test_trace_processor.py b/tools/diff_test_trace_processor.py
index e3e9b75..08e15f5 100755
--- a/tools/diff_test_trace_processor.py
+++ b/tools/diff_test_trace_processor.py
@@ -143,15 +143,16 @@
     trace_descriptor_path = args.trace_descriptor
   else:
     out_path = os.path.dirname(args.trace_processor)
-    trace_protos_path = os.path.join(out_path, "gen", "protos", "trace")
+    trace_protos_path = os.path.join(
+        out_path, "gcc_like_host", "gen", "protos", "trace")
     trace_descriptor_path = os.path.join(trace_protos_path, "trace.descriptor")
 
   if args.metrics_descriptor:
     metrics_descriptor_path = args.metrics_descriptor
   else:
     out_path = os.path.dirname(args.trace_processor)
-    metrics_protos_path = os.path.join(out_path, "gen", "protos", "perfetto",
-                                       "metrics")
+    metrics_protos_path = os.path.join(
+        out_path, "gcc_like_host", "gen", "protos", "perfetto", "metrics")
     metrics_descriptor_path = os.path.join(metrics_protos_path,
                                            "metrics.descriptor")
 
diff --git a/tools/gen_bazel b/tools/gen_bazel
index 8eeb397..1ed0ff7 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -225,7 +225,8 @@
   def __init__(self, type, name, gn_name=None):
     assert type in ('cc_binary', 'cc_library', 'cc_proto_library',
                     'proto_library', 'filegroup', 'alias',
-                    'pbzero_cc_proto_library', 'genrule', )
+                    'pbzero_cc_proto_library', 'genrule',
+                    'transitive_descriptor_set', )
     self.type = type
     self.name = name
     self.srcs = set()
@@ -233,7 +234,7 @@
     self.deps = set()
     self.visibility = set()
     self.gn_name = gn_name
-    self.is_pbzero = False
+    self.cc_proto_fields = False
     self.src_proto_library = None
     self.outs = set()
     self.cmd = None
@@ -248,7 +249,8 @@
     writer.variable('srcs', self.srcs)
     writer.variable('hdrs', self.hdrs)
 
-    if self.type == 'proto_library' and not self.is_pbzero:
+    if self.cc_proto_fields:
+      assert(self.type == 'proto_library')
       if self.srcs:
         writer.variable('has_services', 1)
       writer.variable('cc_api_version', 2)
@@ -462,28 +464,50 @@
     gn_target_name_no_toolchain = gn_utils.label_without_toolchain(
         gn_target_name)
     stripped_path = gn_target_name_no_toolchain.replace("protos/perfetto/", "")
+
+    is_descriptor = any('descriptor_set_out' in arg for arg in args)
+    is_pbzero = any("pbzero" in arg for arg in args)
+    is_proto_lite = not is_pbzero and not is_descriptor
+
     pretty_target_name = gn_utils.label_to_target_name_with_path(stripped_path)
     pretty_target_name = pretty_target_name.replace("_lite_gen", "")
     pretty_target_name = pretty_target_name.replace("_zero_gen", "_zero")
+    if is_descriptor:
+      pretty_target_name = pretty_target_name.replace("_gen", "")
+      proto_target_name = pretty_target_name + "_proto"
+    else:
+      proto_target_name = pretty_target_name
 
     proto_target = Target(
       'proto_library',
-      pretty_target_name,
+      proto_target_name,
       gn_name=gn_target_name_no_toolchain
     )
-    proto_target.is_pbzero = any("pbzero" in arg for arg in args)
+    proto_target.cc_proto_fields = is_proto_lite
     proto_target.srcs.update([
       Label(gn_utils.label_to_path(src).replace('protos/', ''))
       for src in target_desc.get('sources', [])
     ])
-    if not proto_target.is_pbzero:
+    if is_proto_lite:
       proto_target.visibility.add("//visibility:public")
     self.proto_build.add_target(proto_target)
 
     for dep_name in self.resolve_dependencies(gn_target_name):
       self.apply_module_dependency(proto_target, dep_name)
 
-    if proto_target.is_pbzero:
+    if is_descriptor:
+      descriptor_set_target = Target(
+        'transitive_descriptor_set',
+        pretty_target_name,
+        gn_name=gn_target_name_no_toolchain
+      )
+      descriptor_set_target.deps.update([
+        "//third_party/perfetto/protos:{}".format(proto_target.name)
+      ])
+      self.proto_build.add_target(descriptor_set_target)
+
+      return (descriptor_set_target, descriptor_set_target)
+    elif is_pbzero:
       # Remove all the protozero srcs from the proto_library.
       proto_target.srcs.difference_update(
           [src for src in proto_target.srcs if not src.label.endswith('.proto')])