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."""
16 DEFAULT_GCLIENT_CUSTOM_DEPS
= {
17 "src/data/page_cycler": "https://chrome-internal.googlesource.com/"
18 "chrome/data/page_cycler/.git",
19 "src/data/dom_perf": "https://chrome-internal.googlesource.com/"
20 "chrome/data/dom_perf/.git",
21 "src/data/mach_ports": "https://chrome-internal.googlesource.com/"
22 "chrome/data/mach_ports/.git",
23 "src/tools/perf/data": "https://chrome-internal.googlesource.com/"
24 "chrome/tools/perf/data/.git",
25 "src/third_party/adobe/flash/binaries/ppapi/linux":
26 "https://chrome-internal.googlesource.com/"
27 "chrome/deps/adobe/flash/binaries/ppapi/linux/.git",
28 "src/third_party/adobe/flash/binaries/ppapi/linux_x64":
29 "https://chrome-internal.googlesource.com/"
30 "chrome/deps/adobe/flash/binaries/ppapi/linux_x64/.git",
31 "src/third_party/adobe/flash/binaries/ppapi/mac":
32 "https://chrome-internal.googlesource.com/"
33 "chrome/deps/adobe/flash/binaries/ppapi/mac/.git",
34 "src/third_party/adobe/flash/binaries/ppapi/mac_64":
35 "https://chrome-internal.googlesource.com/"
36 "chrome/deps/adobe/flash/binaries/ppapi/mac_64/.git",
37 "src/third_party/adobe/flash/binaries/ppapi/win":
38 "https://chrome-internal.googlesource.com/"
39 "chrome/deps/adobe/flash/binaries/ppapi/win/.git",
40 "src/third_party/adobe/flash/binaries/ppapi/win_x64":
41 "https://chrome-internal.googlesource.com/"
42 "chrome/deps/adobe/flash/binaries/ppapi/win_x64/.git",}
46 "url" : "https://chromium.googlesource.com/chromium/src.git",
47 "deps_file" : ".DEPS.git",
53 GCLIENT_SPEC_ANDROID
= "\ntarget_os = ['android']"
54 GCLIENT_CUSTOM_DEPS_V8
= {"src/v8_bleeding_edge": "git://github.com/v8/v8.git"}
55 FILE_DEPS_GIT
= '.DEPS.git'
58 'https://chrome-internal.googlesource.com/chromeos/manifest-internal/',
60 'https://git.chromium.org/external/repo.git'
63 REPO_SYNC_COMMAND
= 'git checkout -f $(git rev-list --max-count=1 '\
64 '--before=%d remotes/m/master)'
68 def OutputAnnotationStepStart(name
):
69 """Outputs appropriate annotation to signal the start of a step to
73 name: The name of the step.
76 print '@@@SEED_STEP %s@@@' % name
77 print '@@@STEP_CURSOR %s@@@' % name
78 print '@@@STEP_STARTED@@@'
83 def OutputAnnotationStepClosed():
84 """Outputs appropriate annotation to signal the closing of a step to
87 print '@@@STEP_CLOSED@@@'
92 def CreateAndChangeToSourceDirectory(working_directory
):
93 """Creates a directory 'bisect' as a subdirectory of 'working_directory'. If
94 the function is successful, the current working directory will change to that
95 of the new 'bisect' directory.
98 True if the directory was successfully created (or already existed).
101 os
.chdir(working_directory
)
105 if e
.errno
!= errno
.EEXIST
:
111 def SubprocessCall(cmd
, cwd
=None):
112 """Runs a subprocess with specified parameters.
115 params: A list of parameters to pass to gclient.
116 cwd: Working directory to run from.
119 The return code of the call.
122 # "HOME" isn't normally defined on windows, but is needed
123 # for git to find the user's .netrc file.
124 if not os
.getenv('HOME'):
125 os
.environ
['HOME'] = os
.environ
['USERPROFILE']
126 shell
= os
.name
== 'nt'
127 return subprocess
.call(cmd
, shell
=shell
, cwd
=cwd
)
130 def RunGClient(params
, cwd
=None):
131 """Runs gclient with the specified parameters.
134 params: A list of parameters to pass to gclient.
135 cwd: Working directory to run from.
138 The return code of the call.
140 cmd
= ['gclient'] + params
142 return SubprocessCall(cmd
, cwd
=cwd
)
146 """Runs cros repo command with specified parameters.
149 params: A list of parameters to pass to gclient.
152 The return code of the call.
154 cmd
= ['repo'] + params
156 return SubprocessCall(cmd
)
159 def RunRepoSyncAtTimestamp(timestamp
):
160 """Syncs all git depots to the timestamp specified using repo forall.
163 params: Unix timestamp to sync to.
166 The return code of the call.
168 repo_sync
= REPO_SYNC_COMMAND
% timestamp
169 cmd
= ['forall', '-c', REPO_SYNC_COMMAND
% timestamp
]
173 def RunGClientAndCreateConfig(opts
, custom_deps
=None, cwd
=None):
174 """Runs gclient and creates a config containing both src and src-internal.
177 opts: The options parsed from the command line through parse_args().
178 custom_deps: A dictionary of additional dependencies to add to .gclient.
179 cwd: Working directory to run from.
182 The return code of the call.
184 spec
= GCLIENT_SPEC_DATA
187 for k
, v
in custom_deps
.iteritems():
188 spec
[0]['custom_deps'][k
] = v
190 # Cannot have newlines in string on windows
191 spec
= 'solutions =' + str(spec
)
192 spec
= ''.join([l
for l
in spec
.splitlines()])
194 if opts
.target_platform
== 'android':
195 spec
+= GCLIENT_SPEC_ANDROID
197 return_code
= RunGClient(
198 ['config', '--spec=%s' % spec
, '--git-deps'], cwd
=cwd
)
202 def IsDepsFileBlink():
203 """Reads .DEPS.git and returns whether or not we're using blink.
206 True if blink, false if webkit.
208 locals = {'Var': lambda _
: locals["vars"][_
],
209 'From': lambda *args
: None}
210 execfile(FILE_DEPS_GIT
, {}, locals)
211 return 'blink.git' in locals['vars']['webkit_url']
214 def RemoveThirdPartyWebkitDirectory():
215 """Removes third_party/WebKit.
221 path_to_dir
= os
.path
.join(os
.getcwd(), 'third_party', 'WebKit')
222 if os
.path
.exists(path_to_dir
):
223 shutil
.rmtree(path_to_dir
)
225 if e
.errno
!= errno
.ENOENT
:
230 def OnAccessError(func
, path
, exc_info
):
232 Source: http://stackoverflow.com/questions/2656322/python-shutil-rmtree-fails-on-windows-with-access-is-denied
234 Error handler for ``shutil.rmtree``.
236 If the error is due to an access error (read only file)
237 it attempts to add write permission and then retries.
239 If the error is for another reason it re-raises the error.
242 func: The function that raised the error.
243 path: The path name passed to func.
244 exc_info: Exception information returned by sys.exc_info().
246 if not os
.access(path
, os
.W_OK
):
247 # Is the error an access error ?
248 os
.chmod(path
, stat
.S_IWUSR
)
254 def RemoveThirdPartyLibjingleDirectory():
255 """Removes third_party/libjingle. At some point, libjingle was causing issues
256 syncing when using the git workflow (crbug.com/266324).
261 path_to_dir
= os
.path
.join(os
.getcwd(), 'third_party', 'libjingle')
263 if os
.path
.exists(path_to_dir
):
264 shutil
.rmtree(path_to_dir
, onerror
=OnAccessError
)
266 print 'Error #%d while running shutil.rmtree(%s): %s' % (
267 e
.errno
, path_to_dir
, str(e
))
268 if e
.errno
!= errno
.ENOENT
:
273 def RunGClientAndSync(cwd
=None):
274 """Runs gclient and does a normal sync.
277 cwd: Working directory to run from.
280 The return code of the call.
282 params
= ['sync', '--verbose', '--nohooks', '--reset', '--force']
283 return RunGClient(params
, cwd
=cwd
)
286 def SetupGitDepot(opts
, custom_deps
):
287 """Sets up the depot for the bisection. The depot will be located in a
288 subdirectory called 'bisect'.
291 opts: The options parsed from the command line through parse_args().
292 custom_deps: A dictionary of additional dependencies to add to .gclient.
295 True if gclient successfully created the config file and did a sync, False
298 name
= 'Setting up Bisection Depot'
300 if opts
.output_buildbot_annotations
:
301 OutputAnnotationStepStart(name
)
305 if not RunGClientAndCreateConfig(opts
, custom_deps
):
306 passed_deps_check
= True
307 if os
.path
.isfile(os
.path
.join('src', FILE_DEPS_GIT
)):
310 if not IsDepsFileBlink():
311 passed_deps_check
= RemoveThirdPartyWebkitDirectory()
313 passed_deps_check
= True
314 if passed_deps_check
:
315 passed_deps_check
= RemoveThirdPartyLibjingleDirectory()
318 if passed_deps_check
:
319 RunGClient(['revert'])
320 if not RunGClientAndSync():
323 if opts
.output_buildbot_annotations
:
325 OutputAnnotationStepClosed()
331 """Sets up cros repo for bisecting chromeos.
334 Returns 0 on success.
340 if e
.errno
!= errno
.EEXIST
:
344 cmd
= ['init', '-u'] + REPO_PARAMS
349 if not RunRepo(['sync']):
356 def CopyAndSaveOriginalEnvironmentVars():
357 """Makes a copy of the current environment variables."""
358 # TODO: Waiting on crbug.com/255689, will remove this after.
360 for k
, v
in os
.environ
.iteritems():
362 vars_to_remove
.append(k
)
363 vars_to_remove
.append('CHROME_SRC')
364 vars_to_remove
.append('CHROMIUM_GYP_FILE')
365 vars_to_remove
.append('GYP_CROSSCOMPILE')
366 vars_to_remove
.append('GYP_DEFINES')
367 vars_to_remove
.append('GYP_GENERATORS')
368 vars_to_remove
.append('GYP_GENERATOR_FLAGS')
369 vars_to_remove
.append('OBJCOPY')
370 for k
in vars_to_remove
:
371 if os
.environ
.has_key(k
):
375 ORIGINAL_ENV
= os
.environ
.copy()
378 def SetupAndroidBuildEnvironment(opts
):
379 """Sets up the android build environment.
382 opts: The options parsed from the command line through parse_args().
383 path_to_file: Path to the bisect script's directory.
389 # Revert the environment variables back to default before setting them up
391 env_vars
= os
.environ
.copy()
392 for k
, _
in env_vars
.iteritems():
394 for k
, v
in ORIGINAL_ENV
.iteritems():
397 path_to_file
= os
.path
.join('build', 'android', 'envsetup.sh')
398 proc
= subprocess
.Popen(['bash', '-c', 'source %s && env' % path_to_file
],
399 stdout
=subprocess
.PIPE
,
400 stderr
=subprocess
.PIPE
,
402 (out
, _
) = proc
.communicate()
404 for line
in out
.splitlines():
405 (k
, _
, v
) = line
.partition('=')
407 return not proc
.returncode
410 def SetupPlatformBuildEnvironment(opts
):
411 """Performs any platform specific setup.
414 opts: The options parsed from the command line through parse_args().
415 path_to_file: Path to the bisect script's directory.
420 if opts
.target_platform
== 'android':
421 CopyAndSaveOriginalEnvironmentVars()
422 return SetupAndroidBuildEnvironment(opts
)
423 elif opts
.target_platform
== 'cros':
424 return SetupCrosRepo()
429 def CheckIfBisectDepotExists(opts
):
430 """Checks if the bisect directory already exists.
433 opts: The options parsed from the command line through parse_args().
436 Returns True if it exists.
438 path_to_dir
= os
.path
.join(opts
.working_directory
, 'bisect', 'src')
439 return os
.path
.exists(path_to_dir
)
442 def CreateBisectDirectoryAndSetupDepot(opts
, custom_deps
):
443 """Sets up a subdirectory 'bisect' and then retrieves a copy of the depot
447 opts: The options parsed from the command line through parse_args().
448 custom_deps: A dictionary of additional dependencies to add to .gclient.
451 Returns 0 on success, otherwise 1.
453 if not CreateAndChangeToSourceDirectory(opts
.working_directory
):
454 print 'Error: Could not create bisect directory.'
458 if not SetupGitDepot(opts
, custom_deps
):
459 print 'Error: Failed to grab source.'