blob: 63e5bc7d3987ed1aa873c0417afe9a97e35c7b46 [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"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500180 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700181
Nico Sallembien6623b212010-05-11 12:57:01 -0700182 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000183 try:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500184 self.PARALLEL_JOBS = self.manifest.default.sync_j
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000185 except ManifestParseError:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500186 pass
187 super()._Options(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700188
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500189 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200190 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400191 help='obsolete option (to be deleted in the future)')
192 p.add_option('--fail-fast',
193 dest='fail_fast', action='store_true',
194 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700195 p.add_option('--force-sync',
196 dest='force_sync', action='store_true',
197 help="overwrite an existing git directory if it needs to "
198 "point to a different object directory. WARNING: this "
199 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500200 p.add_option('--force-remove-dirty',
201 dest='force_remove_dirty', action='store_true',
202 help="force remove projects with uncommitted modifications if "
203 "projects no longer exist in the manifest. "
204 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900205 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700206 dest='local_only', action='store_true',
207 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900208 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100209 dest='mp_update', action='store_false', default='true',
210 help='use the existing manifest checkout as-is. '
211 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900212 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700213 dest='network_only', action='store_true',
214 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900215 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700216 dest='detach_head', action='store_true',
217 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900218 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700219 dest='current_branch_only', action='store_true',
220 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500221 p.add_option('-v', '--verbose',
222 dest='output_mode', action='store_true',
223 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900224 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500225 dest='output_mode', action='store_false',
226 help='only show errors')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500227 p.add_option('-m', '--manifest-name',
228 dest='manifest_name',
229 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700230 p.add_option('--clone-bundle', action='store_true',
231 help='enable use of /clone.bundle on HTTP/HTTPS')
232 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700233 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800234 p.add_option('-u', '--manifest-server-username', action='store',
235 dest='manifest_server_username',
236 help='username to authenticate with the manifest server')
237 p.add_option('-p', '--manifest-server-password', action='store',
238 dest='manifest_server_password',
239 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800240 p.add_option('--fetch-submodules',
241 dest='fetch_submodules', action='store_true',
242 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800243 p.add_option('--use-superproject', action='store_true',
244 help='use the manifest superproject to sync projects')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700245 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500246 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700247 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900248 p.add_option('--optimized-fetch',
249 dest='optimized_fetch', action='store_true',
250 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600251 p.add_option('--retry-fetches',
252 default=0, action='store', type='int',
253 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900254 p.add_option('--prune', dest='prune', action='store_true',
255 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700256 if show_smart:
257 p.add_option('-s', '--smart-sync',
258 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900259 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200260 p.add_option('-t', '--smart-tag',
261 dest='smart_tag', action='store',
262 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700263
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700264 g = p.add_option_group('repo Version options')
265 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500266 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700268 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800269 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700270 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700271
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800272 def _GetBranch(self):
273 """Returns the branch name for getting the approved manifest."""
274 p = self.manifest.manifestProject
275 b = p.GetBranch(p.CurrentBranch)
276 branch = b.merge
277 if branch.startswith(R_HEADS):
278 branch = branch[len(R_HEADS):]
279 return branch
280
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800281 def _UpdateProjectsRevisionId(self, opt, args):
282 """Update revisionId of every project with the SHA from superproject.
283
284 This function updates each project's revisionId with SHA from superproject.
285 It writes the updated manifest into a file and reloads the manifest from it.
286
287 Args:
288 opt: Program options returned from optparse. See _Options().
289 args: Arguments to pass to GetProjects. See the GetProjects
290 docstring for details.
291
292 Returns:
293 Returns path to the overriding manifest file.
294 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800295 superproject = git_superproject.Superproject(self.manifest,
296 self.repodir)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800297 all_projects = self.GetProjects(args,
298 missing_ok=True,
299 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800300 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800301 if not manifest_path:
302 print('error: Update of revsionId from superproject has failed',
303 file=sys.stderr)
304 sys.exit(1)
305 self._ReloadManifest(manifest_path)
306 return manifest_path
307
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500308 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700309 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800310
David James8d201162013-10-11 17:03:19 -0700311 Delegates most of the work to _FetchHelper.
312
313 Args:
314 opt: Program options returned from optparse. See _Options().
315 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500316 sem: We'll release() this semaphore when we exit so that another thread
317 can be started up.
David James89ece422014-01-09 18:51:58 -0800318 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700319 _FetchHelper docstring for details.
320 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500321 try:
322 for project in projects:
323 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400324 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500325 break
326 finally:
327 sem.release()
David James8d201162013-10-11 17:03:19 -0700328
Xin Li745be2e2019-06-03 11:24:30 -0700329 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
330 clone_filter):
David James8d201162013-10-11 17:03:19 -0700331 """Fetch git objects for a single project.
332
David Pursehousec1b86a22012-11-14 11:36:51 +0900333 Args:
334 opt: Program options returned from optparse. See _Options().
335 project: Project object for the project to fetch.
336 lock: Lock for accessing objects that are shared amongst multiple
337 _FetchHelper() threads.
338 fetched: set object that we will add project.gitdir to when we're done
339 (with our lock held).
340 pm: Instance of a Project object. We will call pm.update() (with our
341 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900342 err_event: We'll set this event in the case of an error (after printing
343 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700344 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700345
346 Returns:
347 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900348 """
349 # We'll set to true once we've locked the lock.
350 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700351
David Pursehousec1b86a22012-11-14 11:36:51 +0900352 # Encapsulate everything in a try/except/finally so that:
353 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900354 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700355 start = time.time()
356 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900357 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700358 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900359 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900360 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500361 verbose=opt.verbose,
David Pursehouseabdf7502020-02-12 14:58:39 +0900362 current_branch_only=opt.current_branch_only,
363 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500364 clone_bundle=opt.clone_bundle,
365 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900366 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600367 retry_fetches=opt.retry_fetches,
David Pursehouseabdf7502020-02-12 14:58:39 +0900368 prune=opt.prune,
369 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900370 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700371
David Pursehousec1b86a22012-11-14 11:36:51 +0900372 # Lock around all the rest of the code, since printing, updating a set
373 # and Progress.update() are not thread safe.
374 lock.acquire()
375 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700376
David Pursehousec1b86a22012-11-14 11:36:51 +0900377 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800378 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700379 print('error: Cannot fetch %s from %s'
380 % (project.name, project.remote.url),
381 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400382 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900383 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700384
David Pursehousec1b86a22012-11-14 11:36:51 +0900385 fetched.add(project.gitdir)
Mike Frysinger3538dd22019-08-26 15:32:06 -0400386 pm.update(msg=project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900387 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800388 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400389 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900390 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900391 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900392 err_event.set()
393 raise
394 finally:
395 if did_lock:
396 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700397 finish = time.time()
398 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
399 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800400
David James8d201162013-10-11 17:03:19 -0700401 return success
402
Mike Frysinger5a033082019-09-23 19:21:20 -0400403 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700404 fetched = set()
David James89ece422014-01-09 18:51:58 -0800405 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200406 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200407 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800408
David James89ece422014-01-09 18:51:58 -0800409 objdir_project_map = dict()
410 for project in projects:
411 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700412
David James89ece422014-01-09 18:51:58 -0800413 threads = set()
414 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800415 for project_list in objdir_project_map.values():
416 # Check for any errors before running any more tasks.
417 # ...we'll let existing threads finish, though.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500418 if err_event.is_set() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800419 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700420
David James89ece422014-01-09 18:51:58 -0800421 sem.acquire()
422 kwargs = dict(opt=opt,
423 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500424 sem=sem,
David James89ece422014-01-09 18:51:58 -0800425 lock=lock,
426 fetched=fetched,
427 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700428 err_event=err_event,
429 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800430 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900431 t = _threading.Thread(target=self._FetchProjectList,
432 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200433 # Ensure that Ctrl-C will not freeze the repo process.
434 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800435 threads.add(t)
436 t.start()
David James89ece422014-01-09 18:51:58 -0800437 else:
438 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800439
David James89ece422014-01-09 18:51:58 -0800440 for t in threads:
441 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800442
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700443 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700444 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700445
Julien Campergue335f5ef2013-10-16 11:02:35 +0200446 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400447 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200448
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700449 return fetched
450
Xin Li745be2e2019-06-03 11:24:30 -0700451 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
452 """Main function of the fetch threads.
453
454 Delegates most of the work to _CheckoutOne.
455
456 Args:
457 opt: Program options returned from optparse. See _Options().
458 projects: Projects to fetch.
459 sem: We'll release() this semaphore when we exit so that another thread
460 can be started up.
461 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
462 _CheckoutOne docstring for details.
463 """
464 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400465 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700466 finally:
467 sem.release()
468
Vadim Bendeburydff91942019-11-06 11:05:00 -0800469 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700470 """Checkout work tree for one project
471
472 Args:
473 opt: Program options returned from optparse. See _Options().
474 project: Project object for the project to checkout.
475 lock: Lock for accessing objects that are shared amongst multiple
476 _CheckoutWorker() threads.
477 pm: Instance of a Project object. We will call pm.update() (with our
478 lock held).
479 err_event: We'll set this event in the case of an error (after printing
480 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800481 err_results: A list of strings, paths to git repos where checkout
482 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700483
484 Returns:
485 Whether the fetch was successful.
486 """
487 # We'll set to true once we've locked the lock.
488 did_lock = False
489
Xin Li745be2e2019-06-03 11:24:30 -0700490 # Encapsulate everything in a try/except/finally so that:
491 # - We always set err_event in the case of an exception.
492 # - We always make sure we unlock the lock if we locked it.
493 start = time.time()
494 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
495 detach_head=opt.detach_head)
496 success = False
497 try:
498 try:
499 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700500
501 # Lock around all the rest of the code, since printing, updating a set
502 # and Progress.update() are not thread safe.
503 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400504 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700505 did_lock = True
506
507 if not success:
508 err_event.set()
509 print('error: Cannot checkout %s' % (project.name),
510 file=sys.stderr)
511 raise _CheckoutError()
512
Mike Frysinger3538dd22019-08-26 15:32:06 -0400513 pm.update(msg=project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700514 except _CheckoutError:
515 pass
516 except Exception as e:
517 print('error: Cannot checkout %s: %s: %s' %
518 (project.name, type(e).__name__, str(e)),
519 file=sys.stderr)
520 err_event.set()
521 raise
522 finally:
523 if did_lock:
Vadim Bendeburydff91942019-11-06 11:05:00 -0800524 if not success:
525 err_results.append(project.relpath)
Xin Li745be2e2019-06-03 11:24:30 -0700526 lock.release()
527 finish = time.time()
528 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
529 start, finish, success)
530
531 return success
532
Mike Frysinger5a033082019-09-23 19:21:20 -0400533 def _Checkout(self, all_projects, opt, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700534 """Checkout projects listed in all_projects
535
536 Args:
537 all_projects: List of all projects that should be checked out.
538 opt: Program options returned from optparse. See _Options().
Mike Frysinger5a033082019-09-23 19:21:20 -0400539 err_event: We'll set this event in the case of an error (after printing
540 out info about the error).
541 err_results: A list of strings, paths to git repos where checkout
542 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700543 """
544
545 # Perform checkouts in multiple threads when we are using partial clone.
546 # Without partial clone, all needed git objects are already downloaded,
547 # in this situation it's better to use only one process because the checkout
548 # would be mostly disk I/O; with partial clone, the objects are only
549 # downloaded when demanded (at checkout time), which is similar to the
550 # Sync_NetworkHalf case and parallelism would be helpful.
551 if self.manifest.CloneFilter:
552 syncjobs = self.jobs
553 else:
554 syncjobs = 1
555
556 lock = _threading.Lock()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400557 pm = Progress('Checking out projects', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700558
559 threads = set()
560 sem = _threading.Semaphore(syncjobs)
Xin Li745be2e2019-06-03 11:24:30 -0700561
562 for project in all_projects:
563 # Check for any errors before running any more tasks.
564 # ...we'll let existing threads finish, though.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500565 if err_event.is_set() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700566 break
567
568 sem.acquire()
569 if project.worktree:
570 kwargs = dict(opt=opt,
571 sem=sem,
572 project=project,
573 lock=lock,
574 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800575 err_event=err_event,
576 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700577 if syncjobs > 1:
578 t = _threading.Thread(target=self._CheckoutWorker,
579 kwargs=kwargs)
580 # Ensure that Ctrl-C will not freeze the repo process.
581 t.daemon = True
582 threads.add(t)
583 t.start()
584 else:
585 self._CheckoutWorker(**kwargs)
586
587 for t in threads:
588 t.join()
589
590 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700591
Mike Frysinger5a033082019-09-23 19:21:20 -0400592 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700593 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700594 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500595 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500596 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900597 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100598 if not opt.quiet:
599 print('%s: Shared project %s found, disabling pruning.' %
600 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500601 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500602 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500603 else:
604 # This isn't perfect, but it's the best we can do with old git.
605 print('%s: WARNING: shared projects are unreliable when using old '
606 'versions of git; please upgrade to git-2.7.0+.'
607 % (project.relpath,),
608 file=sys.stderr)
609 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700610 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700611
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500612 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700613 cpu_count = multiprocessing.cpu_count()
614 else:
615 cpu_count = 1
616 jobs = min(self.jobs, cpu_count)
617
618 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700619 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700620 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700621 return
622
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400623 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700624
625 threads = set()
626 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700627
David James8d201162013-10-11 17:03:19 -0700628 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700629 try:
630 try:
David James8d201162013-10-11 17:03:19 -0700631 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700632 except GitError:
633 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900634 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700635 err_event.set()
636 raise
637 finally:
638 sem.release()
639
Gabe Black2ff30292014-10-09 17:54:35 -0700640 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500641 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700642 break
643 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700644 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700645 t.daemon = True
646 threads.add(t)
647 t.start()
648
649 for t in threads:
650 t.join()
651
Tim Kilbourn07669002013-03-08 15:02:49 -0800652 def _ReloadManifest(self, manifest_name=None):
653 if manifest_name:
654 # Override calls _Unload already
655 self.manifest.Override(manifest_name)
656 else:
657 self.manifest._Unload()
658
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500659 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700660 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700661 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700662 if project.relpath:
663 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700664 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500665 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700666 old_project_paths = []
667
668 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500669 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700670 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800671 # In reversed order, so subfolders are deleted before parent folder.
672 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700673 if not path:
674 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700675 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900676 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700677 gitdir = os.path.join(self.manifest.topdir, path, '.git')
678 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900679 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900680 manifest=self.manifest,
681 name=path,
682 remote=RemoteSpec('origin'),
683 gitdir=gitdir,
684 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500685 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900686 worktree=os.path.join(self.manifest.topdir, path),
687 relpath=path,
688 revisionExpr='HEAD',
689 revisionId=None,
690 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500691 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900692 quiet=opt.quiet,
693 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400694 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700695
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700696 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500697 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700698 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700699 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700700 return 0
701
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400702 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
703 if not self.manifest.manifest_server:
704 print('error: cannot smart sync: no manifest server defined in '
705 'manifest', file=sys.stderr)
706 sys.exit(1)
707
708 manifest_server = self.manifest.manifest_server
709 if not opt.quiet:
710 print('Using manifest server %s' % manifest_server)
711
David Pursehouseeeff3532020-02-12 11:24:10 +0900712 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400713 username = None
714 password = None
715 if opt.manifest_server_username and opt.manifest_server_password:
716 username = opt.manifest_server_username
717 password = opt.manifest_server_password
718 else:
719 try:
720 info = netrc.netrc()
721 except IOError:
722 # .netrc file does not exist or could not be opened
723 pass
724 else:
725 try:
726 parse_result = urllib.parse.urlparse(manifest_server)
727 if parse_result.hostname:
728 auth = info.authenticators(parse_result.hostname)
729 if auth:
730 username, _account, password = auth
731 else:
732 print('No credentials found for %s in .netrc'
733 % parse_result.hostname, file=sys.stderr)
734 except netrc.NetrcParseError as e:
735 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
736
737 if (username and password):
738 manifest_server = manifest_server.replace('://', '://%s:%s@' %
739 (username, password),
740 1)
741
742 transport = PersistentTransport(manifest_server)
743 if manifest_server.startswith('persistent-'):
744 manifest_server = manifest_server[len('persistent-'):]
745
746 try:
747 server = xmlrpc.client.Server(manifest_server, transport=transport)
748 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800749 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400750
Mike Frysinger56ce3462019-12-04 19:30:48 -0500751 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500752 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400753 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500754 elif ('TARGET_PRODUCT' in os.environ and
755 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500756 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
757 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400758 [success, manifest_str] = server.GetApprovedManifest(branch, target)
759 else:
760 [success, manifest_str] = server.GetApprovedManifest(branch)
761 else:
762 assert(opt.smart_tag)
763 [success, manifest_str] = server.GetManifest(opt.smart_tag)
764
765 if success:
766 manifest_name = os.path.basename(smart_sync_manifest_path)
767 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500768 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400769 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400770 except IOError as e:
771 print('error: cannot write manifest to %s:\n%s'
772 % (smart_sync_manifest_path, e),
773 file=sys.stderr)
774 sys.exit(1)
775 self._ReloadManifest(manifest_name)
776 else:
777 print('error: manifest server RPC call failed: %s' %
778 manifest_str, file=sys.stderr)
779 sys.exit(1)
780 except (socket.error, IOError, xmlrpc.client.Fault) as e:
781 print('error: cannot connect to manifest server %s:\n%s'
782 % (self.manifest.manifest_server, e), file=sys.stderr)
783 sys.exit(1)
784 except xmlrpc.client.ProtocolError as e:
785 print('error: cannot connect to manifest server %s:\n%d %s'
786 % (self.manifest.manifest_server, e.errcode, e.errmsg),
787 file=sys.stderr)
788 sys.exit(1)
789
790 return manifest_name
791
Mike Frysingerfb527e32019-08-27 02:34:32 -0400792 def _UpdateManifestProject(self, opt, mp, manifest_name):
793 """Fetch & update the local manifest project."""
794 if not opt.local_only:
795 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500796 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400797 current_branch_only=opt.current_branch_only,
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200798 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500799 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400800 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600801 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400802 submodules=self.manifest.HasSubmodules,
803 clone_filter=self.manifest.CloneFilter)
804 finish = time.time()
805 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
806 start, finish, success)
807
808 if mp.HasChanges:
809 syncbuf = SyncBuffer(mp.config)
810 start = time.time()
811 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
812 clean = syncbuf.Finish()
813 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
814 start, time.time(), clean)
815 if not clean:
816 sys.exit(1)
817 self._ReloadManifest(opt.manifest_name)
818 if opt.jobs is None:
819 self.jobs = self.manifest.default.sync_j
820
Mike Frysingerae6cb082019-08-27 01:10:59 -0400821 def ValidateOptions(self, opt, args):
822 if opt.force_broken:
823 print('warning: -f/--force-broken is now the default behavior, and the '
824 'options are deprecated', file=sys.stderr)
825 if opt.network_only and opt.detach_head:
826 self.OptionParser.error('cannot combine -n and -d')
827 if opt.network_only and opt.local_only:
828 self.OptionParser.error('cannot combine -n and -l')
829 if opt.manifest_name and opt.smart_sync:
830 self.OptionParser.error('cannot combine -m and -s')
831 if opt.manifest_name and opt.smart_tag:
832 self.OptionParser.error('cannot combine -m and -t')
833 if opt.manifest_server_username or opt.manifest_server_password:
834 if not (opt.smart_sync or opt.smart_tag):
835 self.OptionParser.error('-u and -p may only be combined with -s or -t')
836 if None in [opt.manifest_server_username, opt.manifest_server_password]:
837 self.OptionParser.error('both -u and -p must be given')
838
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700839 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800840 if opt.jobs:
841 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700842 if self.jobs > 1:
843 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400844 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700845
Mike Frysinger521d01b2020-02-17 01:51:49 -0500846 opt.quiet = opt.output_mode is False
847 opt.verbose = opt.output_mode is True
848
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500849 if opt.manifest_name:
850 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700851
Chirayu Desaia892b102013-06-11 14:18:46 +0530852 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900853 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900854 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530855
Xin Lid79a4bc2020-05-20 16:03:45 -0700856 if opt.clone_bundle is None:
857 opt.clone_bundle = self.manifest.CloneBundle
858
Victor Boivie08c880d2011-04-19 10:32:52 +0200859 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400860 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
861 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900862 if os.path.isfile(smart_sync_manifest_path):
863 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800864 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900865 except OSError as e:
866 print('error: failed to remove existing smart sync override manifest: %s' %
867 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700868
Mike Frysinger5a033082019-09-23 19:21:20 -0400869 err_event = _threading.Event()
870
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700871 rp = self.manifest.repoProject
872 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500873 cb = rp.CurrentBranch
874 if cb:
875 base = rp.GetBranch(cb).merge
876 if not base or not base.startswith('refs/heads/'):
877 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400878 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500879 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700880
881 mp = self.manifest.manifestProject
882 mp.PreSync()
883
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800884 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700885 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800886
Fredrik de Grootcc960972019-11-22 09:04:31 +0100887 if not opt.mp_update:
888 print('Skipping update of local manifest project.')
889 else:
890 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700891
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800892 if opt.use_superproject:
893 manifest_name = self._UpdateProjectsRevisionId(opt, args)
894
Simran Basib9a1b732015-08-20 12:19:28 -0700895 if self.gitc_manifest:
896 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700897 missing_ok=True)
898 gitc_projects = []
899 opened_projects = []
900 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700901 if project.relpath in self.gitc_manifest.paths and \
902 self.gitc_manifest.paths[project.relpath].old_revision:
903 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700904 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700905 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700906
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700907 if not args:
908 gitc_projects = None
909
910 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700911 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700912 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
913 if manifest_name:
914 manifest.Override(manifest_name)
915 else:
916 manifest.Override(self.manifest.manifestFile)
917 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
918 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700919 gitc_projects)
920 print('GITC client successfully synced.')
921
922 # The opened projects need to be synced as normal, therefore we
923 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700924 # TODO: make this more reliable -- if there's a project name/path overlap,
925 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900926 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
927 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700928 if not args:
929 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800930 all_projects = self.GetProjects(args,
931 missing_ok=True,
932 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700933
Mike Frysinger5a033082019-09-23 19:21:20 -0400934 err_network_sync = False
935 err_update_projects = False
936 err_checkout = False
937
Dave Borowitz67700e92012-10-23 15:00:54 -0700938 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700939 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700940 to_fetch = []
941 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700942 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700943 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900944 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700945 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700946
Mike Frysinger5a033082019-09-23 19:21:20 -0400947 fetched = self._Fetch(to_fetch, opt, err_event)
948
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500949 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700950 if opt.network_only:
951 # bail out now; the rest touches the working tree
Mike Frysingerbe24a542021-02-23 03:24:12 -0500952 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400953 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
954 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700955 return
956
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800957 # Iteratively fetch missing and/or nested unregistered submodules
958 previously_missing_set = set()
959 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100960 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800961 all_projects = self.GetProjects(args,
962 missing_ok=True,
963 submodules_ok=opt.fetch_submodules)
964 missing = []
965 for project in all_projects:
966 if project.gitdir not in fetched:
967 missing.append(project)
968 if not missing:
969 break
970 # Stop us from non-stopped fetching actually-missing repos: If set of
971 # missing repos has not been changed from last fetch, we break.
972 missing_set = set(p.name for p in missing)
973 if previously_missing_set == missing_set:
974 break
975 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400976 fetched.update(self._Fetch(missing, opt, err_event))
977
978 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500979 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400980 err_network_sync = True
981 if opt.fail_fast:
982 print('\nerror: Exited sync due to fetch errors.\n'
983 'Local checkouts *not* updated. Resolve network issues & '
984 'retry.\n'
985 '`repo sync -l` will update some local checkouts.',
986 file=sys.stderr)
987 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800988
Julien Campergue335f5ef2013-10-16 11:02:35 +0200989 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700990 # bail out now, we have no working tree
991 return
992
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500993 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400994 err_event.set()
995 err_update_projects = True
996 if opt.fail_fast:
997 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
998 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700999
Mike Frysinger5a033082019-09-23 19:21:20 -04001000 err_results = []
1001 self._Checkout(all_projects, opt, err_event, err_results)
Mike Frysingerbe24a542021-02-23 03:24:12 -05001002 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001003 err_checkout = True
1004 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001005
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001006 # If there's a notice that's supposed to print at the end of the sync, print
1007 # it now...
1008 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001009 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001010
Mike Frysinger5a033082019-09-23 19:21:20 -04001011 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001012 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001013 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1014 if err_network_sync:
1015 print('error: Downloading network changes failed.', file=sys.stderr)
1016 if err_update_projects:
1017 print('error: Updating local project lists failed.', file=sys.stderr)
1018 if err_checkout:
1019 print('error: Checking out local projects failed.', file=sys.stderr)
1020 if err_results:
1021 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1022 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1023 file=sys.stderr)
1024 sys.exit(1)
1025
Mike Frysingere19d9e12020-02-12 11:23:32 -05001026 if not opt.quiet:
1027 print('repo sync has finished successfully.')
1028
David Pursehouse819827a2020-02-12 15:20:19 +09001029
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001030def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001031 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001032 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001033 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001034 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001035 if project.Exists:
1036 project.PostRepoUpgrade()
1037
David Pursehouse819827a2020-02-12 15:20:19 +09001038
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001039def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001040 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001041 print('info: A new version of repo is available', file=sys.stderr)
1042 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001043 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001044 syncbuf = SyncBuffer(rp.config)
1045 rp.Sync_LocalHalf(syncbuf)
1046 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001047 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001048 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001049 raise RepoChangedException(['--repo-upgraded'])
1050 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001051 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001052 else:
1053 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001054 print('repo version %s is current' % rp.work_git.describe(HEAD),
1055 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001056
David Pursehouse819827a2020-02-12 15:20:19 +09001057
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001058def _VerifyTag(project):
1059 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1060 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001061 print('warning: GnuPG was not available during last "repo init"\n'
1062 'warning: Cannot automatically authenticate repo."""',
1063 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001064 return True
1065
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001066 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001067 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001068 except GitError:
1069 cur = None
1070
1071 if not cur \
1072 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001073 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001074 if rev.startswith(R_HEADS):
1075 rev = rev[len(R_HEADS):]
1076
Sarah Owenscecd1d82012-11-01 22:59:27 -07001077 print(file=sys.stderr)
1078 print("warning: project '%s' branch '%s' is not signed"
1079 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001080 return False
1081
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001082 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001083 env['GIT_DIR'] = project.gitdir
1084 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001085
1086 cmd = [GIT, 'tag', '-v', cur]
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001087 result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
1088 env=env, check=False)
1089 if result.returncode:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001090 print(file=sys.stderr)
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001091 print(result.stdout, file=sys.stderr)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001092 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001093 return False
1094 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001095
David Rileye0684ad2017-04-05 00:02:59 -07001096
Dave Borowitz67700e92012-10-23 15:00:54 -07001097class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001098 _ALPHA = 0.5
1099
Dave Borowitz67700e92012-10-23 15:00:54 -07001100 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001101 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001102 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001103 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001104
1105 def Get(self, project):
1106 self._Load()
1107 return self._times.get(project.name, _ONE_DAY_S)
1108
1109 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001110 self._Load()
1111 name = project.name
1112 old = self._times.get(name, t)
1113 self._seen.add(name)
1114 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001115 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001116
1117 def _Load(self):
1118 if self._times is None:
1119 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001120 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001121 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001122 except (IOError, ValueError):
1123 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001124 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001125 except OSError:
1126 pass
1127 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001128
1129 def Save(self):
1130 if self._times is None:
1131 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001132
1133 to_delete = []
1134 for name in self._times:
1135 if name not in self._seen:
1136 to_delete.append(name)
1137 for name in to_delete:
1138 del self._times[name]
1139
Dave Borowitz67700e92012-10-23 15:00:54 -07001140 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001141 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001142 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001143 except (IOError, TypeError):
1144 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001145 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001146 except OSError:
1147 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001148
1149# This is a replacement for xmlrpc.client.Transport using urllib2
1150# and supporting persistent-http[s]. It cannot change hosts from
1151# request to request like the normal transport, the real url
1152# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001153
1154
Dan Willemsen0745bb22015-08-17 13:41:45 -07001155class PersistentTransport(xmlrpc.client.Transport):
1156 def __init__(self, orig_host):
1157 self.orig_host = orig_host
1158
1159 def request(self, host, handler, request_body, verbose=False):
1160 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1161 # Python doesn't understand cookies with the #HttpOnly_ prefix
1162 # Since we're only using them for HTTP, copy the file temporarily,
1163 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001164 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001165 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001166 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001167 try:
1168 with open(cookiefile) as f:
1169 for line in f:
1170 if line.startswith("#HttpOnly_"):
1171 line = line[len("#HttpOnly_"):]
1172 tmpcookiefile.write(line)
1173 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001174
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001175 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001176 try:
1177 cookiejar.load()
1178 except cookielib.LoadError:
1179 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001180 finally:
1181 tmpcookiefile.close()
1182 else:
1183 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001184
1185 proxyhandler = urllib.request.ProxyHandler
1186 if proxy:
1187 proxyhandler = urllib.request.ProxyHandler({
1188 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001189 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001190
1191 opener = urllib.request.build_opener(
1192 urllib.request.HTTPCookieProcessor(cookiejar),
1193 proxyhandler)
1194
1195 url = urllib.parse.urljoin(self.orig_host, handler)
1196 parse_results = urllib.parse.urlparse(url)
1197
1198 scheme = parse_results.scheme
1199 if scheme == 'persistent-http':
1200 scheme = 'http'
1201 if scheme == 'persistent-https':
1202 # If we're proxying through persistent-https, use http. The
1203 # proxy itself will do the https.
1204 if proxy:
1205 scheme = 'http'
1206 else:
1207 scheme = 'https'
1208
1209 # Parse out any authentication information using the base class
1210 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1211
1212 url = urllib.parse.urlunparse((
1213 scheme,
1214 host,
1215 parse_results.path,
1216 parse_results.params,
1217 parse_results.query,
1218 parse_results.fragment))
1219
1220 request = urllib.request.Request(url, request_body)
1221 if extra_headers is not None:
1222 for (name, header) in extra_headers:
1223 request.add_header(name, header)
1224 request.add_header('Content-Type', 'text/xml')
1225 try:
1226 response = opener.open(request)
1227 except urllib.error.HTTPError as e:
1228 if e.code == 501:
1229 # We may have been redirected through a login process
1230 # but our POST turned into a GET. Retry.
1231 response = opener.open(request)
1232 else:
1233 raise
1234
1235 p, u = xmlrpc.client.getparser()
1236 while 1:
1237 data = response.read(1024)
1238 if not data:
1239 break
1240 p.feed(data)
1241 p.close()
1242 return u.close()
1243
1244 def close(self):
1245 pass