Add tracing:convert_chart_json gn target.

This CL also:
 - adds a third_party/vinn gn target
 - reworks generate_telemetry_build.py to support data_deps (and
   thus reuse the new third_party/vinn target)
 - generally cleans up some parts of generate_telemetry_build.py

Bug: chromium:807273
Change-Id: Ide2ab30554352f345d888bb50122d4e92626f997
Reviewed-on: https://chromium-review.googlesource.com/c/catapult/+/1580599
Commit-Queue: John Budorick <jbudorick@chromium.org>
Reviewed-by: Caleb Rouleau <crouleau@chromium.org>
diff --git a/generate_telemetry_build.py b/generate_telemetry_build.py
index c76d23e..fe38e8f 100755
--- a/generate_telemetry_build.py
+++ b/generate_telemetry_build.py
@@ -24,89 +24,46 @@
 import optparse
 import sys
 import subprocess
+import textwrap
 
-LICENSE = """# Copyright 2018 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.
+LICENSE = textwrap.dedent(
+    """\
+    # Copyright 2018 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.
 
-"""
+    """)
 
-DO_NOT_EDIT_WARNING = """# This file is auto-generated from
-#    //third_party/catapult/generated_telemetry_build.py
-# DO NOT EDIT!
+DO_NOT_EDIT_WARNING = textwrap.dedent(
+    """\
+    # This file is auto-generated from
+    #    //third_party/catapult/generated_telemetry_build.py
+    # DO NOT EDIT!
 
-"""
+    """)
 
 TELEMETRY_SUPPORT_GROUP_NAME = 'telemetry_chrome_test_support'
 
-EXCLUDED_PATHS = [
-  {
-    # needed for --chromium option; can remove once this CL lands.
-    "path": "BUILD.gn",
-  },
-  {
-    "path": "common/node_runner",
-  },
-  {
-    "path": "docs",
-  },
-  {
-    "path": "experimental",
-  },
-  {
-    # needed for --chromium option; can remove once this CL lands.
-    "path": "generate_telemetry_build.py",
-  },
-  {
-    "path": "telemetry/telemetry/data",
-  },
-  {
-    "path": "telemetry/telemetry/bin",
-  },
-  {
-    "path": "telemetry/telemetry/internal/bin",
-  },
-  {
-    # needed for --check option
-    "path": "TEMP.gn",
-  },
-  {
-    "path": "third_party/google-endpoints",
-  },
-  {
-    "path": "third_party/Paste",
-  },
-  {
-    "path": "third_party/polymer2",
-  },
-  {
-    "path": "third_party/vinn/third_party/v8/linux/arm",
-    "condition": "is_chromeos",
-  },
-  {
-    "path": "third_party/vinn/third_party/v8/linux/mips",
-    "condition": "is_chromeos",
-  },
-  {
-    "path": "third_party/vinn/third_party/v8/linux/mips64",
-    "condition": "is_chromeos",
-  },
-  {
-    "path": "third_party/vinn/third_party/v8/linux/x86_64",
-    "condition": "is_linux || is_android",
-  },
-  {
-    "path": "third_party/vinn/third_party/v8/mac",
-    "condition": "is_mac",
-  },
-  {
-    "path": "third_party/vinn/third_party/v8/win",
-    "condition": "is_win",
-  },
-  {
-    "path": "tracing/test_data",
-  },
-]
+EXCLUDED_PATHS = {
+  'BUILD.gn',
+  'TEMP.gn',
+  'common/node_runner/',
+  'docs/',
+  'experimental/',
+  'generate_telemetry_build.py',
+  'telemetry/telemetry/bin/',
+  'telemetry/telemetry/data/',
+  'telemetry/telemetry/internal/bin/',
+  'third_party/Paste/',
+  'third_party/google-endpoints/',
+  'third_party/polymer2/',
+  'tracing/test_data/',
+}
+
+
+SEPARATE_TARGETS = {
+  'third_party/vinn': 'third_party/vinn',
+}
 
 
 def GetUntrackedPaths():
@@ -118,117 +75,63 @@
   return [os.path.abspath(p) for p in paths if p]
 
 
-def GetFileCondition(rel_path):
-  # Return 'true' if the file should be included; return 'false' if it should
-  # be excluded; return a condition string if it should only be included if
-  # the condition is true.
-  processed_rel_path = rel_path.replace('\\', '/')
-  for exclusion in EXCLUDED_PATHS:
-    assert 'path' in exclusion
-    if exclusion['path'] == processed_rel_path:
-      if 'condition' in exclusion:
-        return exclusion['condition']
-      else:
-        return 'false'
-  return 'true'
-
-
-def GetDirCondition(rel_path):
-  # Return 'true' if the dir should be included; return 'false' if it should
-  # be excluded; return a condition string if it should only be included if
-  # the condition is true; return 'expand' if some files or sub-dirs under it
-  # are excluded or conditionally included, so the parser needs to go inside
-  # the dir and process further.
-  processed_rel_path = rel_path.replace('\\', '/')
-  for exclusion in EXCLUDED_PATHS:
-    assert 'path' in exclusion
-    if exclusion['path'] == processed_rel_path:
-      if 'condition' in exclusion:
-        return exclusion['condition']
-      else:
-        return 'false'
-    elif exclusion['path'].startswith(processed_rel_path + '/'):
-      return 'expand'
-  return 'true'
-
-
-def WriteLists(lists, conditional_lists, build_file, path_prefix):
-  first_entry = True
-  for path_list in lists:
-    for path in path_list:
-      path = path.replace('\\', '/')
+def WriteLists(data, data_deps, build_file, path_prefix):
+  if data:
+    build_file.write('  data += [\n')
+    for path in data:
       if path_prefix:
         path = path_prefix + path
