blob: 4d95b023ecc2a59de24ee5043b4c2d31991cb5a8 [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
jiajia tanga590e642021-04-25 20:02:02 +080015import errno
Mike Frysingerebf04a42021-02-23 20:48:04 -050016import functools
Mike Frysingeracf63b22019-06-13 02:24:21 -040017import http.cookiejar as cookielib
Mike Frysinger7b586f22021-02-23 18:38:39 -050018import io
Anthony King85b24ac2014-05-06 15:57:48 +010019import json
Mike Frysingerebf04a42021-02-23 20:48:04 -050020import multiprocessing
David Pursehouse86d973d2012-08-24 10:21:02 +090021import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070022from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import os
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070024import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040028import urllib.error
29import urllib.parse
30import urllib.request
31import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070032
Roy Lee18afd7f2010-05-09 04:32:08 +080033try:
34 import threading as _threading
35except ImportError:
36 import dummy_threading as _threading
37
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070038try:
39 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090040
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070041 def _rlimit_nofile():
42 return resource.getrlimit(resource.RLIMIT_NOFILE)
43except ImportError:
44 def _rlimit_nofile():
45 return (256, 256)
46
David Rileye0684ad2017-04-05 00:02:59 -070047import event_log
Mike Frysinger347f9ed2021-03-15 14:58:52 -040048from git_command import git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090049from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090050from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080051import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070052import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070053from project import Project
54from project import RemoteSpec
Mike Frysingerd41eed02021-04-20 23:21:29 -040055from command import Command, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080056from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070057import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070058from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070059from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080060from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070061from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070062
Dave Borowitz67700e92012-10-23 15:00:54 -070063_ONE_DAY_S = 24 * 60 * 60
64
David Pursehouse819827a2020-02-12 15:20:19 +090065
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080066class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080067 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070068 common = True
69 helpSummary = "Update working tree to the latest revision"
70 helpUsage = """
71%prog [<project>...]
72"""
73 helpDescription = """
74The '%prog' command synchronizes local project directories
75with the remote repositories specified in the manifest. If a local
76project does not yet exist, it will clone a new local directory from
77the remote repository and set up tracking branches as specified in
78the manifest. If the local project already exists, '%prog'
79will update the remote branches and rebase any new local changes
80on top of the new remote changes.
81
82'%prog' will synchronize all projects listed at the command
83line. Projects can be specified either by name, or by a relative
84or absolute path to the project's local directory. If no projects
85are specified, '%prog' will synchronize all projects listed in
86the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070087
88The -d/--detach option can be used to switch specified projects
89back to the manifest revision. This option is especially helpful
90if the project is currently on a topic branch, but the manifest
91revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070092
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070093The -s/--smart-sync option can be used to sync to a known good
94build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020095manifest. The -t/--smart-tag option is similar and allows you to
96specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070097
David Pursehousecf76b1b2012-09-14 10:31:42 +090098The -u/--manifest-server-username and -p/--manifest-server-password
99options can be used to specify a username and password to authenticate
100with the manifest server when using the -s or -t option.
101
102If -u and -p are not specified when using the -s or -t option, '%prog'
103will attempt to read authentication credentials for the manifest server
104from the user's .netrc file.
105
106'%prog' will not use authentication credentials from -u/-p or .netrc
107if the manifest server specified in the manifest file already includes
108credentials.
109
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400110By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400111to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500112
Kevin Degiabaa7f32014-11-12 11:27:45 -0700113The --force-sync option can be used to overwrite existing git
114directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900115object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700116refs may be removed when overwriting.
117
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500118The --force-remove-dirty option can be used to remove previously used
119projects with uncommitted changes. WARNING: This may cause data to be
120lost since uncommitted changes may be removed with projects that no longer
121exist in the manifest.
122
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700123The --no-clone-bundle option disables any attempt to use
124$URL/clone.bundle to bootstrap a new Git repository from a
125resumeable bundle file on a content delivery network. This
126may be necessary if there are problems with the local Python
127HTTP client or proxy configuration, but the Git binary works.
128
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800129The --fetch-submodules option enables fetching Git submodules
130of a project from server.
131
David Pursehousef2fad612015-01-29 14:36:28 +0900132The -c/--current-branch option can be used to only fetch objects that
133are on the branch specified by a project's revision.
134
David Pursehouseb1553542014-09-04 21:28:09 +0900135The --optimized-fetch option can be used to only fetch projects that
136are fixed to a sha1 revision if the sha1 revision does not already
137exist locally.
138
David Pursehouse74cfd272015-10-14 10:50:15 +0900139The --prune option can be used to remove any refs that no longer
140exist on the remote.
141
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400142# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700143
144If at least one project remote URL uses an SSH connection (ssh://,
145git+ssh://, or user@host:path syntax) repo will automatically
146enable the SSH ControlMaster option when connecting to that host.
147This feature permits other projects in the same '%prog' session to
148reuse the same SSH tunnel, saving connection setup overheads.
149
150To disable this behavior on UNIX platforms, set the GIT_SSH
151environment variable to 'ssh'. For example:
152
153 export GIT_SSH=ssh
154 %prog
155
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400156# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700157
158This feature is automatically disabled on Windows, due to the lack
159of UNIX domain socket support.
160
161This feature is not compatible with url.insteadof rewrites in the
162user's ~/.gitconfig. '%prog' is currently not able to perform the
163rewrite early enough to establish the ControlMaster tunnel.
164
165If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
166later is required to fix a server side protocol bug.
167
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700168"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500169 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170
Mike Frysinger9180a072021-04-13 14:57:40 -0400171 def _CommonOptions(self, p):
Mike Frysingerc177f942021-05-04 08:06:36 -0400172 if self.manifest:
173 try:
174 self.PARALLEL_JOBS = self.manifest.default.sync_j
175 except ManifestParseError:
176 pass
Mike Frysinger9180a072021-04-13 14:57:40 -0400177 super()._CommonOptions(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700178
Mike Frysinger9180a072021-04-13 14:57:40 -0400179 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400180 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
181 help='number of network jobs to run in parallel (defaults to --jobs)')
182 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
183 help='number of local checkout jobs to run in parallel (defaults to --jobs)')
184
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500185 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200186 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400187 help='obsolete option (to be deleted in the future)')
188 p.add_option('--fail-fast',
189 dest='fail_fast', action='store_true',
190 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700191 p.add_option('--force-sync',
192 dest='force_sync', action='store_true',
193 help="overwrite an existing git directory if it needs to "
194 "point to a different object directory. WARNING: this "
195 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500196 p.add_option('--force-remove-dirty',
197 dest='force_remove_dirty', action='store_true',
198 help="force remove projects with uncommitted modifications if "
199 "projects no longer exist in the manifest. "
200 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900201 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700202 dest='local_only', action='store_true',
203 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900204 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100205 dest='mp_update', action='store_false', default='true',
206 help='use the existing manifest checkout as-is. '
207 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900208 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700209 dest='network_only', action='store_true',
210 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700212 dest='detach_head', action='store_true',
213 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700215 dest='current_branch_only', action='store_true',
216 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400217 p.add_option('--no-current-branch',
218 dest='current_branch_only', action='store_false',
219 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500220 p.add_option('-m', '--manifest-name',
221 dest='manifest_name',
222 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700223 p.add_option('--clone-bundle', action='store_true',
224 help='enable use of /clone.bundle on HTTP/HTTPS')
225 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700226 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800227 p.add_option('-u', '--manifest-server-username', action='store',
228 dest='manifest_server_username',
229 help='username to authenticate with the manifest server')
230 p.add_option('-p', '--manifest-server-password', action='store',
231 dest='manifest_server_password',
232 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800233 p.add_option('--fetch-submodules',
234 dest='fetch_submodules', action='store_true',
235 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800236 p.add_option('--use-superproject', action='store_true',
237 help='use the manifest superproject to sync projects')
Mike Frysingerd68ed632021-05-03 01:21:35 -0400238 p.add_option('--tags',
239 action='store_false',
240 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700241 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400242 dest='tags', action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700243 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900244 p.add_option('--optimized-fetch',
245 dest='optimized_fetch', action='store_true',
246 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600247 p.add_option('--retry-fetches',
248 default=0, action='store', type='int',
249 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900250 p.add_option('--prune', dest='prune', action='store_true',
251 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700252 if show_smart:
253 p.add_option('-s', '--smart-sync',
254 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900255 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200256 p.add_option('-t', '--smart-tag',
257 dest='smart_tag', action='store',
258 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700259
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700260 g = p.add_option_group('repo Version options')
261 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500262 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700263 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700264 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800265 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700266 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800268 def _GetBranch(self):
269 """Returns the branch name for getting the approved manifest."""
270 p = self.manifest.manifestProject
271 b = p.GetBranch(p.CurrentBranch)
272 branch = b.merge
273 if branch.startswith(R_HEADS):
274 branch = branch[len(R_HEADS):]
275 return branch
276
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700277 def _UseSuperproject(self, opt):
278 """Returns True if use-superproject option is enabled"""
279 return (opt.use_superproject or
280 self.manifest.manifestProject.config.GetBoolean(
281 'repo.superproject'))
282
283 def _GetCurrentBranchOnly(self, opt):
284 """Returns True if current-branch or use-superproject options are enabled."""
285 return opt.current_branch_only or self._UseSuperproject(opt)
286
Raman Tennetifeb28912021-05-02 19:47:29 -0700287 def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800288 """Update revisionId of every project with the SHA from superproject.
289
290 This function updates each project's revisionId with SHA from superproject.
291 It writes the updated manifest into a file and reloads the manifest from it.
292
293 Args:
294 opt: Program options returned from optparse. See _Options().
295 args: Arguments to pass to GetProjects. See the GetProjects
296 docstring for details.
Raman Tennetifeb28912021-05-02 19:47:29 -0700297 load_local_manifests: Whether to load local manifests.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800298
299 Returns:
300 Returns path to the overriding manifest file.
301 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800302 superproject = git_superproject.Superproject(self.manifest,
Raman Tennetief99ec02021-03-04 10:29:40 -0800303 self.repodir,
304 quiet=opt.quiet)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800305 all_projects = self.GetProjects(args,
306 missing_ok=True,
307 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800308 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800309 if not manifest_path:
Raman Tennetif907ced2021-05-07 10:22:16 -0700310 print('error: Update of revsionId from superproject has failed. '
311 'Please resync with --no-use-superproject option',
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800312 file=sys.stderr)
313 sys.exit(1)
Raman Tennetifeb28912021-05-02 19:47:29 -0700314 self._ReloadManifest(manifest_path, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800315 return manifest_path
316
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500317 def _FetchProjectList(self, opt, projects):
318 """Main function of the fetch worker.
319
320 The projects we're given share the same underlying git object store, so we
321 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800322
David James8d201162013-10-11 17:03:19 -0700323 Delegates most of the work to _FetchHelper.
324
325 Args:
326 opt: Program options returned from optparse. See _Options().
327 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700328 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500329 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700330
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500331 def _FetchOne(self, opt, project):
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.
David James8d201162013-10-11 17:03:19 -0700337
338 Returns:
339 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900340 """
David Rileye0684ad2017-04-05 00:02:59 -0700341 start = time.time()
342 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500343 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900344 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500345 success = project.Sync_NetworkHalf(
346 quiet=opt.quiet,
347 verbose=opt.verbose,
348 output_redir=buf,
349 current_branch_only=self._GetCurrentBranchOnly(opt),
350 force_sync=opt.force_sync,
351 clone_bundle=opt.clone_bundle,
352 tags=opt.tags, archive=self.manifest.IsArchive,
353 optimized_fetch=opt.optimized_fetch,
354 retry_fetches=opt.retry_fetches,
355 prune=opt.prune,
Raman Tennetif32f2432021-04-12 20:57:25 -0700356 clone_filter=self.manifest.CloneFilter,
357 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700358
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500359 output = buf.getvalue()
360 if opt.verbose and output:
361 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700362
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500363 if not success:
364 print('error: Cannot fetch %s from %s'
365 % (project.name, project.remote.url),
366 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700367 except GitError as e:
368 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500369 except Exception as e:
370 print('error: Cannot fetch %s (%s: %s)'
371 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
372 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500373
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500374 finish = time.time()
375 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700376
Mike Frysinger5a033082019-09-23 19:21:20 -0400377 def _Fetch(self, projects, opt, err_event):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500378 ret = True
379
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400380 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700381 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400382 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800383
David James89ece422014-01-09 18:51:58 -0800384 objdir_project_map = dict()
385 for project in projects:
386 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500387 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700388
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500389 def _ProcessResults(results_sets):
390 ret = True
391 for results in results_sets:
392 for (success, project, start, finish) in results:
393 self._fetch_times.Set(project, finish - start)
394 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
395 start, finish, success)
396 # Check for any errors before running any more tasks.
397 # ...we'll let existing jobs finish, though.
398 if not success:
399 ret = False
400 else:
401 fetched.add(project.gitdir)
402 pm.update(msg=project.name)
403 if not ret and opt.fail_fast:
404 break
405 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700406
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500407 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400408 if len(projects_list) == 1 or jobs == 1:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500409 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
410 ret = False
411 else:
412 # Favor throughput over responsiveness when quiet. It seems that imap()
413 # will yield results in batches relative to chunksize, so even as the
414 # children finish a sync, we won't see the result until one child finishes
415 # ~chunksize jobs. When using a large --jobs with large chunksize, this
416 # can be jarring as there will be a large initial delay where repo looks
417 # like it isn't doing anything and sits at 0%, but then suddenly completes
418 # a lot of jobs all at once. Since this code is more network bound, we
419 # can accept a bit more CPU overhead with a smaller chunksize so that the
420 # user sees more immediate & continuous feedback.
421 if opt.quiet:
422 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800423 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500424 pm.update(inc=0, msg='warming up')
425 chunksize = 4
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400426 with multiprocessing.Pool(jobs) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500427 results = pool.imap_unordered(
428 functools.partial(self._FetchProjectList, opt),
429 projects_list,
430 chunksize=chunksize)
431 if not _ProcessResults(results):
432 ret = False
433 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800434
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700435 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700436 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700437
Julien Campergue335f5ef2013-10-16 11:02:35 +0200438 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400439 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200440
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500441 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700442
Mike Frysingerb4429432021-05-05 20:03:26 -0400443 def _FetchMain(self, opt, args, all_projects, err_event, manifest_name,
444 load_local_manifests):
445 """The main network fetch loop.
446
447 Args:
448 opt: Program options returned from optparse. See _Options().
449 args: Command line args used to filter out projects.
450 all_projects: List of all projects that should be checked out.
451 err_event: Whether an error was hit while processing.
452 manifest_name: Manifest file to be reloaded.
453 load_local_manifests: Whether to load local manifests.
454 """
455 rp = self.manifest.repoProject
456
457 to_fetch = []
458 now = time.time()
459 if _ONE_DAY_S <= (now - rp.LastFetch):
460 to_fetch.append(rp)
461 to_fetch.extend(all_projects)
462 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
463
464 success, fetched = self._Fetch(to_fetch, opt, err_event)
465 if not success:
466 err_event.set()
467
468 _PostRepoFetch(rp, opt.repo_verify)
469 if opt.network_only:
470 # bail out now; the rest touches the working tree
471 if err_event.is_set():
472 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
473 sys.exit(1)
474 return
475
476 # Iteratively fetch missing and/or nested unregistered submodules
477 previously_missing_set = set()
478 while True:
479 self._ReloadManifest(manifest_name, load_local_manifests)
480 all_projects = self.GetProjects(args,
481 missing_ok=True,
482 submodules_ok=opt.fetch_submodules)
483 missing = []
484 for project in all_projects:
485 if project.gitdir not in fetched:
486 missing.append(project)
487 if not missing:
488 break
489 # Stop us from non-stopped fetching actually-missing repos: If set of
490 # missing repos has not been changed from last fetch, we break.
491 missing_set = set(p.name for p in missing)
492 if previously_missing_set == missing_set:
493 break
494 previously_missing_set = missing_set
495 success, new_fetched = self._Fetch(missing, opt, err_event)
496 if not success:
497 err_event.set()
498 fetched.update(new_fetched)
499
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500500 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700501 """Checkout work tree for one project
502
503 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500504 detach_head: Whether to leave a detached HEAD.
505 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700506 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700507
508 Returns:
509 Whether the fetch was successful.
510 """
Xin Li745be2e2019-06-03 11:24:30 -0700511 start = time.time()
512 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500513 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700514 success = False
515 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500516 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500517 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700518 except GitError as e:
519 print('error.GitError: Cannot checkout %s: %s' %
520 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500521 except Exception as e:
522 print('error: Cannot checkout %s: %s: %s' %
523 (project.name, type(e).__name__, str(e)),
524 file=sys.stderr)
525 raise
Xin Li745be2e2019-06-03 11:24:30 -0700526
Mike Frysingerebf04a42021-02-23 20:48:04 -0500527 if not success:
528 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
529 finish = time.time()
530 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700531
Mike Frysingerebf04a42021-02-23 20:48:04 -0500532 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700533 """Checkout projects listed in all_projects
534
535 Args:
536 all_projects: List of all projects that should be checked out.
537 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500538 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700539 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500540 # Only checkout projects with worktrees.
541 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700542
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500543 def _ProcessResults(pool, pm, results):
544 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500545 for (success, project, start, finish) in results:
546 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
547 start, finish, success)
548 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500549 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500550 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500551 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500552 err_results.append(project.relpath)
553 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500554 if pool:
555 pool.close()
556 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500557 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500558 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700559
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500560 return self.ExecuteInParallel(
561 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
562 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
563 all_projects,
564 callback=_ProcessResults,
565 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500566
Mike Frysinger5a033082019-09-23 19:21:20 -0400567 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400568 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400569 pm.update(inc=0, msg='prescan')
570
Gabe Black2ff30292014-10-09 17:54:35 -0700571 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700572 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500573 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500574 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900575 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100576 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400577 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100578 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500579 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500580 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500581 else:
582 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400583 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500584 'versions of git; please upgrade to git-2.7.0+.'
585 % (project.relpath,),
586 file=sys.stderr)
587 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700588 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700589
Mike Frysinger65af2602021-04-08 22:47:44 -0400590 pm.update(inc=len(projects) - len(gc_gitdirs), msg='warming up')
591
592 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700593 jobs = min(self.jobs, cpu_count)
594
595 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700596 for bare_git in gc_gitdirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400597 pm.update(msg=bare_git._project.name)
David James8d201162013-10-11 17:03:19 -0700598 bare_git.gc('--auto')
Mike Frysinger65af2602021-04-08 22:47:44 -0400599 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700600 return
601
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400602 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700603
604 threads = set()
605 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700606
David James8d201162013-10-11 17:03:19 -0700607 def GC(bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400608 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700609 try:
610 try:
David James8d201162013-10-11 17:03:19 -0700611 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700612 except GitError:
613 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900614 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700615 err_event.set()
616 raise
617 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400618 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700619 sem.release()
620
Gabe Black2ff30292014-10-09 17:54:35 -0700621 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500622 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700623 break
624 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700625 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700626 t.daemon = True
627 threads.add(t)
628 t.start()
629
630 for t in threads:
631 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400632 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700633
Raman Tennetifeb28912021-05-02 19:47:29 -0700634 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
635 """Reload the manfiest from the file specified by the |manifest_name|.
636
637 It unloads the manifest if |manifest_name| is None.
638
639 Args:
640 manifest_name: Manifest file to be reloaded.
641 load_local_manifests: Whether to load local manifests.
642 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800643 if manifest_name:
644 # Override calls _Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700645 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800646 else:
647 self.manifest._Unload()
648
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500649 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700650 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700651 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700652 if project.relpath:
653 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700654 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500655 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700656 old_project_paths = []
657
658 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500659 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700660 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800661 # In reversed order, so subfolders are deleted before parent folder.
662 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700663 if not path:
664 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700665 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900666 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700667 gitdir = os.path.join(self.manifest.topdir, path, '.git')
668 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900669 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900670 manifest=self.manifest,
671 name=path,
672 remote=RemoteSpec('origin'),
673 gitdir=gitdir,
674 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500675 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900676 worktree=os.path.join(self.manifest.topdir, path),
677 relpath=path,
678 revisionExpr='HEAD',
679 revisionId=None,
680 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500681 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900682 quiet=opt.quiet,
683 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400684 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700685
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700686 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500687 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700688 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700689 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700690 return 0
691
jiajia tanga590e642021-04-25 20:02:02 +0800692 def UpdateCopyLinkfileList(self):
693 """Save all dests of copyfile and linkfile, and update them if needed.
694
695 Returns:
696 Whether update was successful.
697 """
698 new_paths = {}
699 new_linkfile_paths = []
700 new_copyfile_paths = []
701 for project in self.GetProjects(None, missing_ok=True):
702 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
703 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
704
705 new_paths = {
706 'linkfile': new_linkfile_paths,
707 'copyfile': new_copyfile_paths,
708 }
709
710 copylinkfile_name = 'copy-link-files.json'
711 copylinkfile_path = os.path.join(self.manifest.repodir, copylinkfile_name)
712 old_copylinkfile_paths = {}
713
714 if os.path.exists(copylinkfile_path):
715 with open(copylinkfile_path, 'rb') as fp:
716 try:
717 old_copylinkfile_paths = json.load(fp)
718 except:
719 print('error: %s is not a json formatted file.' %
720 copylinkfile_path, file=sys.stderr)
721 platform_utils.remove(copylinkfile_path)
722 return False
723
724 need_remove_files = []
725 need_remove_files.extend(
726 set(old_copylinkfile_paths.get('linkfile', [])) -
727 set(new_linkfile_paths))
728 need_remove_files.extend(
729 set(old_copylinkfile_paths.get('copyfile', [])) -
730 set(new_copyfile_paths))
731
732 for need_remove_file in need_remove_files:
733 try:
734 platform_utils.remove(need_remove_file)
735 except OSError as e:
736 if e.errno == errno.ENOENT:
737 # Try to remove the updated copyfile or linkfile.
738 # So, if the file is not exist, nothing need to do.
739 pass
740
741 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
742 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
743 json.dump(new_paths, fp)
744 return True
745
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400746 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
747 if not self.manifest.manifest_server:
748 print('error: cannot smart sync: no manifest server defined in '
749 'manifest', file=sys.stderr)
750 sys.exit(1)
751
752 manifest_server = self.manifest.manifest_server
753 if not opt.quiet:
754 print('Using manifest server %s' % manifest_server)
755
David Pursehouseeeff3532020-02-12 11:24:10 +0900756 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400757 username = None
758 password = None
759 if opt.manifest_server_username and opt.manifest_server_password:
760 username = opt.manifest_server_username
761 password = opt.manifest_server_password
762 else:
763 try:
764 info = netrc.netrc()
765 except IOError:
766 # .netrc file does not exist or could not be opened
767 pass
768 else:
769 try:
770 parse_result = urllib.parse.urlparse(manifest_server)
771 if parse_result.hostname:
772 auth = info.authenticators(parse_result.hostname)
773 if auth:
774 username, _account, password = auth
775 else:
776 print('No credentials found for %s in .netrc'
777 % parse_result.hostname, file=sys.stderr)
778 except netrc.NetrcParseError as e:
779 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
780
781 if (username and password):
782 manifest_server = manifest_server.replace('://', '://%s:%s@' %
783 (username, password),
784 1)
785
786 transport = PersistentTransport(manifest_server)
787 if manifest_server.startswith('persistent-'):
788 manifest_server = manifest_server[len('persistent-'):]
789
790 try:
791 server = xmlrpc.client.Server(manifest_server, transport=transport)
792 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800793 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400794
Mike Frysinger56ce3462019-12-04 19:30:48 -0500795 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500796 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400797 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500798 elif ('TARGET_PRODUCT' in os.environ and
799 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500800 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
801 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400802 [success, manifest_str] = server.GetApprovedManifest(branch, target)
803 else:
804 [success, manifest_str] = server.GetApprovedManifest(branch)
805 else:
806 assert(opt.smart_tag)
807 [success, manifest_str] = server.GetManifest(opt.smart_tag)
808
809 if success:
810 manifest_name = os.path.basename(smart_sync_manifest_path)
811 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500812 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400813 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400814 except IOError as e:
815 print('error: cannot write manifest to %s:\n%s'
816 % (smart_sync_manifest_path, e),
817 file=sys.stderr)
818 sys.exit(1)
819 self._ReloadManifest(manifest_name)
820 else:
821 print('error: manifest server RPC call failed: %s' %
822 manifest_str, file=sys.stderr)
823 sys.exit(1)
824 except (socket.error, IOError, xmlrpc.client.Fault) as e:
825 print('error: cannot connect to manifest server %s:\n%s'
826 % (self.manifest.manifest_server, e), file=sys.stderr)
827 sys.exit(1)
828 except xmlrpc.client.ProtocolError as e:
829 print('error: cannot connect to manifest server %s:\n%d %s'
830 % (self.manifest.manifest_server, e.errcode, e.errmsg),
831 file=sys.stderr)
832 sys.exit(1)
833
834 return manifest_name
835
Mike Frysingerfb527e32019-08-27 02:34:32 -0400836 def _UpdateManifestProject(self, opt, mp, manifest_name):
837 """Fetch & update the local manifest project."""
838 if not opt.local_only:
839 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500840 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700841 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200842 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500843 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400844 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600845 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400846 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700847 clone_filter=self.manifest.CloneFilter,
848 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400849 finish = time.time()
850 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
851 start, finish, success)
852
853 if mp.HasChanges:
854 syncbuf = SyncBuffer(mp.config)
855 start = time.time()
856 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
857 clean = syncbuf.Finish()
858 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
859 start, time.time(), clean)
860 if not clean:
861 sys.exit(1)
Mike Frysinger05638bf2021-05-04 15:33:31 -0400862 self._ReloadManifest(manifest_name)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400863 if opt.jobs is None:
864 self.jobs = self.manifest.default.sync_j
865
Mike Frysingerae6cb082019-08-27 01:10:59 -0400866 def ValidateOptions(self, opt, args):
867 if opt.force_broken:
868 print('warning: -f/--force-broken is now the default behavior, and the '
869 'options are deprecated', file=sys.stderr)
870 if opt.network_only and opt.detach_head:
871 self.OptionParser.error('cannot combine -n and -d')
872 if opt.network_only and opt.local_only:
873 self.OptionParser.error('cannot combine -n and -l')
874 if opt.manifest_name and opt.smart_sync:
875 self.OptionParser.error('cannot combine -m and -s')
876 if opt.manifest_name and opt.smart_tag:
877 self.OptionParser.error('cannot combine -m and -t')
878 if opt.manifest_server_username or opt.manifest_server_password:
879 if not (opt.smart_sync or opt.smart_tag):
880 self.OptionParser.error('-u and -p may only be combined with -s or -t')
881 if None in [opt.manifest_server_username, opt.manifest_server_password]:
882 self.OptionParser.error('both -u and -p must be given')
883
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700884 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800885 if opt.jobs:
886 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700887 if self.jobs > 1:
888 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400889 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700890
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500891 if opt.manifest_name:
892 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700893
Chirayu Desaia892b102013-06-11 14:18:46 +0530894 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900895 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900896 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530897
Xin Lid79a4bc2020-05-20 16:03:45 -0700898 if opt.clone_bundle is None:
899 opt.clone_bundle = self.manifest.CloneBundle
900
Victor Boivie08c880d2011-04-19 10:32:52 +0200901 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400902 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
903 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900904 if os.path.isfile(smart_sync_manifest_path):
905 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800906 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900907 except OSError as e:
908 print('error: failed to remove existing smart sync override manifest: %s' %
909 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700910
Mike Frysingerc99322a2021-05-04 15:32:43 -0400911 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -0400912
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700913 rp = self.manifest.repoProject
914 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500915 cb = rp.CurrentBranch
916 if cb:
917 base = rp.GetBranch(cb).merge
918 if not base or not base.startswith('refs/heads/'):
919 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400920 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500921 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700922
923 mp = self.manifest.manifestProject
924 mp.PreSync()
925
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800926 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700927 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800928
Fredrik de Grootcc960972019-11-22 09:04:31 +0100929 if not opt.mp_update:
930 print('Skipping update of local manifest project.')
931 else:
932 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700933
Raman Tennetifeb28912021-05-02 19:47:29 -0700934 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700935 if self._UseSuperproject(opt):
Raman Tennetifeb28912021-05-02 19:47:29 -0700936 manifest_name = self._UpdateProjectsRevisionId(opt, args, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800937
Simran Basib9a1b732015-08-20 12:19:28 -0700938 if self.gitc_manifest:
939 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700940 missing_ok=True)
941 gitc_projects = []
942 opened_projects = []
943 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700944 if project.relpath in self.gitc_manifest.paths and \
945 self.gitc_manifest.paths[project.relpath].old_revision:
946 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700947 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700948 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700949
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700950 if not args:
951 gitc_projects = None
952
953 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700954 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700955 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
956 if manifest_name:
957 manifest.Override(manifest_name)
958 else:
959 manifest.Override(self.manifest.manifestFile)
960 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
961 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700962 gitc_projects)
963 print('GITC client successfully synced.')
964
965 # The opened projects need to be synced as normal, therefore we
966 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700967 # TODO: make this more reliable -- if there's a project name/path overlap,
968 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900969 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
970 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700971 if not args:
972 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800973 all_projects = self.GetProjects(args,
974 missing_ok=True,
975 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700976
Mike Frysinger5a033082019-09-23 19:21:20 -0400977 err_network_sync = False
978 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -0400979
Dave Borowitz67700e92012-10-23 15:00:54 -0700980 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700981 if not opt.local_only:
Mike Frysingerb4429432021-05-05 20:03:26 -0400982 self._FetchMain(opt, args, all_projects, err_event, manifest_name,
983 load_local_manifests)
Mike Frysinger5a033082019-09-23 19:21:20 -0400984
985 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500986 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400987 err_network_sync = True
988 if opt.fail_fast:
989 print('\nerror: Exited sync due to fetch errors.\n'
990 'Local checkouts *not* updated. Resolve network issues & '
991 'retry.\n'
992 '`repo sync -l` will update some local checkouts.',
993 file=sys.stderr)
994 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800995
Julien Campergue335f5ef2013-10-16 11:02:35 +0200996 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700997 # bail out now, we have no working tree
998 return
999
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001000 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001001 err_event.set()
1002 err_update_projects = True
1003 if opt.fail_fast:
1004 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1005 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001006
Mike Frysinger14208f42021-05-04 15:31:51 -04001007 err_update_linkfiles = not self.UpdateCopyLinkfileList()
1008 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +08001009 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +08001010 if opt.fail_fast:
1011 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1012 sys.exit(1)
1013
Mike Frysinger5a033082019-09-23 19:21:20 -04001014 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001015 # NB: We don't exit here because this is the last step.
1016 err_checkout = not self._Checkout(all_projects, opt, err_results)
1017 if err_checkout:
1018 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001019
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001020 # If there's a notice that's supposed to print at the end of the sync, print
1021 # it now...
1022 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001023 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001024
Mike Frysinger5a033082019-09-23 19:21:20 -04001025 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001026 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001027 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1028 if err_network_sync:
1029 print('error: Downloading network changes failed.', file=sys.stderr)
1030 if err_update_projects:
1031 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001032 if err_update_linkfiles:
1033 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001034 if err_checkout:
1035 print('error: Checking out local projects failed.', file=sys.stderr)
1036 if err_results:
1037 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1038 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1039 file=sys.stderr)
1040 sys.exit(1)
1041
Mike Frysingere19d9e12020-02-12 11:23:32 -05001042 if not opt.quiet:
1043 print('repo sync has finished successfully.')
1044
David Pursehouse819827a2020-02-12 15:20:19 +09001045
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001046def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001047 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001048 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001049 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001050 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001051 if project.Exists:
1052 project.PostRepoUpgrade()
1053
David Pursehouse819827a2020-02-12 15:20:19 +09001054
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001055def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001056 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001057 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001058 wrapper = Wrapper()
1059 try:
1060 rev = rp.bare_git.describe(rp.GetRevisionId())
1061 except GitError:
1062 rev = None
1063 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1064 # See if we're held back due to missing signed tag.
1065 current_revid = rp.bare_git.rev_parse('HEAD')
1066 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1067 if current_revid != new_revid:
1068 # We want to switch to the new rev, but also not trash any uncommitted
1069 # changes. This helps with local testing/hacking.
1070 # If a local change has been made, we will throw that away.
1071 # We also have to make sure this will switch to an older commit if that's
1072 # the latest tag in order to support release rollback.
1073 try:
1074 rp.work_git.reset('--keep', new_rev)
1075 except GitError as e:
1076 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001077 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001078 raise RepoChangedException(['--repo-upgraded'])
1079 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001080 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001081 else:
1082 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001083 print('repo version %s is current' % rp.work_git.describe(HEAD),
1084 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001085
David Pursehouse819827a2020-02-12 15:20:19 +09001086
Dave Borowitz67700e92012-10-23 15:00:54 -07001087class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001088 _ALPHA = 0.5
1089
Dave Borowitz67700e92012-10-23 15:00:54 -07001090 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001091 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001092 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001093 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001094
1095 def Get(self, project):
1096 self._Load()
1097 return self._times.get(project.name, _ONE_DAY_S)
1098
1099 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001100 self._Load()
1101 name = project.name
1102 old = self._times.get(name, t)
1103 self._seen.add(name)
1104 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001105 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001106
1107 def _Load(self):
1108 if self._times is None:
1109 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001110 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001111 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001112 except (IOError, ValueError):
1113 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001114 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001115 except OSError:
1116 pass
1117 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001118
1119 def Save(self):
1120 if self._times is None:
1121 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001122
1123 to_delete = []
1124 for name in self._times:
1125 if name not in self._seen:
1126 to_delete.append(name)
1127 for name in to_delete:
1128 del self._times[name]
1129
Dave Borowitz67700e92012-10-23 15:00:54 -07001130 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001131 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001132 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001133 except (IOError, TypeError):
1134 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001135 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001136 except OSError:
1137 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001138
1139# This is a replacement for xmlrpc.client.Transport using urllib2
1140# and supporting persistent-http[s]. It cannot change hosts from
1141# request to request like the normal transport, the real url
1142# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001143
1144
Dan Willemsen0745bb22015-08-17 13:41:45 -07001145class PersistentTransport(xmlrpc.client.Transport):
1146 def __init__(self, orig_host):
1147 self.orig_host = orig_host
1148
1149 def request(self, host, handler, request_body, verbose=False):
1150 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1151 # Python doesn't understand cookies with the #HttpOnly_ prefix
1152 # Since we're only using them for HTTP, copy the file temporarily,
1153 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001154 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001155 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001156 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001157 try:
1158 with open(cookiefile) as f:
1159 for line in f:
1160 if line.startswith("#HttpOnly_"):
1161 line = line[len("#HttpOnly_"):]
1162 tmpcookiefile.write(line)
1163 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001164
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001165 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001166 try:
1167 cookiejar.load()
1168 except cookielib.LoadError:
1169 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001170 finally:
1171 tmpcookiefile.close()
1172 else:
1173 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001174
1175 proxyhandler = urllib.request.ProxyHandler
1176 if proxy:
1177 proxyhandler = urllib.request.ProxyHandler({
1178 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001179 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001180
1181 opener = urllib.request.build_opener(
1182 urllib.request.HTTPCookieProcessor(cookiejar),
1183 proxyhandler)
1184
1185 url = urllib.parse.urljoin(self.orig_host, handler)
1186 parse_results = urllib.parse.urlparse(url)
1187
1188 scheme = parse_results.scheme
1189 if scheme == 'persistent-http':
1190 scheme = 'http'
1191 if scheme == 'persistent-https':
1192 # If we're proxying through persistent-https, use http. The
1193 # proxy itself will do the https.
1194 if proxy:
1195 scheme = 'http'
1196 else:
1197 scheme = 'https'
1198
1199 # Parse out any authentication information using the base class
1200 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1201
1202 url = urllib.parse.urlunparse((
1203 scheme,
1204 host,
1205 parse_results.path,
1206 parse_results.params,
1207 parse_results.query,
1208 parse_results.fragment))
1209
1210 request = urllib.request.Request(url, request_body)
1211 if extra_headers is not None:
1212 for (name, header) in extra_headers:
1213 request.add_header(name, header)
1214 request.add_header('Content-Type', 'text/xml')
1215 try:
1216 response = opener.open(request)
1217 except urllib.error.HTTPError as e:
1218 if e.code == 501:
1219 # We may have been redirected through a login process
1220 # but our POST turned into a GET. Retry.
1221 response = opener.open(request)
1222 else:
1223 raise
1224
1225 p, u = xmlrpc.client.getparser()
1226 while 1:
1227 data = response.read(1024)
1228 if not data:
1229 break
1230 p.feed(data)
1231 p.close()
1232 return u.close()
1233
1234 def close(self):
1235 pass