blob: 4fdba8740e6706c11775fabfae93fafc490ce96e [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 {
46 # needed for --chromium option; can remove once this CL lands.
47 "path": "generate_telemetry_build.py",
48 },
49 {
50 "path": "telemetry/telemetry/bin",
51 },
52 {
53 "path": "telemetry/telemetry/internal/bin",
54 },
55 {
56 # needed for --check option
57 "path": "TEMP.gn",
58 },
59 {
60 "path": "third_party/google-endpoints",
61 },
62 {
63 "path": "third_party/Paste",
64 },
65 {
66 "path": "third_party/polymer2",
67 },
68 {
69 "path": "third_party/vinn/third_party/v8/linux/arm",
70 "condition": "is_chromeos",
71 },
72 {
73 "path": "third_party/vinn/third_party/v8/linux/mips",
74 "condition": "is_chromeos",
75 },
76 {
77 "path": "third_party/vinn/third_party/v8/linux/mips64",
78 "condition": "is_chromeos",
79 },
80 {
81 "path": "third_party/vinn/third_party/v8/linux/x86_64",
82 "condition": "is_linux || is_android",
83 },
84 {
85 "path": "third_party/vinn/third_party/v8/mac",
86 "condition": "is_mac",
87 },
88 {
89 "path": "third_party/vinn/third_party/v8/win",
90 "condition": "is_win",
91 },
92 {
93 "path": "tracing/test_data",
94 },
95]
96
97def GetFileCondition(rel_path):
98 # Return 'true' if the file should be included; return 'false' if it should
99 # be excluded; return a condition string if it should only be included if
100 # the condition is true.
101 processed_rel_path = rel_path.replace('\\', '/')
102 for exclusion in EXCLUDED_PATHS:
103 assert 'path' in exclusion
104 if exclusion['path'] == processed_rel_path:
105 if 'condition' in exclusion:
106 return exclusion['condition']
107 else:
108 return 'false'
109 return 'true'
110
111def GetDirCondition(rel_path):
112 # Return 'true' if the dir should be included; return 'false' if it should
113 # be excluded; return a condition string if it should only be included if
114 # the condition is true; return 'expand' if some files or sub-dirs under it
115 # are excluded or conditionally included, so the parser needs to go inside
116 # the dir and process further.
117 processed_rel_path = rel_path.replace('\\', '/')
118 for exclusion in EXCLUDED_PATHS:
119 assert 'path' in exclusion
120 if exclusion['path'] == processed_rel_path:
121 if 'condition' in exclusion:
122 return exclusion['condition']
123 else:
124 return 'false'
125 elif exclusion['path'].startswith(processed_rel_path + '/'):
126 return 'expand'
127 return 'true'
128
129def WriteLists(lists, conditional_lists, build_file, path_prefix):
130 first_entry = True
131 for path_list in lists:
132 for path in path_list:
133 path = path.replace('\\', '/')
134 if path_prefix:
135 path = path_prefix + path
136 if first_entry:
137 build_file.write(' data += [\n')
138 first_entry = False
139 build_file.write(' "%s",\n' % path)
140 if not first_entry:
141 build_file.write(' ]\n\n')
142 for conditional_list in conditional_lists:
143 for entry in conditional_list:
144 assert 'path' in entry
145 assert 'condition' in entry
146 path = entry['path'].replace('\\', '/')
147 if path_prefix:
148 path = path_prefix + path
149 build_file.write(""" if (%s) {
150 data += [ "%s" ]
151 }
152
153""" % (entry['condition'], path))
154
155def ProcessDir(root_path, path, build_file, path_prefix):
156 # Write all dirs and files directly under |path| unless they are excluded
157 # or need to be processed further because some of their children are excldued
158 # or conditionally included.
159 # Return a list of dirs that needs to processed further.
160 logging.debug('GenerateList for ' + path)
161 entry_list = os.listdir(path)
162 file_list = []
163 dir_list = []
164 conditional_list = []
165 expand_list = []
166 for entry in entry_list:
167 full_path = os.path.join(path, entry)
168 rel_path = os.path.relpath(full_path, root_path)
169 if entry.startswith('.') or entry.endswith('~') or entry.endswith('.pyc'):
170 logging.debug('ignored ' + rel_path)
171 continue
172 if os.path.isfile(full_path):
173 condition = GetFileCondition(rel_path)
174 if condition == 'true':
175 file_list.append(rel_path)
176 elif condition == 'false':
177 logging.debug('excluded ' + rel_path)
178 continue
179 else:
180 conditional_list.append({
181 "condition": condition,
182 "path": rel_path,
183 });
184 elif os.path.isdir(full_path):
185 condition = GetDirCondition(rel_path)
186 if condition == 'true':
187 dir_list.append(rel_path + '/')
188 elif condition == 'false':
189 logging.debug('excluded ' + rel_path)
190 elif condition == 'expand':
191 expand_list.append(full_path)
192 else:
193 conditional_list.append({
194 "condition": condition,
195 "path": rel_path + '/',
196 });
197 else:
198 assert False
199 file_list.sort()
200 dir_list.sort()
201 WriteLists([file_list, dir_list], [conditional_list],
202 build_file, path_prefix)
203 return expand_list
204
205def WriteBuildFileHeader(build_file):
206 build_file.write(LICENSE)
207 build_file.write(DO_NOT_EDIT_WARNING)
208 build_file.write('import("//build/config/compiler/compiler.gni")\n\n')
209
210def WriteBuildFileBody(build_file, root_path, path_prefix):
211 build_file.write("""group("%s") {
212 testonly = true
213 data = []
214
215""" % TELEMETRY_SUPPORT_GROUP_NAME)
216
217 candidates = [root_path]
218 while len(candidates) > 0:
219 candidate = candidates.pop(0)
220 more = ProcessDir(root_path, candidate, build_file, path_prefix)
221 candidates.extend(more)
222
223 build_file.write("}")
224
225def GenerateBuildFile(root_path, output_path, chromium):
226 CHROMIUM_GROUP = 'group("telemetry_chrome_test_without_chrome")'
227 CATAPULT_PREFIX = '//third_party/catapult'
228 CATAPULT_GROUP_NAME = CATAPULT_PREFIX + ':' + TELEMETRY_SUPPORT_GROUP_NAME
229 TELEMETRY_SUPPORT_GROUP = 'group("%s")' % TELEMETRY_SUPPORT_GROUP_NAME
230 if chromium:
231 build_file = open(output_path, 'r+')
232 contents = build_file.readlines()
233 build_file.seek(0)
234 remove_telemetry_support_group = False
235 for line in contents:
236 if TELEMETRY_SUPPORT_GROUP in line:
237 # --chromium has already run once, so remove the previously inserted
238 # TELEMETRY_SUPPORT_GROUP so we could add an updated one.
239 remove_telemetry_support_group = True
240 continue
241 if remove_telemetry_support_group:
242 if line == '}\n':
243 remove_telemetry_support_group = False
244 continue
245 if CHROMIUM_GROUP in line:
246 WriteBuildFileBody(build_file, root_path, CATAPULT_PREFIX + '/')
247 build_file.write('\n')
248 elif CATAPULT_GROUP_NAME in line:
249 line = line.replace(CATAPULT_GROUP_NAME,
250 ':' + TELEMETRY_SUPPORT_GROUP_NAME)
251 build_file.write(line)
252 build_file.close()
253 else:
254 build_file = open(output_path, 'w')
255 WriteBuildFileHeader(build_file)
256 WriteBuildFileBody(build_file, root_path, None)
257 build_file.close()
258
259def CheckForChanges():
260 root_path = os.path.dirname(os.path.realpath(__file__))
261 temp_path = os.path.join(root_path, "TEMP.gn")
262 GenerateBuildFile(root_path, temp_path, chromium=False)
263
264 ref_path = os.path.join(root_path, "BUILD.gn")
265 if not os.path.exists(ref_path):
266 logging.error("Can't localte BUILD.gn!")
267 return False
268
269 temp_file = open(temp_path, 'r')
270 temp_content = temp_file.readlines()
271 temp_file.close()
272 os.remove(temp_path)
273 ref_file = open(ref_path, 'r')
274 ref_content = ref_file.readlines()
275 ref_file.close()
276
277 diff = difflib.unified_diff(temp_content, ref_content, fromfile=temp_path,
278 tofile=ref_path, lineterm='')
279 diff_data = []
280 for line in diff:
281 diff_data.append(line)
282 if len(diff_data) > 0:
283 logging.error('Diff found. Please rerun generate_telemetry_build.py.')
284 logging.debug('\n' + ''.join(diff_data))
285 return False
286 logging.debug('No diff found. Everything is good.')
287 return True
288
289
290def main(argv):
291 parser = optparse.OptionParser()
292 parser.add_option("-v", "--verbose", action="store_true", default=False,
293 help="print out debug information")
294 parser.add_option("-c", "--check", action="store_true", default=False,
295 help="generate a temporary build file and compare if it "
296 "is the same as the current BUILD.gn")
297 parser.add_option("--chromium", action="store_true", default=False,
298 help="generate the build file into chromium workspace. "
299 "This is for the purpose of running try jobs in Chrome.")
300 (options, _) = parser.parse_args(args=argv)
301 if options.verbose:
302 logging.basicConfig(level=logging.DEBUG)
303
304 if options.check:
305 CheckForChanges()
306 elif options.chromium:
307 root_path = os.path.dirname(os.path.realpath(__file__))
308 output_path = os.path.join(
309 root_path, "../../tools/perf/chrome_telemetry_build/BUILD.gn")
310 GenerateBuildFile(root_path, output_path, chromium=True)
311 else:
312 root_path = os.path.dirname(os.path.realpath(__file__))
313 output_path = os.path.join(root_path, "BUILD.gn")
314 GenerateBuildFile(root_path, output_path, chromium=False)
315
316if __name__ == '__main__':
317 sys.exit(main(sys.argv[1:]))