blob: 3d42a0a7db3dc59f4fd509f090177fac73cfc441 [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Anthony King85b24ac2014-05-06 15:57:48 +010018import json
David Pursehouse86d973d2012-08-24 10:21:02 +090019import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070020from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import os
22import re
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070023import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import subprocess
25import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
David Pursehouse59bbb582013-05-17 10:49:33 +090028
29from pyversion import is_python3
30if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070031 import http.cookiejar as cookielib
32 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053033 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070034 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090035 import xmlrpc.client
36else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070037 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053038 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070039 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053040 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090041 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053042 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070043 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053044 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070045 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053046 xmlrpc = imp.new_module('xmlrpc')
47 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070048
Roy Lee18afd7f2010-05-09 04:32:08 +080049try:
50 import threading as _threading
51except ImportError:
52 import dummy_threading as _threading
53
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070054try:
55 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090056
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070057 def _rlimit_nofile():
58 return resource.getrlimit(resource.RLIMIT_NOFILE)
59except ImportError:
60 def _rlimit_nofile():
61 return (256, 256)
62
Dave Borowitz18857212012-10-23 17:02:59 -070063try:
64 import multiprocessing
65except ImportError:
66 multiprocessing = None
67
David Rileye0684ad2017-04-05 00:02:59 -070068import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070069from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090070from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090071from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070072import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070073from project import Project
74from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080075from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000076from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070077import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070078from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070079from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080080from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070081from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070082
Dave Borowitz67700e92012-10-23 15:00:54 -070083_ONE_DAY_S = 24 * 60 * 60
84
David Pursehouse819827a2020-02-12 15:20:19 +090085
Doug Andersonfc06ced2011-03-16 15:49:18 -070086class _FetchError(Exception):
87 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
88 pass
89
David Pursehouse819827a2020-02-12 15:20:19 +090090
Xin Li745be2e2019-06-03 11:24:30 -070091class _CheckoutError(Exception):
92 """Internal error thrown in _CheckoutOne() when we don't want stack trace."""
93
David Pursehouse819827a2020-02-12 15:20:19 +090094
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080095class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080096 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070097 common = True
98 helpSummary = "Update working tree to the latest revision"
99 helpUsage = """
100%prog [...]
101"""
102 helpDescription = """
103The '%prog' command synchronizes local project directories
104with the remote repositories specified in the manifest. If a local
105project does not yet exist, it will clone a new local directory from
106the remote repository and set up tracking branches as specified in
107the manifest. If the local project already exists, '%prog'
108will update the remote branches and rebase any new local changes
109on top of the new remote changes.
110
111'%prog' will synchronize all projects listed at the command
112line. Projects can be specified either by name, or by a relative
113or absolute path to the project's local directory. If no projects
114are specified, '%prog' will synchronize all projects listed in
115the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700116
117The -d/--detach option can be used to switch specified projects
118back to the manifest revision. This option is especially helpful
119if the project is currently on a topic branch, but the manifest
120revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700121
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700122The -s/--smart-sync option can be used to sync to a known good
123build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200124manifest. The -t/--smart-tag option is similar and allows you to
125specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700126
David Pursehousecf76b1b2012-09-14 10:31:42 +0900127The -u/--manifest-server-username and -p/--manifest-server-password
128options can be used to specify a username and password to authenticate
129with the manifest server when using the -s or -t option.
130
131If -u and -p are not specified when using the -s or -t option, '%prog'
132will attempt to read authentication credentials for the manifest server
133from the user's .netrc file.
134
135'%prog' will not use authentication credentials from -u/-p or .netrc
136if the manifest server specified in the manifest file already includes
137credentials.
138
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400139By default, all projects will be synced. The --fail-fast option can be used
140to halt syncing as soon as possible when the the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500141
Kevin Degiabaa7f32014-11-12 11:27:45 -0700142The --force-sync option can be used to overwrite existing git
143directories if they have previously been linked to a different
144object direcotry. WARNING: This may cause data to be lost since
145refs may be removed when overwriting.
146
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500147The --force-remove-dirty option can be used to remove previously used
148projects with uncommitted changes. WARNING: This may cause data to be
149lost since uncommitted changes may be removed with projects that no longer
150exist in the manifest.
151
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700152The --no-clone-bundle option disables any attempt to use
153$URL/clone.bundle to bootstrap a new Git repository from a
154resumeable bundle file on a content delivery network. This
155may be necessary if there are problems with the local Python
156HTTP client or proxy configuration, but the Git binary works.
157
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800158The --fetch-submodules option enables fetching Git submodules
159of a project from server.
160
David Pursehousef2fad612015-01-29 14:36:28 +0900161The -c/--current-branch option can be used to only fetch objects that
162are on the branch specified by a project's revision.
163
David Pursehouseb1553542014-09-04 21:28:09 +0900164The --optimized-fetch option can be used to only fetch projects that
165are fixed to a sha1 revision if the sha1 revision does not already
166exist locally.
167
David Pursehouse74cfd272015-10-14 10:50:15 +0900168The --prune option can be used to remove any refs that no longer
169exist on the remote.
170
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400171# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700172
173If at least one project remote URL uses an SSH connection (ssh://,
174git+ssh://, or user@host:path syntax) repo will automatically
175enable the SSH ControlMaster option when connecting to that host.
176This feature permits other projects in the same '%prog' session to
177reuse the same SSH tunnel, saving connection setup overheads.
178
179To disable this behavior on UNIX platforms, set the GIT_SSH
180environment variable to 'ssh'. For example:
181
182 export GIT_SSH=ssh
183 %prog
184
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400185# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700186
187This feature is automatically disabled on Windows, due to the lack
188of UNIX domain socket support.
189
190This feature is not compatible with url.insteadof rewrites in the
191user's ~/.gitconfig. '%prog' is currently not able to perform the
192rewrite early enough to establish the ControlMaster tunnel.
193
194If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
195later is required to fix a server side protocol bug.
196
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700197"""
198
Nico Sallembien6623b212010-05-11 12:57:01 -0700199 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000200 try:
201 self.jobs = self.manifest.default.sync_j
202 except ManifestParseError:
203 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700204
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500205 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200206 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400207 help='obsolete option (to be deleted in the future)')
208 p.add_option('--fail-fast',
209 dest='fail_fast', action='store_true',
210 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700211 p.add_option('--force-sync',
212 dest='force_sync', action='store_true',
213 help="overwrite an existing git directory if it needs to "
214 "point to a different object directory. WARNING: this "
215 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500216 p.add_option('--force-remove-dirty',
217 dest='force_remove_dirty', action='store_true',
218 help="force remove projects with uncommitted modifications if "
219 "projects no longer exist in the manifest. "
220 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900221 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700222 dest='local_only', action='store_true',
223 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900224 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100225 dest='mp_update', action='store_false', default='true',
226 help='use the existing manifest checkout as-is. '
227 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900228 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700229 dest='network_only', action='store_true',
230 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900231 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700232 dest='detach_head', action='store_true',
233 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900234 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700235 dest='current_branch_only', action='store_true',
236 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900237 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700238 dest='quiet', action='store_true',
239 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900240 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800241 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700242 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500243 p.add_option('-m', '--manifest-name',
244 dest='manifest_name',
245 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700246 p.add_option('--no-clone-bundle',
247 dest='no_clone_bundle', action='store_true',
248 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800249 p.add_option('-u', '--manifest-server-username', action='store',
250 dest='manifest_server_username',
251 help='username to authenticate with the manifest server')
252 p.add_option('-p', '--manifest-server-password', action='store',
253 dest='manifest_server_password',
254 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800255 p.add_option('--fetch-submodules',
256 dest='fetch_submodules', action='store_true',
257 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700258 p.add_option('--no-tags',
259 dest='no_tags', action='store_true',
260 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900261 p.add_option('--optimized-fetch',
262 dest='optimized_fetch', action='store_true',
263 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900264 p.add_option('--prune', dest='prune', action='store_true',
265 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700266 if show_smart:
267 p.add_option('-s', '--smart-sync',
268 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900269 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200270 p.add_option('-t', '--smart-tag',
271 dest='smart_tag', action='store',
272 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700273
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700274 g = p.add_option_group('repo Version options')
275 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700276 dest='no_repo_verify', action='store_true',
277 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700278 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800279 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700280 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700281
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500282 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700283 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800284
David James8d201162013-10-11 17:03:19 -0700285 Delegates most of the work to _FetchHelper.
286
287 Args:
288 opt: Program options returned from optparse. See _Options().
289 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500290 sem: We'll release() this semaphore when we exit so that another thread
291 can be started up.
David James89ece422014-01-09 18:51:58 -0800292 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700293 _FetchHelper docstring for details.
294 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500295 try:
296 for project in projects:
297 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400298 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500299 break
300 finally:
301 sem.release()
David James8d201162013-10-11 17:03:19 -0700302
Xin Li745be2e2019-06-03 11:24:30 -0700303 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
304 clone_filter):
David James8d201162013-10-11 17:03:19 -0700305 """Fetch git objects for a single project.
306
David Pursehousec1b86a22012-11-14 11:36:51 +0900307 Args:
308 opt: Program options returned from optparse. See _Options().
309 project: Project object for the project to fetch.
310 lock: Lock for accessing objects that are shared amongst multiple
311 _FetchHelper() threads.
312 fetched: set object that we will add project.gitdir to when we're done
313 (with our lock held).
314 pm: Instance of a Project object. We will call pm.update() (with our
315 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900316 err_event: We'll set this event in the case of an error (after printing
317 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700318 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700319
320 Returns:
321 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900322 """
323 # We'll set to true once we've locked the lock.
324 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700325
David Pursehousec1b86a22012-11-14 11:36:51 +0900326 # Encapsulate everything in a try/except/finally so that:
327 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900328 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700329 start = time.time()
330 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900331 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700332 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900333 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900334 quiet=opt.quiet,
335 current_branch_only=opt.current_branch_only,
336 force_sync=opt.force_sync,
337 clone_bundle=not opt.no_clone_bundle,
338 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
339 optimized_fetch=opt.optimized_fetch,
340 prune=opt.prune,
341 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900342 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700343
David Pursehousec1b86a22012-11-14 11:36:51 +0900344 # Lock around all the rest of the code, since printing, updating a set
345 # and Progress.update() are not thread safe.
346 lock.acquire()
347 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700348
David Pursehousec1b86a22012-11-14 11:36:51 +0900349 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800350 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700351 print('error: Cannot fetch %s from %s'
352 % (project.name, project.remote.url),
353 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400354 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900355 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700356
David Pursehousec1b86a22012-11-14 11:36:51 +0900357 fetched.add(project.gitdir)
Mike Frysinger3538dd22019-08-26 15:32:06 -0400358 pm.update(msg=project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900359 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800360 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400361 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900362 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900363 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900364 err_event.set()
365 raise
366 finally:
367 if did_lock:
368 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700369 finish = time.time()
370 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
371 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800372
David James8d201162013-10-11 17:03:19 -0700373 return success
374
Mike Frysinger5a033082019-09-23 19:21:20 -0400375 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700376 fetched = set()
David James89ece422014-01-09 18:51:58 -0800377 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200378 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200379 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800380
David James89ece422014-01-09 18:51:58 -0800381 objdir_project_map = dict()
382 for project in projects:
383 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700384
David James89ece422014-01-09 18:51:58 -0800385 threads = set()
386 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800387 for project_list in objdir_project_map.values():
388 # Check for any errors before running any more tasks.
389 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400390 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800391 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700392
David James89ece422014-01-09 18:51:58 -0800393 sem.acquire()
394 kwargs = dict(opt=opt,
395 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500396 sem=sem,
David James89ece422014-01-09 18:51:58 -0800397 lock=lock,
398 fetched=fetched,
399 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700400 err_event=err_event,
401 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800402 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900403 t = _threading.Thread(target=self._FetchProjectList,
404 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200405 # Ensure that Ctrl-C will not freeze the repo process.
406 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800407 threads.add(t)
408 t.start()
David James89ece422014-01-09 18:51:58 -0800409 else:
410 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800411
David James89ece422014-01-09 18:51:58 -0800412 for t in threads:
413 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800414
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700415 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700416 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700417
Julien Campergue335f5ef2013-10-16 11:02:35 +0200418 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400419 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200420
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700421 return fetched
422
Xin Li745be2e2019-06-03 11:24:30 -0700423 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
424 """Main function of the fetch threads.
425
426 Delegates most of the work to _CheckoutOne.
427
428 Args:
429 opt: Program options returned from optparse. See _Options().
430 projects: Projects to fetch.
431 sem: We'll release() this semaphore when we exit so that another thread
432 can be started up.
433 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
434 _CheckoutOne docstring for details.
435 """
436 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400437 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700438 finally:
439 sem.release()
440
Vadim Bendeburydff91942019-11-06 11:05:00 -0800441 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700442 """Checkout work tree for one project
443
444 Args:
445 opt: Program options returned from optparse. See _Options().
446 project: Project object for the project to checkout.
447 lock: Lock for accessing objects that are shared amongst multiple
448 _CheckoutWorker() threads.
449 pm: Instance of a Project object. We will call pm.update() (with our
450 lock held).
451 err_event: We'll set this event in the case of an error (after printing
452 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800453 err_results: A list of strings, paths to git repos where checkout
454 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700455
456 Returns:
457 Whether the fetch was successful.
458 """
459 # We'll set to true once we've locked the lock.
460 did_lock = False
461
Xin Li745be2e2019-06-03 11:24:30 -0700462 # Encapsulate everything in a try/except/finally so that:
463 # - We always set err_event in the case of an exception.
464 # - We always make sure we unlock the lock if we locked it.
465 start = time.time()
466 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
467 detach_head=opt.detach_head)
468 success = False
469 try:
470 try:
471 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700472
473 # Lock around all the rest of the code, since printing, updating a set
474 # and Progress.update() are not thread safe.
475 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400476 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700477 did_lock = True
478
479 if not success:
480 err_event.set()
481 print('error: Cannot checkout %s' % (project.name),
482 file=sys.stderr)
483 raise _CheckoutError()
484
Mike Frysinger3538dd22019-08-26 15:32:06 -0400485 pm.update(msg=project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700486 except _CheckoutError:
487 pass
488 except Exception as e:
489 print('error: Cannot checkout %s: %s: %s' %
490 (project.name, type(e).__name__, str(e)),
491 file=sys.stderr)
492 err_event.set()
493 raise
494 finally:
495 if did_lock:
Vadim Bendeburydff91942019-11-06 11:05:00 -0800496 if not success:
497 err_results.append(project.relpath)
Xin Li745be2e2019-06-03 11:24:30 -0700498 lock.release()
499 finish = time.time()
500 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
501 start, finish, success)
502
503 return success
504
Mike Frysinger5a033082019-09-23 19:21:20 -0400505 def _Checkout(self, all_projects, opt, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700506 """Checkout projects listed in all_projects
507
508 Args:
509 all_projects: List of all projects that should be checked out.
510 opt: Program options returned from optparse. See _Options().
Mike Frysinger5a033082019-09-23 19:21:20 -0400511 err_event: We'll set this event in the case of an error (after printing
512 out info about the error).
513 err_results: A list of strings, paths to git repos where checkout
514 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700515 """
516
517 # Perform checkouts in multiple threads when we are using partial clone.
518 # Without partial clone, all needed git objects are already downloaded,
519 # in this situation it's better to use only one process because the checkout
520 # would be mostly disk I/O; with partial clone, the objects are only
521 # downloaded when demanded (at checkout time), which is similar to the
522 # Sync_NetworkHalf case and parallelism would be helpful.
523 if self.manifest.CloneFilter:
524 syncjobs = self.jobs
525 else:
526 syncjobs = 1
527
528 lock = _threading.Lock()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400529 pm = Progress('Checking out projects', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700530
531 threads = set()
532 sem = _threading.Semaphore(syncjobs)
Xin Li745be2e2019-06-03 11:24:30 -0700533
534 for project in all_projects:
535 # Check for any errors before running any more tasks.
536 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400537 if err_event.isSet() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700538 break
539
540 sem.acquire()
541 if project.worktree:
542 kwargs = dict(opt=opt,
543 sem=sem,
544 project=project,
545 lock=lock,
546 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800547 err_event=err_event,
548 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700549 if syncjobs > 1:
550 t = _threading.Thread(target=self._CheckoutWorker,
551 kwargs=kwargs)
552 # Ensure that Ctrl-C will not freeze the repo process.
553 t.daemon = True
554 threads.add(t)
555 t.start()
556 else:
557 self._CheckoutWorker(**kwargs)
558
559 for t in threads:
560 t.join()
561
562 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700563
Mike Frysinger5a033082019-09-23 19:21:20 -0400564 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700565 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700566 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500567 # Make sure pruning never kicks in with shared projects.
Gabe Black2ff30292014-10-09 17:54:35 -0700568 if len(project.manifest.GetProjectsWithName(project.name)) > 1:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500569 print('%s: Shared project %s found, disabling pruning.' %
570 (project.relpath, project.name))
571 if git_require((2, 7, 0)):
572 project.config.SetString('core.repositoryFormatVersion', '1')
573 project.config.SetString('extensions.preciousObjects', 'true')
574 else:
575 # This isn't perfect, but it's the best we can do with old git.
576 print('%s: WARNING: shared projects are unreliable when using old '
577 'versions of git; please upgrade to git-2.7.0+.'
578 % (project.relpath,),
579 file=sys.stderr)
580 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700581 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700582
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500583 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700584 cpu_count = multiprocessing.cpu_count()
585 else:
586 cpu_count = 1
587 jobs = min(self.jobs, cpu_count)
588
589 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700590 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700591 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700592 return
593
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400594 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700595
596 threads = set()
597 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700598
David James8d201162013-10-11 17:03:19 -0700599 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700600 try:
601 try:
David James8d201162013-10-11 17:03:19 -0700602 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700603 except GitError:
604 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900605 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700606 err_event.set()
607 raise
608 finally:
609 sem.release()
610
Gabe Black2ff30292014-10-09 17:54:35 -0700611 for bare_git in gc_gitdirs.values():
Mike Frysinger5a033082019-09-23 19:21:20 -0400612 if err_event.isSet() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700613 break
614 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700615 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700616 t.daemon = True
617 threads.add(t)
618 t.start()
619
620 for t in threads:
621 t.join()
622
Tim Kilbourn07669002013-03-08 15:02:49 -0800623 def _ReloadManifest(self, manifest_name=None):
624 if manifest_name:
625 # Override calls _Unload already
626 self.manifest.Override(manifest_name)
627 else:
628 self.manifest._Unload()
629
Dan Willemsen43507912016-09-01 16:26:02 -0700630 def _DeleteProject(self, path):
631 print('Deleting obsolete path %s' % path, file=sys.stderr)
632
633 # Delete the .git directory first, so we're less likely to have a partially
634 # working git repository around. There shouldn't be any git projects here,
635 # so rmtree works.
636 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -0700637 platform_utils.rmtree(os.path.join(path, '.git'))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700638 except OSError as e:
639 print('Failed to remove %s (%s)' % (os.path.join(path, '.git'), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700640 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
641 print(' remove manually, then run sync again', file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400642 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700643
644 # Delete everything under the worktree, except for directories that contain
645 # another git project
646 dirs_to_remove = []
647 failed = False
Renaud Paquaybed8b622018-09-27 10:46:58 -0700648 for root, dirs, files in platform_utils.walk(path):
Dan Willemsen43507912016-09-01 16:26:02 -0700649 for f in files:
650 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800651 platform_utils.remove(os.path.join(root, f))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700652 except OSError as e:
653 print('Failed to remove %s (%s)' % (os.path.join(root, f), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700654 failed = True
655 dirs[:] = [d for d in dirs
656 if not os.path.lexists(os.path.join(root, d, '.git'))]
657 dirs_to_remove += [os.path.join(root, d) for d in dirs
658 if os.path.join(root, d) not in dirs_to_remove]
659 for d in reversed(dirs_to_remove):
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700660 if platform_utils.islink(d):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700661 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800662 platform_utils.remove(d)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700663 except OSError as e:
664 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700665 failed = True
Renaud Paquaybed8b622018-09-27 10:46:58 -0700666 elif len(platform_utils.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700667 try:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700668 platform_utils.rmdir(d)
669 except OSError as e:
670 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700671 failed = True
672 continue
673 if failed:
674 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
675 print(' remove manually, then run sync again', file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400676 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700677
678 # Try deleting parent dirs if they are empty
679 project_dir = path
680 while project_dir != self.manifest.topdir:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700681 if len(platform_utils.listdir(project_dir)) == 0:
682 platform_utils.rmdir(project_dir)
Dan Willemsen43507912016-09-01 16:26:02 -0700683 else:
684 break
685 project_dir = os.path.dirname(project_dir)
686
687 return 0
688
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500689 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700690 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700691 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700692 if project.relpath:
693 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700694 file_name = 'project.list'
695 file_path = os.path.join(self.manifest.repodir, file_name)
696 old_project_paths = []
697
698 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500699 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700700 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800701 # In reversed order, so subfolders are deleted before parent folder.
702 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700703 if not path:
704 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700705 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900706 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700707 gitdir = os.path.join(self.manifest.topdir, path, '.git')
708 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900709 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900710 manifest=self.manifest,
711 name=path,
712 remote=RemoteSpec('origin'),
713 gitdir=gitdir,
714 objdir=gitdir,
715 worktree=os.path.join(self.manifest.topdir, path),
716 relpath=path,
717 revisionExpr='HEAD',
718 revisionId=None,
719 groups=None)
Anthonyf3fdf822009-09-26 13:38:52 -0400720
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500721 if project.IsDirty() and opt.force_remove_dirty:
722 print('WARNING: Removing dirty project "%s": uncommitted changes '
723 'erased' % project.relpath, file=sys.stderr)
724 self._DeleteProject(project.worktree)
725 elif project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900726 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900727 'are present' % project.relpath, file=sys.stderr)
728 print(' commit changes, then run sync again',
729 file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400730 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700731 elif self._DeleteProject(project.worktree):
Mike Frysingera850ca22019-08-07 17:19:24 -0400732 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700733
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700734 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500735 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700736 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700737 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700738 return 0
739
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400740 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
741 if not self.manifest.manifest_server:
742 print('error: cannot smart sync: no manifest server defined in '
743 'manifest', file=sys.stderr)
744 sys.exit(1)
745
746 manifest_server = self.manifest.manifest_server
747 if not opt.quiet:
748 print('Using manifest server %s' % manifest_server)
749
David Pursehouseeeff3532020-02-12 11:24:10 +0900750 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400751 username = None
752 password = None
753 if opt.manifest_server_username and opt.manifest_server_password:
754 username = opt.manifest_server_username
755 password = opt.manifest_server_password
756 else:
757 try:
758 info = netrc.netrc()
759 except IOError:
760 # .netrc file does not exist or could not be opened
761 pass
762 else:
763 try:
764 parse_result = urllib.parse.urlparse(manifest_server)
765 if parse_result.hostname:
766 auth = info.authenticators(parse_result.hostname)
767 if auth:
768 username, _account, password = auth
769 else:
770 print('No credentials found for %s in .netrc'
771 % parse_result.hostname, file=sys.stderr)
772 except netrc.NetrcParseError as e:
773 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
774
775 if (username and password):
776 manifest_server = manifest_server.replace('://', '://%s:%s@' %
777 (username, password),
778 1)
779
780 transport = PersistentTransport(manifest_server)
781 if manifest_server.startswith('persistent-'):
782 manifest_server = manifest_server[len('persistent-'):]
783
784 try:
785 server = xmlrpc.client.Server(manifest_server, transport=transport)
786 if opt.smart_sync:
787 p = self.manifest.manifestProject
788 b = p.GetBranch(p.CurrentBranch)
789 branch = b.merge
790 if branch.startswith(R_HEADS):
791 branch = branch[len(R_HEADS):]
792
793 env = os.environ.copy()
794 if 'SYNC_TARGET' in env:
795 target = env['SYNC_TARGET']
796 [success, manifest_str] = server.GetApprovedManifest(branch, target)
797 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
798 target = '%s-%s' % (env['TARGET_PRODUCT'],
799 env['TARGET_BUILD_VARIANT'])
800 [success, manifest_str] = server.GetApprovedManifest(branch, target)
801 else:
802 [success, manifest_str] = server.GetApprovedManifest(branch)
803 else:
804 assert(opt.smart_tag)
805 [success, manifest_str] = server.GetManifest(opt.smart_tag)
806
807 if success:
808 manifest_name = os.path.basename(smart_sync_manifest_path)
809 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500810 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400811 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400812 except IOError as e:
813 print('error: cannot write manifest to %s:\n%s'
814 % (smart_sync_manifest_path, e),
815 file=sys.stderr)
816 sys.exit(1)
817 self._ReloadManifest(manifest_name)
818 else:
819 print('error: manifest server RPC call failed: %s' %
820 manifest_str, file=sys.stderr)
821 sys.exit(1)
822 except (socket.error, IOError, xmlrpc.client.Fault) as e:
823 print('error: cannot connect to manifest server %s:\n%s'
824 % (self.manifest.manifest_server, e), file=sys.stderr)
825 sys.exit(1)
826 except xmlrpc.client.ProtocolError as e:
827 print('error: cannot connect to manifest server %s:\n%d %s'
828 % (self.manifest.manifest_server, e.errcode, e.errmsg),
829 file=sys.stderr)
830 sys.exit(1)
831
832 return manifest_name
833
Mike Frysingerfb527e32019-08-27 02:34:32 -0400834 def _UpdateManifestProject(self, opt, mp, manifest_name):
835 """Fetch & update the local manifest project."""
836 if not opt.local_only:
837 start = time.time()
838 success = mp.Sync_NetworkHalf(quiet=opt.quiet,
839 current_branch_only=opt.current_branch_only,
840 no_tags=opt.no_tags,
841 optimized_fetch=opt.optimized_fetch,
842 submodules=self.manifest.HasSubmodules,
843 clone_filter=self.manifest.CloneFilter)
844 finish = time.time()
845 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
846 start, finish, success)
847
848 if mp.HasChanges:
849 syncbuf = SyncBuffer(mp.config)
850 start = time.time()
851 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
852 clean = syncbuf.Finish()
853 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
854 start, time.time(), clean)
855 if not clean:
856 sys.exit(1)
857 self._ReloadManifest(opt.manifest_name)
858 if opt.jobs is None:
859 self.jobs = self.manifest.default.sync_j
860
Mike Frysingerae6cb082019-08-27 01:10:59 -0400861 def ValidateOptions(self, opt, args):
862 if opt.force_broken:
863 print('warning: -f/--force-broken is now the default behavior, and the '
864 'options are deprecated', file=sys.stderr)
865 if opt.network_only and opt.detach_head:
866 self.OptionParser.error('cannot combine -n and -d')
867 if opt.network_only and opt.local_only:
868 self.OptionParser.error('cannot combine -n and -l')
869 if opt.manifest_name and opt.smart_sync:
870 self.OptionParser.error('cannot combine -m and -s')
871 if opt.manifest_name and opt.smart_tag:
872 self.OptionParser.error('cannot combine -m and -t')
873 if opt.manifest_server_username or opt.manifest_server_password:
874 if not (opt.smart_sync or opt.smart_tag):
875 self.OptionParser.error('-u and -p may only be combined with -s or -t')
876 if None in [opt.manifest_server_username, opt.manifest_server_password]:
877 self.OptionParser.error('both -u and -p must be given')
878
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700879 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800880 if opt.jobs:
881 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700882 if self.jobs > 1:
883 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400884 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700885
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500886 if opt.manifest_name:
887 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700888
Chirayu Desaia892b102013-06-11 14:18:46 +0530889 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900890 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900891 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530892
Victor Boivie08c880d2011-04-19 10:32:52 +0200893 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400894 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
895 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900896 if os.path.isfile(smart_sync_manifest_path):
897 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800898 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900899 except OSError as e:
900 print('error: failed to remove existing smart sync override manifest: %s' %
901 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700902
Mike Frysinger5a033082019-09-23 19:21:20 -0400903 err_event = _threading.Event()
904
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700905 rp = self.manifest.repoProject
906 rp.PreSync()
907
908 mp = self.manifest.manifestProject
909 mp.PreSync()
910
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800911 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700912 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800913
Fredrik de Grootcc960972019-11-22 09:04:31 +0100914 if not opt.mp_update:
915 print('Skipping update of local manifest project.')
916 else:
917 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700918
Simran Basib9a1b732015-08-20 12:19:28 -0700919 if self.gitc_manifest:
920 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700921 missing_ok=True)
922 gitc_projects = []
923 opened_projects = []
924 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700925 if project.relpath in self.gitc_manifest.paths and \
926 self.gitc_manifest.paths[project.relpath].old_revision:
927 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700928 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700929 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700930
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700931 if not args:
932 gitc_projects = None
933
934 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700935 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700936 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
937 if manifest_name:
938 manifest.Override(manifest_name)
939 else:
940 manifest.Override(self.manifest.manifestFile)
941 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
942 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700943 gitc_projects)
944 print('GITC client successfully synced.')
945
946 # The opened projects need to be synced as normal, therefore we
947 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700948 # TODO: make this more reliable -- if there's a project name/path overlap,
949 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900950 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
951 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700952 if not args:
953 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800954 all_projects = self.GetProjects(args,
955 missing_ok=True,
956 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700957
Mike Frysinger5a033082019-09-23 19:21:20 -0400958 err_network_sync = False
959 err_update_projects = False
960 err_checkout = False
961
Dave Borowitz67700e92012-10-23 15:00:54 -0700962 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700963 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700964 to_fetch = []
965 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700966 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700967 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900968 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700969 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700970
Mike Frysinger5a033082019-09-23 19:21:20 -0400971 fetched = self._Fetch(to_fetch, opt, err_event)
972
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700973 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700974 if opt.network_only:
975 # bail out now; the rest touches the working tree
Mike Frysinger5a033082019-09-23 19:21:20 -0400976 if err_event.isSet():
977 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
978 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700979 return
980
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800981 # Iteratively fetch missing and/or nested unregistered submodules
982 previously_missing_set = set()
983 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100984 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800985 all_projects = self.GetProjects(args,
986 missing_ok=True,
987 submodules_ok=opt.fetch_submodules)
988 missing = []
989 for project in all_projects:
990 if project.gitdir not in fetched:
991 missing.append(project)
992 if not missing:
993 break
994 # Stop us from non-stopped fetching actually-missing repos: If set of
995 # missing repos has not been changed from last fetch, we break.
996 missing_set = set(p.name for p in missing)
997 if previously_missing_set == missing_set:
998 break
999 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -04001000 fetched.update(self._Fetch(missing, opt, err_event))
1001
1002 # If we saw an error, exit with code 1 so that other scripts can check.
1003 if err_event.isSet():
1004 err_network_sync = True
1005 if opt.fail_fast:
1006 print('\nerror: Exited sync due to fetch errors.\n'
1007 'Local checkouts *not* updated. Resolve network issues & '
1008 'retry.\n'
1009 '`repo sync -l` will update some local checkouts.',
1010 file=sys.stderr)
1011 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001012
Julien Campergue335f5ef2013-10-16 11:02:35 +02001013 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001014 # bail out now, we have no working tree
1015 return
1016
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001017 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001018 err_event.set()
1019 err_update_projects = True
1020 if opt.fail_fast:
1021 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1022 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001023
Mike Frysinger5a033082019-09-23 19:21:20 -04001024 err_results = []
1025 self._Checkout(all_projects, opt, err_event, err_results)
1026 if err_event.isSet():
1027 err_checkout = True
1028 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001029
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001030 # If there's a notice that's supposed to print at the end of the sync, print
1031 # it now...
1032 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001033 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001034
Mike Frysinger5a033082019-09-23 19:21:20 -04001035 # If we saw an error, exit with code 1 so that other scripts can check.
1036 if err_event.isSet():
1037 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1038 if err_network_sync:
1039 print('error: Downloading network changes failed.', file=sys.stderr)
1040 if err_update_projects:
1041 print('error: Updating local project lists failed.', file=sys.stderr)
1042 if err_checkout:
1043 print('error: Checking out local projects failed.', file=sys.stderr)
1044 if err_results:
1045 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1046 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1047 file=sys.stderr)
1048 sys.exit(1)
1049
Mike Frysingere19d9e12020-02-12 11:23:32 -05001050 if not opt.quiet:
1051 print('repo sync has finished successfully.')
1052
David Pursehouse819827a2020-02-12 15:20:19 +09001053
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001054def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001055 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001056 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001057 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001058 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001059 if project.Exists:
1060 project.PostRepoUpgrade()
1061
David Pursehouse819827a2020-02-12 15:20:19 +09001062
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001063def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
1064 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001065 print('info: A new version of repo is available', file=sys.stderr)
1066 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001067 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001068 syncbuf = SyncBuffer(rp.config)
1069 rp.Sync_LocalHalf(syncbuf)
1070 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001071 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001072 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001073 raise RepoChangedException(['--repo-upgraded'])
1074 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001075 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001076 else:
1077 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001078 print('repo version %s is current' % rp.work_git.describe(HEAD),
1079 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001080
David Pursehouse819827a2020-02-12 15:20:19 +09001081
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001082def _VerifyTag(project):
1083 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1084 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001085 print('warning: GnuPG was not available during last "repo init"\n'
1086 'warning: Cannot automatically authenticate repo."""',
1087 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001088 return True
1089
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001090 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001091 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001092 except GitError:
1093 cur = None
1094
1095 if not cur \
1096 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001097 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001098 if rev.startswith(R_HEADS):
1099 rev = rev[len(R_HEADS):]
1100
Sarah Owenscecd1d82012-11-01 22:59:27 -07001101 print(file=sys.stderr)
1102 print("warning: project '%s' branch '%s' is not signed"
1103 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001104 return False
1105
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001106 env = os.environ.copy()
1107 env['GIT_DIR'] = project.gitdir.encode()
1108 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001109
1110 cmd = [GIT, 'tag', '-v', cur]
1111 proc = subprocess.Popen(cmd,
David Pursehousee5913ae2020-02-12 13:56:59 +09001112 stdout=subprocess.PIPE,
1113 stderr=subprocess.PIPE,
1114 env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001115 out = proc.stdout.read()
1116 proc.stdout.close()
1117
1118 err = proc.stderr.read()
1119 proc.stderr.close()
1120
1121 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001122 print(file=sys.stderr)
1123 print(out, file=sys.stderr)
1124 print(err, file=sys.stderr)
1125 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001126 return False
1127 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001128
David Rileye0684ad2017-04-05 00:02:59 -07001129
Dave Borowitz67700e92012-10-23 15:00:54 -07001130class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001131 _ALPHA = 0.5
1132
Dave Borowitz67700e92012-10-23 15:00:54 -07001133 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001134 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001135 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001136 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001137
1138 def Get(self, project):
1139 self._Load()
1140 return self._times.get(project.name, _ONE_DAY_S)
1141
1142 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001143 self._Load()
1144 name = project.name
1145 old = self._times.get(name, t)
1146 self._seen.add(name)
1147 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001148 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001149
1150 def _Load(self):
1151 if self._times is None:
1152 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001153 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001154 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001155 except (IOError, ValueError):
1156 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001157 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001158 except OSError:
1159 pass
1160 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001161
1162 def Save(self):
1163 if self._times is None:
1164 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001165
1166 to_delete = []
1167 for name in self._times:
1168 if name not in self._seen:
1169 to_delete.append(name)
1170 for name in to_delete:
1171 del self._times[name]
1172
Dave Borowitz67700e92012-10-23 15:00:54 -07001173 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001174 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001175 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001176 except (IOError, TypeError):
1177 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001178 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001179 except OSError:
1180 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001181
1182# This is a replacement for xmlrpc.client.Transport using urllib2
1183# and supporting persistent-http[s]. It cannot change hosts from
1184# request to request like the normal transport, the real url
1185# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001186
1187
Dan Willemsen0745bb22015-08-17 13:41:45 -07001188class PersistentTransport(xmlrpc.client.Transport):
1189 def __init__(self, orig_host):
1190 self.orig_host = orig_host
1191
1192 def request(self, host, handler, request_body, verbose=False):
1193 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1194 # Python doesn't understand cookies with the #HttpOnly_ prefix
1195 # Since we're only using them for HTTP, copy the file temporarily,
1196 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001197 if cookiefile:
1198 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001199 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001200 try:
1201 with open(cookiefile) as f:
1202 for line in f:
1203 if line.startswith("#HttpOnly_"):
1204 line = line[len("#HttpOnly_"):]
1205 tmpcookiefile.write(line)
1206 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001207
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001208 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001209 try:
1210 cookiejar.load()
1211 except cookielib.LoadError:
1212 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001213 finally:
1214 tmpcookiefile.close()
1215 else:
1216 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001217
1218 proxyhandler = urllib.request.ProxyHandler
1219 if proxy:
1220 proxyhandler = urllib.request.ProxyHandler({
1221 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001222 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001223
1224 opener = urllib.request.build_opener(
1225 urllib.request.HTTPCookieProcessor(cookiejar),
1226 proxyhandler)
1227
1228 url = urllib.parse.urljoin(self.orig_host, handler)
1229 parse_results = urllib.parse.urlparse(url)
1230
1231 scheme = parse_results.scheme
1232 if scheme == 'persistent-http':
1233 scheme = 'http'
1234 if scheme == 'persistent-https':
1235 # If we're proxying through persistent-https, use http. The
1236 # proxy itself will do the https.
1237 if proxy:
1238 scheme = 'http'
1239 else:
1240 scheme = 'https'
1241
1242 # Parse out any authentication information using the base class
1243 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1244
1245 url = urllib.parse.urlunparse((
1246 scheme,
1247 host,
1248 parse_results.path,
1249 parse_results.params,
1250 parse_results.query,
1251 parse_results.fragment))
1252
1253 request = urllib.request.Request(url, request_body)
1254 if extra_headers is not None:
1255 for (name, header) in extra_headers:
1256 request.add_header(name, header)
1257 request.add_header('Content-Type', 'text/xml')
1258 try:
1259 response = opener.open(request)
1260 except urllib.error.HTTPError as e:
1261 if e.code == 501:
1262 # We may have been redirected through a login process
1263 # but our POST turned into a GET. Retry.
1264 response = opener.open(request)
1265 else:
1266 raise
1267
1268 p, u = xmlrpc.client.getparser()
1269 while 1:
1270 data = response.read(1024)
1271 if not data:
1272 break
1273 p.feed(data)
1274 p.close()
1275 return u.close()
1276
1277 def close(self):
1278 pass