-      if first_entry:
-        build_file.write('  data += [\n')
-        first_entry = False
       build_file.write('    "%s",\n' % path)
-  if not first_entry:
     build_file.write('  ]\n\n')
-  for conditional_list in conditional_lists:
-    for entry in conditional_list:
-      assert 'path' in entry
-      assert 'condition' in entry
-      path = entry['path'].replace('\\', '/')
-      if path_prefix:
-        path = path_prefix + path
-      build_file.write("""  if (%s) {
-    data += [ "%s" ]
-  }
 
-""" % (entry['condition'], path))
+  if data_deps:
+    build_file.write('  data_deps += [\n')
+    for data_dep in data_deps:
+      build_file.write('    "%s",\n' % data_dep)
+    build_file.write('  ]\n\n')
+
 
 def ProcessDir(root_path, path, build_file, path_prefix):
   # Write all dirs and files directly under |path| unless they are excluded
-  # or need to be processed further because some of their children are excldued
-  # or conditionally included.
+  # or need to be processed further because some of their children are excluded.
   # Return a list of dirs that needs to processed further.
   logging.debug('GenerateList for ' + path)
-  entry_list = os.listdir(path)
-  entry_list.sort()
-  file_list = []
-  dir_list = []
-  conditional_list = []
-  expand_list = []
+  entries = os.listdir(path)
+  entries.sort()
+  files = []
+  dirs = []
+  data_deps = []
+  dirs_to_expand = []
   untracked_paths = GetUntrackedPaths()
-  for entry in entry_list:
+  for entry in entries:
     full_path = os.path.join(path, entry)
-    rel_path = os.path.relpath(full_path, root_path)
+    rel_path = os.path.relpath(full_path, root_path).replace('\\', '/')
     if (any(full_path.startswith(p) for p in untracked_paths) or
         entry.startswith('.') or entry.endswith('~') or
         entry.endswith('.pyc') or entry.endswith('#')):
       logging.debug('ignored %s', rel_path)
       continue
-    if os.path.isfile(full_path):
-      condition = GetFileCondition(rel_path)
-      if condition == 'true':
-        file_list.append(rel_path)
-      elif condition == 'false':
+    if rel_path in SEPARATE_TARGETS:
+      data_deps.append(SEPARATE_TARGETS[rel_path])
+    elif os.path.isfile(full_path):
+      if rel_path in EXCLUDED_PATHS:
         logging.debug('excluded %s', rel_path)
-        continue
       else:
-        conditional_list.append({
-          "condition": condition,
-          "path": rel_path,
-        });
+        files.append(rel_path)
     elif os.path.isdir(full_path):
-      condition = GetDirCondition(rel_path)
-      if condition == 'true':
-        dir_list.append(rel_path + '/')
-      elif condition == 'false':
+      rel_path = rel_path + '/'
+      if rel_path in EXCLUDED_PATHS:
         logging.debug('excluded %s', rel_path)
-      elif condition == 'expand':
-        expand_list.append(full_path)
+      elif any(e.startswith(rel_path) for e in EXCLUDED_PATHS):
+        dirs_to_expand.append(full_path)
       else:
-        conditional_list.append({
-          "condition": condition,
-          "path": rel_path + '/',
-        });
+        dirs.append(rel_path)
     else:
       assert False
-  WriteLists([file_list, dir_list], [conditional_list],
+  WriteLists(files + dirs,
+             data_deps,
              build_file, path_prefix)
-  return expand_list
+  return dirs_to_expand
 
 def WriteBuildFileHeader(build_file):
   build_file.write(LICENSE)
@@ -236,11 +139,14 @@
   build_file.write('import("//build/config/compiler/compiler.gni")\n\n')
 
 def WriteBuildFileBody(build_file, root_path, path_prefix):
-  build_file.write("""group("%s") {
-  testonly = true
-  data = []
+  build_file.write(textwrap.dedent(
+      """\
+      group("%s") {
+        testonly = true
+        data = []
+        data_deps = []
 
-""" % TELEMETRY_SUPPORT_GROUP_NAME)
+      """ % TELEMETRY_SUPPORT_GROUP_NAME))
 
   candidates = [root_path]
   while len(candidates) > 0: