blob: b4cb036ba9b1ca724329316fa4ce612d38232e39 [file] [log] [blame]
Zhenyao Mo72712b02018-08-23 15:55:12 -07001# Copyright 2018 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Generate BUILD.gn to define all data required to run telemetry test.
6
7If a file/folder of large size is added to catapult but is not needed to run
8telemetry test, then it should be added to the EXCLUDED_PATHS below and rerun
9this script.
10
11This script can also run with --check to see if it needs to rerun to update
12BUILD.gn.
13
14This script can also run with --chromium and rewrite the chromium file
15 //tools/perf/chrome_telemetry_build/BUILD.gn
16This is for the purpose of running try jobs in chromium.
17
18"""
19
20import difflib
21import logging
22import os
23import optparse
24import sys
25
26LICENSE = """# Copyright 2018 The Chromium Authors. All rights reserved.
27# Use of this source code is governed by a BSD-style license that can be
28# found in the LICENSE file.
29
30"""
31
32DO_NOT_EDIT_WARNING = """# This file is auto-generated from
33# //third_party/catapult/generated_telemetry_build.py
34# DO NOT EDIT!
35
36"""
37
38TELEMETRY_SUPPORT_GROUP_NAME = 'telemetry_chrome_test_support'
39
40EXCLUDED_PATHS = [
41 {
42 # needed for --chromium option; can remove once this CL lands.
43 "path": "BUILD.gn",
44 },
45 {
Zhenyao Mobc2c0a92018-08-24 14:26:04 -070046 "path": "common/node_runner/node_runner/bin",
47 },
48 {
49 "path": "common/node_runner/node_runner/node_modules",
50 },
51 {
52 "path": "docs",
53 },
54 {
55 "path": "experimental",
56 },
57 {
Zhenyao Mo72712b02018-08-23 15:55:12 -070058 # needed for --chromium option; can remove once this CL lands.
59 "path": "generate_telemetry_build.py",
60 },
61 {
Zhenyao Mob18e8eb2018-08-28 16:43:56 -070062 "path": "telemetry/telemetry/data",
63 },
64 {
Zhenyao Mo72712b02018-08-23 15:55:12 -070065 "path": "telemetry/telemetry/bin",
66 },
67 {
68 "path": "telemetry/telemetry/internal/bin",
69 },
70 {
71 # needed for --check option
72 "path": "TEMP.gn",
73 },
74 {
75 "path": "third_party/google-endpoints",
76 },
77 {
78 "path": "third_party/Paste",
79 },
80 {
81 "path": "third_party/polymer2",
82 },
83 {
84 "path": "third_party/vinn/third_party/v8/linux/arm",
85 "condition": "is_chromeos",
86 },
87 {
88 "path": "third_party/vinn/third_party/v8/linux/mips",
89 "condition": "is_chromeos",
90 },
91 {
92 "path": "third_party/vinn/third_party/v8/linux/mips64",
93 "condition": "is_chromeos",
94 },
95 {
96 "path": "third_party/vinn/third_party/v8/linux/x86_64",
97 "condition": "is_linux || is_android",
98 },
99 {
100 "path": "third_party/vinn/third_party/v8/mac",
101 "condition": "is_mac",
102 },
103 {
104 "path": "third_party/vinn/third_party/v8/win",
105 "condition": "is_win",
106 },
107 {
108 "path": "tracing/test_data",
109 },
110]
111
112def GetFileCondition(rel_path):
113 # Return 'true' if the file should be included; return 'false' if it should
114 # be excluded; return a condition string if it should only be included if
115 # the condition is true.
116 processed_rel_path = rel_path.replace('\\', '/')
117 for exclusion in EXCLUDED_PATHS:
118 assert 'path' in exclusion
119 if exclusion['path'] == processed_rel_path:
120 if 'condition' in exclusion:
121 return exclusion['condition']
122 else:
123 return 'false'
124 return 'true'
125
126def GetDirCondition(rel_path):
127 # Return 'true' if the dir should be included; return 'false' if it should
128 # be excluded; return a condition string if it should only be included if
129 # the condition is true; return 'expand' if some files or sub-dirs under it
130 # are excluded or conditionally included, so the parser needs to go inside
131 # the dir and process further.
132 processed_rel_path = rel_path.replace('\\', '/')
133 for exclusion in EXCLUDED_PATHS:
134 assert 'path' in exclusion
135 if exclusion['path'] == processed_rel_path:
136 if 'condition' in exclusion:
137 return exclusion['condition']
138 else:
139 return 'false'
140 elif exclusion['path'].startswith(processed_rel_path + '/'):
141 return 'expand'
142 return 'true'
143
144def WriteLists(lists, conditional_lists, build_file, path_prefix):
145 first_entry = True
146 for path_list in lists:
147 for path in path_list:
148 path = path.replace('\\', '/')
149 if path_prefix:
150 path = path_prefix + path
151 if first_entry:
152 build_file.write(' data += [\n')
153 first_entry = False
154 build_file.write(' "%s",\n' % path)
155 if not first_entry:
156 build_file.write(' ]\n\n')
157 for conditional_list in conditional_lists:
158 for entry in conditional_list:
159 assert 'path' in entry
160 assert 'condition' in entry
161 path = entry['path'].replace('\\', '/')
162 if path_prefix:
163 path = path_prefix + path
164 build_file.write(""" if (%s) {
165 data += [ "%s" ]
166 }
167
168""" % (entry['condition'], path))
169
170def ProcessDir(root_path, path, build_file, path_prefix):
171 # Write all dirs and files directly under |path| unless they are excluded
172 # or need to be processed further because some of their children are excldued
173 # or conditionally included.
174 # Return a list of dirs that needs to processed further.
175 logging.debug('GenerateList for ' + path)
176 entry_list = os.listdir(path)
Zhenyao Mob18e8eb2018-08-28 16:43:56 -0700177 entry_list.sort()
Zhenyao Mo72712b02018-08-23 15:55:12 -0700178 file_list = []
179 dir_list = []
180 conditional_list = []
181 expand_list = []
182 for entry in entry_list:
183 full_path = os.path.join(path, entry)
184 rel_path = os.path.relpath(full_path, root_path)
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700185 if (entry.startswith('.') or entry.endswith('~') or
186 entry.endswith('.pyc') or entry.endswith('#')):
Zhenyao Mo72712b02018-08-23 15:55:12 -0700187 logging.debug('ignored ' + rel_path)
188 continue
189 if os.path.isfile(full_path):
190 condition = GetFileCondition(rel_path)
191 if condition == 'true':
192 file_list.append(rel_path)
193 elif condition == 'false':
194 logging.debug('excluded ' + rel_path)
195 continue
196 else:
197 conditional_list.append({
198 "condition": condition,
199 "path": rel_path,
200 });
201 elif os.path.isdir(full_path):
202 condition = GetDirCondition(rel_path)
203 if condition == 'true':
204 dir_list.append(rel_path + '/')
205 elif condition == 'false':
206 logging.debug('excluded ' + rel_path)
207 elif condition == 'expand':
208 expand_list.append(full_path)
209 else:
210 conditional_list.append({
211 "condition": condition,
212 "path": rel_path + '/',
213 });
214 else:
215 assert False
Zhenyao Mo72712b02018-08-23 15:55:12 -0700216 WriteLists([file_list, dir_list], [conditional_list],
217 build_file, path_prefix)
218 return expand_list
219
220def WriteBuildFileHeader(build_file):
221 build_file.write(LICENSE)
222 build_file.write(DO_NOT_EDIT_WARNING)
223 build_file.write('import("//build/config/compiler/compiler.gni")\n\n')
224
225def WriteBuildFileBody(build_file, root_path, path_prefix):
226 build_file.write("""group("%s") {
227 testonly = true
228 data = []
229
230""" % TELEMETRY_SUPPORT_GROUP_NAME)
231
232 candidates = [root_path]
233 while len(candidates) > 0:
234 candidate = candidates.pop(0)
235 more = ProcessDir(root_path, candidate, build_file, path_prefix)
236 candidates.extend(more)
237
238 build_file.write("}")
239
240def GenerateBuildFile(root_path, output_path, chromium):
241 CHROMIUM_GROUP = 'group("telemetry_chrome_test_without_chrome")'
242 CATAPULT_PREFIX = '//third_party/catapult'
243 CATAPULT_GROUP_NAME = CATAPULT_PREFIX + ':' + TELEMETRY_SUPPORT_GROUP_NAME
244 TELEMETRY_SUPPORT_GROUP = 'group("%s")' % TELEMETRY_SUPPORT_GROUP_NAME
245 if chromium:
246 build_file = open(output_path, 'r+')
247 contents = build_file.readlines()
248 build_file.seek(0)
249 remove_telemetry_support_group = False
250 for line in contents:
251 if TELEMETRY_SUPPORT_GROUP in line:
252 # --chromium has already run once, so remove the previously inserted
253 # TELEMETRY_SUPPORT_GROUP so we could add an updated one.
254 remove_telemetry_support_group = True
255 continue
256 if remove_telemetry_support_group:
257 if line == '}\n':
258 remove_telemetry_support_group = False
259 continue
260 if CHROMIUM_GROUP in line:
261 WriteBuildFileBody(build_file, root_path, CATAPULT_PREFIX + '/')
262 build_file.write('\n')
263 elif CATAPULT_GROUP_NAME in line:
264 line = line.replace(CATAPULT_GROUP_NAME,
265 ':' + TELEMETRY_SUPPORT_GROUP_NAME)
266 build_file.write(line)
267 build_file.close()
268 else:
269 build_file = open(output_path, 'w')
270 WriteBuildFileHeader(build_file)
271 WriteBuildFileBody(build_file, root_path, None)
272 build_file.close()
273
274def CheckForChanges():
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700275 # Return 0 if no changes are detected; return 1 otherwise.
Zhenyao Mo72712b02018-08-23 15:55:12 -0700276 root_path = os.path.dirname(os.path.realpath(__file__))
277 temp_path = os.path.join(root_path, "TEMP.gn")
278 GenerateBuildFile(root_path, temp_path, chromium=False)
279
280 ref_path = os.path.join(root_path, "BUILD.gn")
281 if not os.path.exists(ref_path):
282 logging.error("Can't localte BUILD.gn!")
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700283 return 1
Zhenyao Mo72712b02018-08-23 15:55:12 -0700284
285 temp_file = open(temp_path, 'r')
286 temp_content = temp_file.readlines()
287 temp_file.close()
288 os.remove(temp_path)
289 ref_file = open(ref_path, 'r')
290 ref_content = ref_file.readlines()
291 ref_file.close()
292
293 diff = difflib.unified_diff(temp_content, ref_content, fromfile=temp_path,
294 tofile=ref_path, lineterm='')
295 diff_data = []
296 for line in diff:
297 diff_data.append(line)
298 if len(diff_data) > 0:
299 logging.error('Diff found. Please rerun generate_telemetry_build.py.')
300 logging.debug('\n' + ''.join(diff_data))
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700301 return 1
Zhenyao Mo72712b02018-08-23 15:55:12 -0700302 logging.debug('No diff found. Everything is good.')
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700303 return 0
Zhenyao Mo72712b02018-08-23 15:55:12 -0700304
305
306def main(argv):
307 parser = optparse.OptionParser()
308 parser.add_option("-v", "--verbose", action="store_true", default=False,
309 help="print out debug information")
310 parser.add_option("-c", "--check", action="store_true", default=False,
311 help="generate a temporary build file and compare if it "
312 "is the same as the current BUILD.gn")
313 parser.add_option("--chromium", action="store_true", default=False,
314 help="generate the build file into chromium workspace. "
315 "This is for the purpose of running try jobs in Chrome.")
316 (options, _) = parser.parse_args(args=argv)
317 if options.verbose:
318 logging.basicConfig(level=logging.DEBUG)
319
320 if options.check:
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700321 return CheckForChanges()
322 if options.chromium:
Zhenyao Mo72712b02018-08-23 15:55:12 -0700323 root_path = os.path.dirname(os.path.realpath(__file__))
324 output_path = os.path.join(
325 root_path, "../../tools/perf/chrome_telemetry_build/BUILD.gn")
326 GenerateBuildFile(root_path, output_path, chromium=True)
327 else:
328 root_path = os.path.dirname(os.path.realpath(__file__))
329 output_path = os.path.join(root_path, "BUILD.gn")
330 GenerateBuildFile(root_path, output_path, chromium=False)
Zhenyao Mo074cebc2018-08-27 11:31:05 -0700331 return 0
Zhenyao Mo72712b02018-08-23 15:55:12 -0700332
333if __name__ == '__main__':
334 sys.exit(main(sys.argv[1:]))