Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(240)

Side by Side Diff: tools/bisect_utils.py

Issue 359013002: Make a directory in which to put bisect-related modules. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: bisect -> auto_bisect Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/bisect-perf-regression.py ('k') | tools/post_perf_builder_job.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2013 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 """Set of operations/utilities related to checking out the depot, and
6 outputting annotations on the buildbot waterfall. These are intended to be
7 used by the bisection scripts."""
8
9 import errno
10 import imp
11 import os
12 import shutil
13 import stat
14 import subprocess
15 import sys
16
17 DEFAULT_GCLIENT_CUSTOM_DEPS = {
18 "src/data/page_cycler": "https://chrome-internal.googlesource.com/"
19 "chrome/data/page_cycler/.git",
20 "src/data/dom_perf": "https://chrome-internal.googlesource.com/"
21 "chrome/data/dom_perf/.git",
22 "src/data/mach_ports": "https://chrome-internal.googlesource.com/"
23 "chrome/data/mach_ports/.git",
24 "src/tools/perf/data": "https://chrome-internal.googlesource.com/"
25 "chrome/tools/perf/data/.git",
26 "src/third_party/adobe/flash/binaries/ppapi/linux":
27 "https://chrome-internal.googlesource.com/"
28 "chrome/deps/adobe/flash/binaries/ppapi/linux/.git",
29 "src/third_party/adobe/flash/binaries/ppapi/linux_x64":
30 "https://chrome-internal.googlesource.com/"
31 "chrome/deps/adobe/flash/binaries/ppapi/linux_x64/.git",
32 "src/third_party/adobe/flash/binaries/ppapi/mac":
33 "https://chrome-internal.googlesource.com/"
34 "chrome/deps/adobe/flash/binaries/ppapi/mac/.git",
35 "src/third_party/adobe/flash/binaries/ppapi/mac_64":
36 "https://chrome-internal.googlesource.com/"
37 "chrome/deps/adobe/flash/binaries/ppapi/mac_64/.git",
38 "src/third_party/adobe/flash/binaries/ppapi/win":
39 "https://chrome-internal.googlesource.com/"
40 "chrome/deps/adobe/flash/binaries/ppapi/win/.git",
41 "src/third_party/adobe/flash/binaries/ppapi/win_x64":
42 "https://chrome-internal.googlesource.com/"
43 "chrome/deps/adobe/flash/binaries/ppapi/win_x64/.git",
44 "src/chrome/tools/test/reference_build/chrome_win": None,
45 "src/chrome/tools/test/reference_build/chrome_mac": None,
46 "src/chrome/tools/test/reference_build/chrome_linux": None,
47 "src/third_party/WebKit/LayoutTests": None,
48 "src/tools/valgrind": None,}
49
50 GCLIENT_SPEC_DATA = [
51 { "name" : "src",
52 "url" : "https://chromium.googlesource.com/chromium/src.git",
53 "deps_file" : ".DEPS.git",
54 "managed" : True,
55 "custom_deps" : {},
56 "safesync_url": "",
57 },
58 ]
59 GCLIENT_SPEC_ANDROID = "\ntarget_os = ['android']"
60 GCLIENT_CUSTOM_DEPS_V8 = {"src/v8_bleeding_edge": "git://github.com/v8/v8.git"}
61 FILE_DEPS_GIT = '.DEPS.git'
62 FILE_DEPS = 'DEPS'
63
64 REPO_PARAMS = [
65 'https://chrome-internal.googlesource.com/chromeos/manifest-internal/',
66 '--repo-url',
67 'https://git.chromium.org/external/repo.git'
68 ]
69
70 REPO_SYNC_COMMAND = 'git checkout -f $(git rev-list --max-count=1 '\
71 '--before=%d remotes/m/master)'
72
73 ORIGINAL_ENV = {}
74
75 def OutputAnnotationStepStart(name):
76 """Outputs appropriate annotation to signal the start of a step to
77 a trybot.
78
79 Args:
80 name: The name of the step.
81 """
82 print
83 print '@@@SEED_STEP %s@@@' % name
84 print '@@@STEP_CURSOR %s@@@' % name
85 print '@@@STEP_STARTED@@@'
86 print
87 sys.stdout.flush()
88
89
90 def OutputAnnotationStepClosed():
91 """Outputs appropriate annotation to signal the closing of a step to
92 a trybot."""
93 print
94 print '@@@STEP_CLOSED@@@'
95 print
96 sys.stdout.flush()
97
98
99 def OutputAnnotationStepLink(label, url):
100 """Outputs appropriate annotation to print a link.
101
102 Args:
103 label: The name to print.
104 url: The url to print.
105 """
106 print
107 print '@@@STEP_LINK@%s@%s@@@' % (label, url)
108 print
109 sys.stdout.flush()
110
111
112 def LoadExtraSrc(path_to_file):
113 """Attempts to load an extra source file. If this is successful, uses the
114 new module to override some global values, such as gclient spec data.
115
116 Returns:
117 The loaded src module, or None."""
118 try:
119 global GCLIENT_SPEC_DATA
120 global GCLIENT_SPEC_ANDROID
121 extra_src = imp.load_source('data', path_to_file)
122 GCLIENT_SPEC_DATA = extra_src.GetGClientSpec()
123 GCLIENT_SPEC_ANDROID = extra_src.GetGClientSpecExtraParams()
124 return extra_src
125 except ImportError, e:
126 return None
127
128
129 def IsTelemetryCommand(command):
130 """Attempts to discern whether or not a given command is running telemetry."""
131 return ('tools/perf/run_' in command or 'tools\\perf\\run_' in command)
132
133
134 def CreateAndChangeToSourceDirectory(working_directory):
135 """Creates a directory 'bisect' as a subdirectory of 'working_directory'. If
136 the function is successful, the current working directory will change to that
137 of the new 'bisect' directory.
138
139 Returns:
140 True if the directory was successfully created (or already existed).
141 """
142 cwd = os.getcwd()
143 os.chdir(working_directory)
144 try:
145 os.mkdir('bisect')
146 except OSError, e:
147 if e.errno != errno.EEXIST:
148 return False
149 os.chdir('bisect')
150 return True
151
152
153 def SubprocessCall(cmd, cwd=None):
154 """Runs a subprocess with specified parameters.
155
156 Args:
157 params: A list of parameters to pass to gclient.
158 cwd: Working directory to run from.
159
160 Returns:
161 The return code of the call.
162 """
163 if os.name == 'nt':
164 # "HOME" isn't normally defined on windows, but is needed
165 # for git to find the user's .netrc file.
166 if not os.getenv('HOME'):
167 os.environ['HOME'] = os.environ['USERPROFILE']
168 shell = os.name == 'nt'
169 return subprocess.call(cmd, shell=shell, cwd=cwd)
170
171
172 def RunGClient(params, cwd=None):
173 """Runs gclient with the specified parameters.
174
175 Args:
176 params: A list of parameters to pass to gclient.
177 cwd: Working directory to run from.
178
179 Returns:
180 The return code of the call.
181 """
182 cmd = ['gclient'] + params
183
184 return SubprocessCall(cmd, cwd=cwd)
185
186
187 def RunRepo(params):
188 """Runs cros repo command with specified parameters.
189
190 Args:
191 params: A list of parameters to pass to gclient.
192
193 Returns:
194 The return code of the call.
195 """
196 cmd = ['repo'] + params
197
198 return SubprocessCall(cmd)
199
200
201 def RunRepoSyncAtTimestamp(timestamp):
202 """Syncs all git depots to the timestamp specified using repo forall.
203
204 Args:
205 params: Unix timestamp to sync to.
206
207 Returns:
208 The return code of the call.
209 """
210 repo_sync = REPO_SYNC_COMMAND % timestamp
211 cmd = ['forall', '-c', REPO_SYNC_COMMAND % timestamp]
212 return RunRepo(cmd)
213
214
215 def RunGClientAndCreateConfig(opts, custom_deps=None, cwd=None):
216 """Runs gclient and creates a config containing both src and src-internal.
217
218 Args:
219 opts: The options parsed from the command line through parse_args().
220 custom_deps: A dictionary of additional dependencies to add to .gclient.
221 cwd: Working directory to run from.
222
223 Returns:
224 The return code of the call.
225 """
226 spec = GCLIENT_SPEC_DATA
227
228 if custom_deps:
229 for k, v in custom_deps.iteritems():
230 spec[0]['custom_deps'][k] = v
231
232 # Cannot have newlines in string on windows
233 spec = 'solutions =' + str(spec)
234 spec = ''.join([l for l in spec.splitlines()])
235
236 if 'android' in opts.target_platform:
237 spec += GCLIENT_SPEC_ANDROID
238
239 return_code = RunGClient(
240 ['config', '--spec=%s' % spec, '--git-deps'], cwd=cwd)
241 return return_code
242
243
244 def IsDepsFileBlink():
245 """Reads .DEPS.git and returns whether or not we're using blink.
246
247 Returns:
248 True if blink, false if webkit.
249 """
250 locals = {'Var': lambda _: locals["vars"][_],
251 'From': lambda *args: None}
252 execfile(FILE_DEPS_GIT, {}, locals)
253 return 'blink.git' in locals['vars']['webkit_url']
254
255
256 def OnAccessError(func, path, exc_info):
257 """
258 Source: http://stackoverflow.com/questions/2656322/python-shutil-rmtree-fails- on-windows-with-access-is-denied
259
260 Error handler for ``shutil.rmtree``.
261
262 If the error is due to an access error (read only file)
263 it attempts to add write permission and then retries.
264
265 If the error is for another reason it re-raises the error.
266
267 Args:
268 func: The function that raised the error.
269 path: The path name passed to func.
270 exc_info: Exception information returned by sys.exc_info().
271 """
272 if not os.access(path, os.W_OK):
273 # Is the error an access error ?
274 os.chmod(path, stat.S_IWUSR)
275 func(path)
276 else:
277 raise
278
279
280 def RemoveThirdPartyDirectory(dir_name):
281 """Removes third_party directory from the source.
282
283 At some point, some of the third_parties were causing issues to changes in
284 the way they are synced. We remove such folder in order to avoid sync errors
285 while bisecting.
286
287 Returns:
288 True on success, otherwise False.
289 """
290 path_to_dir = os.path.join(os.getcwd(), 'third_party', dir_name)
291 try:
292 if os.path.exists(path_to_dir):
293 shutil.rmtree(path_to_dir, onerror=OnAccessError)
294 except OSError, e:
295 print 'Error #%d while running shutil.rmtree(%s): %s' % (
296 e.errno, path_to_dir, str(e))
297 if e.errno != errno.ENOENT:
298 return False
299 return True
300
301
302 def _CleanupPreviousGitRuns():
303 """Performs necessary cleanup between runs."""
304 # If a previous run of git crashed, bot was reset, etc... we
305 # might end up with leftover index.lock files.
306 for (path, dir, files) in os.walk(os.getcwd()):
307 for cur_file in files:
308 if cur_file.endswith('index.lock'):
309 path_to_file = os.path.join(path, cur_file)
310 os.remove(path_to_file)
311
312
313 def RunGClientAndSync(cwd=None):
314 """Runs gclient and does a normal sync.
315
316 Args:
317 cwd: Working directory to run from.
318
319 Returns:
320 The return code of the call.
321 """
322 params = ['sync', '--verbose', '--nohooks', '--reset', '--force']
323 return RunGClient(params, cwd=cwd)
324
325
326 def SetupGitDepot(opts, custom_deps):
327 """Sets up the depot for the bisection. The depot will be located in a
328 subdirectory called 'bisect'.
329
330 Args:
331 opts: The options parsed from the command line through parse_args().
332 custom_deps: A dictionary of additional dependencies to add to .gclient.
333
334 Returns:
335 True if gclient successfully created the config file and did a sync, False
336 otherwise.
337 """
338 name = 'Setting up Bisection Depot'
339
340 if opts.output_buildbot_annotations:
341 OutputAnnotationStepStart(name)
342
343 passed = False
344
345 if not RunGClientAndCreateConfig(opts, custom_deps):
346 passed_deps_check = True
347 if os.path.isfile(os.path.join('src', FILE_DEPS_GIT)):
348 cwd = os.getcwd()
349 os.chdir('src')
350 if not IsDepsFileBlink():
351 passed_deps_check = RemoveThirdPartyDirectory('Webkit')
352 else:
353 passed_deps_check = True
354 if passed_deps_check:
355 passed_deps_check = RemoveThirdPartyDirectory('libjingle')
356 if passed_deps_check:
357 passed_deps_check = RemoveThirdPartyDirectory('skia')
358 os.chdir(cwd)
359
360 if passed_deps_check:
361 _CleanupPreviousGitRuns()
362
363 RunGClient(['revert'])
364 if not RunGClientAndSync():
365 passed = True
366
367 if opts.output_buildbot_annotations:
368 print
369 OutputAnnotationStepClosed()
370
371 return passed
372
373
374 def SetupCrosRepo():
375 """Sets up cros repo for bisecting chromeos.
376
377 Returns:
378 Returns 0 on success.
379 """
380 cwd = os.getcwd()
381 try:
382 os.mkdir('cros')
383 except OSError, e:
384 if e.errno != errno.EEXIST:
385 return False
386 os.chdir('cros')
387
388 cmd = ['init', '-u'] + REPO_PARAMS
389
390 passed = False
391
392 if not RunRepo(cmd):
393 if not RunRepo(['sync']):
394 passed = True
395 os.chdir(cwd)
396
397 return passed
398
399
400 def CopyAndSaveOriginalEnvironmentVars():
401 """Makes a copy of the current environment variables."""
402 # TODO: Waiting on crbug.com/255689, will remove this after.
403 vars_to_remove = []
404 for k, v in os.environ.iteritems():
405 if 'ANDROID' in k:
406 vars_to_remove.append(k)
407 vars_to_remove.append('CHROME_SRC')
408 vars_to_remove.append('CHROMIUM_GYP_FILE')
409 vars_to_remove.append('GYP_CROSSCOMPILE')
410 vars_to_remove.append('GYP_DEFINES')
411 vars_to_remove.append('GYP_GENERATORS')
412 vars_to_remove.append('GYP_GENERATOR_FLAGS')
413 vars_to_remove.append('OBJCOPY')
414 for k in vars_to_remove:
415 if os.environ.has_key(k):
416 del os.environ[k]
417
418 global ORIGINAL_ENV
419 ORIGINAL_ENV = os.environ.copy()
420
421
422 def SetupAndroidBuildEnvironment(opts, path_to_src=None):
423 """Sets up the android build environment.
424
425 Args:
426 opts: The options parsed from the command line through parse_args().
427 path_to_src: Path to the src checkout.
428
429 Returns:
430 True if successful.
431 """
432
433 # Revert the environment variables back to default before setting them up
434 # with envsetup.sh.
435 env_vars = os.environ.copy()
436 for k, _ in env_vars.iteritems():
437 del os.environ[k]
438 for k, v in ORIGINAL_ENV.iteritems():
439 os.environ[k] = v
440
441 path_to_file = os.path.join('build', 'android', 'envsetup.sh')
442 proc = subprocess.Popen(['bash', '-c', 'source %s && env' % path_to_file],
443 stdout=subprocess.PIPE,
444 stderr=subprocess.PIPE,
445 cwd=path_to_src)
446 (out, _) = proc.communicate()
447
448 for line in out.splitlines():
449 (k, _, v) = line.partition('=')
450 os.environ[k] = v
451 # envsetup.sh no longer sets OS=android to GYP_DEFINES env variable
452 # (CL/170273005). Set this variable explicitly inorder to build chrome on
453 # android.
454 try:
455 if 'OS=android' not in os.environ['GYP_DEFINES']:
456 os.environ['GYP_DEFINES'] = '%s %s' % (os.environ['GYP_DEFINES'],
457 'OS=android')
458 except KeyError:
459 os.environ['GYP_DEFINES'] = 'OS=android'
460
461 if opts.use_goma:
462 os.environ['GYP_DEFINES'] = '%s %s' % (os.environ['GYP_DEFINES'],
463 'use_goma=1')
464 return not proc.returncode
465
466
467 def SetupPlatformBuildEnvironment(opts):
468 """Performs any platform specific setup.
469
470 Args:
471 opts: The options parsed from the command line through parse_args().
472
473 Returns:
474 True if successful.
475 """
476 if 'android' in opts.target_platform:
477 CopyAndSaveOriginalEnvironmentVars()
478 return SetupAndroidBuildEnvironment(opts)
479 elif opts.target_platform == 'cros':
480 return SetupCrosRepo()
481
482 return True
483
484
485 def CheckIfBisectDepotExists(opts):
486 """Checks if the bisect directory already exists.
487
488 Args:
489 opts: The options parsed from the command line through parse_args().
490
491 Returns:
492 Returns True if it exists.
493 """
494 path_to_dir = os.path.join(opts.working_directory, 'bisect', 'src')
495 return os.path.exists(path_to_dir)
496
497
498 def CreateBisectDirectoryAndSetupDepot(opts, custom_deps):
499 """Sets up a subdirectory 'bisect' and then retrieves a copy of the depot
500 there using gclient.
501
502 Args:
503 opts: The options parsed from the command line through parse_args().
504 custom_deps: A dictionary of additional dependencies to add to .gclient.
505 """
506 if not CreateAndChangeToSourceDirectory(opts.working_directory):
507 raise RuntimeError('Could not create bisect directory.')
508
509 if not SetupGitDepot(opts, custom_deps):
510 raise RuntimeError('Failed to grab source.')
OLDNEW
« no previous file with comments | « tools/bisect-perf-regression.py ('k') | tools/post_perf_builder_job.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698