blob: 5b1024df15fbef505c64e4db42d0071fdf90161c [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Mike Frysingeracf63b22019-06-13 02:24:21 -040015import http.cookiejar as cookielib
Anthony King85b24ac2014-05-06 15:57:48 +010016import json
David Pursehouse86d973d2012-08-24 10:21:02 +090017import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070018from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
20import re
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070021import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022import subprocess
23import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070024import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070025import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040026import urllib.error
27import urllib.parse
28import urllib.request
29import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070030
Roy Lee18afd7f2010-05-09 04:32:08 +080031try:
32 import threading as _threading
33except ImportError:
34 import dummy_threading as _threading
35
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070036try:
37 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090038
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070039 def _rlimit_nofile():
40 return resource.getrlimit(resource.RLIMIT_NOFILE)
41except ImportError:
42 def _rlimit_nofile():
43 return (256, 256)
44
Dave Borowitz18857212012-10-23 17:02:59 -070045try:
46 import multiprocessing
47except ImportError:
48 multiprocessing = None
49
David Rileye0684ad2017-04-05 00:02:59 -070050import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070051from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090052from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090053from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080054import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070055import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070056from project import Project
57from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080058from command import Command, MirrorSafeCommand
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080059from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070060import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070061from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070062from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080063from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070064from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070065
Dave Borowitz67700e92012-10-23 15:00:54 -070066_ONE_DAY_S = 24 * 60 * 60
67
David Pursehouse819827a2020-02-12 15:20:19 +090068
Doug Andersonfc06ced2011-03-16 15:49:18 -070069class _FetchError(Exception):
70 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
Doug Andersonfc06ced2011-03-16 15:49:18 -070071
David Pursehouse819827a2020-02-12 15:20:19 +090072
Xin Li745be2e2019-06-03 11:24:30 -070073class _CheckoutError(Exception):
74 """Internal error thrown in _CheckoutOne() when we don't want stack trace."""
75
David Pursehouse819827a2020-02-12 15:20:19 +090076
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080077class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080078 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070079 common = True
80 helpSummary = "Update working tree to the latest revision"
81 helpUsage = """
82%prog [<project>...]
83"""
84 helpDescription = """
85The '%prog' command synchronizes local project directories
86with the remote repositories specified in the manifest. If a local
87project does not yet exist, it will clone a new local directory from
88the remote repository and set up tracking branches as specified in
89the manifest. If the local project already exists, '%prog'
90will update the remote branches and rebase any new local changes
91on top of the new remote changes.
92
93'%prog' will synchronize all projects listed at the command
94line. Projects can be specified either by name, or by a relative
95or absolute path to the project's local directory. If no projects
96are specified, '%prog' will synchronize all projects listed in
97the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070098
99The -d/--detach option can be used to switch specified projects
100back to the manifest revision. This option is especially helpful
101if the project is currently on a topic branch, but the manifest
102revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700103
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700104The -s/--smart-sync option can be used to sync to a known good
105build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200106manifest. The -t/--smart-tag option is similar and allows you to
107specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700108
David Pursehousecf76b1b2012-09-14 10:31:42 +0900109The -u/--manifest-server-username and -p/--manifest-server-password
110options can be used to specify a username and password to authenticate
111with the manifest server when using the -s or -t option.
112
113If -u and -p are not specified when using the -s or -t option, '%prog'
114will attempt to read authentication credentials for the manifest server
115from the user's .netrc file.
116
117'%prog' will not use authentication credentials from -u/-p or .netrc
118if the manifest server specified in the manifest file already includes
119credentials.
120
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400121By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400122to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500123
Kevin Degiabaa7f32014-11-12 11:27:45 -0700124The --force-sync option can be used to overwrite existing git
125directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900126object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700127refs may be removed when overwriting.
128
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500129The --force-remove-dirty option can be used to remove previously used
130projects with uncommitted changes. WARNING: This may cause data to be
131lost since uncommitted changes may be removed with projects that no longer
132exist in the manifest.
133
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700134The --no-clone-bundle option disables any attempt to use
135$URL/clone.bundle to bootstrap a new Git repository from a
136resumeable bundle file on a content delivery network. This
137may be necessary if there are problems with the local Python
138HTTP client or proxy configuration, but the Git binary works.
139
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800140The --fetch-submodules option enables fetching Git submodules
141of a project from server.
142
David Pursehousef2fad612015-01-29 14:36:28 +0900143The -c/--current-branch option can be used to only fetch objects that
144are on the branch specified by a project's revision.
145
David Pursehouseb1553542014-09-04 21:28:09 +0900146The --optimized-fetch option can be used to only fetch projects that
147are fixed to a sha1 revision if the sha1 revision does not already
148exist locally.
149
David Pursehouse74cfd272015-10-14 10:50:15 +0900150The --prune option can be used to remove any refs that no longer
151exist on the remote.
152
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400153# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700154
155If at least one project remote URL uses an SSH connection (ssh://,
156git+ssh://, or user@host:path syntax) repo will automatically
157enable the SSH ControlMaster option when connecting to that host.
158This feature permits other projects in the same '%prog' session to
159reuse the same SSH tunnel, saving connection setup overheads.
160
161To disable this behavior on UNIX platforms, set the GIT_SSH
162environment variable to 'ssh'. For example:
163
164 export GIT_SSH=ssh
165 %prog
166
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400167# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700168
169This feature is automatically disabled on Windows, due to the lack
170of UNIX domain socket support.
171
172This feature is not compatible with url.insteadof rewrites in the
173user's ~/.gitconfig. '%prog' is currently not able to perform the
174rewrite early enough to establish the ControlMaster tunnel.
175
176If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
177later is required to fix a server side protocol bug.
178
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700179"""
180
Nico Sallembien6623b212010-05-11 12:57:01 -0700181 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000182 try:
183 self.jobs = self.manifest.default.sync_j
184 except ManifestParseError:
185 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700186
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500187 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200188 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400189 help='obsolete option (to be deleted in the future)')
190 p.add_option('--fail-fast',
191 dest='fail_fast', action='store_true',
192 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700193 p.add_option('--force-sync',
194 dest='force_sync', action='store_true',
195 help="overwrite an existing git directory if it needs to "
196 "point to a different object directory. WARNING: this "
197 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500198 p.add_option('--force-remove-dirty',
199 dest='force_remove_dirty', action='store_true',
200 help="force remove projects with uncommitted modifications if "
201 "projects no longer exist in the manifest. "
202 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900203 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700204 dest='local_only', action='store_true',
205 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900206 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100207 dest='mp_update', action='store_false', default='true',
208 help='use the existing manifest checkout as-is. '
209 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900210 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700211 dest='network_only', action='store_true',
212 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900213 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700214 dest='detach_head', action='store_true',
215 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900216 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700217 dest='current_branch_only', action='store_true',
218 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500219 p.add_option('-v', '--verbose',
220 dest='output_mode', action='store_true',
221 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900222 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500223 dest='output_mode', action='store_false',
224 help='only show errors')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900225 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800226 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700227 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500228 p.add_option('-m', '--manifest-name',
229 dest='manifest_name',
230 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700231 p.add_option('--clone-bundle', action='store_true',
232 help='enable use of /clone.bundle on HTTP/HTTPS')
233 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700234 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800235 p.add_option('-u', '--manifest-server-username', action='store',
236 dest='manifest_server_username',
237 help='username to authenticate with the manifest server')
238 p.add_option('-p', '--manifest-server-password', action='store',
239 dest='manifest_server_password',
240 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800241 p.add_option('--fetch-submodules',
242 dest='fetch_submodules', action='store_true',
243 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800244 p.add_option('--use-superproject', action='store_true',
245 help='use the manifest superproject to sync projects')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700246 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500247 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700248 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900249 p.add_option('--optimized-fetch',
250 dest='optimized_fetch', action='store_true',
251 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600252 p.add_option('--retry-fetches',
253 default=0, action='store', type='int',
254 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900255 p.add_option('--prune', dest='prune', action='store_true',
256 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700257 if show_smart:
258 p.add_option('-s', '--smart-sync',
259 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900260 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200261 p.add_option('-t', '--smart-tag',
262 dest='smart_tag', action='store',
263 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700264
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700265 g = p.add_option_group('repo Version options')
266 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500267 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700268 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700269 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800270 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700271 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700272
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800273 def _GetBranch(self):
274 """Returns the branch name for getting the approved manifest."""
275 p = self.manifest.manifestProject
276 b = p.GetBranch(p.CurrentBranch)
277 branch = b.merge
278 if branch.startswith(R_HEADS):
279 branch = branch[len(R_HEADS):]
280 return branch
281
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800282 def _UpdateProjectsRevisionId(self, opt, args):
283 """Update revisionId of every project with the SHA from superproject.
284
285 This function updates each project's revisionId with SHA from superproject.
286 It writes the updated manifest into a file and reloads the manifest from it.
287
288 Args:
289 opt: Program options returned from optparse. See _Options().
290 args: Arguments to pass to GetProjects. See the GetProjects
291 docstring for details.
292
293 Returns:
294 Returns path to the overriding manifest file.
295 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800296 superproject = git_superproject.Superproject(self.manifest,
297 self.repodir)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800298 all_projects = self.GetProjects(args,
299 missing_ok=True,
300 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800301 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800302 if not manifest_path:
303 print('error: Update of revsionId from superproject has failed',
304 file=sys.stderr)
305 sys.exit(1)
306 self._ReloadManifest(manifest_path)
307 return manifest_path
308
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500309 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700310 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800311
David James8d201162013-10-11 17:03:19 -0700312 Delegates most of the work to _FetchHelper.
313
314 Args:
315 opt: Program options returned from optparse. See _Options().
316 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500317 sem: We'll release() this semaphore when we exit so that another thread
318 can be started up.
David James89ece422014-01-09 18:51:58 -0800319 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700320 _FetchHelper docstring for details.
321 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500322 try:
323 for project in projects:
324 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400325 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500326 break
327 finally:
328 sem.release()
David James8d201162013-10-11 17:03:19 -0700329
Xin Li745be2e2019-06-03 11:24:30 -0700330 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
331 clone_filter):
David James8d201162013-10-11 17:03:19 -0700332 """Fetch git objects for a single project.
333
David Pursehousec1b86a22012-11-14 11:36:51 +0900334 Args:
335 opt: Program options returned from optparse. See _Options().
336 project: Project object for the project to fetch.
337 lock: Lock for accessing objects that are shared amongst multiple
338 _FetchHelper() threads.
339 fetched: set object that we will add project.gitdir to when we're done
340 (with our lock held).
341 pm: Instance of a Project object. We will call pm.update() (with our
342 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900343 err_event: We'll set this event in the case of an error (after printing
344 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700345 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700346
347 Returns:
348 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900349 """
350 # We'll set to true once we've locked the lock.
351 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700352
David Pursehousec1b86a22012-11-14 11:36:51 +0900353 # Encapsulate everything in a try/except/finally so that:
354 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900355 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700356 start = time.time()
357 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900358 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700359 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900360 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900361 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500362 verbose=opt.verbose,
David Pursehouseabdf7502020-02-12 14:58:39 +0900363 current_branch_only=opt.current_branch_only,
364 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500365 clone_bundle=opt.clone_bundle,
366 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900367 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600368 retry_fetches=opt.retry_fetches,
David Pursehouseabdf7502020-02-12 14:58:39 +0900369 prune=opt.prune,
370 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900371 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700372
David Pursehousec1b86a22012-11-14 11:36:51 +0900373 # Lock around all the rest of the code, since printing, updating a set
374 # and Progress.update() are not thread safe.
375 lock.acquire()
376 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700377
David Pursehousec1b86a22012-11-14 11:36:51 +0900378 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800379 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700380 print('error: Cannot fetch %s from %s'
381 % (project.name, project.remote.url),
382 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400383 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900384 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700385
David Pursehousec1b86a22012-11-14 11:36:51 +0900386 fetched.add(project.gitdir)
Mike Frysinger3538dd22019-08-26 15:32:06 -0400387 pm.update(msg=project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900388 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800389 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400390 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900391 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900392 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900393 err_event.set()
394 raise
395 finally:
396 if did_lock:
397 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700398 finish = time.time()
399 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
400 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800401
David James8d201162013-10-11 17:03:19 -0700402 return success
403
Mike Frysinger5a033082019-09-23 19:21:20 -0400404 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700405 fetched = set()
David James89ece422014-01-09 18:51:58 -0800406 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200407 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200408 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800409
David James89ece422014-01-09 18:51:58 -0800410 objdir_project_map = dict()
411 for project in projects:
412 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700413
David James89ece422014-01-09 18:51:58 -0800414 threads = set()
415 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800416 for project_list in objdir_project_map.values():
417 # Check for any errors before running any more tasks.
418 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400419 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800420 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700421
David James89ece422014-01-09 18:51:58 -0800422 sem.acquire()
423 kwargs = dict(opt=opt,
424 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500425 sem=sem,
David James89ece422014-01-09 18:51:58 -0800426 lock=lock,
427 fetched=fetched,
428 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700429 err_event=err_event,
430 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800431 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900432 t = _threading.Thread(target=self._FetchProjectList,
433 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200434 # Ensure that Ctrl-C will not freeze the repo process.
435 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800436 threads.add(t)
437 t.start()
David James89ece422014-01-09 18:51:58 -0800438 else:
439 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800440
David James89ece422014-01-09 18:51:58 -0800441 for t in threads:
442 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800443
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700444 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700445 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700446
Julien Campergue335f5ef2013-10-16 11:02:35 +0200447 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400448 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200449
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700450 return fetched
451
Xin Li745be2e2019-06-03 11:24:30 -0700452 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
453 """Main function of the fetch threads.
454
455 Delegates most of the work to _CheckoutOne.
456
457 Args:
458 opt: Program options returned from optparse. See _Options().
459 projects: Projects to fetch.
460 sem: We'll release() this semaphore when we exit so that another thread
461 can be started up.
462 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
463 _CheckoutOne docstring for details.
464 """
465 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400466 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700467 finally:
468 sem.release()
469
Vadim Bendeburydff91942019-11-06 11:05:00 -0800470 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700471 """Checkout work tree for one project
472
473 Args:
474 opt: Program options returned from optparse. See _Options().
475 project: Project object for the project to checkout.
476 lock: Lock for accessing objects that are shared amongst multiple
477 _CheckoutWorker() threads.
478 pm: Instance of a Project object. We will call pm.update() (with our
479 lock held).
480 err_event: We'll set this event in the case of an error (after printing
481 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800482 err_results: A list of strings, paths to git repos where checkout
483 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700484
485 Returns:
486 Whether the fetch was successful.
487 """
488 # We'll set to true once we've locked the lock.
489 did_lock = False
490
Xin Li745be2e2019-06-03 11:24:30 -0700491 # Encapsulate everything in a try/except/finally so that:
492 # - We always set err_event in the case of an exception.
493 # - We always make sure we unlock the lock if we locked it.
494 start = time.time()
495 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
496 detach_head=opt.detach_head)
497 success = False
498 try:
499 try:
500 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700501
502 # Lock around all the rest of the code, since printing, updating a set
503 # and Progress.update() are not thread safe.
504 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400505 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700506 did_lock = True
507
508 if not success:
509 err_event.set()
510 print('error: Cannot checkout %s' % (project.name),
511 file=sys.stderr)
512 raise _CheckoutError()
513
Mike Frysinger3538dd22019-08-26 15:32:06 -0400514 pm.update(msg=project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700515 except _CheckoutError:
516 pass
517 except Exception as e:
518 print('error: Cannot checkout %s: %s: %s' %
519 (project.name, type(e).__name__, str(e)),
520 file=sys.stderr)
521 err_event.set()
522 raise
523 finally:
524 if did_lock:
Vadim Bendeburydff91942019-11-06 11:05:00 -0800525 if not success:
526 err_results.append(project.relpath)
Xin Li745be2e2019-06-03 11:24:30 -0700527 lock.release()
528 finish = time.time()
529 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
530 start, finish, success)
531
532 return success
533
Mike Frysinger5a033082019-09-23 19:21:20 -0400534 def _Checkout(self, all_projects, opt, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700535 """Checkout projects listed in all_projects
536
537 Args:
538 all_projects: List of all projects that should be checked out.
539 opt: Program options returned from optparse. See _Options().
Mike Frysinger5a033082019-09-23 19:21:20 -0400540 err_event: We'll set this event in the case of an error (after printing
541 out info about the error).
542 err_results: A list of strings, paths to git repos where checkout
543 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700544 """
545
546 # Perform checkouts in multiple threads when we are using partial clone.
547 # Without partial clone, all needed git objects are already downloaded,
548 # in this situation it's better to use only one process because the checkout
549 # would be mostly disk I/O; with partial clone, the objects are only
550 # downloaded when demanded (at checkout time), which is similar to the
551 # Sync_NetworkHalf case and parallelism would be helpful.
552 if self.manifest.CloneFilter:
553 syncjobs = self.jobs
554 else:
555 syncjobs = 1
556
557 lock = _threading.Lock()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400558 pm = Progress('Checking out projects', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700559
560 threads = set()
561 sem = _threading.Semaphore(syncjobs)
Xin Li745be2e2019-06-03 11:24:30 -0700562
563 for project in all_projects:
564 # Check for any errors before running any more tasks.
565 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400566 if err_event.isSet() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700567 break
568
569 sem.acquire()
570 if project.worktree:
571 kwargs = dict(opt=opt,
572 sem=sem,
573 project=project,
574 lock=lock,
575 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800576 err_event=err_event,
577 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700578 if syncjobs > 1:
579 t = _threading.Thread(target=self._CheckoutWorker,
580 kwargs=kwargs)
581 # Ensure that Ctrl-C will not freeze the repo process.
582 t.daemon = True
583 threads.add(t)
584 t.start()
585 else:
586 self._CheckoutWorker(**kwargs)
587
588 for t in threads:
589 t.join()
590
591 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700592
Mike Frysinger5a033082019-09-23 19:21:20 -0400593 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700594 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700595 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500596 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500597 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900598 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100599 if not opt.quiet:
600 print('%s: Shared project %s found, disabling pruning.' %
601 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500602 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500603 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500604 else:
605 # This isn't perfect, but it's the best we can do with old git.
606 print('%s: WARNING: shared projects are unreliable when using old '
607 'versions of git; please upgrade to git-2.7.0+.'
608 % (project.relpath,),
609 file=sys.stderr)
610 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700611 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700612
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500613 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700614 cpu_count = multiprocessing.cpu_count()
615 else:
616 cpu_count = 1
617 jobs = min(self.jobs, cpu_count)
618
619 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700620 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700621 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700622 return
623
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400624 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700625
626 threads = set()
627 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700628
David James8d201162013-10-11 17:03:19 -0700629 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700630 try:
631 try:
David James8d201162013-10-11 17:03:19 -0700632 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700633 except GitError:
634 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900635 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700636 err_event.set()
637 raise
638 finally:
639 sem.release()
640
Gabe Black2ff30292014-10-09 17:54:35 -0700641 for bare_git in gc_gitdirs.values():
Mike Frysinger5a033082019-09-23 19:21:20 -0400642 if err_event.isSet() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700643 break
644 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700645 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700646 t.daemon = True
647 threads.add(t)
648 t.start()
649
650 for t in threads:
651 t.join()
652
Tim Kilbourn07669002013-03-08 15:02:49 -0800653 def _ReloadManifest(self, manifest_name=None):
654 if manifest_name:
655 # Override calls _Unload already
656 self.manifest.Override(manifest_name)
657 else:
658 self.manifest._Unload()
659
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500660 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700661 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700662 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700663 if project.relpath:
664 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700665 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500666 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700667 old_project_paths = []
668
669 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500670 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700671 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800672 # In reversed order, so subfolders are deleted before parent folder.
673 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700674 if not path:
675 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700676 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900677 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700678 gitdir = os.path.join(self.manifest.topdir, path, '.git')
679 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900680 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900681 manifest=self.manifest,
682 name=path,
683 remote=RemoteSpec('origin'),
684 gitdir=gitdir,
685 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500686 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900687 worktree=os.path.join(self.manifest.topdir, path),
688 relpath=path,
689 revisionExpr='HEAD',
690 revisionId=None,
691 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500692 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900693 quiet=opt.quiet,
694 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400695 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700696
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700697 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500698 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700699 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700700 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700701 return 0
702
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400703 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
704 if not self.manifest.manifest_server:
705 print('error: cannot smart sync: no manifest server defined in '
706 'manifest', file=sys.stderr)
707 sys.exit(1)
708
709 manifest_server = self.manifest.manifest_server
710 if not opt.quiet:
711 print('Using manifest server %s' % manifest_server)
712
David Pursehouseeeff3532020-02-12 11:24:10 +0900713 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400714 username = None
715 password = None
716 if opt.manifest_server_username and opt.manifest_server_password:
717 username = opt.manifest_server_username
718 password = opt.manifest_server_password
719 else:
720 try:
721 info = netrc.netrc()
722 except IOError:
723 # .netrc file does not exist or could not be opened
724 pass
725 else:
726 try:
727 parse_result = urllib.parse.urlparse(manifest_server)
728 if parse_result.hostname:
729 auth = info.authenticators(parse_result.hostname)
730 if auth:
731 username, _account, password = auth
732 else:
733 print('No credentials found for %s in .netrc'
734 % parse_result.hostname, file=sys.stderr)
735 except netrc.NetrcParseError as e:
736 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
737
738 if (username and password):
739 manifest_server = manifest_server.replace('://', '://%s:%s@' %
740 (username, password),
741 1)
742
743 transport = PersistentTransport(manifest_server)
744 if manifest_server.startswith('persistent-'):
745 manifest_server = manifest_server[len('persistent-'):]
746
747 try:
748 server = xmlrpc.client.Server(manifest_server, transport=transport)
749 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800750 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400751
Mike Frysinger56ce3462019-12-04 19:30:48 -0500752 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500753 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400754 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500755 elif ('TARGET_PRODUCT' in os.environ and
756 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500757 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
758 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400759 [success, manifest_str] = server.GetApprovedManifest(branch, target)
760 else:
761 [success, manifest_str] = server.GetApprovedManifest(branch)
762 else:
763 assert(opt.smart_tag)
764 [success, manifest_str] = server.GetManifest(opt.smart_tag)
765
766 if success:
767 manifest_name = os.path.basename(smart_sync_manifest_path)
768 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500769 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400770 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400771 except IOError as e:
772 print('error: cannot write manifest to %s:\n%s'
773 % (smart_sync_manifest_path, e),
774 file=sys.stderr)
775 sys.exit(1)
776 self._ReloadManifest(manifest_name)
777 else:
778 print('error: manifest server RPC call failed: %s' %
779 manifest_str, file=sys.stderr)
780 sys.exit(1)
781 except (socket.error, IOError, xmlrpc.client.Fault) as e:
782 print('error: cannot connect to manifest server %s:\n%s'
783 % (self.manifest.manifest_server, e), file=sys.stderr)
784 sys.exit(1)
785 except xmlrpc.client.ProtocolError as e:
786 print('error: cannot connect to manifest server %s:\n%d %s'
787 % (self.manifest.manifest_server, e.errcode, e.errmsg),
788 file=sys.stderr)
789 sys.exit(1)
790
791 return manifest_name
792
Mike Frysingerfb527e32019-08-27 02:34:32 -0400793 def _UpdateManifestProject(self, opt, mp, manifest_name):
794 """Fetch & update the local manifest project."""
795 if not opt.local_only:
796 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500797 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400798 current_branch_only=opt.current_branch_only,
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200799 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500800 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400801 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600802 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400803 submodules=self.manifest.HasSubmodules,
804 clone_filter=self.manifest.CloneFilter)
805 finish = time.time()
806 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
807 start, finish, success)
808
809 if mp.HasChanges:
810 syncbuf = SyncBuffer(mp.config)
811 start = time.time()
812 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
813 clean = syncbuf.Finish()
814 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
815 start, time.time(), clean)
816 if not clean:
817 sys.exit(1)
818 self._ReloadManifest(opt.manifest_name)
819 if opt.jobs is None:
820 self.jobs = self.manifest.default.sync_j
821
Mike Frysingerae6cb082019-08-27 01:10:59 -0400822 def ValidateOptions(self, opt, args):
823 if opt.force_broken:
824 print('warning: -f/--force-broken is now the default behavior, and the '
825 'options are deprecated', file=sys.stderr)
826 if opt.network_only and opt.detach_head:
827 self.OptionParser.error('cannot combine -n and -d')
828 if opt.network_only and opt.local_only:
829 self.OptionParser.error('cannot combine -n and -l')
830 if opt.manifest_name and opt.smart_sync:
831 self.OptionParser.error('cannot combine -m and -s')
832 if opt.manifest_name and opt.smart_tag:
833 self.OptionParser.error('cannot combine -m and -t')
834 if opt.manifest_server_username or opt.manifest_server_password:
835 if not (opt.smart_sync or opt.smart_tag):
836 self.OptionParser.error('-u and -p may only be combined with -s or -t')
837 if None in [opt.manifest_server_username, opt.manifest_server_password]:
838 self.OptionParser.error('both -u and -p must be given')
839
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700840 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800841 if opt.jobs:
842 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700843 if self.jobs > 1:
844 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400845 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700846
Mike Frysinger521d01b2020-02-17 01:51:49 -0500847 opt.quiet = opt.output_mode is False
848 opt.verbose = opt.output_mode is True
849
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500850 if opt.manifest_name:
851 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700852
Chirayu Desaia892b102013-06-11 14:18:46 +0530853 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900854 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900855 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530856
Xin Lid79a4bc2020-05-20 16:03:45 -0700857 if opt.clone_bundle is None:
858 opt.clone_bundle = self.manifest.CloneBundle
859
Victor Boivie08c880d2011-04-19 10:32:52 +0200860 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400861 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
862 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900863 if os.path.isfile(smart_sync_manifest_path):
864 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800865 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900866 except OSError as e:
867 print('error: failed to remove existing smart sync override manifest: %s' %
868 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700869
Mike Frysinger5a033082019-09-23 19:21:20 -0400870 err_event = _threading.Event()
871
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700872 rp = self.manifest.repoProject
873 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500874 cb = rp.CurrentBranch
875 if cb:
876 base = rp.GetBranch(cb).merge
877 if not base or not base.startswith('refs/heads/'):
878 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400879 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500880 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700881
882 mp = self.manifest.manifestProject
883 mp.PreSync()
884
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800885 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700886 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800887
Fredrik de Grootcc960972019-11-22 09:04:31 +0100888 if not opt.mp_update:
889 print('Skipping update of local manifest project.')
890 else:
891 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700892
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800893 if opt.use_superproject:
894 manifest_name = self._UpdateProjectsRevisionId(opt, args)
895
Simran Basib9a1b732015-08-20 12:19:28 -0700896 if self.gitc_manifest:
897 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700898 missing_ok=True)
899 gitc_projects = []
900 opened_projects = []
901 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700902 if project.relpath in self.gitc_manifest.paths and \
903 self.gitc_manifest.paths[project.relpath].old_revision:
904 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700905 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700906 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700907
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700908 if not args:
909 gitc_projects = None
910
911 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700912 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700913 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
914 if manifest_name:
915 manifest.Override(manifest_name)
916 else:
917 manifest.Override(self.manifest.manifestFile)
918 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
919 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700920 gitc_projects)
921 print('GITC client successfully synced.')
922
923 # The opened projects need to be synced as normal, therefore we
924 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700925 # TODO: make this more reliable -- if there's a project name/path overlap,
926 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900927 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
928 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700929 if not args:
930 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800931 all_projects = self.GetProjects(args,
932 missing_ok=True,
933 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700934
Mike Frysinger5a033082019-09-23 19:21:20 -0400935 err_network_sync = False
936 err_update_projects = False
937 err_checkout = False
938
Dave Borowitz67700e92012-10-23 15:00:54 -0700939 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700940 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700941 to_fetch = []
942 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700943 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700944 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900945 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700946 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700947
Mike Frysinger5a033082019-09-23 19:21:20 -0400948 fetched = self._Fetch(to_fetch, opt, err_event)
949
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500950 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700951 if opt.network_only:
952 # bail out now; the rest touches the working tree
Mike Frysinger5a033082019-09-23 19:21:20 -0400953 if err_event.isSet():
954 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
955 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700956 return
957
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800958 # Iteratively fetch missing and/or nested unregistered submodules
959 previously_missing_set = set()
960 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100961 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800962 all_projects = self.GetProjects(args,
963 missing_ok=True,
964 submodules_ok=opt.fetch_submodules)
965 missing = []
966 for project in all_projects:
967 if project.gitdir not in fetched:
968 missing.append(project)
969 if not missing:
970 break
971 # Stop us from non-stopped fetching actually-missing repos: If set of
972 # missing repos has not been changed from last fetch, we break.
973 missing_set = set(p.name for p in missing)
974 if previously_missing_set == missing_set:
975 break
976 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400977 fetched.update(self._Fetch(missing, opt, err_event))
978
979 # If we saw an error, exit with code 1 so that other scripts can check.
980 if err_event.isSet():
981 err_network_sync = True
982 if opt.fail_fast:
983 print('\nerror: Exited sync due to fetch errors.\n'
984 'Local checkouts *not* updated. Resolve network issues & '
985 'retry.\n'
986 '`repo sync -l` will update some local checkouts.',
987 file=sys.stderr)
988 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800989
Julien Campergue335f5ef2013-10-16 11:02:35 +0200990 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700991 # bail out now, we have no working tree
992 return
993
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500994 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400995 err_event.set()
996 err_update_projects = True
997 if opt.fail_fast:
998 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
999 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001000
Mike Frysinger5a033082019-09-23 19:21:20 -04001001 err_results = []
1002 self._Checkout(all_projects, opt, err_event, err_results)
1003 if err_event.isSet():
1004 err_checkout = True
1005 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001006
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001007 # If there's a notice that's supposed to print at the end of the sync, print
1008 # it now...
1009 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001010 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001011
Mike Frysinger5a033082019-09-23 19:21:20 -04001012 # If we saw an error, exit with code 1 so that other scripts can check.
1013 if err_event.isSet():
1014 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1015 if err_network_sync:
1016 print('error: Downloading network changes failed.', file=sys.stderr)
1017 if err_update_projects:
1018 print('error: Updating local project lists failed.', file=sys.stderr)
1019 if err_checkout:
1020 print('error: Checking out local projects failed.', file=sys.stderr)
1021 if err_results:
1022 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1023 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1024 file=sys.stderr)
1025 sys.exit(1)
1026
Mike Frysingere19d9e12020-02-12 11:23:32 -05001027 if not opt.quiet:
1028 print('repo sync has finished successfully.')
1029
David Pursehouse819827a2020-02-12 15:20:19 +09001030
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001031def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001032 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001033 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001034 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001035 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001036 if project.Exists:
1037 project.PostRepoUpgrade()
1038
David Pursehouse819827a2020-02-12 15:20:19 +09001039
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001040def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001041 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001042 print('info: A new version of repo is available', file=sys.stderr)
1043 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001044 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001045 syncbuf = SyncBuffer(rp.config)
1046 rp.Sync_LocalHalf(syncbuf)
1047 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001048 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001049 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001050 raise RepoChangedException(['--repo-upgraded'])
1051 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001052 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001053 else:
1054 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001055 print('repo version %s is current' % rp.work_git.describe(HEAD),
1056 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001057
David Pursehouse819827a2020-02-12 15:20:19 +09001058
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001059def _VerifyTag(project):
1060 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1061 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001062 print('warning: GnuPG was not available during last "repo init"\n'
1063 'warning: Cannot automatically authenticate repo."""',
1064 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001065 return True
1066
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001067 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001068 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001069 except GitError:
1070 cur = None
1071
1072 if not cur \
1073 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001074 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075 if rev.startswith(R_HEADS):
1076 rev = rev[len(R_HEADS):]
1077
Sarah Owenscecd1d82012-11-01 22:59:27 -07001078 print(file=sys.stderr)
1079 print("warning: project '%s' branch '%s' is not signed"
1080 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001081 return False
1082
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001083 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001084 env['GIT_DIR'] = project.gitdir
1085 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001086
1087 cmd = [GIT, 'tag', '-v', cur]
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001088 result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
1089 env=env, check=False)
1090 if result.returncode:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001091 print(file=sys.stderr)
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001092 print(result.stdout, file=sys.stderr)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001093 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001094 return False
1095 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001096
David Rileye0684ad2017-04-05 00:02:59 -07001097
Dave Borowitz67700e92012-10-23 15:00:54 -07001098class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001099 _ALPHA = 0.5
1100
Dave Borowitz67700e92012-10-23 15:00:54 -07001101 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001102 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001103 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001104 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001105
1106 def Get(self, project):
1107 self._Load()
1108 return self._times.get(project.name, _ONE_DAY_S)
1109
1110 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001111 self._Load()
1112 name = project.name
1113 old = self._times.get(name, t)
1114 self._seen.add(name)
1115 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001116 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001117
1118 def _Load(self):
1119 if self._times is None:
1120 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001121 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001122 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001123 except (IOError, ValueError):
1124 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001125 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001126 except OSError:
1127 pass
1128 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001129
1130 def Save(self):
1131 if self._times is None:
1132 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001133
1134 to_delete = []
1135 for name in self._times:
1136 if name not in self._seen:
1137 to_delete.append(name)
1138 for name in to_delete:
1139 del self._times[name]
1140
Dave Borowitz67700e92012-10-23 15:00:54 -07001141 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001142 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001143 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001144 except (IOError, TypeError):
1145 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001146 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001147 except OSError:
1148 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001149
1150# This is a replacement for xmlrpc.client.Transport using urllib2
1151# and supporting persistent-http[s]. It cannot change hosts from
1152# request to request like the normal transport, the real url
1153# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001154
1155
Dan Willemsen0745bb22015-08-17 13:41:45 -07001156class PersistentTransport(xmlrpc.client.Transport):
1157 def __init__(self, orig_host):
1158 self.orig_host = orig_host
1159
1160 def request(self, host, handler, request_body, verbose=False):
1161 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1162 # Python doesn't understand cookies with the #HttpOnly_ prefix
1163 # Since we're only using them for HTTP, copy the file temporarily,
1164 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001165 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001166 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001167 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001168 try:
1169 with open(cookiefile) as f:
1170 for line in f:
1171 if line.startswith("#HttpOnly_"):
1172 line = line[len("#HttpOnly_"):]
1173 tmpcookiefile.write(line)
1174 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001175
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001176 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001177 try:
1178 cookiejar.load()
1179 except cookielib.LoadError:
1180 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001181 finally:
1182 tmpcookiefile.close()
1183 else:
1184 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001185
1186 proxyhandler = urllib.request.ProxyHandler
1187 if proxy:
1188 proxyhandler = urllib.request.ProxyHandler({
1189 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001190 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001191
1192 opener = urllib.request.build_opener(
1193 urllib.request.HTTPCookieProcessor(cookiejar),
1194 proxyhandler)
1195
1196 url = urllib.parse.urljoin(self.orig_host, handler)
1197 parse_results = urllib.parse.urlparse(url)
1198
1199 scheme = parse_results.scheme
1200 if scheme == 'persistent-http':
1201 scheme = 'http'
1202 if scheme == 'persistent-https':
1203 # If we're proxying through persistent-https, use http. The
1204 # proxy itself will do the https.
1205 if proxy:
1206 scheme = 'http'
1207 else:
1208 scheme = 'https'
1209
1210 # Parse out any authentication information using the base class
1211 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1212
1213 url = urllib.parse.urlunparse((
1214 scheme,
1215 host,
1216 parse_results.path,
1217 parse_results.params,
1218 parse_results.query,
1219 parse_results.fragment))
1220
1221 request = urllib.request.Request(url, request_body)
1222 if extra_headers is not None:
1223 for (name, header) in extra_headers:
1224 request.add_header(name, header)
1225 request.add_header('Content-Type', 'text/xml')
1226 try:
1227 response = opener.open(request)
1228 except urllib.error.HTTPError as e:
1229 if e.code == 501:
1230 # We may have been redirected through a login process
1231 # but our POST turned into a GET. Retry.
1232 response = opener.open(request)
1233 else:
1234 raise
1235
1236 p, u = xmlrpc.client.getparser()
1237 while 1:
1238 data = response.read(1024)
1239 if not data:
1240 break
1241 p.feed(data)
1242 p.close()
1243 return u.close()
1244
1245 def close(self):
1246 pass