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",
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,}
52 "url" : "https://chromium.googlesource.com/chromium/src.git",
53 "deps_file" : ".DEPS.git",
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'
65 'https://chrome-internal.googlesource.com/chromeos/manifest-internal/',
67 'https://git.chromium.org/external/repo.git'
70 REPO_SYNC_COMMAND
= 'git checkout -f $(git rev-list --max-count=1 '\
71 '--before=%d remotes/m/master)'
75 def OutputAnnotationStepStart(name
):
76 """Outputs appropriate annotation to signal the start of a step to
80 name: The name of the step.
83 print '@@@SEED_STEP %s@@@' % name
84 print '@@@STEP_CURSOR %s@@@' % name
85 print '@@@STEP_STARTED@@@'
90 def OutputAnnotationStepClosed():
91 """Outputs appropriate annotation to signal the closing of a step to
94 print '@@@STEP_CLOSED@@@'
99 def OutputAnnotationStepLink(label
, url
):
100 """Outputs appropriate annotation to print a link.
103 label: The name to print.
104 url: The url to print.
107 print '@@@STEP_LINK@%s@%s@@@' % (label
, url
)
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.
117 The loaded src module, or None."""
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()
125 except ImportError, e
:
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
)
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.
140 True if the directory was successfully created (or already existed).
143 os
.chdir(working_directory
)
147 if e
.errno
!= errno
.EEXIST
:
153 def SubprocessCall(cmd
, cwd
=None):
154 """Runs a subprocess with specified parameters.
157 params: A list of parameters to pass to gclient.
158 cwd: Working directory to run from.
161 The return code of the call.
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
)
172 def RunGClient(params
, cwd
=None):
173 """Runs gclient with the specified parameters.
176 params: A list of parameters to pass to gclient.
177 cwd: Working directory to run from.
180 The return code of the call.
182 cmd
= ['gclient'] + params
184 return SubprocessCall(cmd
, cwd
=cwd
)
188 """Runs cros repo command with specified parameters.
191 params: A list of parameters to pass to gclient.
194 The return code of the call.
196 cmd
= ['repo'] + params
198 return SubprocessCall(cmd
)
201 def RunRepoSyncAtTimestamp(timestamp
):
202 """Syncs all git depots to the timestamp specified using repo forall.
205 params: Unix timestamp to sync to.
208 The return code of the call.
210 repo_sync
= REPO_SYNC_COMMAND
% timestamp
211 cmd
= ['forall', '-c', REPO_SYNC_COMMAND
% timestamp
]
215 def RunGClientAndCreateConfig(opts
, custom_deps
=None, cwd
=None):
216 """Runs gclient and creates a config containing both src and src-internal.
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.
224 The return code of the call.
226 spec
= GCLIENT_SPEC_DATA
229 for k
, v
in custom_deps
.iteritems():
230 spec
[0]['custom_deps'][k
] = v
232 # Cannot have newlines in string on windows
233 spec
= 'solutions =' + str(spec
)
234 spec
= ''.join([l
for l
in spec
.splitlines()])
236 if 'android' in opts
.target_platform
:
237 spec
+= GCLIENT_SPEC_ANDROID
239 return_code
= RunGClient(
240 ['config', '--spec=%s' % spec
, '--git-deps'], cwd
=cwd
)
244 def IsDepsFileBlink():
245 """Reads .DEPS.git and returns whether or not we're using blink.
248 True if blink, false if webkit.
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']
256 def RemoveThirdPartyWebkitDirectory():
257 """Removes third_party/WebKit.
263 path_to_dir
= os
.path
.join(os
.getcwd(), 'third_party', 'WebKit')
264 if os
.path
.exists(path_to_dir
):
265 shutil
.rmtree(path_to_dir
)
267 if e
.errno
!= errno
.ENOENT
:
272 def OnAccessError(func
, path
, exc_info
):
274 Source: http://stackoverflow.com/questions/2656322/python-shutil-rmtree-fails-on-windows-with-access-is-denied
276 Error handler for ``shutil.rmtree``.
278 If the error is due to an access error (read only file)
279 it attempts to add write permission and then retries.
281 If the error is for another reason it re-raises the error.
284 func: The function that raised the error.
285 path: The path name passed to func.
286 exc_info: Exception information returned by sys.exc_info().
288 if not os
.access(path
, os
.W_OK
):
289 # Is the error an access error ?
290 os
.chmod(path
, stat
.S_IWUSR
)
296 def RemoveThirdPartyLibjingleDirectory():
297 """Removes third_party/libjingle. At some point, libjingle was causing issues
298 syncing when using the git workflow (crbug.com/266324).
303 path_to_dir
= os
.path
.join(os
.getcwd(), 'third_party', 'libjingle')
305 if os
.path
.exists(path_to_dir
):
306 shutil
.rmtree(path_to_dir
, onerror
=OnAccessError
)
308 print 'Error #%d while running shutil.rmtree(%s): %s' % (
309 e
.errno
, path_to_dir
, str(e
))
310 if e
.errno
!= errno
.ENOENT
:
315 def _CleanupPreviousGitRuns():
316 """Performs necessary cleanup between runs."""
317 # If a previous run of git crashed, bot was reset, etc... we
318 # might end up with leftover index.lock files.
319 for (path
, dir, files
) in os
.walk(os
.getcwd()):
320 for cur_file
in files
:
321 if cur_file
.endswith('index.lock'):
322 path_to_file
= os
.path
.join(path
, cur_file
)
323 os
.remove(path_to_file
)
326 def RunGClientAndSync(cwd
=None):
327 """Runs gclient and does a normal sync.
330 cwd: Working directory to run from.
333 The return code of the call.
335 params
= ['sync', '--verbose', '--nohooks', '--reset', '--force']
336 return RunGClient(params
, cwd
=cwd
)
339 def SetupGitDepot(opts
, custom_deps
):
340 """Sets up the depot for the bisection. The depot will be located in a
341 subdirectory called 'bisect'.
344 opts: The options parsed from the command line through parse_args().
345 custom_deps: A dictionary of additional dependencies to add to .gclient.
348 True if gclient successfully created the config file and did a sync, False
351 name
= 'Setting up Bisection Depot'
353 if opts
.output_buildbot_annotations
:
354 OutputAnnotationStepStart(name
)
358 if not RunGClientAndCreateConfig(opts
, custom_deps
):
359 passed_deps_check
= True
360 if os
.path
.isfile(os
.path
.join('src', FILE_DEPS_GIT
)):
363 if not IsDepsFileBlink():
364 passed_deps_check
= RemoveThirdPartyWebkitDirectory()
366 passed_deps_check
= True
367 if passed_deps_check
:
368 passed_deps_check
= RemoveThirdPartyLibjingleDirectory()
371 if passed_deps_check
:
372 _CleanupPreviousGitRuns()
374 RunGClient(['revert'])
375 if not RunGClientAndSync():
378 if opts
.output_buildbot_annotations
:
380 OutputAnnotationStepClosed()
386 """Sets up cros repo for bisecting chromeos.
389 Returns 0 on success.
395 if e
.errno
!= errno
.EEXIST
:
399 cmd
= ['init', '-u'] + REPO_PARAMS
404 if not RunRepo(['sync']):
411 def CopyAndSaveOriginalEnvironmentVars():
412 """Makes a copy of the current environment variables."""
413 # TODO: Waiting on crbug.com/255689, will remove this after.
415 for k
, v
in os
.environ
.iteritems():
417 vars_to_remove
.append(k
)
418 vars_to_remove
.append('CHROME_SRC')
419 vars_to_remove
.append('CHROMIUM_GYP_FILE')
420 vars_to_remove
.append('GYP_CROSSCOMPILE')
421 vars_to_remove
.append('GYP_DEFINES')
422 vars_to_remove
.append('GYP_GENERATORS')
423 vars_to_remove
.append('GYP_GENERATOR_FLAGS')
424 vars_to_remove
.append('OBJCOPY')
425 for k
in vars_to_remove
:
426 if os
.environ
.has_key(k
):
430 ORIGINAL_ENV
= os
.environ
.copy()
433 def SetupAndroidBuildEnvironment(opts
, path_to_src
=None):
434 """Sets up the android build environment.
437 opts: The options parsed from the command line through parse_args().
438 path_to_src: Path to the src checkout.
444 # Revert the environment variables back to default before setting them up
446 env_vars
= os
.environ
.copy()
447 for k
, _
in env_vars
.iteritems():
449 for k
, v
in ORIGINAL_ENV
.iteritems():
452 path_to_file
= os
.path
.join('build', 'android', 'envsetup.sh')
453 proc
= subprocess
.Popen(['bash', '-c', 'source %s && env' % path_to_file
],
454 stdout
=subprocess
.PIPE
,
455 stderr
=subprocess
.PIPE
,
457 (out
, _
) = proc
.communicate()
459 for line
in out
.splitlines():
460 (k
, _
, v
) = line
.partition('=')
462 # envsetup.sh no longer sets OS=android to GYP_DEFINES env variable
463 # (CL/170273005). Set this variable explicitly inorder to build chrome on
466 if 'OS=android' not in os
.environ
['GYP_DEFINES']:
467 os
.environ
['GYP_DEFINES'] = '%s %s' % (os
.environ
['GYP_DEFINES'],
470 os
.environ
['GYP_DEFINES'] = 'OS=android'
472 return not proc
.returncode
475 def SetupPlatformBuildEnvironment(opts
):
476 """Performs any platform specific setup.
479 opts: The options parsed from the command line through parse_args().
484 if 'android' in opts
.target_platform
:
485 CopyAndSaveOriginalEnvironmentVars()
486 return SetupAndroidBuildEnvironment(opts
)
487 elif opts
.target_platform
== 'cros':
488 return SetupCrosRepo()
493 def CheckIfBisectDepotExists(opts
):
494 """Checks if the bisect directory already exists.
497 opts: The options parsed from the command line through parse_args().
500 Returns True if it exists.
502 path_to_dir
= os
.path
.join(opts
.working_directory
, 'bisect', 'src')
503 return os
.path
.exists(path_to_dir
)
506 def CreateBisectDirectoryAndSetupDepot(opts
, custom_deps
):
507 """Sets up a subdirectory 'bisect' and then retrieves a copy of the depot
511 opts: The options parsed from the command line through parse_args().
512 custom_deps: A dictionary of additional dependencies to add to .gclient.
514 if not CreateAndChangeToSourceDirectory(opts
.working_directory
):
515 raise RuntimeError('Could not create bisect directory.')
517 if not SetupGitDepot(opts
, custom_deps
):
518 raise RuntimeError('Failed to grab source.')