blob: b4546c1548c1a71341c25cdc3feeea1568630240 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
2# Copyright (C) 2008 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
Sarah Owenscecd1d82012-11-01 22:59:27 -070016from __future__ import print_function
Anthony King85b24ac2014-05-06 15:57:48 +010017import json
David Pursehouse86d973d2012-08-24 10:21:02 +090018import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070019from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import os
21import re
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070022import shutil
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070023import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import subprocess
25import sys
Shawn O. Pearcef6906872009-04-18 10:49:00 -070026import time
David Pursehouse59bbb582013-05-17 10:49:33 +090027
28from pyversion import is_python3
29if is_python3():
Chirayu Desai217ea7d2013-03-01 19:14:38 +053030 import urllib.parse
David Pursehouse59bbb582013-05-17 10:49:33 +090031 import xmlrpc.client
32else:
Chirayu Desai217ea7d2013-03-01 19:14:38 +053033 import imp
34 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090035 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053036 urllib = imp.new_module('urllib')
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053037 urllib.parse = urlparse
Chirayu Desai217ea7d2013-03-01 19:14:38 +053038 xmlrpc = imp.new_module('xmlrpc')
39 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070040
Roy Lee18afd7f2010-05-09 04:32:08 +080041try:
42 import threading as _threading
43except ImportError:
44 import dummy_threading as _threading
45
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070046try:
47 import resource
48 def _rlimit_nofile():
49 return resource.getrlimit(resource.RLIMIT_NOFILE)
50except ImportError:
51 def _rlimit_nofile():
52 return (256, 256)
53
Dave Borowitz18857212012-10-23 17:02:59 -070054try:
55 import multiprocessing
56except ImportError:
57 multiprocessing = None
58
Dave Borowitze2152672012-10-31 12:24:38 -070059from git_command import GIT, git_require
David Pursehoused94aaef2012-09-07 09:52:04 +090060from git_refs import R_HEADS, HEAD
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070061from project import Project
62from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080063from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000064from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070065from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070066from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080067from wrapper import Wrapper
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070068
Dave Borowitz67700e92012-10-23 15:00:54 -070069_ONE_DAY_S = 24 * 60 * 60
70
Doug Andersonfc06ced2011-03-16 15:49:18 -070071class _FetchError(Exception):
72 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
73 pass
74
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080075class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080076 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070077 common = True
78 helpSummary = "Update working tree to the latest revision"
79 helpUsage = """
80%prog [<project>...]
81"""
82 helpDescription = """
83The '%prog' command synchronizes local project directories
84with the remote repositories specified in the manifest. If a local
85project does not yet exist, it will clone a new local directory from
86the remote repository and set up tracking branches as specified in
87the manifest. If the local project already exists, '%prog'
88will update the remote branches and rebase any new local changes
89on top of the new remote changes.
90
91'%prog' will synchronize all projects listed at the command
92line. Projects can be specified either by name, or by a relative
93or absolute path to the project's local directory. If no projects
94are specified, '%prog' will synchronize all projects listed in
95the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070096
97The -d/--detach option can be used to switch specified projects
98back to the manifest revision. This option is especially helpful
99if the project is currently on a topic branch, but the manifest
100revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700101
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700102The -s/--smart-sync option can be used to sync to a known good
103build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200104manifest. The -t/--smart-tag option is similar and allows you to
105specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700106
David Pursehousecf76b1b2012-09-14 10:31:42 +0900107The -u/--manifest-server-username and -p/--manifest-server-password
108options can be used to specify a username and password to authenticate
109with the manifest server when using the -s or -t option.
110
111If -u and -p are not specified when using the -s or -t option, '%prog'
112will attempt to read authentication credentials for the manifest server
113from the user's .netrc file.
114
115'%prog' will not use authentication credentials from -u/-p or .netrc
116if the manifest server specified in the manifest file already includes
117credentials.
118
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500119The -f/--force-broken option can be used to proceed with syncing
120other projects if a project sync fails.
121
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700122The --no-clone-bundle option disables any attempt to use
123$URL/clone.bundle to bootstrap a new Git repository from a
124resumeable bundle file on a content delivery network. This
125may be necessary if there are problems with the local Python
126HTTP client or proxy configuration, but the Git binary works.
127
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800128The --fetch-submodules option enables fetching Git submodules
129of a project from server.
130
David Pursehousef2fad612015-01-29 14:36:28 +0900131The -c/--current-branch option can be used to only fetch objects that
132are on the branch specified by a project's revision.
133
David Pursehouseb1553542014-09-04 21:28:09 +0900134The --optimized-fetch option can be used to only fetch projects that
135are fixed to a sha1 revision if the sha1 revision does not already
136exist locally.
137
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700138SSH Connections
139---------------
140
141If at least one project remote URL uses an SSH connection (ssh://,
142git+ssh://, or user@host:path syntax) repo will automatically
143enable the SSH ControlMaster option when connecting to that host.
144This feature permits other projects in the same '%prog' session to
145reuse the same SSH tunnel, saving connection setup overheads.
146
147To disable this behavior on UNIX platforms, set the GIT_SSH
148environment variable to 'ssh'. For example:
149
150 export GIT_SSH=ssh
151 %prog
152
153Compatibility
154~~~~~~~~~~~~~
155
156This feature is automatically disabled on Windows, due to the lack
157of UNIX domain socket support.
158
159This feature is not compatible with url.insteadof rewrites in the
160user's ~/.gitconfig. '%prog' is currently not able to perform the
161rewrite early enough to establish the ControlMaster tunnel.
162
163If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
164later is required to fix a server side protocol bug.
165
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700166"""
167
Nico Sallembien6623b212010-05-11 12:57:01 -0700168 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000169 try:
170 self.jobs = self.manifest.default.sync_j
171 except ManifestParseError:
172 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700173
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500174 p.add_option('-f', '--force-broken',
175 dest='force_broken', action='store_true',
176 help="continue sync even if a project fails to sync")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900177 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700178 dest='local_only', action='store_true',
179 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900180 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700181 dest='network_only', action='store_true',
182 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900183 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700184 dest='detach_head', action='store_true',
185 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900186 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700187 dest='current_branch_only', action='store_true',
188 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900189 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700190 dest='quiet', action='store_true',
191 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900192 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800193 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700194 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500195 p.add_option('-m', '--manifest-name',
196 dest='manifest_name',
197 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700198 p.add_option('--no-clone-bundle',
199 dest='no_clone_bundle', action='store_true',
200 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800201 p.add_option('-u', '--manifest-server-username', action='store',
202 dest='manifest_server_username',
203 help='username to authenticate with the manifest server')
204 p.add_option('-p', '--manifest-server-password', action='store',
205 dest='manifest_server_password',
206 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800207 p.add_option('--fetch-submodules',
208 dest='fetch_submodules', action='store_true',
209 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700210 p.add_option('--no-tags',
211 dest='no_tags', action='store_true',
212 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900213 p.add_option('--optimized-fetch',
214 dest='optimized_fetch', action='store_true',
215 help='only fetch projects fixed to sha1 if revision does not exist locally')
Nico Sallembien6623b212010-05-11 12:57:01 -0700216 if show_smart:
217 p.add_option('-s', '--smart-sync',
218 dest='smart_sync', action='store_true',
219 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200220 p.add_option('-t', '--smart-tag',
221 dest='smart_tag', action='store',
222 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700223
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700224 g = p.add_option_group('repo Version options')
225 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700226 dest='no_repo_verify', action='store_true',
227 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700228 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800229 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700230 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700231
David James89ece422014-01-09 18:51:58 -0800232 def _FetchProjectList(self, opt, projects, *args, **kwargs):
David Pursehousec1b86a22012-11-14 11:36:51 +0900233 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800234
David James8d201162013-10-11 17:03:19 -0700235 Delegates most of the work to _FetchHelper.
236
237 Args:
238 opt: Program options returned from optparse. See _Options().
239 projects: Projects to fetch.
David James89ece422014-01-09 18:51:58 -0800240 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700241 _FetchHelper docstring for details.
242 """
243 for project in projects:
David James89ece422014-01-09 18:51:58 -0800244 success = self._FetchHelper(opt, project, *args, **kwargs)
David James8d201162013-10-11 17:03:19 -0700245 if not success and not opt.force_broken:
246 break
247
248 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
249 """Fetch git objects for a single project.
250
David Pursehousec1b86a22012-11-14 11:36:51 +0900251 Args:
252 opt: Program options returned from optparse. See _Options().
253 project: Project object for the project to fetch.
254 lock: Lock for accessing objects that are shared amongst multiple
255 _FetchHelper() threads.
256 fetched: set object that we will add project.gitdir to when we're done
257 (with our lock held).
258 pm: Instance of a Project object. We will call pm.update() (with our
259 lock held).
260 sem: We'll release() this semaphore when we exit so that another thread
261 can be started up.
262 err_event: We'll set this event in the case of an error (after printing
263 out info about the error).
David James8d201162013-10-11 17:03:19 -0700264
265 Returns:
266 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900267 """
268 # We'll set to true once we've locked the lock.
269 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700270
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530271 if not opt.quiet:
272 print('Fetching project %s' % project.name)
273
David Pursehousec1b86a22012-11-14 11:36:51 +0900274 # Encapsulate everything in a try/except/finally so that:
275 # - We always set err_event in the case of an exception.
276 # - We always make sure we call sem.release().
277 # - We always make sure we unlock the lock if we locked it.
278 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700279 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900280 start = time.time()
281 success = project.Sync_NetworkHalf(
282 quiet=opt.quiet,
283 current_branch_only=opt.current_branch_only,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700284 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900285 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
286 optimized_fetch=opt.optimized_fetch)
David Pursehousec1b86a22012-11-14 11:36:51 +0900287 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700288
David Pursehousec1b86a22012-11-14 11:36:51 +0900289 # Lock around all the rest of the code, since printing, updating a set
290 # and Progress.update() are not thread safe.
291 lock.acquire()
292 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700293
David Pursehousec1b86a22012-11-14 11:36:51 +0900294 if not success:
295 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
296 if opt.force_broken:
297 print('warn: --force-broken, continuing to sync',
298 file=sys.stderr)
299 else:
300 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700301
David Pursehousec1b86a22012-11-14 11:36:51 +0900302 fetched.add(project.gitdir)
303 pm.update()
304 except _FetchError:
305 err_event.set()
306 except:
307 err_event.set()
308 raise
309 finally:
310 if did_lock:
311 lock.release()
312 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800313
David James8d201162013-10-11 17:03:19 -0700314 return success
315
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700316 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700317 fetched = set()
David James89ece422014-01-09 18:51:58 -0800318 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700319 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800320
David James89ece422014-01-09 18:51:58 -0800321 objdir_project_map = dict()
322 for project in projects:
323 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700324
David James89ece422014-01-09 18:51:58 -0800325 threads = set()
326 sem = _threading.Semaphore(self.jobs)
327 err_event = _threading.Event()
328 for project_list in objdir_project_map.values():
329 # Check for any errors before running any more tasks.
330 # ...we'll let existing threads finish, though.
331 if err_event.isSet() and not opt.force_broken:
332 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700333
David James89ece422014-01-09 18:51:58 -0800334 sem.acquire()
335 kwargs = dict(opt=opt,
336 projects=project_list,
337 lock=lock,
338 fetched=fetched,
339 pm=pm,
340 sem=sem,
341 err_event=err_event)
342 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700343 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800344 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200345 # Ensure that Ctrl-C will not freeze the repo process.
346 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800347 threads.add(t)
348 t.start()
David James89ece422014-01-09 18:51:58 -0800349 else:
350 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800351
David James89ece422014-01-09 18:51:58 -0800352 for t in threads:
353 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800354
David James89ece422014-01-09 18:51:58 -0800355 # If we saw an error, exit with code 1 so that other scripts can check.
356 if err_event.isSet():
357 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
358 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700359
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700360 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700361 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700362
Julien Campergue335f5ef2013-10-16 11:02:35 +0200363 if not self.manifest.IsArchive:
364 self._GCProjects(projects)
365
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700366 return fetched
367
Dave Borowitz18857212012-10-23 17:02:59 -0700368 def _GCProjects(self, projects):
David James8d201162013-10-11 17:03:19 -0700369 gitdirs = {}
370 for project in projects:
371 gitdirs[project.gitdir] = project.bare_git
372
Dave Borowitze2152672012-10-31 12:24:38 -0700373 has_dash_c = git_require((1, 7, 2))
374 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700375 cpu_count = multiprocessing.cpu_count()
376 else:
377 cpu_count = 1
378 jobs = min(self.jobs, cpu_count)
379
380 if jobs < 2:
David James8d201162013-10-11 17:03:19 -0700381 for bare_git in gitdirs.values():
382 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700383 return
384
385 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
386
387 threads = set()
388 sem = _threading.Semaphore(jobs)
389 err_event = _threading.Event()
390
David James8d201162013-10-11 17:03:19 -0700391 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700392 try:
393 try:
David James8d201162013-10-11 17:03:19 -0700394 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700395 except GitError:
396 err_event.set()
397 except:
398 err_event.set()
399 raise
400 finally:
401 sem.release()
402
David James8d201162013-10-11 17:03:19 -0700403 for bare_git in gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700404 if err_event.isSet():
405 break
406 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700407 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700408 t.daemon = True
409 threads.add(t)
410 t.start()
411
412 for t in threads:
413 t.join()
414
415 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700416 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700417 sys.exit(1)
418
Tim Kilbourn07669002013-03-08 15:02:49 -0800419 def _ReloadManifest(self, manifest_name=None):
420 if manifest_name:
421 # Override calls _Unload already
422 self.manifest.Override(manifest_name)
423 else:
424 self.manifest._Unload()
425
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700426 def UpdateProjectList(self):
427 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700428 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700429 if project.relpath:
430 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700431 file_name = 'project.list'
432 file_path = os.path.join(self.manifest.repodir, file_name)
433 old_project_paths = []
434
435 if os.path.exists(file_path):
436 fd = open(file_path, 'r')
437 try:
438 old_project_paths = fd.read().split('\n')
439 finally:
440 fd.close()
441 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700442 if not path:
443 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700444 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900445 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400446 if os.path.exists(self.manifest.topdir + '/' + path):
David James8d201162013-10-11 17:03:19 -0700447 gitdir = os.path.join(self.manifest.topdir, path, '.git')
David Pursehousec1b86a22012-11-14 11:36:51 +0900448 project = Project(
449 manifest = self.manifest,
450 name = path,
451 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700452 gitdir = gitdir,
453 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900454 worktree = os.path.join(self.manifest.topdir, path),
455 relpath = path,
456 revisionExpr = 'HEAD',
457 revisionId = None,
458 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400459
David Pursehousec1b86a22012-11-14 11:36:51 +0900460 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900461 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900462 'are present' % project.relpath, file=sys.stderr)
463 print(' commit changes, then run sync again',
464 file=sys.stderr)
465 return -1
466 else:
467 print('Deleting obsolete path %s' % project.worktree,
468 file=sys.stderr)
469 shutil.rmtree(project.worktree)
470 # Try deleting parent subdirs if they are empty
471 project_dir = os.path.dirname(project.worktree)
472 while project_dir != self.manifest.topdir:
473 try:
474 os.rmdir(project_dir)
475 except OSError:
476 break
477 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700478
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700479 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700480 fd = open(file_path, 'w')
481 try:
482 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700483 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700484 finally:
485 fd.close()
486 return 0
487
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700488 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800489 if opt.jobs:
490 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700491 if self.jobs > 1:
492 soft_limit, _ = _rlimit_nofile()
493 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
494
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700495 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700496 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700497 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700498 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700499 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700500 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500501 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700502 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500503 sys.exit(1)
504 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700505 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500506 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900507 if opt.manifest_server_username or opt.manifest_server_password:
508 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700509 print('error: -u and -p may only be combined with -s or -t',
510 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900511 sys.exit(1)
512 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700513 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900514 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500515
516 if opt.manifest_name:
517 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700518
Chirayu Desaia892b102013-06-11 14:18:46 +0530519 manifest_name = opt.manifest_name
520
Victor Boivie08c880d2011-04-19 10:32:52 +0200521 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700522 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900523 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700524 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700525 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900526
527 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900528 if not opt.quiet:
529 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900530
David Pursehouse86d973d2012-08-24 10:21:02 +0900531 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900532 username = None
533 password = None
534 if opt.manifest_server_username and opt.manifest_server_password:
535 username = opt.manifest_server_username
536 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900537 else:
538 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900539 info = netrc.netrc()
540 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700541 print('.netrc file does not exist or could not be opened',
542 file=sys.stderr)
David Pursehouse86d973d2012-08-24 10:21:02 +0900543 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900544 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530545 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900546 if parse_result.hostname:
547 username, _account, password = \
548 info.authenticators(parse_result.hostname)
549 except TypeError:
550 # TypeError is raised when the given hostname is not present
551 # in the .netrc file.
Sarah Owenscecd1d82012-11-01 22:59:27 -0700552 print('No credentials found for %s in .netrc'
553 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700554 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700555 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900556
557 if (username and password):
558 manifest_server = manifest_server.replace('://', '://%s:%s@' %
559 (username, password),
560 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900561
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700562 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530563 server = xmlrpc.client.Server(manifest_server)
Victor Boivie08c880d2011-04-19 10:32:52 +0200564 if opt.smart_sync:
565 p = self.manifest.manifestProject
566 b = p.GetBranch(p.CurrentBranch)
567 branch = b.merge
568 if branch.startswith(R_HEADS):
569 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700570
Victor Boivie08c880d2011-04-19 10:32:52 +0200571 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700572 if 'SYNC_TARGET' in env:
573 target = env['SYNC_TARGET']
574 [success, manifest_str] = server.GetApprovedManifest(branch, target)
575 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200576 target = '%s-%s' % (env['TARGET_PRODUCT'],
577 env['TARGET_BUILD_VARIANT'])
578 [success, manifest_str] = server.GetApprovedManifest(branch, target)
579 else:
580 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700581 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200582 assert(opt.smart_tag)
583 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700584
585 if success:
586 manifest_name = "smart_sync_override.xml"
587 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
588 manifest_name)
589 try:
590 f = open(manifest_path, 'w')
591 try:
592 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700593 finally:
594 f.close()
595 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700596 print('error: cannot write manifest to %s' % manifest_path,
597 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700598 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100599 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700600 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900601 print('error: manifest server RPC call failed: %s' %
602 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700603 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530604 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700605 print('error: cannot connect to manifest server %s:\n%s'
606 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900607 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530608 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700609 print('error: cannot connect to manifest server %s:\n%d %s'
610 % (self.manifest.manifest_server, e.errcode, e.errmsg),
611 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700612 sys.exit(1)
613
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700614 rp = self.manifest.repoProject
615 rp.PreSync()
616
617 mp = self.manifest.manifestProject
618 mp.PreSync()
619
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800620 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700621 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800622
Nico Sallembien9bb18162009-12-07 15:38:01 -0800623 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700624 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700625 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900626 no_tags=opt.no_tags,
627 optimized_fetch=opt.optimized_fetch)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800628
629 if mp.HasChanges:
630 syncbuf = SyncBuffer(mp.config)
631 mp.Sync_LocalHalf(syncbuf)
632 if not syncbuf.Finish():
633 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100634 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700635 if opt.jobs is None:
636 self.jobs = self.manifest.default.sync_j
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800637 all_projects = self.GetProjects(args,
638 missing_ok=True,
639 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700640
Dave Borowitz67700e92012-10-23 15:00:54 -0700641 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700642 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700643 to_fetch = []
644 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700645 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700646 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900647 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700648 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700649
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800650 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700651 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700652 if opt.network_only:
653 # bail out now; the rest touches the working tree
654 return
655
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800656 # Iteratively fetch missing and/or nested unregistered submodules
657 previously_missing_set = set()
658 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100659 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800660 all_projects = self.GetProjects(args,
661 missing_ok=True,
662 submodules_ok=opt.fetch_submodules)
663 missing = []
664 for project in all_projects:
665 if project.gitdir not in fetched:
666 missing.append(project)
667 if not missing:
668 break
669 # Stop us from non-stopped fetching actually-missing repos: If set of
670 # missing repos has not been changed from last fetch, we break.
671 missing_set = set(p.name for p in missing)
672 if previously_missing_set == missing_set:
673 break
674 previously_missing_set = missing_set
675 fetched.update(self._Fetch(missing, opt))
676
Julien Campergue335f5ef2013-10-16 11:02:35 +0200677 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700678 # bail out now, we have no working tree
679 return
680
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700681 if self.UpdateProjectList():
682 sys.exit(1)
683
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700684 syncbuf = SyncBuffer(mp.config,
685 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900686 pm = Progress('Syncing work tree', len(all_projects))
687 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700688 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800689 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700690 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700691 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700692 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700693 if not syncbuf.Finish():
694 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700695
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700696 # If there's a notice that's supposed to print at the end of the sync, print
697 # it now...
698 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700699 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700700
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700701def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800702 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700703 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700704 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800705 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700706 if project.Exists:
707 project.PostRepoUpgrade()
708
709def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
710 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700711 print('info: A new version of repo is available', file=sys.stderr)
712 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700713 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700714 syncbuf = SyncBuffer(rp.config)
715 rp.Sync_LocalHalf(syncbuf)
716 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700717 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700718 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700719 raise RepoChangedException(['--repo-upgraded'])
720 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700721 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700722 else:
723 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700724 print('repo version %s is current' % rp.work_git.describe(HEAD),
725 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700726
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700727def _VerifyTag(project):
728 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
729 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700730 print('warning: GnuPG was not available during last "repo init"\n'
731 'warning: Cannot automatically authenticate repo."""',
732 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700733 return True
734
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700735 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700736 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700737 except GitError:
738 cur = None
739
740 if not cur \
741 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700742 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700743 if rev.startswith(R_HEADS):
744 rev = rev[len(R_HEADS):]
745
Sarah Owenscecd1d82012-11-01 22:59:27 -0700746 print(file=sys.stderr)
747 print("warning: project '%s' branch '%s' is not signed"
748 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700749 return False
750
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800751 env = os.environ.copy()
752 env['GIT_DIR'] = project.gitdir.encode()
753 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700754
755 cmd = [GIT, 'tag', '-v', cur]
756 proc = subprocess.Popen(cmd,
757 stdout = subprocess.PIPE,
758 stderr = subprocess.PIPE,
759 env = env)
760 out = proc.stdout.read()
761 proc.stdout.close()
762
763 err = proc.stderr.read()
764 proc.stderr.close()
765
766 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700767 print(file=sys.stderr)
768 print(out, file=sys.stderr)
769 print(err, file=sys.stderr)
770 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700771 return False
772 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700773
774class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700775 _ALPHA = 0.5
776
Dave Borowitz67700e92012-10-23 15:00:54 -0700777 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100778 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700779 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700780 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700781
782 def Get(self, project):
783 self._Load()
784 return self._times.get(project.name, _ONE_DAY_S)
785
786 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700787 self._Load()
788 name = project.name
789 old = self._times.get(name, t)
790 self._seen.add(name)
791 a = self._ALPHA
792 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700793
794 def _Load(self):
795 if self._times is None:
796 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100797 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700798 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100799 self._times = json.load(f)
800 finally:
801 f.close()
802 except (IOError, ValueError):
803 try:
804 os.remove(self._path)
805 except OSError:
806 pass
807 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700808
809 def Save(self):
810 if self._times is None:
811 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700812
813 to_delete = []
814 for name in self._times:
815 if name not in self._seen:
816 to_delete.append(name)
817 for name in to_delete:
818 del self._times[name]
819
Dave Borowitz67700e92012-10-23 15:00:54 -0700820 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100821 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700822 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100823 json.dump(self._times, f, indent=2)
824 finally:
825 f.close()
826 except (IOError, TypeError):
827 try:
828 os.remove(self._path)
829 except OSError:
830 pass