blob: 6f5b5644e74d85fa125dfb6df4bad8e359aedf91 [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 [...]
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')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700238 p.add_option('--no-use-superproject', action='store_false',
239 dest='use_superproject',
240 help='disable use of manifest superprojects')
Mike Frysingerd68ed632021-05-03 01:21:35 -0400241 p.add_option('--tags',
242 action='store_false',
243 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700244 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400245 dest='tags', action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700246 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900247 p.add_option('--optimized-fetch',
248 dest='optimized_fetch', action='store_true',
249 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600250 p.add_option('--retry-fetches',
251 default=0, action='store', type='int',
252 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900253 p.add_option('--prune', dest='prune', action='store_true',
254 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700255 if show_smart:
256 p.add_option('-s', '--smart-sync',
257 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900258 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200259 p.add_option('-t', '--smart-tag',
260 dest='smart_tag', action='store',
261 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700262
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700263 g = p.add_option_group('repo Version options')
264 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500265 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700266 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700267 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800268 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700269 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700270
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800271 def _GetBranch(self):
272 """Returns the branch name for getting the approved manifest."""
273 p = self.manifest.manifestProject
274 b = p.GetBranch(p.CurrentBranch)
275 branch = b.merge
276 if branch.startswith(R_HEADS):
277 branch = branch[len(R_HEADS):]
278 return branch
279
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700280 def _UseSuperproject(self, opt):
281 """Returns True if use-superproject option is enabled"""
Raman Tenneti23ea7542021-05-07 14:01:54 -0700282 if opt.use_superproject is not None:
283 return opt.use_superproject
284 else:
285 return self.manifest.manifestProject.config.GetBoolean('repo.superproject')
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700286
287 def _GetCurrentBranchOnly(self, opt):
288 """Returns True if current-branch or use-superproject options are enabled."""
289 return opt.current_branch_only or self._UseSuperproject(opt)
290
Raman Tennetifeb28912021-05-02 19:47:29 -0700291 def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800292 """Update revisionId of every project with the SHA from superproject.
293
294 This function updates each project's revisionId with SHA from superproject.
295 It writes the updated manifest into a file and reloads the manifest from it.
296
297 Args:
298 opt: Program options returned from optparse. See _Options().
299 args: Arguments to pass to GetProjects. See the GetProjects
300 docstring for details.
Raman Tennetifeb28912021-05-02 19:47:29 -0700301 load_local_manifests: Whether to load local manifests.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800302
303 Returns:
304 Returns path to the overriding manifest file.
305 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800306 superproject = git_superproject.Superproject(self.manifest,
Raman Tennetief99ec02021-03-04 10:29:40 -0800307 self.repodir,
308 quiet=opt.quiet)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800309 all_projects = self.GetProjects(args,
310 missing_ok=True,
311 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800312 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800313 if not manifest_path:
Raman Tennetif907ced2021-05-07 10:22:16 -0700314 print('error: Update of revsionId from superproject has failed. '
315 'Please resync with --no-use-superproject option',
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800316 file=sys.stderr)
317 sys.exit(1)
Raman Tennetifeb28912021-05-02 19:47:29 -0700318 self._ReloadManifest(manifest_path, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800319 return manifest_path
320
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500321 def _FetchProjectList(self, opt, projects):
322 """Main function of the fetch worker.
323
324 The projects we're given share the same underlying git object store, so we
325 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800326
David James8d201162013-10-11 17:03:19 -0700327 Delegates most of the work to _FetchHelper.
328
329 Args:
330 opt: Program options returned from optparse. See _Options().
331 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700332 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500333 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700334
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500335 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700336 """Fetch git objects for a single project.
337
David Pursehousec1b86a22012-11-14 11:36:51 +0900338 Args:
339 opt: Program options returned from optparse. See _Options().
340 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700341
342 Returns:
343 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900344 """
David Rileye0684ad2017-04-05 00:02:59 -0700345 start = time.time()
346 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500347 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900348 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500349 success = project.Sync_NetworkHalf(
350 quiet=opt.quiet,
351 verbose=opt.verbose,
352 output_redir=buf,
353 current_branch_only=self._GetCurrentBranchOnly(opt),
354 force_sync=opt.force_sync,
355 clone_bundle=opt.clone_bundle,
356 tags=opt.tags, archive=self.manifest.IsArchive,
357 optimized_fetch=opt.optimized_fetch,
358 retry_fetches=opt.retry_fetches,
359 prune=opt.prune,
Raman Tennetif32f2432021-04-12 20:57:25 -0700360 clone_filter=self.manifest.CloneFilter,
361 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700362
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500363 output = buf.getvalue()
364 if opt.verbose and output:
365 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700366
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500367 if not success:
368 print('error: Cannot fetch %s from %s'
369 % (project.name, project.remote.url),
370 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700371 except GitError as e:
372 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500373 except Exception as e:
374 print('error: Cannot fetch %s (%s: %s)'
375 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
376 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500377
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500378 finish = time.time()
379 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700380
Mike Frysinger5a033082019-09-23 19:21:20 -0400381 def _Fetch(self, projects, opt, err_event):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500382 ret = True
383
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400384 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700385 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400386 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800387
David James89ece422014-01-09 18:51:58 -0800388 objdir_project_map = dict()
389 for project in projects:
390 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500391 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700392
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500393 def _ProcessResults(results_sets):
394 ret = True
395 for results in results_sets:
396 for (success, project, start, finish) in results:
397 self._fetch_times.Set(project, finish - start)
398 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
399 start, finish, success)
400 # Check for any errors before running any more tasks.
401 # ...we'll let existing jobs finish, though.
402 if not success:
403 ret = False
404 else:
405 fetched.add(project.gitdir)
406 pm.update(msg=project.name)
407 if not ret and opt.fail_fast:
408 break
409 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700410
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500411 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400412 if len(projects_list) == 1 or jobs == 1:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500413 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
414 ret = False
415 else:
416 # Favor throughput over responsiveness when quiet. It seems that imap()
417 # will yield results in batches relative to chunksize, so even as the
418 # children finish a sync, we won't see the result until one child finishes
419 # ~chunksize jobs. When using a large --jobs with large chunksize, this
420 # can be jarring as there will be a large initial delay where repo looks
421 # like it isn't doing anything and sits at 0%, but then suddenly completes
422 # a lot of jobs all at once. Since this code is more network bound, we
423 # can accept a bit more CPU overhead with a smaller chunksize so that the
424 # user sees more immediate & continuous feedback.
425 if opt.quiet:
426 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800427 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500428 pm.update(inc=0, msg='warming up')
429 chunksize = 4
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400430 with multiprocessing.Pool(jobs) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500431 results = pool.imap_unordered(
432 functools.partial(self._FetchProjectList, opt),
433 projects_list,
434 chunksize=chunksize)
435 if not _ProcessResults(results):
436 ret = False
437 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800438
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700439 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700440 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700441
Julien Campergue335f5ef2013-10-16 11:02:35 +0200442 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400443 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200444
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500445 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700446
Mike Frysingerb4429432021-05-05 20:03:26 -0400447 def _FetchMain(self, opt, args, all_projects, err_event, manifest_name,
448 load_local_manifests):
449 """The main network fetch loop.
450
451 Args:
452 opt: Program options returned from optparse. See _Options().
453 args: Command line args used to filter out projects.
454 all_projects: List of all projects that should be checked out.
455 err_event: Whether an error was hit while processing.
456 manifest_name: Manifest file to be reloaded.
457 load_local_manifests: Whether to load local manifests.
458 """
459 rp = self.manifest.repoProject
460
461 to_fetch = []
462 now = time.time()
463 if _ONE_DAY_S <= (now - rp.LastFetch):
464 to_fetch.append(rp)
465 to_fetch.extend(all_projects)
466 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
467
468 success, fetched = self._Fetch(to_fetch, opt, err_event)
469 if not success:
470 err_event.set()
471
472 _PostRepoFetch(rp, opt.repo_verify)
473 if opt.network_only:
474 # bail out now; the rest touches the working tree
475 if err_event.is_set():
476 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
477 sys.exit(1)
478 return
479
480 # Iteratively fetch missing and/or nested unregistered submodules
481 previously_missing_set = set()
482 while True:
483 self._ReloadManifest(manifest_name, load_local_manifests)
484 all_projects = self.GetProjects(args,
485 missing_ok=True,
486 submodules_ok=opt.fetch_submodules)
487 missing = []
488 for project in all_projects:
489 if project.gitdir not in fetched:
490 missing.append(project)
491 if not missing:
492 break
493 # Stop us from non-stopped fetching actually-missing repos: If set of
494 # missing repos has not been changed from last fetch, we break.
495 missing_set = set(p.name for p in missing)
496 if previously_missing_set == missing_set:
497 break
498 previously_missing_set = missing_set
499 success, new_fetched = self._Fetch(missing, opt, err_event)
500 if not success:
501 err_event.set()
502 fetched.update(new_fetched)
503
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500504 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700505 """Checkout work tree for one project
506
507 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500508 detach_head: Whether to leave a detached HEAD.
509 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700510 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700511
512 Returns:
513 Whether the fetch was successful.
514 """
Xin Li745be2e2019-06-03 11:24:30 -0700515 start = time.time()
516 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500517 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700518 success = False
519 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500520 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500521 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700522 except GitError as e:
523 print('error.GitError: Cannot checkout %s: %s' %
524 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500525 except Exception as e:
526 print('error: Cannot checkout %s: %s: %s' %
527 (project.name, type(e).__name__, str(e)),
528 file=sys.stderr)
529 raise
Xin Li745be2e2019-06-03 11:24:30 -0700530
Mike Frysingerebf04a42021-02-23 20:48:04 -0500531 if not success:
532 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
533 finish = time.time()
534 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700535
Mike Frysingerebf04a42021-02-23 20:48:04 -0500536 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700537 """Checkout projects listed in all_projects
538
539 Args:
540 all_projects: List of all projects that should be checked out.
541 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500542 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700543 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500544 # Only checkout projects with worktrees.
545 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700546
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500547 def _ProcessResults(pool, pm, results):
548 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500549 for (success, project, start, finish) in results:
550 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
551 start, finish, success)
552 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500553 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500554 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500555 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500556 err_results.append(project.relpath)
557 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500558 if pool:
559 pool.close()
560 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500561 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500562 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700563
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500564 return self.ExecuteInParallel(
565 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
566 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
567 all_projects,
568 callback=_ProcessResults,
569 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500570
Mike Frysinger5a033082019-09-23 19:21:20 -0400571 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400572 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400573 pm.update(inc=0, msg='prescan')
574
Gabe Black2ff30292014-10-09 17:54:35 -0700575 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700576 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500577 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500578 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900579 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100580 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400581 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100582 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500583 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500584 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500585 else:
586 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400587 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500588 'versions of git; please upgrade to git-2.7.0+.'
589 % (project.relpath,),
590 file=sys.stderr)
591 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700592 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700593
Mike Frysinger65af2602021-04-08 22:47:44 -0400594 pm.update(inc=len(projects) - len(gc_gitdirs), msg='warming up')
595
596 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700597 jobs = min(self.jobs, cpu_count)
598
599 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700600 for bare_git in gc_gitdirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400601 pm.update(msg=bare_git._project.name)
David James8d201162013-10-11 17:03:19 -0700602 bare_git.gc('--auto')
Mike Frysinger65af2602021-04-08 22:47:44 -0400603 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700604 return
605
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400606 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700607
608 threads = set()
609 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700610
David James8d201162013-10-11 17:03:19 -0700611 def GC(bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400612 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700613 try:
614 try:
David James8d201162013-10-11 17:03:19 -0700615 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700616 except GitError:
617 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900618 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700619 err_event.set()
620 raise
621 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400622 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700623 sem.release()
624
Gabe Black2ff30292014-10-09 17:54:35 -0700625 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500626 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700627 break
628 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700629 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700630 t.daemon = True
631 threads.add(t)
632 t.start()
633
634 for t in threads:
635 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400636 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700637
Raman Tennetifeb28912021-05-02 19:47:29 -0700638 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
639 """Reload the manfiest from the file specified by the |manifest_name|.
640
641 It unloads the manifest if |manifest_name| is None.
642
643 Args:
644 manifest_name: Manifest file to be reloaded.
645 load_local_manifests: Whether to load local manifests.
646 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800647 if manifest_name:
648 # Override calls _Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700649 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800650 else:
651 self.manifest._Unload()
652
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500653 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700654 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700655 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700656 if project.relpath:
657 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700658 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500659 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700660 old_project_paths = []
661
662 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500663 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700664 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800665 # In reversed order, so subfolders are deleted before parent folder.
666 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700667 if not path:
668 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700669 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900670 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700671 gitdir = os.path.join(self.manifest.topdir, path, '.git')
672 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900673 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900674 manifest=self.manifest,
675 name=path,
676 remote=RemoteSpec('origin'),
677 gitdir=gitdir,
678 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500679 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900680 worktree=os.path.join(self.manifest.topdir, path),
681 relpath=path,
682 revisionExpr='HEAD',
683 revisionId=None,
684 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500685 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900686 quiet=opt.quiet,
687 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400688 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700689
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700690 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500691 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700692 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700693 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700694 return 0
695
jiajia tanga590e642021-04-25 20:02:02 +0800696 def UpdateCopyLinkfileList(self):
697 """Save all dests of copyfile and linkfile, and update them if needed.
698
699 Returns:
700 Whether update was successful.
701 """
702 new_paths = {}
703 new_linkfile_paths = []
704 new_copyfile_paths = []
705 for project in self.GetProjects(None, missing_ok=True):
706 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
707 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
708
709 new_paths = {
710 'linkfile': new_linkfile_paths,
711 'copyfile': new_copyfile_paths,
712 }
713
714 copylinkfile_name = 'copy-link-files.json'
715 copylinkfile_path = os.path.join(self.manifest.repodir, copylinkfile_name)
716 old_copylinkfile_paths = {}
717
718 if os.path.exists(copylinkfile_path):
719 with open(copylinkfile_path, 'rb') as fp:
720 try:
721 old_copylinkfile_paths = json.load(fp)
722 except:
723 print('error: %s is not a json formatted file.' %
724 copylinkfile_path, file=sys.stderr)
725 platform_utils.remove(copylinkfile_path)
726 return False
727
728 need_remove_files = []
729 need_remove_files.extend(
730 set(old_copylinkfile_paths.get('linkfile', [])) -
731 set(new_linkfile_paths))
732 need_remove_files.extend(
733 set(old_copylinkfile_paths.get('copyfile', [])) -
734 set(new_copyfile_paths))
735
736 for need_remove_file in need_remove_files:
737 try:
738 platform_utils.remove(need_remove_file)
739 except OSError as e:
740 if e.errno == errno.ENOENT:
741 # Try to remove the updated copyfile or linkfile.
742 # So, if the file is not exist, nothing need to do.
743 pass
744
745 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
746 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
747 json.dump(new_paths, fp)
748 return True
749
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400750 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
751 if not self.manifest.manifest_server:
752 print('error: cannot smart sync: no manifest server defined in '
753 'manifest', file=sys.stderr)
754 sys.exit(1)
755
756 manifest_server = self.manifest.manifest_server
757 if not opt.quiet:
758 print('Using manifest server %s' % manifest_server)
759
David Pursehouseeeff3532020-02-12 11:24:10 +0900760 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400761 username = None
762 password = None
763 if opt.manifest_server_username and opt.manifest_server_password:
764 username = opt.manifest_server_username
765 password = opt.manifest_server_password
766 else:
767 try:
768 info = netrc.netrc()
769 except IOError:
770 # .netrc file does not exist or could not be opened
771 pass
772 else:
773 try:
774 parse_result = urllib.parse.urlparse(manifest_server)
775 if parse_result.hostname:
776 auth = info.authenticators(parse_result.hostname)
777 if auth:
778 username, _account, password = auth
779 else:
780 print('No credentials found for %s in .netrc'
781 % parse_result.hostname, file=sys.stderr)
782 except netrc.NetrcParseError as e:
783 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
784
785 if (username and password):
786 manifest_server = manifest_server.replace('://', '://%s:%s@' %
787 (username, password),
788 1)
789
790 transport = PersistentTransport(manifest_server)
791 if manifest_server.startswith('persistent-'):
792 manifest_server = manifest_server[len('persistent-'):]
793
794 try:
795 server = xmlrpc.client.Server(manifest_server, transport=transport)
796 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800797 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400798
Mike Frysinger56ce3462019-12-04 19:30:48 -0500799 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500800 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400801 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500802 elif ('TARGET_PRODUCT' in os.environ and
803 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500804 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
805 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400806 [success, manifest_str] = server.GetApprovedManifest(branch, target)
807 else:
808 [success, manifest_str] = server.GetApprovedManifest(branch)
809 else:
810 assert(opt.smart_tag)
811 [success, manifest_str] = server.GetManifest(opt.smart_tag)
812
813 if success:
814 manifest_name = os.path.basename(smart_sync_manifest_path)
815 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500816 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400817 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400818 except IOError as e:
819 print('error: cannot write manifest to %s:\n%s'
820 % (smart_sync_manifest_path, e),
821 file=sys.stderr)
822 sys.exit(1)
823 self._ReloadManifest(manifest_name)
824 else:
825 print('error: manifest server RPC call failed: %s' %
826 manifest_str, file=sys.stderr)
827 sys.exit(1)
828 except (socket.error, IOError, xmlrpc.client.Fault) as e:
829 print('error: cannot connect to manifest server %s:\n%s'
830 % (self.manifest.manifest_server, e), file=sys.stderr)
831 sys.exit(1)
832 except xmlrpc.client.ProtocolError as e:
833 print('error: cannot connect to manifest server %s:\n%d %s'
834 % (self.manifest.manifest_server, e.errcode, e.errmsg),
835 file=sys.stderr)
836 sys.exit(1)
837
838 return manifest_name
839
Mike Frysingerfb527e32019-08-27 02:34:32 -0400840 def _UpdateManifestProject(self, opt, mp, manifest_name):
841 """Fetch & update the local manifest project."""
842 if not opt.local_only:
843 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500844 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700845 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200846 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500847 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400848 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600849 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400850 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700851 clone_filter=self.manifest.CloneFilter,
852 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400853 finish = time.time()
854 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
855 start, finish, success)
856
857 if mp.HasChanges:
858 syncbuf = SyncBuffer(mp.config)
859 start = time.time()
860 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
861 clean = syncbuf.Finish()
862 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
863 start, time.time(), clean)
864 if not clean:
865 sys.exit(1)
Mike Frysinger05638bf2021-05-04 15:33:31 -0400866 self._ReloadManifest(manifest_name)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400867 if opt.jobs is None:
868 self.jobs = self.manifest.default.sync_j
869
Mike Frysingerae6cb082019-08-27 01:10:59 -0400870 def ValidateOptions(self, opt, args):
871 if opt.force_broken:
872 print('warning: -f/--force-broken is now the default behavior, and the '
873 'options are deprecated', file=sys.stderr)
874 if opt.network_only and opt.detach_head:
875 self.OptionParser.error('cannot combine -n and -d')
876 if opt.network_only and opt.local_only:
877 self.OptionParser.error('cannot combine -n and -l')
878 if opt.manifest_name and opt.smart_sync:
879 self.OptionParser.error('cannot combine -m and -s')
880 if opt.manifest_name and opt.smart_tag:
881 self.OptionParser.error('cannot combine -m and -t')
882 if opt.manifest_server_username or opt.manifest_server_password:
883 if not (opt.smart_sync or opt.smart_tag):
884 self.OptionParser.error('-u and -p may only be combined with -s or -t')
885 if None in [opt.manifest_server_username, opt.manifest_server_password]:
886 self.OptionParser.error('both -u and -p must be given')
887
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700888 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800889 if opt.jobs:
890 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700891 if self.jobs > 1:
892 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400893 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700894
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500895 if opt.manifest_name:
896 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700897
Chirayu Desaia892b102013-06-11 14:18:46 +0530898 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900899 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900900 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530901
Xin Lid79a4bc2020-05-20 16:03:45 -0700902 if opt.clone_bundle is None:
903 opt.clone_bundle = self.manifest.CloneBundle
904
Victor Boivie08c880d2011-04-19 10:32:52 +0200905 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400906 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
907 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900908 if os.path.isfile(smart_sync_manifest_path):
909 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800910 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900911 except OSError as e:
912 print('error: failed to remove existing smart sync override manifest: %s' %
913 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700914
Mike Frysingerc99322a2021-05-04 15:32:43 -0400915 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -0400916
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700917 rp = self.manifest.repoProject
918 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500919 cb = rp.CurrentBranch
920 if cb:
921 base = rp.GetBranch(cb).merge
922 if not base or not base.startswith('refs/heads/'):
923 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400924 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500925 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700926
927 mp = self.manifest.manifestProject
928 mp.PreSync()
929
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800930 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700931 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800932
Fredrik de Grootcc960972019-11-22 09:04:31 +0100933 if not opt.mp_update:
934 print('Skipping update of local manifest project.')
935 else:
936 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700937
Raman Tennetifeb28912021-05-02 19:47:29 -0700938 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700939 if self._UseSuperproject(opt):
Raman Tennetifeb28912021-05-02 19:47:29 -0700940 manifest_name = self._UpdateProjectsRevisionId(opt, args, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800941
Simran Basib9a1b732015-08-20 12:19:28 -0700942 if self.gitc_manifest:
943 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700944 missing_ok=True)
945 gitc_projects = []
946 opened_projects = []
947 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700948 if project.relpath in self.gitc_manifest.paths and \
949 self.gitc_manifest.paths[project.relpath].old_revision:
950 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700951 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700952 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700953
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700954 if not args:
955 gitc_projects = None
956
957 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700958 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700959 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
960 if manifest_name:
961 manifest.Override(manifest_name)
962 else:
963 manifest.Override(self.manifest.manifestFile)
964 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
965 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700966 gitc_projects)
967 print('GITC client successfully synced.')
968
969 # The opened projects need to be synced as normal, therefore we
970 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700971 # TODO: make this more reliable -- if there's a project name/path overlap,
972 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900973 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
974 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700975 if not args:
976 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800977 all_projects = self.GetProjects(args,
978 missing_ok=True,
979 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700980
Mike Frysinger5a033082019-09-23 19:21:20 -0400981 err_network_sync = False
982 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -0400983
Dave Borowitz67700e92012-10-23 15:00:54 -0700984 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700985 if not opt.local_only:
Mike Frysingerb4429432021-05-05 20:03:26 -0400986 self._FetchMain(opt, args, all_projects, err_event, manifest_name,
987 load_local_manifests)
Mike Frysinger5a033082019-09-23 19:21:20 -0400988
989 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500990 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400991 err_network_sync = True
992 if opt.fail_fast:
993 print('\nerror: Exited sync due to fetch errors.\n'
994 'Local checkouts *not* updated. Resolve network issues & '
995 'retry.\n'
996 '`repo sync -l` will update some local checkouts.',
997 file=sys.stderr)
998 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800999
Julien Campergue335f5ef2013-10-16 11:02:35 +02001000 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001001 # bail out now, we have no working tree
1002 return
1003
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001004 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001005 err_event.set()
1006 err_update_projects = True
1007 if opt.fail_fast:
1008 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1009 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001010
Mike Frysinger14208f42021-05-04 15:31:51 -04001011 err_update_linkfiles = not self.UpdateCopyLinkfileList()
1012 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +08001013 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +08001014 if opt.fail_fast:
1015 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1016 sys.exit(1)
1017
Mike Frysinger5a033082019-09-23 19:21:20 -04001018 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001019 # NB: We don't exit here because this is the last step.
1020 err_checkout = not self._Checkout(all_projects, opt, err_results)
1021 if err_checkout:
1022 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001023
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001024 # If there's a notice that's supposed to print at the end of the sync, print
1025 # it now...
1026 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001027 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001028
Mike Frysinger5a033082019-09-23 19:21:20 -04001029 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001030 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001031 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1032 if err_network_sync:
1033 print('error: Downloading network changes failed.', file=sys.stderr)
1034 if err_update_projects:
1035 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001036 if err_update_linkfiles:
1037 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001038 if err_checkout:
1039 print('error: Checking out local projects failed.', file=sys.stderr)
1040 if err_results:
1041 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1042 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1043 file=sys.stderr)
1044 sys.exit(1)
1045
Mike Frysingere19d9e12020-02-12 11:23:32 -05001046 if not opt.quiet:
1047 print('repo sync has finished successfully.')
1048
David Pursehouse819827a2020-02-12 15:20:19 +09001049
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001050def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001051 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001052 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001053 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001054 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001055 if project.Exists:
1056 project.PostRepoUpgrade()
1057
David Pursehouse819827a2020-02-12 15:20:19 +09001058
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001059def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001060 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001061 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001062 wrapper = Wrapper()
1063 try:
1064 rev = rp.bare_git.describe(rp.GetRevisionId())
1065 except GitError:
1066 rev = None
1067 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1068 # See if we're held back due to missing signed tag.
1069 current_revid = rp.bare_git.rev_parse('HEAD')
1070 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1071 if current_revid != new_revid:
1072 # We want to switch to the new rev, but also not trash any uncommitted
1073 # changes. This helps with local testing/hacking.
1074 # If a local change has been made, we will throw that away.
1075 # We also have to make sure this will switch to an older commit if that's
1076 # the latest tag in order to support release rollback.
1077 try:
1078 rp.work_git.reset('--keep', new_rev)
1079 except GitError as e:
1080 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001081 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001082 raise RepoChangedException(['--repo-upgraded'])
1083 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001084 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001085 else:
1086 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001087 print('repo version %s is current' % rp.work_git.describe(HEAD),
1088 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001089
David Pursehouse819827a2020-02-12 15:20:19 +09001090
Dave Borowitz67700e92012-10-23 15:00:54 -07001091class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001092 _ALPHA = 0.5
1093
Dave Borowitz67700e92012-10-23 15:00:54 -07001094 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001095 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001096 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001097 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001098
1099 def Get(self, project):
1100 self._Load()
1101 return self._times.get(project.name, _ONE_DAY_S)
1102
1103 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001104 self._Load()
1105 name = project.name
1106 old = self._times.get(name, t)
1107 self._seen.add(name)
1108 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001109 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001110
1111 def _Load(self):
1112 if self._times is None:
1113 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001114 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001115 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001116 except (IOError, ValueError):
1117 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001118 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001119 except OSError:
1120 pass
1121 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001122
1123 def Save(self):
1124 if self._times is None:
1125 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001126
1127 to_delete = []
1128 for name in self._times:
1129 if name not in self._seen:
1130 to_delete.append(name)
1131 for name in to_delete:
1132 del self._times[name]
1133
Dave Borowitz67700e92012-10-23 15:00:54 -07001134 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001135 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001136 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001137 except (IOError, TypeError):
1138 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001139 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001140 except OSError:
1141 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001142
1143# This is a replacement for xmlrpc.client.Transport using urllib2
1144# and supporting persistent-http[s]. It cannot change hosts from
1145# request to request like the normal transport, the real url
1146# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001147
1148
Dan Willemsen0745bb22015-08-17 13:41:45 -07001149class PersistentTransport(xmlrpc.client.Transport):
1150 def __init__(self, orig_host):
1151 self.orig_host = orig_host
1152
1153 def request(self, host, handler, request_body, verbose=False):
1154 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1155 # Python doesn't understand cookies with the #HttpOnly_ prefix
1156 # Since we're only using them for HTTP, copy the file temporarily,
1157 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001158 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001159 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001160 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001161 try:
1162 with open(cookiefile) as f:
1163 for line in f:
1164 if line.startswith("#HttpOnly_"):
1165 line = line[len("#HttpOnly_"):]
1166 tmpcookiefile.write(line)
1167 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001168
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001169 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001170 try:
1171 cookiejar.load()
1172 except cookielib.LoadError:
1173 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001174 finally:
1175 tmpcookiefile.close()
1176 else:
1177 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001178
1179 proxyhandler = urllib.request.ProxyHandler
1180 if proxy:
1181 proxyhandler = urllib.request.ProxyHandler({
1182 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001183 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001184
1185 opener = urllib.request.build_opener(
1186 urllib.request.HTTPCookieProcessor(cookiejar),
1187 proxyhandler)
1188
1189 url = urllib.parse.urljoin(self.orig_host, handler)
1190 parse_results = urllib.parse.urlparse(url)
1191
1192 scheme = parse_results.scheme
1193 if scheme == 'persistent-http':
1194 scheme = 'http'
1195 if scheme == 'persistent-https':
1196 # If we're proxying through persistent-https, use http. The
1197 # proxy itself will do the https.
1198 if proxy:
1199 scheme = 'http'
1200 else:
1201 scheme = 'https'
1202
1203 # Parse out any authentication information using the base class
1204 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1205
1206 url = urllib.parse.urlunparse((
1207 scheme,
1208 host,
1209 parse_results.path,
1210 parse_results.params,
1211 parse_results.query,
1212 parse_results.fragment))
1213
1214 request = urllib.request.Request(url, request_body)
1215 if extra_headers is not None:
1216 for (name, header) in extra_headers:
1217 request.add_header(name, header)
1218 request.add_header('Content-Type', 'text/xml')
1219 try:
1220 response = opener.open(request)
1221 except urllib.error.HTTPError as e:
1222 if e.code == 501:
1223 # We may have been redirected through a login process
1224 # but our POST turned into a GET. Retry.
1225 response = opener.open(request)
1226 else:
1227 raise
1228
1229 p, u = xmlrpc.client.getparser()
1230 while 1:
1231 data = response.read(1024)
1232 if not data:
1233 break
1234 p.feed(data)
1235 p.close()
1236 return u.close()
1237
1238 def close(self):
1239 pass