blob: f06c61c56c1ce1b1d63e0d4acd24ae4221d8d470 [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):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000172 try:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500173 self.PARALLEL_JOBS = self.manifest.default.sync_j
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000174 except ManifestParseError:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500175 pass
Mike Frysinger9180a072021-04-13 14:57:40 -0400176 super()._CommonOptions(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700177
Mike Frysinger9180a072021-04-13 14:57:40 -0400178 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400179 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
180 help='number of network jobs to run in parallel (defaults to --jobs)')
181 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
182 help='number of local checkout jobs to run in parallel (defaults to --jobs)')
183
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500184 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200185 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400186 help='obsolete option (to be deleted in the future)')
187 p.add_option('--fail-fast',
188 dest='fail_fast', action='store_true',
189 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700190 p.add_option('--force-sync',
191 dest='force_sync', action='store_true',
192 help="overwrite an existing git directory if it needs to "
193 "point to a different object directory. WARNING: this "
194 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500195 p.add_option('--force-remove-dirty',
196 dest='force_remove_dirty', action='store_true',
197 help="force remove projects with uncommitted modifications if "
198 "projects no longer exist in the manifest. "
199 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900200 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700201 dest='local_only', action='store_true',
202 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900203 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100204 dest='mp_update', action='store_false', default='true',
205 help='use the existing manifest checkout as-is. '
206 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900207 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700208 dest='network_only', action='store_true',
209 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900210 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700211 dest='detach_head', action='store_true',
212 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900213 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700214 dest='current_branch_only', action='store_true',
215 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400216 p.add_option('--no-current-branch',
217 dest='current_branch_only', action='store_false',
218 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500219 p.add_option('-m', '--manifest-name',
220 dest='manifest_name',
221 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700222 p.add_option('--clone-bundle', action='store_true',
223 help='enable use of /clone.bundle on HTTP/HTTPS')
224 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700225 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800226 p.add_option('-u', '--manifest-server-username', action='store',
227 dest='manifest_server_username',
228 help='username to authenticate with the manifest server')
229 p.add_option('-p', '--manifest-server-password', action='store',
230 dest='manifest_server_password',
231 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800232 p.add_option('--fetch-submodules',
233 dest='fetch_submodules', action='store_true',
234 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800235 p.add_option('--use-superproject', action='store_true',
236 help='use the manifest superproject to sync projects')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700237 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500238 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700239 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900240 p.add_option('--optimized-fetch',
241 dest='optimized_fetch', action='store_true',
242 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600243 p.add_option('--retry-fetches',
244 default=0, action='store', type='int',
245 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900246 p.add_option('--prune', dest='prune', action='store_true',
247 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700248 if show_smart:
249 p.add_option('-s', '--smart-sync',
250 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900251 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200252 p.add_option('-t', '--smart-tag',
253 dest='smart_tag', action='store',
254 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700255
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700256 g = p.add_option_group('repo Version options')
257 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500258 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700259 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700260 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800261 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700262 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700263
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800264 def _GetBranch(self):
265 """Returns the branch name for getting the approved manifest."""
266 p = self.manifest.manifestProject
267 b = p.GetBranch(p.CurrentBranch)
268 branch = b.merge
269 if branch.startswith(R_HEADS):
270 branch = branch[len(R_HEADS):]
271 return branch
272
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700273 def _UseSuperproject(self, opt):
274 """Returns True if use-superproject option is enabled"""
275 return (opt.use_superproject or
276 self.manifest.manifestProject.config.GetBoolean(
277 'repo.superproject'))
278
279 def _GetCurrentBranchOnly(self, opt):
280 """Returns True if current-branch or use-superproject options are enabled."""
281 return opt.current_branch_only or self._UseSuperproject(opt)
282
Raman Tennetifeb28912021-05-02 19:47:29 -0700283 def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800284 """Update revisionId of every project with the SHA from superproject.
285
286 This function updates each project's revisionId with SHA from superproject.
287 It writes the updated manifest into a file and reloads the manifest from it.
288
289 Args:
290 opt: Program options returned from optparse. See _Options().
291 args: Arguments to pass to GetProjects. See the GetProjects
292 docstring for details.
Raman Tennetifeb28912021-05-02 19:47:29 -0700293 load_local_manifests: Whether to load local manifests.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800294
295 Returns:
296 Returns path to the overriding manifest file.
297 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800298 superproject = git_superproject.Superproject(self.manifest,
Raman Tennetief99ec02021-03-04 10:29:40 -0800299 self.repodir,
300 quiet=opt.quiet)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800301 all_projects = self.GetProjects(args,
302 missing_ok=True,
303 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800304 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800305 if not manifest_path:
306 print('error: Update of revsionId from superproject has failed',
307 file=sys.stderr)
308 sys.exit(1)
Raman Tennetifeb28912021-05-02 19:47:29 -0700309 self._ReloadManifest(manifest_path, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800310 return manifest_path
311
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500312 def _FetchProjectList(self, opt, projects):
313 """Main function of the fetch worker.
314
315 The projects we're given share the same underlying git object store, so we
316 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800317
David James8d201162013-10-11 17:03:19 -0700318 Delegates most of the work to _FetchHelper.
319
320 Args:
321 opt: Program options returned from optparse. See _Options().
322 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700323 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500324 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700325
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500326 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700327 """Fetch git objects for a single project.
328
David Pursehousec1b86a22012-11-14 11:36:51 +0900329 Args:
330 opt: Program options returned from optparse. See _Options().
331 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700332
333 Returns:
334 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900335 """
David Rileye0684ad2017-04-05 00:02:59 -0700336 start = time.time()
337 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500338 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900339 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500340 success = project.Sync_NetworkHalf(
341 quiet=opt.quiet,
342 verbose=opt.verbose,
343 output_redir=buf,
344 current_branch_only=self._GetCurrentBranchOnly(opt),
345 force_sync=opt.force_sync,
346 clone_bundle=opt.clone_bundle,
347 tags=opt.tags, archive=self.manifest.IsArchive,
348 optimized_fetch=opt.optimized_fetch,
349 retry_fetches=opt.retry_fetches,
350 prune=opt.prune,
Raman Tennetif32f2432021-04-12 20:57:25 -0700351 clone_filter=self.manifest.CloneFilter,
352 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700353
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500354 output = buf.getvalue()
355 if opt.verbose and output:
356 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700357
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500358 if not success:
359 print('error: Cannot fetch %s from %s'
360 % (project.name, project.remote.url),
361 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700362 except GitError as e:
363 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500364 except Exception as e:
365 print('error: Cannot fetch %s (%s: %s)'
366 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
367 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500368
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500369 finish = time.time()
370 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700371
Mike Frysinger5a033082019-09-23 19:21:20 -0400372 def _Fetch(self, projects, opt, err_event):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500373 ret = True
374
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400375 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700376 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400377 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800378
David James89ece422014-01-09 18:51:58 -0800379 objdir_project_map = dict()
380 for project in projects:
381 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500382 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700383
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500384 def _ProcessResults(results_sets):
385 ret = True
386 for results in results_sets:
387 for (success, project, start, finish) in results:
388 self._fetch_times.Set(project, finish - start)
389 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
390 start, finish, success)
391 # Check for any errors before running any more tasks.
392 # ...we'll let existing jobs finish, though.
393 if not success:
394 ret = False
395 else:
396 fetched.add(project.gitdir)
397 pm.update(msg=project.name)
398 if not ret and opt.fail_fast:
399 break
400 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700401
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500402 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400403 if len(projects_list) == 1 or jobs == 1:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500404 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
405 ret = False
406 else:
407 # Favor throughput over responsiveness when quiet. It seems that imap()
408 # will yield results in batches relative to chunksize, so even as the
409 # children finish a sync, we won't see the result until one child finishes
410 # ~chunksize jobs. When using a large --jobs with large chunksize, this
411 # can be jarring as there will be a large initial delay where repo looks
412 # like it isn't doing anything and sits at 0%, but then suddenly completes
413 # a lot of jobs all at once. Since this code is more network bound, we
414 # can accept a bit more CPU overhead with a smaller chunksize so that the
415 # user sees more immediate & continuous feedback.
416 if opt.quiet:
417 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800418 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500419 pm.update(inc=0, msg='warming up')
420 chunksize = 4
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400421 with multiprocessing.Pool(jobs) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500422 results = pool.imap_unordered(
423 functools.partial(self._FetchProjectList, opt),
424 projects_list,
425 chunksize=chunksize)
426 if not _ProcessResults(results):
427 ret = False
428 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800429
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700430 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700431 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700432
Julien Campergue335f5ef2013-10-16 11:02:35 +0200433 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400434 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200435
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500436 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700437
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500438 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700439 """Checkout work tree for one project
440
441 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500442 detach_head: Whether to leave a detached HEAD.
443 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700444 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700445
446 Returns:
447 Whether the fetch was successful.
448 """
Xin Li745be2e2019-06-03 11:24:30 -0700449 start = time.time()
450 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500451 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700452 success = False
453 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500454 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500455 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700456 except GitError as e:
457 print('error.GitError: Cannot checkout %s: %s' %
458 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500459 except Exception as e:
460 print('error: Cannot checkout %s: %s: %s' %
461 (project.name, type(e).__name__, str(e)),
462 file=sys.stderr)
463 raise
Xin Li745be2e2019-06-03 11:24:30 -0700464
Mike Frysingerebf04a42021-02-23 20:48:04 -0500465 if not success:
466 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
467 finish = time.time()
468 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700469
Mike Frysingerebf04a42021-02-23 20:48:04 -0500470 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700471 """Checkout projects listed in all_projects
472
473 Args:
474 all_projects: List of all projects that should be checked out.
475 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500476 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700477 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500478 # Only checkout projects with worktrees.
479 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700480
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500481 def _ProcessResults(pool, pm, results):
482 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500483 for (success, project, start, finish) in results:
484 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
485 start, finish, success)
486 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500487 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500488 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500489 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500490 err_results.append(project.relpath)
491 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500492 if pool:
493 pool.close()
494 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500495 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500496 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700497
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500498 return self.ExecuteInParallel(
499 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
500 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
501 all_projects,
502 callback=_ProcessResults,
503 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500504
Mike Frysinger5a033082019-09-23 19:21:20 -0400505 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400506 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400507 pm.update(inc=0, msg='prescan')
508
Gabe Black2ff30292014-10-09 17:54:35 -0700509 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700510 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500511 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500512 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900513 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100514 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400515 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100516 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500517 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500518 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500519 else:
520 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400521 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500522 'versions of git; please upgrade to git-2.7.0+.'
523 % (project.relpath,),
524 file=sys.stderr)
525 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700526 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700527
Mike Frysinger65af2602021-04-08 22:47:44 -0400528 pm.update(inc=len(projects) - len(gc_gitdirs), msg='warming up')
529
530 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700531 jobs = min(self.jobs, cpu_count)
532
533 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700534 for bare_git in gc_gitdirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400535 pm.update(msg=bare_git._project.name)
David James8d201162013-10-11 17:03:19 -0700536 bare_git.gc('--auto')
Mike Frysinger65af2602021-04-08 22:47:44 -0400537 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700538 return
539
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400540 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700541
542 threads = set()
543 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700544
David James8d201162013-10-11 17:03:19 -0700545 def GC(bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400546 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700547 try:
548 try:
David James8d201162013-10-11 17:03:19 -0700549 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700550 except GitError:
551 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900552 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700553 err_event.set()
554 raise
555 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400556 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700557 sem.release()
558
Gabe Black2ff30292014-10-09 17:54:35 -0700559 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500560 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700561 break
562 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700563 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700564 t.daemon = True
565 threads.add(t)
566 t.start()
567
568 for t in threads:
569 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400570 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700571
Raman Tennetifeb28912021-05-02 19:47:29 -0700572 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
573 """Reload the manfiest from the file specified by the |manifest_name|.
574
575 It unloads the manifest if |manifest_name| is None.
576
577 Args:
578 manifest_name: Manifest file to be reloaded.
579 load_local_manifests: Whether to load local manifests.
580 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800581 if manifest_name:
582 # Override calls _Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700583 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800584 else:
585 self.manifest._Unload()
586
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500587 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700588 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700589 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700590 if project.relpath:
591 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700592 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500593 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700594 old_project_paths = []
595
596 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500597 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700598 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800599 # In reversed order, so subfolders are deleted before parent folder.
600 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700601 if not path:
602 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700603 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900604 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700605 gitdir = os.path.join(self.manifest.topdir, path, '.git')
606 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900607 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900608 manifest=self.manifest,
609 name=path,
610 remote=RemoteSpec('origin'),
611 gitdir=gitdir,
612 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500613 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900614 worktree=os.path.join(self.manifest.topdir, path),
615 relpath=path,
616 revisionExpr='HEAD',
617 revisionId=None,
618 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500619 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900620 quiet=opt.quiet,
621 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400622 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700623
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700624 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500625 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700626 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700627 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700628 return 0
629
jiajia tanga590e642021-04-25 20:02:02 +0800630 def UpdateCopyLinkfileList(self):
631 """Save all dests of copyfile and linkfile, and update them if needed.
632
633 Returns:
634 Whether update was successful.
635 """
636 new_paths = {}
637 new_linkfile_paths = []
638 new_copyfile_paths = []
639 for project in self.GetProjects(None, missing_ok=True):
640 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
641 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
642
643 new_paths = {
644 'linkfile': new_linkfile_paths,
645 'copyfile': new_copyfile_paths,
646 }
647
648 copylinkfile_name = 'copy-link-files.json'
649 copylinkfile_path = os.path.join(self.manifest.repodir, copylinkfile_name)
650 old_copylinkfile_paths = {}
651
652 if os.path.exists(copylinkfile_path):
653 with open(copylinkfile_path, 'rb') as fp:
654 try:
655 old_copylinkfile_paths = json.load(fp)
656 except:
657 print('error: %s is not a json formatted file.' %
658 copylinkfile_path, file=sys.stderr)
659 platform_utils.remove(copylinkfile_path)
660 return False
661
662 need_remove_files = []
663 need_remove_files.extend(
664 set(old_copylinkfile_paths.get('linkfile', [])) -
665 set(new_linkfile_paths))
666 need_remove_files.extend(
667 set(old_copylinkfile_paths.get('copyfile', [])) -
668 set(new_copyfile_paths))
669
670 for need_remove_file in need_remove_files:
671 try:
672 platform_utils.remove(need_remove_file)
673 except OSError as e:
674 if e.errno == errno.ENOENT:
675 # Try to remove the updated copyfile or linkfile.
676 # So, if the file is not exist, nothing need to do.
677 pass
678
679 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
680 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
681 json.dump(new_paths, fp)
682 return True
683
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400684 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
685 if not self.manifest.manifest_server:
686 print('error: cannot smart sync: no manifest server defined in '
687 'manifest', file=sys.stderr)
688 sys.exit(1)
689
690 manifest_server = self.manifest.manifest_server
691 if not opt.quiet:
692 print('Using manifest server %s' % manifest_server)
693
David Pursehouseeeff3532020-02-12 11:24:10 +0900694 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400695 username = None
696 password = None
697 if opt.manifest_server_username and opt.manifest_server_password:
698 username = opt.manifest_server_username
699 password = opt.manifest_server_password
700 else:
701 try:
702 info = netrc.netrc()
703 except IOError:
704 # .netrc file does not exist or could not be opened
705 pass
706 else:
707 try:
708 parse_result = urllib.parse.urlparse(manifest_server)
709 if parse_result.hostname:
710 auth = info.authenticators(parse_result.hostname)
711 if auth:
712 username, _account, password = auth
713 else:
714 print('No credentials found for %s in .netrc'
715 % parse_result.hostname, file=sys.stderr)
716 except netrc.NetrcParseError as e:
717 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
718
719 if (username and password):
720 manifest_server = manifest_server.replace('://', '://%s:%s@' %
721 (username, password),
722 1)
723
724 transport = PersistentTransport(manifest_server)
725 if manifest_server.startswith('persistent-'):
726 manifest_server = manifest_server[len('persistent-'):]
727
728 try:
729 server = xmlrpc.client.Server(manifest_server, transport=transport)
730 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800731 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400732
Mike Frysinger56ce3462019-12-04 19:30:48 -0500733 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500734 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400735 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500736 elif ('TARGET_PRODUCT' in os.environ and
737 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500738 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
739 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400740 [success, manifest_str] = server.GetApprovedManifest(branch, target)
741 else:
742 [success, manifest_str] = server.GetApprovedManifest(branch)
743 else:
744 assert(opt.smart_tag)
745 [success, manifest_str] = server.GetManifest(opt.smart_tag)
746
747 if success:
748 manifest_name = os.path.basename(smart_sync_manifest_path)
749 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500750 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400751 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400752 except IOError as e:
753 print('error: cannot write manifest to %s:\n%s'
754 % (smart_sync_manifest_path, e),
755 file=sys.stderr)
756 sys.exit(1)
757 self._ReloadManifest(manifest_name)
758 else:
759 print('error: manifest server RPC call failed: %s' %
760 manifest_str, file=sys.stderr)
761 sys.exit(1)
762 except (socket.error, IOError, xmlrpc.client.Fault) as e:
763 print('error: cannot connect to manifest server %s:\n%s'
764 % (self.manifest.manifest_server, e), file=sys.stderr)
765 sys.exit(1)
766 except xmlrpc.client.ProtocolError as e:
767 print('error: cannot connect to manifest server %s:\n%d %s'
768 % (self.manifest.manifest_server, e.errcode, e.errmsg),
769 file=sys.stderr)
770 sys.exit(1)
771
772 return manifest_name
773
Mike Frysingerfb527e32019-08-27 02:34:32 -0400774 def _UpdateManifestProject(self, opt, mp, manifest_name):
775 """Fetch & update the local manifest project."""
776 if not opt.local_only:
777 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500778 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700779 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200780 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500781 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400782 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600783 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400784 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700785 clone_filter=self.manifest.CloneFilter,
786 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400787 finish = time.time()
788 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
789 start, finish, success)
790
791 if mp.HasChanges:
792 syncbuf = SyncBuffer(mp.config)
793 start = time.time()
794 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
795 clean = syncbuf.Finish()
796 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
797 start, time.time(), clean)
798 if not clean:
799 sys.exit(1)
800 self._ReloadManifest(opt.manifest_name)
801 if opt.jobs is None:
802 self.jobs = self.manifest.default.sync_j
803
Mike Frysingerae6cb082019-08-27 01:10:59 -0400804 def ValidateOptions(self, opt, args):
805 if opt.force_broken:
806 print('warning: -f/--force-broken is now the default behavior, and the '
807 'options are deprecated', file=sys.stderr)
808 if opt.network_only and opt.detach_head:
809 self.OptionParser.error('cannot combine -n and -d')
810 if opt.network_only and opt.local_only:
811 self.OptionParser.error('cannot combine -n and -l')
812 if opt.manifest_name and opt.smart_sync:
813 self.OptionParser.error('cannot combine -m and -s')
814 if opt.manifest_name and opt.smart_tag:
815 self.OptionParser.error('cannot combine -m and -t')
816 if opt.manifest_server_username or opt.manifest_server_password:
817 if not (opt.smart_sync or opt.smart_tag):
818 self.OptionParser.error('-u and -p may only be combined with -s or -t')
819 if None in [opt.manifest_server_username, opt.manifest_server_password]:
820 self.OptionParser.error('both -u and -p must be given')
821
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700822 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800823 if opt.jobs:
824 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700825 if self.jobs > 1:
826 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400827 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700828
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500829 if opt.manifest_name:
830 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700831
Chirayu Desaia892b102013-06-11 14:18:46 +0530832 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900833 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900834 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530835
Xin Lid79a4bc2020-05-20 16:03:45 -0700836 if opt.clone_bundle is None:
837 opt.clone_bundle = self.manifest.CloneBundle
838
Victor Boivie08c880d2011-04-19 10:32:52 +0200839 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400840 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
841 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900842 if os.path.isfile(smart_sync_manifest_path):
843 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800844 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900845 except OSError as e:
846 print('error: failed to remove existing smart sync override manifest: %s' %
847 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700848
Mike Frysinger5a033082019-09-23 19:21:20 -0400849 err_event = _threading.Event()
850
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700851 rp = self.manifest.repoProject
852 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500853 cb = rp.CurrentBranch
854 if cb:
855 base = rp.GetBranch(cb).merge
856 if not base or not base.startswith('refs/heads/'):
857 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400858 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500859 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700860
861 mp = self.manifest.manifestProject
862 mp.PreSync()
863
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800864 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700865 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800866
Fredrik de Grootcc960972019-11-22 09:04:31 +0100867 if not opt.mp_update:
868 print('Skipping update of local manifest project.')
869 else:
870 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700871
Raman Tennetifeb28912021-05-02 19:47:29 -0700872 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700873 if self._UseSuperproject(opt):
Raman Tennetifeb28912021-05-02 19:47:29 -0700874 manifest_name = self._UpdateProjectsRevisionId(opt, args, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800875
Simran Basib9a1b732015-08-20 12:19:28 -0700876 if self.gitc_manifest:
877 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700878 missing_ok=True)
879 gitc_projects = []
880 opened_projects = []
881 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700882 if project.relpath in self.gitc_manifest.paths and \
883 self.gitc_manifest.paths[project.relpath].old_revision:
884 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700885 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700886 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700887
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700888 if not args:
889 gitc_projects = None
890
891 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700892 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700893 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
894 if manifest_name:
895 manifest.Override(manifest_name)
896 else:
897 manifest.Override(self.manifest.manifestFile)
898 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
899 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700900 gitc_projects)
901 print('GITC client successfully synced.')
902
903 # The opened projects need to be synced as normal, therefore we
904 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700905 # TODO: make this more reliable -- if there's a project name/path overlap,
906 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900907 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
908 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700909 if not args:
910 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800911 all_projects = self.GetProjects(args,
912 missing_ok=True,
913 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700914
Mike Frysinger5a033082019-09-23 19:21:20 -0400915 err_network_sync = False
916 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -0400917
Dave Borowitz67700e92012-10-23 15:00:54 -0700918 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700919 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700920 to_fetch = []
921 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700922 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700923 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900924 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700925 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700926
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500927 success, fetched = self._Fetch(to_fetch, opt, err_event)
928 if not success:
929 err_event.set()
Mike Frysinger5a033082019-09-23 19:21:20 -0400930
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500931 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700932 if opt.network_only:
933 # bail out now; the rest touches the working tree
Mike Frysingerbe24a542021-02-23 03:24:12 -0500934 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400935 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
936 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700937 return
938
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800939 # Iteratively fetch missing and/or nested unregistered submodules
940 previously_missing_set = set()
941 while True:
Raman Tennetifeb28912021-05-02 19:47:29 -0700942 self._ReloadManifest(manifest_name, load_local_manifests)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800943 all_projects = self.GetProjects(args,
944 missing_ok=True,
945 submodules_ok=opt.fetch_submodules)
946 missing = []
947 for project in all_projects:
948 if project.gitdir not in fetched:
949 missing.append(project)
950 if not missing:
951 break
952 # Stop us from non-stopped fetching actually-missing repos: If set of
953 # missing repos has not been changed from last fetch, we break.
954 missing_set = set(p.name for p in missing)
955 if previously_missing_set == missing_set:
956 break
957 previously_missing_set = missing_set
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500958 success, new_fetched = self._Fetch(to_fetch, opt, err_event)
959 if not success:
960 err_event.set()
961 fetched.update(new_fetched)
Mike Frysinger5a033082019-09-23 19:21:20 -0400962
963 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500964 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400965 err_network_sync = True
966 if opt.fail_fast:
967 print('\nerror: Exited sync due to fetch errors.\n'
968 'Local checkouts *not* updated. Resolve network issues & '
969 'retry.\n'
970 '`repo sync -l` will update some local checkouts.',
971 file=sys.stderr)
972 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800973
Julien Campergue335f5ef2013-10-16 11:02:35 +0200974 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700975 # bail out now, we have no working tree
976 return
977
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500978 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400979 err_event.set()
980 err_update_projects = True
981 if opt.fail_fast:
982 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
983 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700984
jiajia tanga590e642021-04-25 20:02:02 +0800985 if not self.UpdateCopyLinkfileList():
986 err_event.set()
987 err_update_linkfiles = True
988 if opt.fail_fast:
989 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
990 sys.exit(1)
991
Mike Frysinger5a033082019-09-23 19:21:20 -0400992 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -0500993 # NB: We don't exit here because this is the last step.
994 err_checkout = not self._Checkout(all_projects, opt, err_results)
995 if err_checkout:
996 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700997
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700998 # If there's a notice that's supposed to print at the end of the sync, print
999 # it now...
1000 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001001 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001002
Mike Frysinger5a033082019-09-23 19:21:20 -04001003 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001004 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001005 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1006 if err_network_sync:
1007 print('error: Downloading network changes failed.', file=sys.stderr)
1008 if err_update_projects:
1009 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001010 if err_update_linkfiles:
1011 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001012 if err_checkout:
1013 print('error: Checking out local projects failed.', file=sys.stderr)
1014 if err_results:
1015 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1016 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1017 file=sys.stderr)
1018 sys.exit(1)
1019
Mike Frysingere19d9e12020-02-12 11:23:32 -05001020 if not opt.quiet:
1021 print('repo sync has finished successfully.')
1022
David Pursehouse819827a2020-02-12 15:20:19 +09001023
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001024def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001025 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001026 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001027 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001028 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001029 if project.Exists:
1030 project.PostRepoUpgrade()
1031
David Pursehouse819827a2020-02-12 15:20:19 +09001032
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001033def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001034 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001035 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001036 wrapper = Wrapper()
1037 try:
1038 rev = rp.bare_git.describe(rp.GetRevisionId())
1039 except GitError:
1040 rev = None
1041 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1042 # See if we're held back due to missing signed tag.
1043 current_revid = rp.bare_git.rev_parse('HEAD')
1044 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1045 if current_revid != new_revid:
1046 # We want to switch to the new rev, but also not trash any uncommitted
1047 # changes. This helps with local testing/hacking.
1048 # If a local change has been made, we will throw that away.
1049 # We also have to make sure this will switch to an older commit if that's
1050 # the latest tag in order to support release rollback.
1051 try:
1052 rp.work_git.reset('--keep', new_rev)
1053 except GitError as e:
1054 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001055 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001056 raise RepoChangedException(['--repo-upgraded'])
1057 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001058 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001059 else:
1060 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001061 print('repo version %s is current' % rp.work_git.describe(HEAD),
1062 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001063
David Pursehouse819827a2020-02-12 15:20:19 +09001064
Dave Borowitz67700e92012-10-23 15:00:54 -07001065class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001066 _ALPHA = 0.5
1067
Dave Borowitz67700e92012-10-23 15:00:54 -07001068 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001069 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001070 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001071 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001072
1073 def Get(self, project):
1074 self._Load()
1075 return self._times.get(project.name, _ONE_DAY_S)
1076
1077 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001078 self._Load()
1079 name = project.name
1080 old = self._times.get(name, t)
1081 self._seen.add(name)
1082 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001083 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001084
1085 def _Load(self):
1086 if self._times is None:
1087 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001088 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001089 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001090 except (IOError, ValueError):
1091 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001092 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001093 except OSError:
1094 pass
1095 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001096
1097 def Save(self):
1098 if self._times is None:
1099 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001100
1101 to_delete = []
1102 for name in self._times:
1103 if name not in self._seen:
1104 to_delete.append(name)
1105 for name in to_delete:
1106 del self._times[name]
1107
Dave Borowitz67700e92012-10-23 15:00:54 -07001108 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001109 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001110 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001111 except (IOError, TypeError):
1112 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001113 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001114 except OSError:
1115 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001116
1117# This is a replacement for xmlrpc.client.Transport using urllib2
1118# and supporting persistent-http[s]. It cannot change hosts from
1119# request to request like the normal transport, the real url
1120# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001121
1122
Dan Willemsen0745bb22015-08-17 13:41:45 -07001123class PersistentTransport(xmlrpc.client.Transport):
1124 def __init__(self, orig_host):
1125 self.orig_host = orig_host
1126
1127 def request(self, host, handler, request_body, verbose=False):
1128 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1129 # Python doesn't understand cookies with the #HttpOnly_ prefix
1130 # Since we're only using them for HTTP, copy the file temporarily,
1131 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001132 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001133 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001134 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001135 try:
1136 with open(cookiefile) as f:
1137 for line in f:
1138 if line.startswith("#HttpOnly_"):
1139 line = line[len("#HttpOnly_"):]
1140 tmpcookiefile.write(line)
1141 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001142
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001143 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001144 try:
1145 cookiejar.load()
1146 except cookielib.LoadError:
1147 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001148 finally:
1149 tmpcookiefile.close()
1150 else:
1151 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001152
1153 proxyhandler = urllib.request.ProxyHandler
1154 if proxy:
1155 proxyhandler = urllib.request.ProxyHandler({
1156 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001157 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001158
1159 opener = urllib.request.build_opener(
1160 urllib.request.HTTPCookieProcessor(cookiejar),
1161 proxyhandler)
1162
1163 url = urllib.parse.urljoin(self.orig_host, handler)
1164 parse_results = urllib.parse.urlparse(url)
1165
1166 scheme = parse_results.scheme
1167 if scheme == 'persistent-http':
1168 scheme = 'http'
1169 if scheme == 'persistent-https':
1170 # If we're proxying through persistent-https, use http. The
1171 # proxy itself will do the https.
1172 if proxy:
1173 scheme = 'http'
1174 else:
1175 scheme = 'https'
1176
1177 # Parse out any authentication information using the base class
1178 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1179
1180 url = urllib.parse.urlunparse((
1181 scheme,
1182 host,
1183 parse_results.path,
1184 parse_results.params,
1185 parse_results.query,
1186 parse_results.fragment))
1187
1188 request = urllib.request.Request(url, request_body)
1189 if extra_headers is not None:
1190 for (name, header) in extra_headers:
1191 request.add_header(name, header)
1192 request.add_header('Content-Type', 'text/xml')
1193 try:
1194 response = opener.open(request)
1195 except urllib.error.HTTPError as e:
1196 if e.code == 501:
1197 # We may have been redirected through a login process
1198 # but our POST turned into a GET. Retry.
1199 response = opener.open(request)
1200 else:
1201 raise
1202
1203 p, u = xmlrpc.client.getparser()
1204 while 1:
1205 data = response.read(1024)
1206 if not data:
1207 break
1208 p.feed(data)
1209 p.close()
1210 return u.close()
1211
1212 def close(self):
1213 pass