blob: 9a09be65da0753df347e1baed058df627f303ebd [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:
310 print('error: Update of revsionId from superproject has failed',
311 file=sys.stderr)
312 sys.exit(1)
Raman Tennetifeb28912021-05-02 19:47:29 -0700313 self._ReloadManifest(manifest_path, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800314 return manifest_path
315
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500316 def _FetchProjectList(self, opt, projects):
317 """Main function of the fetch worker.
318
319 The projects we're given share the same underlying git object store, so we
320 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800321
David James8d201162013-10-11 17:03:19 -0700322 Delegates most of the work to _FetchHelper.
323
324 Args:
325 opt: Program options returned from optparse. See _Options().
326 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700327 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500328 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700329
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500330 def _FetchOne(self, opt, project):
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.
David James8d201162013-10-11 17:03:19 -0700336
337 Returns:
338 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900339 """
David Rileye0684ad2017-04-05 00:02:59 -0700340 start = time.time()
341 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500342 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900343 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500344 success = project.Sync_NetworkHalf(
345 quiet=opt.quiet,
346 verbose=opt.verbose,
347 output_redir=buf,
348 current_branch_only=self._GetCurrentBranchOnly(opt),
349 force_sync=opt.force_sync,
350 clone_bundle=opt.clone_bundle,
351 tags=opt.tags, archive=self.manifest.IsArchive,
352 optimized_fetch=opt.optimized_fetch,
353 retry_fetches=opt.retry_fetches,
354 prune=opt.prune,
Raman Tennetif32f2432021-04-12 20:57:25 -0700355 clone_filter=self.manifest.CloneFilter,
356 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700357
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500358 output = buf.getvalue()
359 if opt.verbose and output:
360 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700361
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500362 if not success:
363 print('error: Cannot fetch %s from %s'
364 % (project.name, project.remote.url),
365 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700366 except GitError as e:
367 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500368 except Exception as e:
369 print('error: Cannot fetch %s (%s: %s)'
370 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
371 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500372
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500373 finish = time.time()
374 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700375
Mike Frysinger5a033082019-09-23 19:21:20 -0400376 def _Fetch(self, projects, opt, err_event):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500377 ret = True
378
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400379 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700380 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400381 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800382
David James89ece422014-01-09 18:51:58 -0800383 objdir_project_map = dict()
384 for project in projects:
385 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500386 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700387
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500388 def _ProcessResults(results_sets):
389 ret = True
390 for results in results_sets:
391 for (success, project, start, finish) in results:
392 self._fetch_times.Set(project, finish - start)
393 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
394 start, finish, success)
395 # Check for any errors before running any more tasks.
396 # ...we'll let existing jobs finish, though.
397 if not success:
398 ret = False
399 else:
400 fetched.add(project.gitdir)
401 pm.update(msg=project.name)
402 if not ret and opt.fail_fast:
403 break
404 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700405
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500406 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400407 if len(projects_list) == 1 or jobs == 1:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500408 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
409 ret = False
410 else:
411 # Favor throughput over responsiveness when quiet. It seems that imap()
412 # will yield results in batches relative to chunksize, so even as the
413 # children finish a sync, we won't see the result until one child finishes
414 # ~chunksize jobs. When using a large --jobs with large chunksize, this
415 # can be jarring as there will be a large initial delay where repo looks
416 # like it isn't doing anything and sits at 0%, but then suddenly completes
417 # a lot of jobs all at once. Since this code is more network bound, we
418 # can accept a bit more CPU overhead with a smaller chunksize so that the
419 # user sees more immediate & continuous feedback.
420 if opt.quiet:
421 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800422 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500423 pm.update(inc=0, msg='warming up')
424 chunksize = 4
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400425 with multiprocessing.Pool(jobs) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500426 results = pool.imap_unordered(
427 functools.partial(self._FetchProjectList, opt),
428 projects_list,
429 chunksize=chunksize)
430 if not _ProcessResults(results):
431 ret = False
432 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800433
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700434 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700435 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700436
Julien Campergue335f5ef2013-10-16 11:02:35 +0200437 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400438 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200439
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500440 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700441
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500442 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700443 """Checkout work tree for one project
444
445 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500446 detach_head: Whether to leave a detached HEAD.
447 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700448 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700449
450 Returns:
451 Whether the fetch was successful.
452 """
Xin Li745be2e2019-06-03 11:24:30 -0700453 start = time.time()
454 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500455 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700456 success = False
457 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500458 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500459 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700460 except GitError as e:
461 print('error.GitError: Cannot checkout %s: %s' %
462 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500463 except Exception as e:
464 print('error: Cannot checkout %s: %s: %s' %
465 (project.name, type(e).__name__, str(e)),
466 file=sys.stderr)
467 raise
Xin Li745be2e2019-06-03 11:24:30 -0700468
Mike Frysingerebf04a42021-02-23 20:48:04 -0500469 if not success:
470 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
471 finish = time.time()
472 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700473
Mike Frysingerebf04a42021-02-23 20:48:04 -0500474 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700475 """Checkout projects listed in all_projects
476
477 Args:
478 all_projects: List of all projects that should be checked out.
479 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500480 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700481 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500482 # Only checkout projects with worktrees.
483 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700484
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500485 def _ProcessResults(pool, pm, results):
486 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500487 for (success, project, start, finish) in results:
488 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
489 start, finish, success)
490 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500491 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500492 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500493 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500494 err_results.append(project.relpath)
495 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500496 if pool:
497 pool.close()
498 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500499 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500500 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700501
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500502 return self.ExecuteInParallel(
503 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
504 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
505 all_projects,
506 callback=_ProcessResults,
507 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500508
Mike Frysinger5a033082019-09-23 19:21:20 -0400509 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400510 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400511 pm.update(inc=0, msg='prescan')
512
Gabe Black2ff30292014-10-09 17:54:35 -0700513 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700514 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500515 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500516 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900517 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100518 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400519 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100520 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500521 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500522 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500523 else:
524 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400525 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500526 'versions of git; please upgrade to git-2.7.0+.'
527 % (project.relpath,),
528 file=sys.stderr)
529 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700530 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700531
Mike Frysinger65af2602021-04-08 22:47:44 -0400532 pm.update(inc=len(projects) - len(gc_gitdirs), msg='warming up')
533
534 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700535 jobs = min(self.jobs, cpu_count)
536
537 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700538 for bare_git in gc_gitdirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400539 pm.update(msg=bare_git._project.name)
David James8d201162013-10-11 17:03:19 -0700540 bare_git.gc('--auto')
Mike Frysinger65af2602021-04-08 22:47:44 -0400541 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700542 return
543
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400544 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700545
546 threads = set()
547 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700548
David James8d201162013-10-11 17:03:19 -0700549 def GC(bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400550 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700551 try:
552 try:
David James8d201162013-10-11 17:03:19 -0700553 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700554 except GitError:
555 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900556 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700557 err_event.set()
558 raise
559 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400560 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700561 sem.release()
562
Gabe Black2ff30292014-10-09 17:54:35 -0700563 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500564 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700565 break
566 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700567 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700568 t.daemon = True
569 threads.add(t)
570 t.start()
571
572 for t in threads:
573 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400574 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700575
Raman Tennetifeb28912021-05-02 19:47:29 -0700576 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
577 """Reload the manfiest from the file specified by the |manifest_name|.
578
579 It unloads the manifest if |manifest_name| is None.
580
581 Args:
582 manifest_name: Manifest file to be reloaded.
583 load_local_manifests: Whether to load local manifests.
584 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800585 if manifest_name:
586 # Override calls _Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700587 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800588 else:
589 self.manifest._Unload()
590
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500591 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700592 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700593 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700594 if project.relpath:
595 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700596 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500597 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700598 old_project_paths = []
599
600 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500601 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700602 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800603 # In reversed order, so subfolders are deleted before parent folder.
604 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700605 if not path:
606 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700607 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900608 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700609 gitdir = os.path.join(self.manifest.topdir, path, '.git')
610 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900611 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900612 manifest=self.manifest,
613 name=path,
614 remote=RemoteSpec('origin'),
615 gitdir=gitdir,
616 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500617 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900618 worktree=os.path.join(self.manifest.topdir, path),
619 relpath=path,
620 revisionExpr='HEAD',
621 revisionId=None,
622 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500623 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900624 quiet=opt.quiet,
625 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400626 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700627
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700628 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500629 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700630 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700631 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700632 return 0
633
jiajia tanga590e642021-04-25 20:02:02 +0800634 def UpdateCopyLinkfileList(self):
635 """Save all dests of copyfile and linkfile, and update them if needed.
636
637 Returns:
638 Whether update was successful.
639 """
640 new_paths = {}
641 new_linkfile_paths = []
642 new_copyfile_paths = []
643 for project in self.GetProjects(None, missing_ok=True):
644 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
645 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
646
647 new_paths = {
648 'linkfile': new_linkfile_paths,
649 'copyfile': new_copyfile_paths,
650 }
651
652 copylinkfile_name = 'copy-link-files.json'
653 copylinkfile_path = os.path.join(self.manifest.repodir, copylinkfile_name)
654 old_copylinkfile_paths = {}
655
656 if os.path.exists(copylinkfile_path):
657 with open(copylinkfile_path, 'rb') as fp:
658 try:
659 old_copylinkfile_paths = json.load(fp)
660 except:
661 print('error: %s is not a json formatted file.' %
662 copylinkfile_path, file=sys.stderr)
663 platform_utils.remove(copylinkfile_path)
664 return False
665
666 need_remove_files = []
667 need_remove_files.extend(
668 set(old_copylinkfile_paths.get('linkfile', [])) -
669 set(new_linkfile_paths))
670 need_remove_files.extend(
671 set(old_copylinkfile_paths.get('copyfile', [])) -
672 set(new_copyfile_paths))
673
674 for need_remove_file in need_remove_files:
675 try:
676 platform_utils.remove(need_remove_file)
677 except OSError as e:
678 if e.errno == errno.ENOENT:
679 # Try to remove the updated copyfile or linkfile.
680 # So, if the file is not exist, nothing need to do.
681 pass
682
683 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
684 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
685 json.dump(new_paths, fp)
686 return True
687
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400688 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
689 if not self.manifest.manifest_server:
690 print('error: cannot smart sync: no manifest server defined in '
691 'manifest', file=sys.stderr)
692 sys.exit(1)
693
694 manifest_server = self.manifest.manifest_server
695 if not opt.quiet:
696 print('Using manifest server %s' % manifest_server)
697
David Pursehouseeeff3532020-02-12 11:24:10 +0900698 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400699 username = None
700 password = None
701 if opt.manifest_server_username and opt.manifest_server_password:
702 username = opt.manifest_server_username
703 password = opt.manifest_server_password
704 else:
705 try:
706 info = netrc.netrc()
707 except IOError:
708 # .netrc file does not exist or could not be opened
709 pass
710 else:
711 try:
712 parse_result = urllib.parse.urlparse(manifest_server)
713 if parse_result.hostname:
714 auth = info.authenticators(parse_result.hostname)
715 if auth:
716 username, _account, password = auth
717 else:
718 print('No credentials found for %s in .netrc'
719 % parse_result.hostname, file=sys.stderr)
720 except netrc.NetrcParseError as e:
721 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
722
723 if (username and password):
724 manifest_server = manifest_server.replace('://', '://%s:%s@' %
725 (username, password),
726 1)
727
728 transport = PersistentTransport(manifest_server)
729 if manifest_server.startswith('persistent-'):
730 manifest_server = manifest_server[len('persistent-'):]
731
732 try:
733 server = xmlrpc.client.Server(manifest_server, transport=transport)
734 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800735 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400736
Mike Frysinger56ce3462019-12-04 19:30:48 -0500737 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500738 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400739 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500740 elif ('TARGET_PRODUCT' in os.environ and
741 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500742 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
743 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400744 [success, manifest_str] = server.GetApprovedManifest(branch, target)
745 else:
746 [success, manifest_str] = server.GetApprovedManifest(branch)
747 else:
748 assert(opt.smart_tag)
749 [success, manifest_str] = server.GetManifest(opt.smart_tag)
750
751 if success:
752 manifest_name = os.path.basename(smart_sync_manifest_path)
753 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500754 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400755 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400756 except IOError as e:
757 print('error: cannot write manifest to %s:\n%s'
758 % (smart_sync_manifest_path, e),
759 file=sys.stderr)
760 sys.exit(1)
761 self._ReloadManifest(manifest_name)
762 else:
763 print('error: manifest server RPC call failed: %s' %
764 manifest_str, file=sys.stderr)
765 sys.exit(1)
766 except (socket.error, IOError, xmlrpc.client.Fault) as e:
767 print('error: cannot connect to manifest server %s:\n%s'
768 % (self.manifest.manifest_server, e), file=sys.stderr)
769 sys.exit(1)
770 except xmlrpc.client.ProtocolError as e:
771 print('error: cannot connect to manifest server %s:\n%d %s'
772 % (self.manifest.manifest_server, e.errcode, e.errmsg),
773 file=sys.stderr)
774 sys.exit(1)
775
776 return manifest_name
777
Mike Frysingerfb527e32019-08-27 02:34:32 -0400778 def _UpdateManifestProject(self, opt, mp, manifest_name):
779 """Fetch & update the local manifest project."""
780 if not opt.local_only:
781 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500782 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700783 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200784 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500785 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400786 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600787 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400788 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700789 clone_filter=self.manifest.CloneFilter,
790 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400791 finish = time.time()
792 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
793 start, finish, success)
794
795 if mp.HasChanges:
796 syncbuf = SyncBuffer(mp.config)
797 start = time.time()
798 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
799 clean = syncbuf.Finish()
800 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
801 start, time.time(), clean)
802 if not clean:
803 sys.exit(1)
804 self._ReloadManifest(opt.manifest_name)
805 if opt.jobs is None:
806 self.jobs = self.manifest.default.sync_j
807
Mike Frysingerae6cb082019-08-27 01:10:59 -0400808 def ValidateOptions(self, opt, args):
809 if opt.force_broken:
810 print('warning: -f/--force-broken is now the default behavior, and the '
811 'options are deprecated', file=sys.stderr)
812 if opt.network_only and opt.detach_head:
813 self.OptionParser.error('cannot combine -n and -d')
814 if opt.network_only and opt.local_only:
815 self.OptionParser.error('cannot combine -n and -l')
816 if opt.manifest_name and opt.smart_sync:
817 self.OptionParser.error('cannot combine -m and -s')
818 if opt.manifest_name and opt.smart_tag:
819 self.OptionParser.error('cannot combine -m and -t')
820 if opt.manifest_server_username or opt.manifest_server_password:
821 if not (opt.smart_sync or opt.smart_tag):
822 self.OptionParser.error('-u and -p may only be combined with -s or -t')
823 if None in [opt.manifest_server_username, opt.manifest_server_password]:
824 self.OptionParser.error('both -u and -p must be given')
825
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700826 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800827 if opt.jobs:
828 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700829 if self.jobs > 1:
830 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400831 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700832
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500833 if opt.manifest_name:
834 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700835
Chirayu Desaia892b102013-06-11 14:18:46 +0530836 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900837 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900838 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530839
Xin Lid79a4bc2020-05-20 16:03:45 -0700840 if opt.clone_bundle is None:
841 opt.clone_bundle = self.manifest.CloneBundle
842
Victor Boivie08c880d2011-04-19 10:32:52 +0200843 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400844 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
845 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900846 if os.path.isfile(smart_sync_manifest_path):
847 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800848 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900849 except OSError as e:
850 print('error: failed to remove existing smart sync override manifest: %s' %
851 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700852
Mike Frysinger5a033082019-09-23 19:21:20 -0400853 err_event = _threading.Event()
854
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700855 rp = self.manifest.repoProject
856 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500857 cb = rp.CurrentBranch
858 if cb:
859 base = rp.GetBranch(cb).merge
860 if not base or not base.startswith('refs/heads/'):
861 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400862 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500863 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700864
865 mp = self.manifest.manifestProject
866 mp.PreSync()
867
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800868 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700869 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800870
Fredrik de Grootcc960972019-11-22 09:04:31 +0100871 if not opt.mp_update:
872 print('Skipping update of local manifest project.')
873 else:
874 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700875
Raman Tennetifeb28912021-05-02 19:47:29 -0700876 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700877 if self._UseSuperproject(opt):
Raman Tennetifeb28912021-05-02 19:47:29 -0700878 manifest_name = self._UpdateProjectsRevisionId(opt, args, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800879
Simran Basib9a1b732015-08-20 12:19:28 -0700880 if self.gitc_manifest:
881 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700882 missing_ok=True)
883 gitc_projects = []
884 opened_projects = []
885 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700886 if project.relpath in self.gitc_manifest.paths and \
887 self.gitc_manifest.paths[project.relpath].old_revision:
888 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700889 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700890 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700891
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700892 if not args:
893 gitc_projects = None
894
895 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700896 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700897 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
898 if manifest_name:
899 manifest.Override(manifest_name)
900 else:
901 manifest.Override(self.manifest.manifestFile)
902 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
903 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700904 gitc_projects)
905 print('GITC client successfully synced.')
906
907 # The opened projects need to be synced as normal, therefore we
908 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700909 # TODO: make this more reliable -- if there's a project name/path overlap,
910 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900911 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
912 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700913 if not args:
914 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800915 all_projects = self.GetProjects(args,
916 missing_ok=True,
917 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700918
Mike Frysinger5a033082019-09-23 19:21:20 -0400919 err_network_sync = False
920 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -0400921
Dave Borowitz67700e92012-10-23 15:00:54 -0700922 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700923 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700924 to_fetch = []
925 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700926 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700927 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900928 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700929 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700930
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500931 success, fetched = self._Fetch(to_fetch, opt, err_event)
932 if not success:
933 err_event.set()
Mike Frysinger5a033082019-09-23 19:21:20 -0400934
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500935 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700936 if opt.network_only:
937 # bail out now; the rest touches the working tree
Mike Frysingerbe24a542021-02-23 03:24:12 -0500938 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400939 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
940 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700941 return
942
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800943 # Iteratively fetch missing and/or nested unregistered submodules
944 previously_missing_set = set()
945 while True:
Raman Tennetifeb28912021-05-02 19:47:29 -0700946 self._ReloadManifest(manifest_name, load_local_manifests)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800947 all_projects = self.GetProjects(args,
948 missing_ok=True,
949 submodules_ok=opt.fetch_submodules)
950 missing = []
951 for project in all_projects:
952 if project.gitdir not in fetched:
953 missing.append(project)
954 if not missing:
955 break
956 # Stop us from non-stopped fetching actually-missing repos: If set of
957 # missing repos has not been changed from last fetch, we break.
958 missing_set = set(p.name for p in missing)
959 if previously_missing_set == missing_set:
960 break
961 previously_missing_set = missing_set
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500962 success, new_fetched = self._Fetch(to_fetch, opt, err_event)
963 if not success:
964 err_event.set()
965 fetched.update(new_fetched)
Mike Frysinger5a033082019-09-23 19:21:20 -0400966
967 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500968 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400969 err_network_sync = True
970 if opt.fail_fast:
971 print('\nerror: Exited sync due to fetch errors.\n'
972 'Local checkouts *not* updated. Resolve network issues & '
973 'retry.\n'
974 '`repo sync -l` will update some local checkouts.',
975 file=sys.stderr)
976 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800977
Julien Campergue335f5ef2013-10-16 11:02:35 +0200978 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700979 # bail out now, we have no working tree
980 return
981
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500982 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400983 err_event.set()
984 err_update_projects = True
985 if opt.fail_fast:
986 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
987 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700988
Mike Frysinger14208f42021-05-04 15:31:51 -0400989 err_update_linkfiles = not self.UpdateCopyLinkfileList()
990 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +0800991 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +0800992 if opt.fail_fast:
993 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
994 sys.exit(1)
995
Mike Frysinger5a033082019-09-23 19:21:20 -0400996 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -0500997 # NB: We don't exit here because this is the last step.
998 err_checkout = not self._Checkout(all_projects, opt, err_results)
999 if err_checkout:
1000 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001001
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001002 # If there's a notice that's supposed to print at the end of the sync, print
1003 # it now...
1004 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001005 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001006
Mike Frysinger5a033082019-09-23 19:21:20 -04001007 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001008 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001009 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1010 if err_network_sync:
1011 print('error: Downloading network changes failed.', file=sys.stderr)
1012 if err_update_projects:
1013 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001014 if err_update_linkfiles:
1015 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001016 if err_checkout:
1017 print('error: Checking out local projects failed.', file=sys.stderr)
1018 if err_results:
1019 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1020 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1021 file=sys.stderr)
1022 sys.exit(1)
1023
Mike Frysingere19d9e12020-02-12 11:23:32 -05001024 if not opt.quiet:
1025 print('repo sync has finished successfully.')
1026
David Pursehouse819827a2020-02-12 15:20:19 +09001027
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001028def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001029 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001030 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001031 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001032 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001033 if project.Exists:
1034 project.PostRepoUpgrade()
1035
David Pursehouse819827a2020-02-12 15:20:19 +09001036
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001037def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001038 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001039 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001040 wrapper = Wrapper()
1041 try:
1042 rev = rp.bare_git.describe(rp.GetRevisionId())
1043 except GitError:
1044 rev = None
1045 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1046 # See if we're held back due to missing signed tag.
1047 current_revid = rp.bare_git.rev_parse('HEAD')
1048 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1049 if current_revid != new_revid:
1050 # We want to switch to the new rev, but also not trash any uncommitted
1051 # changes. This helps with local testing/hacking.
1052 # If a local change has been made, we will throw that away.
1053 # We also have to make sure this will switch to an older commit if that's
1054 # the latest tag in order to support release rollback.
1055 try:
1056 rp.work_git.reset('--keep', new_rev)
1057 except GitError as e:
1058 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001059 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001060 raise RepoChangedException(['--repo-upgraded'])
1061 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001062 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001063 else:
1064 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001065 print('repo version %s is current' % rp.work_git.describe(HEAD),
1066 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001067
David Pursehouse819827a2020-02-12 15:20:19 +09001068
Dave Borowitz67700e92012-10-23 15:00:54 -07001069class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001070 _ALPHA = 0.5
1071
Dave Borowitz67700e92012-10-23 15:00:54 -07001072 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001073 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001074 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001075 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001076
1077 def Get(self, project):
1078 self._Load()
1079 return self._times.get(project.name, _ONE_DAY_S)
1080
1081 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001082 self._Load()
1083 name = project.name
1084 old = self._times.get(name, t)
1085 self._seen.add(name)
1086 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001087 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001088
1089 def _Load(self):
1090 if self._times is None:
1091 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001092 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001093 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001094 except (IOError, ValueError):
1095 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001096 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001097 except OSError:
1098 pass
1099 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001100
1101 def Save(self):
1102 if self._times is None:
1103 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001104
1105 to_delete = []
1106 for name in self._times:
1107 if name not in self._seen:
1108 to_delete.append(name)
1109 for name in to_delete:
1110 del self._times[name]
1111
Dave Borowitz67700e92012-10-23 15:00:54 -07001112 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001113 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001114 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001115 except (IOError, TypeError):
1116 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001117 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001118 except OSError:
1119 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001120
1121# This is a replacement for xmlrpc.client.Transport using urllib2
1122# and supporting persistent-http[s]. It cannot change hosts from
1123# request to request like the normal transport, the real url
1124# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001125
1126
Dan Willemsen0745bb22015-08-17 13:41:45 -07001127class PersistentTransport(xmlrpc.client.Transport):
1128 def __init__(self, orig_host):
1129 self.orig_host = orig_host
1130
1131 def request(self, host, handler, request_body, verbose=False):
1132 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1133 # Python doesn't understand cookies with the #HttpOnly_ prefix
1134 # Since we're only using them for HTTP, copy the file temporarily,
1135 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001136 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001137 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001138 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001139 try:
1140 with open(cookiefile) as f:
1141 for line in f:
1142 if line.startswith("#HttpOnly_"):
1143 line = line[len("#HttpOnly_"):]
1144 tmpcookiefile.write(line)
1145 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001146
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001147 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001148 try:
1149 cookiejar.load()
1150 except cookielib.LoadError:
1151 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001152 finally:
1153 tmpcookiefile.close()
1154 else:
1155 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001156
1157 proxyhandler = urllib.request.ProxyHandler
1158 if proxy:
1159 proxyhandler = urllib.request.ProxyHandler({
1160 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001161 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001162
1163 opener = urllib.request.build_opener(
1164 urllib.request.HTTPCookieProcessor(cookiejar),
1165 proxyhandler)
1166
1167 url = urllib.parse.urljoin(self.orig_host, handler)
1168 parse_results = urllib.parse.urlparse(url)
1169
1170 scheme = parse_results.scheme
1171 if scheme == 'persistent-http':
1172 scheme = 'http'
1173 if scheme == 'persistent-https':
1174 # If we're proxying through persistent-https, use http. The
1175 # proxy itself will do the https.
1176 if proxy:
1177 scheme = 'http'
1178 else:
1179 scheme = 'https'
1180
1181 # Parse out any authentication information using the base class
1182 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1183
1184 url = urllib.parse.urlunparse((
1185 scheme,
1186 host,
1187 parse_results.path,
1188 parse_results.params,
1189 parse_results.query,
1190 parse_results.fragment))
1191
1192 request = urllib.request.Request(url, request_body)
1193 if extra_headers is not None:
1194 for (name, header) in extra_headers:
1195 request.add_header(name, header)
1196 request.add_header('Content-Type', 'text/xml')
1197 try:
1198 response = opener.open(request)
1199 except urllib.error.HTTPError as e:
1200 if e.code == 501:
1201 # We may have been redirected through a login process
1202 # but our POST turned into a GET. Retry.
1203 response = opener.open(request)
1204 else:
1205 raise
1206
1207 p, u = xmlrpc.client.getparser()
1208 while 1:
1209 data = response.read(1024)
1210 if not data:
1211 break
1212 p.feed(data)
1213 p.close()
1214 return u.close()
1215
1216 def close(self):
1217 pass