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

Side by Side Diff: infra/bots/utils.py

Issue 1775073002: Add isolate_win_toolchain.py (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Ready for review Created 4 years, 9 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
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright 2016 Google Inc. 3 # Copyright 2016 Google Inc.
4 # 4 #
5 # Use of this source code is governed by a BSD-style license that can be 5 # Use of this source code is governed by a BSD-style license that can be
6 # found in the LICENSE file. 6 # found in the LICENSE file.
7 7
8 8
9 import datetime 9 import datetime
10 import errno
11 import os
12 import shutil
13 import sys
14 import subprocess
15 import tempfile
16 import time
17 import uuid
18
19
20 GCLIENT = 'gclient.bat' if sys.platform == 'win32' else 'gclient'
21 GIT = 'git.bat' if sys.platform == 'win32' else 'git'
10 22
11 23
12 class print_timings(object): 24 class print_timings(object):
13 def __init__(self): 25 def __init__(self):
14 self._start = None 26 self._start = None
15 27
16 def __enter__(self): 28 def __enter__(self):
17 self._start = datetime.datetime.utcnow() 29 self._start = datetime.datetime.utcnow()
18 print 'Task started at %s GMT' % str(self._start) 30 print 'Task started at %s GMT' % str(self._start)
19 31
20 def __exit__(self, t, v, tb): 32 def __exit__(self, t, v, tb):
21 finish = datetime.datetime.utcnow() 33 finish = datetime.datetime.utcnow()
22 duration = (finish-self._start).total_seconds() 34 duration = (finish-self._start).total_seconds()
23 print 'Task finished at %s GMT (%f seconds)' % (str(finish), duration) 35 print 'Task finished at %s GMT (%f seconds)' % (str(finish), duration)
36
37
38 class tmp_dir(object):
39 """Helper class used for creating a temporary directory and working in it."""
40 def __init__(self):
41 self._orig_dir = None
42 self._tmp_dir = None
43
44 def __enter__(self):
45 self._orig_dir = os.getcwd()
46 self._tmp_dir = tempfile.mkdtemp()
47 os.chdir(self._tmp_dir)
48 return self
49
50 def __exit__(self, t, v, tb):
51 os.chdir(self._orig_dir)
52 RemoveDirectory(self._tmp_dir)
53
54 @property
55 def name(self):
56 return self._tmp_dir
57
58
59 class chdir(object):
60 """Helper class used for changing into and out of a directory."""
61 def __init__(self, d):
62 self._dir = d
63 self._orig_dir = None
64
65 def __enter__(self):
66 self._orig_dir = os.getcwd()
67 os.chdir(self._dir)
68 return self
69
70 def __exit__(self, t, v, tb):
71 os.chdir(self._orig_dir)
72
73
74 def git_clone(repo_url, dest_dir):
75 """Clone the given repo into the given destination directory."""
76 subprocess.check_call([GIT, 'clone', repo_url, dest_dir])
77
78
79 class git_branch(object):
80 """Check out a temporary git branch.
81
82 On exit, deletes the branch and attempts to restore the original state.
83 """
84 def __init__(self):
85 self._branch = None
86 self._orig_branch = None
87 self._stashed = False
88
89 def __enter__(self):
90 output = subprocess.check_output([GIT, 'stash'])
91 self._stashed = 'No local changes' not in output
92
93 # Get the original branch name or commit hash.
94 self._orig_branch = subprocess.check_output([
95 GIT, 'rev-parse', '--abbrev-ref', 'HEAD']).rstrip()
96 if self._orig_branch == 'HEAD':
97 self._orig_branch = subprocess.check_output([
98 GIT, 'rev-parse', 'HEAD']).rstrip()
99
100 # Check out a new branch, based at updated origin/master.
101 subprocess.check_call([GIT, 'fetch', 'origin'])
102 self._branch = '_tmp_%s' % uuid.uuid4()
103 subprocess.check_call([GIT, 'checkout', '-b', self._branch,
104 '-t', 'origin/master'])
105 return self
106
107 def __exit__(self, exc_type, _value, _traceback):
108 subprocess.check_call([GIT, 'reset', '--hard', 'HEAD'])
109 subprocess.check_call([GIT, 'checkout', self._orig_branch])
110 if self._stashed:
111 subprocess.check_call([GIT, 'stash', 'pop'])
112 subprocess.check_call([GIT, 'branch', '-D', self._branch])
113
114
115 def RemoveDirectory(*path):
116 """Recursively removes a directory, even if it's marked read-only.
117
118 This was copied from:
119 https://chromium.googlesource.com/chromium/tools/build/+/f3e7ff03613cd59a463b2 ccc49773c3813e77404/scripts/common/chromium_utils.py#491
120
121 Remove the directory located at *path, if it exists.
122
123 shutil.rmtree() doesn't work on Windows if any of the files or directories
124 are read-only, which svn repositories and some .svn files are. We need to
125 be able to force the files to be writable (i.e., deletable) as we traverse
126 the tree.
127
128 Even with all this, Windows still sometimes fails to delete a file, citing
129 a permission error (maybe something to do with antivirus scans or disk
130 indexing). The best suggestion any of the user forums had was to wait a
131 bit and try again, so we do that too. It's hand-waving, but sometimes it
132 works. :/
133 """
134 file_path = os.path.join(*path)
135 if not os.path.exists(file_path):
136 return
137
138 if sys.platform == 'win32':
139 # Give up and use cmd.exe's rd command.
140 file_path = os.path.normcase(file_path)
141 for _ in xrange(3):
142 print 'RemoveDirectory running %s' % (' '.join(
143 ['cmd.exe', '/c', 'rd', '/q', '/s', file_path]))
144 if not subprocess.call(['cmd.exe', '/c', 'rd', '/q', '/s', file_path]):
145 break
146 print ' Failed'
147 time.sleep(3)
148 return
149
150 def RemoveWithRetry_non_win(rmfunc, path):
151 if os.path.islink(path):
152 return os.remove(path)
153 else:
154 return rmfunc(path)
155
156 remove_with_retry = RemoveWithRetry_non_win
157
158 def RmTreeOnError(function, path, excinfo):
159 r"""This works around a problem whereby python 2.x on Windows has no ability
160 to check for symbolic links. os.path.islink always returns False. But
161 shutil.rmtree will fail if invoked on a symbolic link whose target was
162 deleted before the link. E.g., reproduce like this:
163 > mkdir test
164 > mkdir test\1
165 > mklink /D test\current test\1
166 > python -c "import chromium_utils; chromium_utils.RemoveDirectory('test')"
167 To avoid this issue, we pass this error-handling function to rmtree. If
168 we see the exact sort of failure, we ignore it. All other failures we re-
169 raise.
170 """
171
172 exception_type = excinfo[0]
173 exception_value = excinfo[1]
174 # If shutil.rmtree encounters a symbolic link on Windows, os.listdir will
175 # fail with a WindowsError exception with an ENOENT errno (i.e., file not
176 # found). We'll ignore that error. Note that WindowsError is not defined
177 # for non-Windows platforms, so we use OSError (of which it is a subclass)
178 # to avoid lint complaints about an undefined global on non-Windows
179 # platforms.
180 if (function is os.listdir) and issubclass(exception_type, OSError):
181 if exception_value.errno == errno.ENOENT:
182 # File does not exist, and we're trying to delete, so we can ignore the
183 # failure.
184 print 'WARNING: Failed to list %s during rmtree. Ignoring.\n' % path
185 else:
186 raise
187 else:
188 raise
189
190 for root, dirs, files in os.walk(file_path, topdown=False):
191 # For POSIX: making the directory writable guarantees removability.
192 # Windows will ignore the non-read-only bits in the chmod value.
193 os.chmod(root, 0770)
194 for name in files:
195 remove_with_retry(os.remove, os.path.join(root, name))
196 for name in dirs:
197 remove_with_retry(lambda p: shutil.rmtree(p, onerror=RmTreeOnError),
198 os.path.join(root, name))
199
200 remove_with_retry(os.rmdir, file_path)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698