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.
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."""
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",}
47 "url" : "https://chromium.googlesource.com/chromium/src.git",
48 "deps_file" : ".DEPS.git",
54 GCLIENT_SPEC_ANDROID
= "\ntarget_os = ['android']"
55 GCLIENT_CUSTOM_DEPS_V8
= {"src/v8_bleeding_edge": "git://github.com/v8/v8.git"}
56 FILE_DEPS_GIT
= '.DEPS.git'
59 'https://chrome-internal.googlesource.com/chromeos/manifest-internal/',
61 'https://git.chromium.org/external/repo.git'
64 REPO_SYNC_COMMAND
= 'git checkout -f $(git rev-list --max-count=1 '\
65 '--before=%d remotes/m/master)'
69 def OutputAnnotationStepStart(name
):
70 """Outputs appropriate annotation to signal the start of a step to
74 name: The name of the step.
77 print '@@@SEED_STEP %s@@@' % name
78 print '@@@STEP_CURSOR %s@@@' % name
79 print '@@@STEP_STARTED@@@'
84 def OutputAnnotationStepClosed():
85 """Outputs appropriate annotation to signal the closing of a step to
88 print '@@@STEP_CLOSED@@@'
93 def OutputAnnotationStepLink(label
, url
):
94 """Outputs appropriate annotation to print a link.
97 label: The name to print.
98 url: The url to print.
101 print '@@@STEP_LINK@%s@%s@@@' % (label
, url
)
106 def LoadExtraSrc(path_to_file
):
107 """Attempts to load an extra source file. If this is successful, uses the
108 new module to override some global values, such as gclient spec data.
111 The loaded src module, or None."""
113 global GCLIENT_SPEC_DATA
114 global GCLIENT_SPEC_ANDROID
115 extra_src
= imp
.load_source('data', path_to_file
)
116 GCLIENT_SPEC_DATA
= extra_src
.GetGClientSpec()
117 GCLIENT_SPEC_ANDROID
= extra_src
.GetGClientSpecExtraParams()
119 except ImportError, e
:
123 def IsTelemetryCommand(command
):
124 """Attempts to discern whether or not a given command is running telemetry."""
125 return ('tools/perf/run_' in command
or 'tools\\perf\\run_' in command
)
128 def CreateAndChangeToSourceDirectory(working_directory
):
129 """Creates a directory 'bisect' as a subdirectory of 'working_directory'. If
130 the function is successful, the current working directory will change to that
131 of the new 'bisect' directory.
134 True if the directory was successfully created (or already existed).
137 os
.chdir(working_directory
)
141 if e
.errno
!= errno
.EEXIST
:
147 def SubprocessCall(cmd
, cwd
=None):
148 """Runs a subprocess with specified parameters.
151 params: A list of parameters to pass to gclient.
152 cwd: Working directory to run from.
155 The return code of the call.
158 # "HOME" isn't normally defined on windows, but is needed
159 # for git to find the user's .netrc file.
160 if not os
.getenv('HOME'):
161 os
.environ
['HOME'] = os
.environ
['USERPROFILE']
162 shell
= os
.name
== 'nt'
163 return subprocess
.call(cmd
, shell
=shell
, cwd
=cwd
)
166 def RunGClient(params
, cwd
=None):
167 """Runs gclient with the specified parameters.
170 params: A list of parameters to pass to gclient.
171 cwd: Working directory to run from.
174 The return code of the call.
176 cmd
= ['gclient'] + params
178 return SubprocessCall(cmd
, cwd
=cwd
)
182 """Runs cros repo command with specified parameters.
185 params: A list of parameters to pass to gclient.
188 The return code of the call.
190 cmd
= ['repo'] + params
192 return SubprocessCall(cmd
)
195 def RunRepoSyncAtTimestamp(timestamp
):
196 """Syncs all git depots to the timestamp specified using repo forall.
199 params: Unix timestamp to sync to.
202 The return code of the call.
204 repo_sync
= REPO_SYNC_COMMAND
% timestamp
205 cmd
= ['forall', '-c', REPO_SYNC_COMMAND
% timestamp
]
209 def RunGClientAndCreateConfig(opts
, custom_deps
=None, cwd
=None):
210 """Runs gclient and creates a config containing both src and src-internal.
213 opts: The options parsed from the command line through parse_args().
214 custom_deps: A dictionary of additional dependencies to add to .gclient.
215 cwd: Working directory to run from.
218 The return code of the call.
220 spec
= GCLIENT_SPEC_DATA
223 for k
, v
in custom_deps
.iteritems():
224 spec
[0]['custom_deps'][k
] = v
226 # Cannot have newlines in string on windows
227 spec
= 'solutions =' + str(spec
)
228 spec
= ''.join([l
for l
in spec
.splitlines()])
230 if 'android' in opts
.target_platform
:
231 spec
+= GCLIENT_SPEC_ANDROID
233 return_code
= RunGClient(
234 ['config', '--spec=%s' % spec
, '--git-deps'], cwd
=cwd
)
238 def IsDepsFileBlink():
239 """Reads .DEPS.git and returns whether or not we're using blink.
242 True if blink, false if webkit.
244 locals = {'Var': lambda _
: locals["vars"][_
],
245 'From': lambda *args
: None}
246 execfile(FILE_DEPS_GIT
, {}, locals)
247 return 'blink.git' in locals['vars']['webkit_url']
250 def RemoveThirdPartyWebkitDirectory():
251 """Removes third_party/WebKit.
257 path_to_dir
= os
.path
.join(os
.getcwd(), 'third_party', 'WebKit')
258 if os
.path
.exists(path_to_dir
):
259 shutil
.rmtree(path_to_dir
)
261 if e
.errno
!= errno
.ENOENT
:
266 def OnAccessError(func
, path
, exc_info
):
268 Source: http://stackoverflow.com/questions/2656322/python-shutil-rmtree-fails-on-windows-with-access-is-denied
270 Error handler for ``shutil.rmtree``.
272 If the error is due to an access error (read only file)
273 it attempts to add write permission and then retries.
275 If the error is for another reason it re-raises the error.
278 func: The function that raised the error.
279 path: The path name passed to func.
280 exc_info: Exception information returned by sys.exc_info().
282 if not os
.access(path
, os
.W_OK
):
283 # Is the error an access error ?
284 os
.chmod(path
, stat
.S_IWUSR
)
290 def RemoveThirdPartyLibjingleDirectory():
291 """Removes third_party/libjingle. At some point, libjingle was causing issues
292 syncing when using the git workflow (crbug.com/266324).
297 path_to_dir
= os
.path
.join(os
.getcwd(), 'third_party', 'libjingle')
299 if os
.path
.exists(path_to_dir
):
300 shutil
.rmtree(path_to_dir
, onerror
=OnAccessError
)
302 print 'Error #%d while running shutil.rmtree(%s): %s' % (
303 e
.errno
, path_to_dir
, str(e
))
304 if e
.errno
!= errno
.ENOENT
:
309 def _CleanupPreviousGitRuns():
310 """Performs necessary cleanup between runs."""
311 # If a previous run of git crashed, bot was reset, etc... we
312 # might end up with leftover index.lock files.
313 for (path
, dir, files
) in os
.walk(os
.getcwd()):
314 for cur_file
in files
:
315 if cur_file
.endswith('index.lock'):
316 path_to_file
= os
.path
.join(path
, cur_file
)
317 os
.remove(path_to_file
)
320 def RunGClientAndSync(cwd
=None):
321 """Runs gclient and does a normal sync.
324 cwd: Working directory to run from.
327 The return code of the call.
329 params
= ['sync', '--verbose', '--nohooks', '--reset', '--force']
330 return RunGClient(params
, cwd
=cwd
)
333 def SetupGitDepot(opts
, custom_deps
):
334 """Sets up the depot for the bisection. The depot will be located in a
335 subdirectory called 'bisect'.
338 opts: The options parsed from the command line through parse_args().
339 custom_deps: A dictionary of additional dependencies to add to .gclient.
342 True if gclient successfully created the config file and did a sync, False
345 name
= 'Setting up Bisection Depot'
347 if opts
.output_buildbot_annotations
:
348 OutputAnnotationStepStart(name
)
352 if not RunGClientAndCreateConfig(opts
, custom_deps
):
353 passed_deps_check
= True
354 if os
.path
.isfile(os
.path
.join('src', FILE_DEPS_GIT
)):
357 if not IsDepsFileBlink():
358 passed_deps_check
= RemoveThirdPartyWebkitDirectory()
360 passed_deps_check
= True
361 if passed_deps_check
:
362 passed_deps_check
= RemoveThirdPartyLibjingleDirectory()
365 if passed_deps_check
:
366 _CleanupPreviousGitRuns()
368 RunGClient(['revert'])
369 if not RunGClientAndSync():
372 if opts
.output_buildbot_annotations
:
374 OutputAnnotationStepClosed()
380 """Sets up cros repo for bisecting chromeos.
383 Returns 0 on success.
389 if e
.errno
!= errno
.EEXIST
:
393 cmd
= ['init', '-u'] + REPO_PARAMS
398 if not RunRepo(['sync']):
405 def CopyAndSaveOriginalEnvironmentVars():
406 """Makes a copy of the current environment variables."""
407 # TODO: Waiting on crbug.com/255689, will remove this after.
409 for k
, v
in os
.environ
.iteritems():
411 vars_to_remove
.append(k
)
412 vars_to_remove
.append('CHROME_SRC')
413 vars_to_remove
.append('CHROMIUM_GYP_FILE')
414 vars_to_remove
.append('GYP_CROSSCOMPILE')
415 vars_to_remove
.append('GYP_DEFINES')
416 vars_to_remove
.append('GYP_GENERATORS')
417 vars_to_remove
.append('GYP_GENERATOR_FLAGS')
418 vars_to_remove
.append('OBJCOPY')
419 for k
in vars_to_remove
:
420 if os
.environ
.has_key(k
):
424 ORIGINAL_ENV
= os
.environ
.copy()
427 def SetupAndroidBuildEnvironment(opts
, path_to_src
=None):
428 """Sets up the android build environment.
431 opts: The options parsed from the command line through parse_args().
432 path_to_src: Path to the src checkout.
438 # Revert the environment variables back to default before setting them up
440 env_vars
= os
.environ
.copy()
441 for k
, _
in env_vars
.iteritems():
443 for k
, v
in ORIGINAL_ENV
.iteritems():
446 path_to_file
= os
.path
.join('build', 'android', 'envsetup.sh')
447 proc
= subprocess
.Popen(['bash', '-c', 'source %s && env' % path_to_file
],
448 stdout
=subprocess
.PIPE
,
449 stderr
=subprocess
.PIPE
,
451 (out
, _
) = proc
.communicate()
453 for line
in out
.splitlines():
454 (k
, _
, v
) = line
.partition('=')
457 return not proc
.returncode
460 def SetupPlatformBuildEnvironment(opts
):
461 """Performs any platform specific setup.
464 opts: The options parsed from the command line through parse_args().
469 if 'android' in opts
.target_platform
:
470 CopyAndSaveOriginalEnvironmentVars()
471 return SetupAndroidBuildEnvironment(opts
)
472 elif opts
.target_platform
== 'cros':
473 return SetupCrosRepo()
478 def CheckIfBisectDepotExists(opts
):
479 """Checks if the bisect directory already exists.
482 opts: The options parsed from the command line through parse_args().
485 Returns True if it exists.
487 path_to_dir
= os
.path
.join(opts
.working_directory
, 'bisect', 'src')
488 return os
.path
.exists(path_to_dir
)
491 def CreateBisectDirectoryAndSetupDepot(opts
, custom_deps
):
492 """Sets up a subdirectory 'bisect' and then retrieves a copy of the depot
496 opts: The options parsed from the command line through parse_args().
497 custom_deps: A dictionary of additional dependencies to add to .gclient.
499 if not CreateAndChangeToSourceDirectory(opts
.working_directory
):
500 raise RuntimeError('Could not create bisect directory.')
502 if not SetupGitDepot(opts
, custom_deps
):
503 raise RuntimeError('Failed to grab source.')