blob: 5020ea7afa8a1276995e7b4d7dfb0584144ec52f [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Mike Frysingeracf63b22019-06-13 02:24:21 -040015import http.cookiejar as cookielib
Anthony King85b24ac2014-05-06 15:57:48 +010016import json
David Pursehouse86d973d2012-08-24 10:21:02 +090017import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070018from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
20import re
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070021import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022import subprocess
23import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070024import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070025import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040026import urllib.error
27import urllib.parse
28import urllib.request
29import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070030
Roy Lee18afd7f2010-05-09 04:32:08 +080031try:
32 import threading as _threading
33except ImportError:
34 import dummy_threading as _threading
35
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070036try:
37 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090038
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070039 def _rlimit_nofile():
40 return resource.getrlimit(resource.RLIMIT_NOFILE)
41except ImportError:
42 def _rlimit_nofile():
43 return (256, 256)
44
Dave Borowitz18857212012-10-23 17:02:59 -070045try:
46 import multiprocessing
47except ImportError:
48 multiprocessing = None
49
David Rileye0684ad2017-04-05 00:02:59 -070050import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070051from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090052from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090053from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080054import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070055import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070056from project import Project
57from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080058from command import Command, MirrorSafeCommand
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080059from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070060import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070061from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070062from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080063from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070064from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070065
Dave Borowitz67700e92012-10-23 15:00:54 -070066_ONE_DAY_S = 24 * 60 * 60
67
David Pursehouse819827a2020-02-12 15:20:19 +090068
Doug Andersonfc06ced2011-03-16 15:49:18 -070069class _FetchError(Exception):
70 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
71 pass
72
David Pursehouse819827a2020-02-12 15:20:19 +090073
Xin Li745be2e2019-06-03 11:24:30 -070074class _CheckoutError(Exception):
75 """Internal error thrown in _CheckoutOne() when we don't want stack trace."""
76
David Pursehouse819827a2020-02-12 15:20:19 +090077
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080078class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080079 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070080 common = True
81 helpSummary = "Update working tree to the latest revision"
82 helpUsage = """
83%prog [<project>...]
84"""
85 helpDescription = """
86The '%prog' command synchronizes local project directories
87with the remote repositories specified in the manifest. If a local
88project does not yet exist, it will clone a new local directory from
89the remote repository and set up tracking branches as specified in
90the manifest. If the local project already exists, '%prog'
91will update the remote branches and rebase any new local changes
92on top of the new remote changes.
93
94'%prog' will synchronize all projects listed at the command
95line. Projects can be specified either by name, or by a relative
96or absolute path to the project's local directory. If no projects
97are specified, '%prog' will synchronize all projects listed in
98the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070099
100The -d/--detach option can be used to switch specified projects
101back to the manifest revision. This option is especially helpful
102if the project is currently on a topic branch, but the manifest
103revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700104
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700105The -s/--smart-sync option can be used to sync to a known good
106build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200107manifest. The -t/--smart-tag option is similar and allows you to
108specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700109
David Pursehousecf76b1b2012-09-14 10:31:42 +0900110The -u/--manifest-server-username and -p/--manifest-server-password
111options can be used to specify a username and password to authenticate
112with the manifest server when using the -s or -t option.
113
114If -u and -p are not specified when using the -s or -t option, '%prog'
115will attempt to read authentication credentials for the manifest server
116from the user's .netrc file.
117
118'%prog' will not use authentication credentials from -u/-p or .netrc
119if the manifest server specified in the manifest file already includes
120credentials.
121
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400122By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400123to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500124
Kevin Degiabaa7f32014-11-12 11:27:45 -0700125The --force-sync option can be used to overwrite existing git
126directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900127object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700128refs may be removed when overwriting.
129
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500130The --force-remove-dirty option can be used to remove previously used
131projects with uncommitted changes. WARNING: This may cause data to be
132lost since uncommitted changes may be removed with projects that no longer
133exist in the manifest.
134
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700135The --no-clone-bundle option disables any attempt to use
136$URL/clone.bundle to bootstrap a new Git repository from a
137resumeable bundle file on a content delivery network. This
138may be necessary if there are problems with the local Python
139HTTP client or proxy configuration, but the Git binary works.
140
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800141The --fetch-submodules option enables fetching Git submodules
142of a project from server.
143
David Pursehousef2fad612015-01-29 14:36:28 +0900144The -c/--current-branch option can be used to only fetch objects that
145are on the branch specified by a project's revision.
146
David Pursehouseb1553542014-09-04 21:28:09 +0900147The --optimized-fetch option can be used to only fetch projects that
148are fixed to a sha1 revision if the sha1 revision does not already
149exist locally.
150
David Pursehouse74cfd272015-10-14 10:50:15 +0900151The --prune option can be used to remove any refs that no longer
152exist on the remote.
153
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400154# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700155
156If at least one project remote URL uses an SSH connection (ssh://,
157git+ssh://, or user@host:path syntax) repo will automatically
158enable the SSH ControlMaster option when connecting to that host.
159This feature permits other projects in the same '%prog' session to
160reuse the same SSH tunnel, saving connection setup overheads.
161
162To disable this behavior on UNIX platforms, set the GIT_SSH
163environment variable to 'ssh'. For example:
164
165 export GIT_SSH=ssh
166 %prog
167
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400168# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700169
170This feature is automatically disabled on Windows, due to the lack
171of UNIX domain socket support.
172
173This feature is not compatible with url.insteadof rewrites in the
174user's ~/.gitconfig. '%prog' is currently not able to perform the
175rewrite early enough to establish the ControlMaster tunnel.
176
177If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
178later is required to fix a server side protocol bug.
179
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700180"""
181
Nico Sallembien6623b212010-05-11 12:57:01 -0700182 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000183 try:
184 self.jobs = self.manifest.default.sync_j
185 except ManifestParseError:
186 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700187
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500188 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200189 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400190 help='obsolete option (to be deleted in the future)')
191 p.add_option('--fail-fast',
192 dest='fail_fast', action='store_true',
193 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700194 p.add_option('--force-sync',
195 dest='force_sync', action='store_true',
196 help="overwrite an existing git directory if it needs to "
197 "point to a different object directory. WARNING: this "
198 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500199 p.add_option('--force-remove-dirty',
200 dest='force_remove_dirty', action='store_true',
201 help="force remove projects with uncommitted modifications if "
202 "projects no longer exist in the manifest. "
203 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900204 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700205 dest='local_only', action='store_true',
206 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900207 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100208 dest='mp_update', action='store_false', default='true',
209 help='use the existing manifest checkout as-is. '
210 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700212 dest='network_only', action='store_true',
213 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700215 dest='detach_head', action='store_true',
216 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900217 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700218 dest='current_branch_only', action='store_true',
219 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500220 p.add_option('-v', '--verbose',
221 dest='output_mode', action='store_true',
222 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900223 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500224 dest='output_mode', action='store_false',
225 help='only show errors')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900226 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800227 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700228 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500229 p.add_option('-m', '--manifest-name',
230 dest='manifest_name',
231 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700232 p.add_option('--clone-bundle', action='store_true',
233 help='enable use of /clone.bundle on HTTP/HTTPS')
234 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700235 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800236 p.add_option('-u', '--manifest-server-username', action='store',
237 dest='manifest_server_username',
238 help='username to authenticate with the manifest server')
239 p.add_option('-p', '--manifest-server-password', action='store',
240 dest='manifest_server_password',
241 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800242 p.add_option('--fetch-submodules',
243 dest='fetch_submodules', action='store_true',
244 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800245 p.add_option('--use-superproject', action='store_true',
246 help='use the manifest superproject to sync projects')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700247 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500248 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700249 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900250 p.add_option('--optimized-fetch',
251 dest='optimized_fetch', action='store_true',
252 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600253 p.add_option('--retry-fetches',
254 default=0, action='store', type='int',
255 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900256 p.add_option('--prune', dest='prune', action='store_true',
257 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700258 if show_smart:
259 p.add_option('-s', '--smart-sync',
260 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900261 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200262 p.add_option('-t', '--smart-tag',
263 dest='smart_tag', action='store',
264 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700265
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700266 g = p.add_option_group('repo Version options')
267 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500268 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700269 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700270 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800271 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700272 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700273
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800274 def _GetBranch(self):
275 """Returns the branch name for getting the approved manifest."""
276 p = self.manifest.manifestProject
277 b = p.GetBranch(p.CurrentBranch)
278 branch = b.merge
279 if branch.startswith(R_HEADS):
280 branch = branch[len(R_HEADS):]
281 return branch
282
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800283 def _UpdateProjectsRevisionId(self, opt, args):
284 """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.
293
294 Returns:
295 Returns path to the overriding manifest file.
296 """
297 if not self.manifest.superproject:
298 print('error: superproject tag is not defined in manifest.xml',
299 file=sys.stderr)
300 sys.exit(1)
301 print('WARNING: --use-superproject is experimental and not '
302 'for general use', file=sys.stderr)
303
304 superproject_url = self.manifest.superproject['remote'].url
305 if not superproject_url:
306 print('error: superproject URL is not defined in manifest.xml',
307 file=sys.stderr)
308 sys.exit(1)
309
310 superproject = git_superproject.Superproject(self.manifest.repodir)
311 all_projects = self.GetProjects(args,
312 missing_ok=True,
313 submodules_ok=opt.fetch_submodules)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800314 branch = self._GetBranch()
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800315 manifest_path = superproject.UpdateProjectsRevisionId(self.manifest,
316 all_projects,
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800317 url=superproject_url,
318 branch=branch)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800319 if not manifest_path:
320 print('error: Update of revsionId from superproject has failed',
321 file=sys.stderr)
322 sys.exit(1)
323 self._ReloadManifest(manifest_path)
324 return manifest_path
325
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500326 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700327 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800328
David James8d201162013-10-11 17:03:19 -0700329 Delegates most of the work to _FetchHelper.
330
331 Args:
332 opt: Program options returned from optparse. See _Options().
333 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500334 sem: We'll release() this semaphore when we exit so that another thread
335 can be started up.
David James89ece422014-01-09 18:51:58 -0800336 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700337 _FetchHelper docstring for details.
338 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500339 try:
340 for project in projects:
341 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400342 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500343 break
344 finally:
345 sem.release()
David James8d201162013-10-11 17:03:19 -0700346
Xin Li745be2e2019-06-03 11:24:30 -0700347 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
348 clone_filter):
David James8d201162013-10-11 17:03:19 -0700349 """Fetch git objects for a single project.
350
David Pursehousec1b86a22012-11-14 11:36:51 +0900351 Args:
352 opt: Program options returned from optparse. See _Options().
353 project: Project object for the project to fetch.
354 lock: Lock for accessing objects that are shared amongst multiple
355 _FetchHelper() threads.
356 fetched: set object that we will add project.gitdir to when we're done
357 (with our lock held).
358 pm: Instance of a Project object. We will call pm.update() (with our
359 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900360 err_event: We'll set this event in the case of an error (after printing
361 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700362 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700363
364 Returns:
365 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900366 """
367 # We'll set to true once we've locked the lock.
368 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700369
David Pursehousec1b86a22012-11-14 11:36:51 +0900370 # Encapsulate everything in a try/except/finally so that:
371 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900372 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700373 start = time.time()
374 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900375 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700376 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900377 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900378 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500379 verbose=opt.verbose,
David Pursehouseabdf7502020-02-12 14:58:39 +0900380 current_branch_only=opt.current_branch_only,
381 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500382 clone_bundle=opt.clone_bundle,
383 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900384 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600385 retry_fetches=opt.retry_fetches,
David Pursehouseabdf7502020-02-12 14:58:39 +0900386 prune=opt.prune,
387 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900388 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700389
David Pursehousec1b86a22012-11-14 11:36:51 +0900390 # Lock around all the rest of the code, since printing, updating a set
391 # and Progress.update() are not thread safe.
392 lock.acquire()
393 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700394
David Pursehousec1b86a22012-11-14 11:36:51 +0900395 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800396 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700397 print('error: Cannot fetch %s from %s'
398 % (project.name, project.remote.url),
399 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400400 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900401 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700402
David Pursehousec1b86a22012-11-14 11:36:51 +0900403 fetched.add(project.gitdir)
Mike Frysinger3538dd22019-08-26 15:32:06 -0400404 pm.update(msg=project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900405 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800406 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400407 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900408 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900409 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900410 err_event.set()
411 raise
412 finally:
413 if did_lock:
414 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700415 finish = time.time()
416 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
417 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800418
David James8d201162013-10-11 17:03:19 -0700419 return success
420
Mike Frysinger5a033082019-09-23 19:21:20 -0400421 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700422 fetched = set()
David James89ece422014-01-09 18:51:58 -0800423 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200424 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200425 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800426
David James89ece422014-01-09 18:51:58 -0800427 objdir_project_map = dict()
428 for project in projects:
429 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700430
David James89ece422014-01-09 18:51:58 -0800431 threads = set()
432 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800433 for project_list in objdir_project_map.values():
434 # Check for any errors before running any more tasks.
435 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400436 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800437 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700438
David James89ece422014-01-09 18:51:58 -0800439 sem.acquire()
440 kwargs = dict(opt=opt,
441 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500442 sem=sem,
David James89ece422014-01-09 18:51:58 -0800443 lock=lock,
444 fetched=fetched,
445 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700446 err_event=err_event,
447 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800448 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900449 t = _threading.Thread(target=self._FetchProjectList,
450 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200451 # Ensure that Ctrl-C will not freeze the repo process.
452 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800453 threads.add(t)
454 t.start()
David James89ece422014-01-09 18:51:58 -0800455 else:
456 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800457
David James89ece422014-01-09 18:51:58 -0800458 for t in threads:
459 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800460
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700461 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700462 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700463
Julien Campergue335f5ef2013-10-16 11:02:35 +0200464 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400465 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200466
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700467 return fetched
468
Xin Li745be2e2019-06-03 11:24:30 -0700469 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
470 """Main function of the fetch threads.
471
472 Delegates most of the work to _CheckoutOne.
473
474 Args:
475 opt: Program options returned from optparse. See _Options().
476 projects: Projects to fetch.
477 sem: We'll release() this semaphore when we exit so that another thread
478 can be started up.
479 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
480 _CheckoutOne docstring for details.
481 """
482 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400483 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700484 finally:
485 sem.release()
486
Vadim Bendeburydff91942019-11-06 11:05:00 -0800487 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700488 """Checkout work tree for one project
489
490 Args:
491 opt: Program options returned from optparse. See _Options().
492 project: Project object for the project to checkout.
493 lock: Lock for accessing objects that are shared amongst multiple
494 _CheckoutWorker() threads.
495 pm: Instance of a Project object. We will call pm.update() (with our
496 lock held).
497 err_event: We'll set this event in the case of an error (after printing
498 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800499 err_results: A list of strings, paths to git repos where checkout
500 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700501
502 Returns:
503 Whether the fetch was successful.
504 """
505 # We'll set to true once we've locked the lock.
506 did_lock = False
507
Xin Li745be2e2019-06-03 11:24:30 -0700508 # Encapsulate everything in a try/except/finally so that:
509 # - We always set err_event in the case of an exception.
510 # - We always make sure we unlock the lock if we locked it.
511 start = time.time()
512 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
513 detach_head=opt.detach_head)
514 success = False
515 try:
516 try:
517 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700518
519 # Lock around all the rest of the code, since printing, updating a set
520 # and Progress.update() are not thread safe.
521 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400522 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700523 did_lock = True
524
525 if not success:
526 err_event.set()
527 print('error: Cannot checkout %s' % (project.name),
528 file=sys.stderr)
529 raise _CheckoutError()
530
Mike Frysinger3538dd22019-08-26 15:32:06 -0400531 pm.update(msg=project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700532 except _CheckoutError:
533 pass
534 except Exception as e:
535 print('error: Cannot checkout %s: %s: %s' %
536 (project.name, type(e).__name__, str(e)),
537 file=sys.stderr)
538 err_event.set()
539 raise
540 finally:
541 if did_lock:
Vadim Bendeburydff91942019-11-06 11:05:00 -0800542 if not success:
543 err_results.append(project.relpath)
Xin Li745be2e2019-06-03 11:24:30 -0700544 lock.release()
545 finish = time.time()
546 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
547 start, finish, success)
548
549 return success
550
Mike Frysinger5a033082019-09-23 19:21:20 -0400551 def _Checkout(self, all_projects, opt, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700552 """Checkout projects listed in all_projects
553
554 Args:
555 all_projects: List of all projects that should be checked out.
556 opt: Program options returned from optparse. See _Options().
Mike Frysinger5a033082019-09-23 19:21:20 -0400557 err_event: We'll set this event in the case of an error (after printing
558 out info about the error).
559 err_results: A list of strings, paths to git repos where checkout
560 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700561 """
562
563 # Perform checkouts in multiple threads when we are using partial clone.
564 # Without partial clone, all needed git objects are already downloaded,
565 # in this situation it's better to use only one process because the checkout
566 # would be mostly disk I/O; with partial clone, the objects are only
567 # downloaded when demanded (at checkout time), which is similar to the
568 # Sync_NetworkHalf case and parallelism would be helpful.
569 if self.manifest.CloneFilter:
570 syncjobs = self.jobs
571 else:
572 syncjobs = 1
573
574 lock = _threading.Lock()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400575 pm = Progress('Checking out projects', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700576
577 threads = set()
578 sem = _threading.Semaphore(syncjobs)
Xin Li745be2e2019-06-03 11:24:30 -0700579
580 for project in all_projects:
581 # Check for any errors before running any more tasks.
582 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400583 if err_event.isSet() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700584 break
585
586 sem.acquire()
587 if project.worktree:
588 kwargs = dict(opt=opt,
589 sem=sem,
590 project=project,
591 lock=lock,
592 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800593 err_event=err_event,
594 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700595 if syncjobs > 1:
596 t = _threading.Thread(target=self._CheckoutWorker,
597 kwargs=kwargs)
598 # Ensure that Ctrl-C will not freeze the repo process.
599 t.daemon = True
600 threads.add(t)
601 t.start()
602 else:
603 self._CheckoutWorker(**kwargs)
604
605 for t in threads:
606 t.join()
607
608 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700609
Mike Frysinger5a033082019-09-23 19:21:20 -0400610 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700611 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700612 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500613 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500614 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900615 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100616 if not opt.quiet:
617 print('%s: Shared project %s found, disabling pruning.' %
618 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500619 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500620 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500621 else:
622 # This isn't perfect, but it's the best we can do with old git.
623 print('%s: WARNING: shared projects are unreliable when using old '
624 'versions of git; please upgrade to git-2.7.0+.'
625 % (project.relpath,),
626 file=sys.stderr)
627 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700628 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700629
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500630 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700631 cpu_count = multiprocessing.cpu_count()
632 else:
633 cpu_count = 1
634 jobs = min(self.jobs, cpu_count)
635
636 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700637 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700638 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700639 return
640
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400641 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700642
643 threads = set()
644 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700645
David James8d201162013-10-11 17:03:19 -0700646 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700647 try:
648 try:
David James8d201162013-10-11 17:03:19 -0700649 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700650 except GitError:
651 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900652 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700653 err_event.set()
654 raise
655 finally:
656 sem.release()
657
Gabe Black2ff30292014-10-09 17:54:35 -0700658 for bare_git in gc_gitdirs.values():
Mike Frysinger5a033082019-09-23 19:21:20 -0400659 if err_event.isSet() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700660 break
661 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700662 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700663 t.daemon = True
664 threads.add(t)
665 t.start()
666
667 for t in threads:
668 t.join()
669
Tim Kilbourn07669002013-03-08 15:02:49 -0800670 def _ReloadManifest(self, manifest_name=None):
671 if manifest_name:
672 # Override calls _Unload already
673 self.manifest.Override(manifest_name)
674 else:
675 self.manifest._Unload()
676
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500677 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700678 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700679 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700680 if project.relpath:
681 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700682 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500683 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700684 old_project_paths = []
685
686 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500687 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700688 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800689 # In reversed order, so subfolders are deleted before parent folder.
690 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700691 if not path:
692 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700693 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900694 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700695 gitdir = os.path.join(self.manifest.topdir, path, '.git')
696 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900697 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900698 manifest=self.manifest,
699 name=path,
700 remote=RemoteSpec('origin'),
701 gitdir=gitdir,
702 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500703 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900704 worktree=os.path.join(self.manifest.topdir, path),
705 relpath=path,
706 revisionExpr='HEAD',
707 revisionId=None,
708 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500709 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900710 quiet=opt.quiet,
711 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400712 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700713
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700714 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500715 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700716 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700717 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700718 return 0
719
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400720 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
721 if not self.manifest.manifest_server:
722 print('error: cannot smart sync: no manifest server defined in '
723 'manifest', file=sys.stderr)
724 sys.exit(1)
725
726 manifest_server = self.manifest.manifest_server
727 if not opt.quiet:
728 print('Using manifest server %s' % manifest_server)
729
David Pursehouseeeff3532020-02-12 11:24:10 +0900730 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400731 username = None
732 password = None
733 if opt.manifest_server_username and opt.manifest_server_password:
734 username = opt.manifest_server_username
735 password = opt.manifest_server_password
736 else:
737 try:
738 info = netrc.netrc()
739 except IOError:
740 # .netrc file does not exist or could not be opened
741 pass
742 else:
743 try:
744 parse_result = urllib.parse.urlparse(manifest_server)
745 if parse_result.hostname:
746 auth = info.authenticators(parse_result.hostname)
747 if auth:
748 username, _account, password = auth
749 else:
750 print('No credentials found for %s in .netrc'
751 % parse_result.hostname, file=sys.stderr)
752 except netrc.NetrcParseError as e:
753 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
754
755 if (username and password):
756 manifest_server = manifest_server.replace('://', '://%s:%s@' %
757 (username, password),
758 1)
759
760 transport = PersistentTransport(manifest_server)
761 if manifest_server.startswith('persistent-'):
762 manifest_server = manifest_server[len('persistent-'):]
763
764 try:
765 server = xmlrpc.client.Server(manifest_server, transport=transport)
766 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800767 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400768
Mike Frysinger56ce3462019-12-04 19:30:48 -0500769 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500770 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400771 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500772 elif ('TARGET_PRODUCT' in os.environ and
773 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500774 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
775 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400776 [success, manifest_str] = server.GetApprovedManifest(branch, target)
777 else:
778 [success, manifest_str] = server.GetApprovedManifest(branch)
779 else:
780 assert(opt.smart_tag)
781 [success, manifest_str] = server.GetManifest(opt.smart_tag)
782
783 if success:
784 manifest_name = os.path.basename(smart_sync_manifest_path)
785 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500786 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400787 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400788 except IOError as e:
789 print('error: cannot write manifest to %s:\n%s'
790 % (smart_sync_manifest_path, e),
791 file=sys.stderr)
792 sys.exit(1)
793 self._ReloadManifest(manifest_name)
794 else:
795 print('error: manifest server RPC call failed: %s' %
796 manifest_str, file=sys.stderr)
797 sys.exit(1)
798 except (socket.error, IOError, xmlrpc.client.Fault) as e:
799 print('error: cannot connect to manifest server %s:\n%s'
800 % (self.manifest.manifest_server, e), file=sys.stderr)
801 sys.exit(1)
802 except xmlrpc.client.ProtocolError as e:
803 print('error: cannot connect to manifest server %s:\n%d %s'
804 % (self.manifest.manifest_server, e.errcode, e.errmsg),
805 file=sys.stderr)
806 sys.exit(1)
807
808 return manifest_name
809
Mike Frysingerfb527e32019-08-27 02:34:32 -0400810 def _UpdateManifestProject(self, opt, mp, manifest_name):
811 """Fetch & update the local manifest project."""
812 if not opt.local_only:
813 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500814 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400815 current_branch_only=opt.current_branch_only,
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200816 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500817 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400818 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600819 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400820 submodules=self.manifest.HasSubmodules,
821 clone_filter=self.manifest.CloneFilter)
822 finish = time.time()
823 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
824 start, finish, success)
825
826 if mp.HasChanges:
827 syncbuf = SyncBuffer(mp.config)
828 start = time.time()
829 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
830 clean = syncbuf.Finish()
831 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
832 start, time.time(), clean)
833 if not clean:
834 sys.exit(1)
835 self._ReloadManifest(opt.manifest_name)
836 if opt.jobs is None:
837 self.jobs = self.manifest.default.sync_j
838
Mike Frysingerae6cb082019-08-27 01:10:59 -0400839 def ValidateOptions(self, opt, args):
840 if opt.force_broken:
841 print('warning: -f/--force-broken is now the default behavior, and the '
842 'options are deprecated', file=sys.stderr)
843 if opt.network_only and opt.detach_head:
844 self.OptionParser.error('cannot combine -n and -d')
845 if opt.network_only and opt.local_only:
846 self.OptionParser.error('cannot combine -n and -l')
847 if opt.manifest_name and opt.smart_sync:
848 self.OptionParser.error('cannot combine -m and -s')
849 if opt.manifest_name and opt.smart_tag:
850 self.OptionParser.error('cannot combine -m and -t')
851 if opt.manifest_server_username or opt.manifest_server_password:
852 if not (opt.smart_sync or opt.smart_tag):
853 self.OptionParser.error('-u and -p may only be combined with -s or -t')
854 if None in [opt.manifest_server_username, opt.manifest_server_password]:
855 self.OptionParser.error('both -u and -p must be given')
856
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700857 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800858 if opt.jobs:
859 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700860 if self.jobs > 1:
861 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400862 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700863
Mike Frysinger521d01b2020-02-17 01:51:49 -0500864 opt.quiet = opt.output_mode is False
865 opt.verbose = opt.output_mode is True
866
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500867 if opt.manifest_name:
868 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700869
Chirayu Desaia892b102013-06-11 14:18:46 +0530870 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900871 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900872 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530873
Xin Lid79a4bc2020-05-20 16:03:45 -0700874 if opt.clone_bundle is None:
875 opt.clone_bundle = self.manifest.CloneBundle
876
Victor Boivie08c880d2011-04-19 10:32:52 +0200877 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400878 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
879 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900880 if os.path.isfile(smart_sync_manifest_path):
881 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800882 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900883 except OSError as e:
884 print('error: failed to remove existing smart sync override manifest: %s' %
885 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700886
Mike Frysinger5a033082019-09-23 19:21:20 -0400887 err_event = _threading.Event()
888
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700889 rp = self.manifest.repoProject
890 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500891 cb = rp.CurrentBranch
892 if cb:
893 base = rp.GetBranch(cb).merge
894 if not base or not base.startswith('refs/heads/'):
895 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400896 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500897 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700898
899 mp = self.manifest.manifestProject
900 mp.PreSync()
901
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800902 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700903 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800904
Fredrik de Grootcc960972019-11-22 09:04:31 +0100905 if not opt.mp_update:
906 print('Skipping update of local manifest project.')
907 else:
908 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700909
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800910 if opt.use_superproject:
911 manifest_name = self._UpdateProjectsRevisionId(opt, args)
912
Simran Basib9a1b732015-08-20 12:19:28 -0700913 if self.gitc_manifest:
914 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700915 missing_ok=True)
916 gitc_projects = []
917 opened_projects = []
918 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700919 if project.relpath in self.gitc_manifest.paths and \
920 self.gitc_manifest.paths[project.relpath].old_revision:
921 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700922 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700923 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700924
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700925 if not args:
926 gitc_projects = None
927
928 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700929 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700930 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
931 if manifest_name:
932 manifest.Override(manifest_name)
933 else:
934 manifest.Override(self.manifest.manifestFile)
935 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
936 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700937 gitc_projects)
938 print('GITC client successfully synced.')
939
940 # The opened projects need to be synced as normal, therefore we
941 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700942 # TODO: make this more reliable -- if there's a project name/path overlap,
943 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900944 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
945 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700946 if not args:
947 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800948 all_projects = self.GetProjects(args,
949 missing_ok=True,
950 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700951
Mike Frysinger5a033082019-09-23 19:21:20 -0400952 err_network_sync = False
953 err_update_projects = False
954 err_checkout = False
955
Dave Borowitz67700e92012-10-23 15:00:54 -0700956 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700957 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700958 to_fetch = []
959 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700960 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700961 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900962 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700963 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700964
Mike Frysinger5a033082019-09-23 19:21:20 -0400965 fetched = self._Fetch(to_fetch, opt, err_event)
966
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500967 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700968 if opt.network_only:
969 # bail out now; the rest touches the working tree
Mike Frysinger5a033082019-09-23 19:21:20 -0400970 if err_event.isSet():
971 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
972 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700973 return
974
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800975 # Iteratively fetch missing and/or nested unregistered submodules
976 previously_missing_set = set()
977 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100978 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800979 all_projects = self.GetProjects(args,
980 missing_ok=True,
981 submodules_ok=opt.fetch_submodules)
982 missing = []
983 for project in all_projects:
984 if project.gitdir not in fetched:
985 missing.append(project)
986 if not missing:
987 break
988 # Stop us from non-stopped fetching actually-missing repos: If set of
989 # missing repos has not been changed from last fetch, we break.
990 missing_set = set(p.name for p in missing)
991 if previously_missing_set == missing_set:
992 break
993 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400994 fetched.update(self._Fetch(missing, opt, err_event))
995
996 # If we saw an error, exit with code 1 so that other scripts can check.
997 if err_event.isSet():
998 err_network_sync = True
999 if opt.fail_fast:
1000 print('\nerror: Exited sync due to fetch errors.\n'
1001 'Local checkouts *not* updated. Resolve network issues & '
1002 'retry.\n'
1003 '`repo sync -l` will update some local checkouts.',
1004 file=sys.stderr)
1005 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001006
Julien Campergue335f5ef2013-10-16 11:02:35 +02001007 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001008 # bail out now, we have no working tree
1009 return
1010
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001011 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001012 err_event.set()
1013 err_update_projects = True
1014 if opt.fail_fast:
1015 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1016 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001017
Mike Frysinger5a033082019-09-23 19:21:20 -04001018 err_results = []
1019 self._Checkout(all_projects, opt, err_event, err_results)
1020 if err_event.isSet():
1021 err_checkout = True
1022 # NB: We don't exit here because this is the last step.
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.
1030 if err_event.isSet():
1031 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)
1036 if err_checkout:
1037 print('error: Checking out local projects failed.', file=sys.stderr)
1038 if err_results:
1039 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1040 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1041 file=sys.stderr)
1042 sys.exit(1)
1043
Mike Frysingere19d9e12020-02-12 11:23:32 -05001044 if not opt.quiet:
1045 print('repo sync has finished successfully.')
1046
David Pursehouse819827a2020-02-12 15:20:19 +09001047
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001048def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001049 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001050 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001051 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001052 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001053 if project.Exists:
1054 project.PostRepoUpgrade()
1055
David Pursehouse819827a2020-02-12 15:20:19 +09001056
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001057def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001058 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001059 print('info: A new version of repo is available', file=sys.stderr)
1060 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001061 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001062 syncbuf = SyncBuffer(rp.config)
1063 rp.Sync_LocalHalf(syncbuf)
1064 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001065 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001066 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001067 raise RepoChangedException(['--repo-upgraded'])
1068 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001069 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001070 else:
1071 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001072 print('repo version %s is current' % rp.work_git.describe(HEAD),
1073 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001074
David Pursehouse819827a2020-02-12 15:20:19 +09001075
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001076def _VerifyTag(project):
1077 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1078 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001079 print('warning: GnuPG was not available during last "repo init"\n'
1080 'warning: Cannot automatically authenticate repo."""',
1081 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001082 return True
1083
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001084 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001085 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001086 except GitError:
1087 cur = None
1088
1089 if not cur \
1090 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001091 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001092 if rev.startswith(R_HEADS):
1093 rev = rev[len(R_HEADS):]
1094
Sarah Owenscecd1d82012-11-01 22:59:27 -07001095 print(file=sys.stderr)
1096 print("warning: project '%s' branch '%s' is not signed"
1097 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001098 return False
1099
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001100 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001101 env['GIT_DIR'] = project.gitdir
1102 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001103
1104 cmd = [GIT, 'tag', '-v', cur]
1105 proc = subprocess.Popen(cmd,
David Pursehousee5913ae2020-02-12 13:56:59 +09001106 stdout=subprocess.PIPE,
1107 stderr=subprocess.PIPE,
1108 env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001109 out = proc.stdout.read()
1110 proc.stdout.close()
1111
1112 err = proc.stderr.read()
1113 proc.stderr.close()
1114
1115 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001116 print(file=sys.stderr)
1117 print(out, file=sys.stderr)
1118 print(err, file=sys.stderr)
1119 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001120 return False
1121 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001122
David Rileye0684ad2017-04-05 00:02:59 -07001123
Dave Borowitz67700e92012-10-23 15:00:54 -07001124class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001125 _ALPHA = 0.5
1126
Dave Borowitz67700e92012-10-23 15:00:54 -07001127 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001128 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001129 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001130 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001131
1132 def Get(self, project):
1133 self._Load()
1134 return self._times.get(project.name, _ONE_DAY_S)
1135
1136 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001137 self._Load()
1138 name = project.name
1139 old = self._times.get(name, t)
1140 self._seen.add(name)
1141 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001142 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001143
1144 def _Load(self):
1145 if self._times is None:
1146 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001147 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001148 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001149 except (IOError, ValueError):
1150 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001151 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001152 except OSError:
1153 pass
1154 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001155
1156 def Save(self):
1157 if self._times is None:
1158 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001159
1160 to_delete = []
1161 for name in self._times:
1162 if name not in self._seen:
1163 to_delete.append(name)
1164 for name in to_delete:
1165 del self._times[name]
1166
Dave Borowitz67700e92012-10-23 15:00:54 -07001167 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001168 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001169 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001170 except (IOError, TypeError):
1171 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001172 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001173 except OSError:
1174 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001175
1176# This is a replacement for xmlrpc.client.Transport using urllib2
1177# and supporting persistent-http[s]. It cannot change hosts from
1178# request to request like the normal transport, the real url
1179# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001180
1181
Dan Willemsen0745bb22015-08-17 13:41:45 -07001182class PersistentTransport(xmlrpc.client.Transport):
1183 def __init__(self, orig_host):
1184 self.orig_host = orig_host
1185
1186 def request(self, host, handler, request_body, verbose=False):
1187 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1188 # Python doesn't understand cookies with the #HttpOnly_ prefix
1189 # Since we're only using them for HTTP, copy the file temporarily,
1190 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001191 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001192 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001193 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001194 try:
1195 with open(cookiefile) as f:
1196 for line in f:
1197 if line.startswith("#HttpOnly_"):
1198 line = line[len("#HttpOnly_"):]
1199 tmpcookiefile.write(line)
1200 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001201
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001202 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001203 try:
1204 cookiejar.load()
1205 except cookielib.LoadError:
1206 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001207 finally:
1208 tmpcookiefile.close()
1209 else:
1210 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001211
1212 proxyhandler = urllib.request.ProxyHandler
1213 if proxy:
1214 proxyhandler = urllib.request.ProxyHandler({
1215 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001216 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001217
1218 opener = urllib.request.build_opener(
1219 urllib.request.HTTPCookieProcessor(cookiejar),
1220 proxyhandler)
1221
1222 url = urllib.parse.urljoin(self.orig_host, handler)
1223 parse_results = urllib.parse.urlparse(url)
1224
1225 scheme = parse_results.scheme
1226 if scheme == 'persistent-http':
1227 scheme = 'http'
1228 if scheme == 'persistent-https':
1229 # If we're proxying through persistent-https, use http. The
1230 # proxy itself will do the https.
1231 if proxy:
1232 scheme = 'http'
1233 else:
1234 scheme = 'https'
1235
1236 # Parse out any authentication information using the base class
1237 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1238
1239 url = urllib.parse.urlunparse((
1240 scheme,
1241 host,
1242 parse_results.path,
1243 parse_results.params,
1244 parse_results.query,
1245 parse_results.fragment))
1246
1247 request = urllib.request.Request(url, request_body)
1248 if extra_headers is not None:
1249 for (name, header) in extra_headers:
1250 request.add_header(name, header)
1251 request.add_header('Content-Type', 'text/xml')
1252 try:
1253 response = opener.open(request)
1254 except urllib.error.HTTPError as e:
1255 if e.code == 501:
1256 # We may have been redirected through a login process
1257 # but our POST turned into a GET. Retry.
1258 response = opener.open(request)
1259 else:
1260 raise
1261
1262 p, u = xmlrpc.client.getparser()
1263 while 1:
1264 data = response.read(1024)
1265 if not data:
1266 break
1267 p.feed(data)
1268 p.close()
1269 return u.close()
1270
1271 def close(self):
1272 pass