blob: c76d23ecd8f34e0fee9c9cc3064c40ed2c01ae9c [file] [log] [blame]
nednguyen4b719ca2018-09-07 08:51:29 -04001#!/usr/bin/env python
Zhenyao Mo72712b02018-08-23 15:55:12 -07002# Copyright 2018 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Generate BUILD.gn to define all data required to run telemetry test.
7
8If a file/folder of large size is added to catapult but is not needed to run
9telemetry test, then it should be added to the EXCLUDED_PATHS below and rerun
10this script.
11
12This script can also run with --check to see if it needs to rerun to update
13BUILD.gn.
14
15This script can also run with --chromium and rewrite the chromium file
16 //tools/perf/chrome_telemetry_build/BUILD.gn
17This is for the purpose of running try jobs in chromium.
18
19"""
20
21import difflib
22import logging
23import os
24import optparse
25import sys
nednguyenfebcf872018-09-07 14:19:11 -040026import subprocess
Zhenyao Mo72712b02018-08-23 15:55:12 -070027
28LICENSE = """# Copyright 2018 The Chromium Authors. All rights reserved.
29# Use of this source code is governed by a BSD-style license that can be
30# found in the LICENSE file.
31
32"""
33
34DO_NOT_EDIT_WARNING = """# This file is auto-generated from
35# //third_party/catapult/generated_telemetry_build.py
36# DO NOT EDIT!
37
38"""
39
40TELEMETRY_SUPPORT_GROUP_NAME = 'telemetry_chrome_test_support'
41
42EXCLUDED_PATHS = [
43 {
44 # needed for --chromium option; can remove once this CL lands.
45 "path": "BUILD.gn",
46 },
47 {
Zhenyao Moc54a6eb2018-08-29 10:06:31 -070048 "path": "common/node_runner",
Zhenyao Mobc2c0a92018-08-24 14:26:04 -070049 },
50 {
51 "path": "docs",
52 },
53 {
54 "path": "experimental",
55 },
56 {
Zhenyao Mo72712b02018-08-23 15:55:12 -070057 # needed for --chromium option; can remove once this CL lands.
58 "path": "generate_telemetry_build.py",
59 },
60 {
Zhenyao Mob18e8eb2018-08-28 16:43:56 -070061 "path": "telemetry/telemetry/data",
62 },
63 {
Zhenyao Mo72712b02018-08-23 15:55:12 -070064 "path": "telemetry/telemetry/bin",
65 },
66 {
67 "path": "telemetry/telemetry/internal/bin",
68 },
69 {
70 # needed for --check option
71 "path": "TEMP.gn",
72 },
73 {
74 "path": "third_party/google-endpoints",
75 },
76 {
77 "path": "third_party/Paste",
78 },
79 {
80 "path": "third_party/polymer2",
81 },
82 {
83 "path": "third_party/vinn/third_party/v8/linux/arm",
84 "condition": "is_chromeos",
85 },
86 {
87 "path": "third_party/vinn/third_party/v8/linux/mips",
88 "condition": "is_chromeos",
89 },
90 {
91 "path": "third_party/vinn/third_party/v8/linux/mips64",
92 "condition": "is_chromeos",
93 },
94 {
95 "path": "third_party/vinn/third_party/v8/linux/x86_64",
96 "condition": "is_linux || is_android",
97 },
98 {
99 "path": "third_party/vinn/third_party/v8/mac",
100 "condition": "is_mac",
101 },
102 {
103 "path": "third_party/vinn/third_party/v8/win",
104 "condition": "is_win",
105 },
106 {
107 "path": "tracing/test_data",
108 },
109]
110
nednguyenfebcf872018-09-07 14:19:11 -0400111
112def GetUntrackedPaths():
113 """Return directories/files in catapult/ that are not tracked by git."""
114 output = subprocess.check_output([
115 'git', 'ls-files', '--others', '--exclude-standard', '--directory',
116 '--full-name'])
117 paths = output.split('\n')
118 return [os.path.abspath(p) for p in paths if p]
119
120
Zhenyao Mo72712b02018-08-23 15:55:12 -0700121def GetFileCondition(rel_path):
122 # Return 'true' if the file should be included; return 'false' if it should
123 # be excluded; return a condition string if it should only be included if
124 # the condition is true.
125 processed_rel_path = rel_path.replace('\\', '/')
126 for exclusion in EXCLUDED_PATHS:
127 assert 'path' in exclusion
128 if exclusion['path'] == processed_rel_path:
129 if 'condition' in exclusion:
130 return exclusion['condition']
131 else:
132 return 'false'
133 return 'true'
134
nednguyenfebcf872018-09-07 14:19:11 -0400135
Zhenyao Mo72712b02018-08-23 15:55:12 -0700136def GetDirCondition(rel_path):
137 # Return 'true' if the dir should be included; return 'false' if it should
138 # be excluded; return a condition string if it should only be included if
139 # the condition is true; return 'expand' if some files or sub-dirs under it
140 # are excluded or conditionally included, so the parser needs to go inside
141 # the dir and process further.
142 processed_rel_path = rel_path.replace('\\', '/')
143 for exclusion in EXCLUDED_PATHS:
144 assert 'path' in exclusion
145 if exclusion['path'] == processed_rel_path:
146 if 'condition' in exclusion:
147 return exclusion['condition']
148 else:
149 return 'false'
150 elif exclusion['path'].startswith(processed_rel_path + '/'):
151 return 'expand'
152 return 'true'
153
nednguyenfebcf872018-09-07 14:19:11 -0400154
Zhenyao Mo72712b02018-08-23 15:55:12 -0700155def WriteLists(lists, conditional_lists, build_file, path_prefix):
156 first_entry = True
157 for path_list in lists:
158 for path in path_list:
159 path = path.replace('\\', '/')
160 if path_prefix:
161 path = path_prefix + path
162 if first_entry:
163 build_file.write(' data += [\n')
164 first_entry = False
165 build_file.write(' "%s",\n' % path)
166 if not first_entry:
167 build_file.write(' ]\n\n')
168 for conditional_list in conditional_lists:
169 for entry in conditional_list:
170 assert 'path' in entry
171 assert 'condition' in entry
172 path = entry['path'].replace('\\', '/')
173 if path_prefix:
174 path = path_prefix + path
175 build_file.write(""" if (%s) {
176 data += [ "%s" ]
177 }
178
179""" % (entry['condition'], path))
180
181def ProcessDir(root_path, path, build_file, path_prefix):
182 # Write all dirs and files directly under |path| unless they are excluded
183 # or need to be processed further because some of their children are excldued
184 # or conditionally included.
185 # Return a list of dirs that needs to processed further.
186 logging.debug('GenerateList for ' + path)
187 entry_list = os.listdir(path)
Zhenyao Mob18e8eb2018-08-28 16:43:56 -0700188 entry_list.sort()
Zhenyao Mo72712b02018-08-23 15:55:12 -0700189 file_list = []
190 dir_list = []
191 conditional_list = []
192 expand_list = []
nednguyenfebcf872018-09-07 14:19:11 -0400193 untracked_paths = GetUntrackedPaths()
Zhenyao Mo72712b02018-08-23 15:55:12 -0700194 for entry in entry_list:
195 full_path = os.path.join(path, entry)
196 rel_path = os.path.relpath(full_path, root_path)
nednguyenfebcf872018-09-07 14:19:11 -0400197 if (any(full_path.startswith(p) for p in untracked_paths) or
198 entry.startswith('.') or entry.endswith('~') or
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700199 entry.endswith('.pyc') or entry.endswith('#')):
nednguyenfebcf872018-09-07 14:19:11 -0400200 logging.debug('ignored %s', rel_path)
Zhenyao Mo72712b02018-08-23 15:55:12 -0700201 continue
202 if os.path.isfile(full_path):
203 condition = GetFileCondition(rel_path)
204 if condition == 'true':
205 file_list.append(rel_path)
206 elif condition == 'false':
nednguyenfebcf872018-09-07 14:19:11 -0400207 logging.debug('excluded %s', rel_path)
Zhenyao Mo72712b02018-08-23 15:55:12 -0700208 continue
209 else:
210 conditional_list.append({
211 "condition": condition,
212 "path": rel_path,
213 });
214 elif os.path.isdir(full_path):
215 condition = GetDirCondition(rel_path)
216 if condition == 'true':
217 dir_list.append(rel_path + '/')
218 elif condition == 'false':
nednguyenfebcf872018-09-07 14:19:11 -0400219 logging.debug('excluded %s', rel_path)
Zhenyao Mo72712b02018-08-23 15:55:12 -0700220 elif condition == 'expand':
221 expand_list.append(full_path)
222 else:
223 conditional_list.append({
224 "condition": condition,
225 "path": rel_path + '/',
226 });
227 else:
228 assert False
Zhenyao Mo72712b02018-08-23 15:55:12 -0700229 WriteLists([file_list, dir_list], [conditional_list],
230 build_file, path_prefix)
231 return expand_list
232
233def WriteBuildFileHeader(build_file):
234 build_file.write(LICENSE)
235 build_file.write(DO_NOT_EDIT_WARNING)
236 build_file.write('import("//build/config/compiler/compiler.gni")\n\n')
237
238def WriteBuildFileBody(build_file, root_path, path_prefix):
239 build_file.write("""group("%s") {
240 testonly = true
241 data = []
242
243""" % TELEMETRY_SUPPORT_GROUP_NAME)
244
245 candidates = [root_path]
246 while len(candidates) > 0:
247 candidate = candidates.pop(0)
248 more = ProcessDir(root_path, candidate, build_file, path_prefix)
249 candidates.extend(more)
250
251 build_file.write("}")
252
253def GenerateBuildFile(root_path, output_path, chromium):
254 CHROMIUM_GROUP = 'group("telemetry_chrome_test_without_chrome")'
255 CATAPULT_PREFIX = '//third_party/catapult'
256 CATAPULT_GROUP_NAME = CATAPULT_PREFIX + ':' + TELEMETRY_SUPPORT_GROUP_NAME
257 TELEMETRY_SUPPORT_GROUP = 'group("%s")' % TELEMETRY_SUPPORT_GROUP_NAME
258 if chromium:
259 build_file = open(output_path, 'r+')
260 contents = build_file.readlines()
261 build_file.seek(0)
262 remove_telemetry_support_group = False
263 for line in contents:
264 if TELEMETRY_SUPPORT_GROUP in line:
265 # --chromium has already run once, so remove the previously inserted
266 # TELEMETRY_SUPPORT_GROUP so we could add an updated one.
267 remove_telemetry_support_group = True
268 continue
269 if remove_telemetry_support_group:
270 if line == '}\n':
271 remove_telemetry_support_group = False
272 continue
273 if CHROMIUM_GROUP in line:
274 WriteBuildFileBody(build_file, root_path, CATAPULT_PREFIX + '/')
275 build_file.write('\n')
276 elif CATAPULT_GROUP_NAME in line:
277 line = line.replace(CATAPULT_GROUP_NAME,
278 ':' + TELEMETRY_SUPPORT_GROUP_NAME)
279 build_file.write(line)
280 build_file.close()
281 else:
282 build_file = open(output_path, 'w')
283 WriteBuildFileHeader(build_file)
284 WriteBuildFileBody(build_file, root_path, None)
285 build_file.close()
286
287def CheckForChanges():
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700288 # Return 0 if no changes are detected; return 1 otherwise.
Zhenyao Mo72712b02018-08-23 15:55:12 -0700289 root_path = os.path.dirname(os.path.realpath(__file__))
290 temp_path = os.path.join(root_path, "TEMP.gn")
291 GenerateBuildFile(root_path, temp_path, chromium=False)
292
293 ref_path = os.path.join(root_path, "BUILD.gn")
294 if not os.path.exists(ref_path):
295 logging.error("Can't localte BUILD.gn!")
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700296 return 1
Zhenyao Mo72712b02018-08-23 15:55:12 -0700297
298 temp_file = open(temp_path, 'r')
299 temp_content = temp_file.readlines()
300 temp_file.close()
301 os.remove(temp_path)
302 ref_file = open(ref_path, 'r')
303 ref_content = ref_file.readlines()
304 ref_file.close()
305
306 diff = difflib.unified_diff(temp_content, ref_content, fromfile=temp_path,
307 tofile=ref_path, lineterm='')
308 diff_data = []
309 for line in diff:
310 diff_data.append(line)
311 if len(diff_data) > 0:
312 logging.error('Diff found. Please rerun generate_telemetry_build.py.')
313 logging.debug('\n' + ''.join(diff_data))
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700314 return 1
Zhenyao Mo72712b02018-08-23 15:55:12 -0700315 logging.debug('No diff found. Everything is good.')
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700316 return 0
Zhenyao Mo72712b02018-08-23 15:55:12 -0700317
318
319def main(argv):
320 parser = optparse.OptionParser()
321 parser.add_option("-v", "--verbose", action="store_true", default=False,
322 help="print out debug information")
323 parser.add_option("-c", "--check", action="store_true", default=False,
324 help="generate a temporary build file and compare if it "
325 "is the same as the current BUILD.gn")
326 parser.add_option("--chromium", action="store_true", default=False,
327 help="generate the build file into chromium workspace. "
328 "This is for the purpose of running try jobs in Chrome.")
329 (options, _) = parser.parse_args(args=argv)
330 if options.verbose:
331 logging.basicConfig(level=logging.DEBUG)
332
333 if options.check:
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700334 return CheckForChanges()
335 if options.chromium:
Zhenyao Mo72712b02018-08-23 15:55:12 -0700336 root_path = os.path.dirname(os.path.realpath(__file__))
337 output_path = os.path.join(
338 root_path, "../../tools/perf/chrome_telemetry_build/BUILD.gn")
339 GenerateBuildFile(root_path, output_path, chromium=True)
340 else:
341 root_path = os.path.dirname(os.path.realpath(__file__))
342 output_path = os.path.join(root_path, "BUILD.gn")
343 GenerateBuildFile(root_path, output_path, chromium=False)
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700344 return 0
Zhenyao Mo72712b02018-08-23 15:55:12 -0700345
346if __name__ == '__main__':
347 sys.exit(main(sys.argv[1:]))