blob: 0a3cde7db1c9d2e45e3bcb3e465d3058d4c7c8f8 [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."""
Doug Andersonfc06ced2011-03-16 15:49:18 -070071
David Pursehouse819827a2020-02-12 15:20:19 +090072
Xin Li745be2e2019-06-03 11:24:30 -070073class _CheckoutError(Exception):
74 """Internal error thrown in _CheckoutOne() when we don't want stack trace."""
75
David Pursehouse819827a2020-02-12 15:20:19 +090076
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080077class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080078 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070079 common = True
80 helpSummary = "Update working tree to the latest revision"
81 helpUsage = """
82%prog [<project>...]
83"""
84 helpDescription = """
85The '%prog' command synchronizes local project directories
86with the remote repositories specified in the manifest. If a local
87project does not yet exist, it will clone a new local directory from
88the remote repository and set up tracking branches as specified in
89the manifest. If the local project already exists, '%prog'
90will update the remote branches and rebase any new local changes
91on top of the new remote changes.
92
93'%prog' will synchronize all projects listed at the command
94line. Projects can be specified either by name, or by a relative
95or absolute path to the project's local directory. If no projects
96are specified, '%prog' will synchronize all projects listed in
97the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070098
99The -d/--detach option can be used to switch specified projects
100back to the manifest revision. This option is especially helpful
101if the project is currently on a topic branch, but the manifest
102revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700103
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700104The -s/--smart-sync option can be used to sync to a known good
105build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200106manifest. The -t/--smart-tag option is similar and allows you to
107specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700108
David Pursehousecf76b1b2012-09-14 10:31:42 +0900109The -u/--manifest-server-username and -p/--manifest-server-password
110options can be used to specify a username and password to authenticate
111with the manifest server when using the -s or -t option.
112
113If -u and -p are not specified when using the -s or -t option, '%prog'
114will attempt to read authentication credentials for the manifest server
115from the user's .netrc file.
116
117'%prog' will not use authentication credentials from -u/-p or .netrc
118if the manifest server specified in the manifest file already includes
119credentials.
120
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400121By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400122to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500123
Kevin Degiabaa7f32014-11-12 11:27:45 -0700124The --force-sync option can be used to overwrite existing git
125directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900126object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700127refs may be removed when overwriting.
128
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500129The --force-remove-dirty option can be used to remove previously used
130projects with uncommitted changes. WARNING: This may cause data to be
131lost since uncommitted changes may be removed with projects that no longer
132exist in the manifest.
133
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700134The --no-clone-bundle option disables any attempt to use
135$URL/clone.bundle to bootstrap a new Git repository from a
136resumeable bundle file on a content delivery network. This
137may be necessary if there are problems with the local Python
138HTTP client or proxy configuration, but the Git binary works.
139
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800140The --fetch-submodules option enables fetching Git submodules
141of a project from server.
142
David Pursehousef2fad612015-01-29 14:36:28 +0900143The -c/--current-branch option can be used to only fetch objects that
144are on the branch specified by a project's revision.
145
David Pursehouseb1553542014-09-04 21:28:09 +0900146The --optimized-fetch option can be used to only fetch projects that
147are fixed to a sha1 revision if the sha1 revision does not already
148exist locally.
149
David Pursehouse74cfd272015-10-14 10:50:15 +0900150The --prune option can be used to remove any refs that no longer
151exist on the remote.
152
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400153# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700154
155If at least one project remote URL uses an SSH connection (ssh://,
156git+ssh://, or user@host:path syntax) repo will automatically
157enable the SSH ControlMaster option when connecting to that host.
158This feature permits other projects in the same '%prog' session to
159reuse the same SSH tunnel, saving connection setup overheads.
160
161To disable this behavior on UNIX platforms, set the GIT_SSH
162environment variable to 'ssh'. For example:
163
164 export GIT_SSH=ssh
165 %prog
166
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400167# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700168
169This feature is automatically disabled on Windows, due to the lack
170of UNIX domain socket support.
171
172This feature is not compatible with url.insteadof rewrites in the
173user's ~/.gitconfig. '%prog' is currently not able to perform the
174rewrite early enough to establish the ControlMaster tunnel.
175
176If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
177later is required to fix a server side protocol bug.
178
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700179"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500180 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700181
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:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500184 self.PARALLEL_JOBS = self.manifest.default.sync_j
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000185 except ManifestParseError:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500186 pass
187 super()._Options(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700188
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500189 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200190 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400191 help='obsolete option (to be deleted in the future)')
192 p.add_option('--fail-fast',
193 dest='fail_fast', action='store_true',
194 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700195 p.add_option('--force-sync',
196 dest='force_sync', action='store_true',
197 help="overwrite an existing git directory if it needs to "
198 "point to a different object directory. WARNING: this "
199 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500200 p.add_option('--force-remove-dirty',
201 dest='force_remove_dirty', action='store_true',
202 help="force remove projects with uncommitted modifications if "
203 "projects no longer exist in the manifest. "
204 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900205 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700206 dest='local_only', action='store_true',
207 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900208 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100209 dest='mp_update', action='store_false', default='true',
210 help='use the existing manifest checkout as-is. '
211 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900212 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700213 dest='network_only', action='store_true',
214 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900215 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700216 dest='detach_head', action='store_true',
217 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900218 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700219 dest='current_branch_only', action='store_true',
220 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500221 p.add_option('-v', '--verbose',
222 dest='output_mode', action='store_true',
223 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900224 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500225 dest='output_mode', action='store_false',
226 help='only show errors')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500227 p.add_option('-m', '--manifest-name',
228 dest='manifest_name',
229 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700230 p.add_option('--clone-bundle', action='store_true',
231 help='enable use of /clone.bundle on HTTP/HTTPS')
232 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700233 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800234 p.add_option('-u', '--manifest-server-username', action='store',
235 dest='manifest_server_username',
236 help='username to authenticate with the manifest server')
237 p.add_option('-p', '--manifest-server-password', action='store',
238 dest='manifest_server_password',
239 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800240 p.add_option('--fetch-submodules',
241 dest='fetch_submodules', action='store_true',
242 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800243 p.add_option('--use-superproject', action='store_true',
244 help='use the manifest superproject to sync projects')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700245 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500246 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700247 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900248 p.add_option('--optimized-fetch',
249 dest='optimized_fetch', action='store_true',
250 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600251 p.add_option('--retry-fetches',
252 default=0, action='store', type='int',
253 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900254 p.add_option('--prune', dest='prune', action='store_true',
255 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700256 if show_smart:
257 p.add_option('-s', '--smart-sync',
258 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900259 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200260 p.add_option('-t', '--smart-tag',
261 dest='smart_tag', action='store',
262 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700263
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700264 g = p.add_option_group('repo Version options')
265 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500266 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700268 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800269 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700270 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700271
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800272 def _GetBranch(self):
273 """Returns the branch name for getting the approved manifest."""
274 p = self.manifest.manifestProject
275 b = p.GetBranch(p.CurrentBranch)
276 branch = b.merge
277 if branch.startswith(R_HEADS):
278 branch = branch[len(R_HEADS):]
279 return branch
280
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800281 def _UpdateProjectsRevisionId(self, opt, args):
282 """Update revisionId of every project with the SHA from superproject.
283
284 This function updates each project's revisionId with SHA from superproject.
285 It writes the updated manifest into a file and reloads the manifest from it.
286
287 Args:
288 opt: Program options returned from optparse. See _Options().
289 args: Arguments to pass to GetProjects. See the GetProjects
290 docstring for details.
291
292 Returns:
293 Returns path to the overriding manifest file.
294 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800295 superproject = git_superproject.Superproject(self.manifest,
296 self.repodir)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800297 all_projects = self.GetProjects(args,
298 missing_ok=True,
299 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800300 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800301 if not manifest_path:
302 print('error: Update of revsionId from superproject has failed',
303 file=sys.stderr)
304 sys.exit(1)
305 self._ReloadManifest(manifest_path)
306 return manifest_path
307
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500308 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700309 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800310
David James8d201162013-10-11 17:03:19 -0700311 Delegates most of the work to _FetchHelper.
312
313 Args:
314 opt: Program options returned from optparse. See _Options().
315 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500316 sem: We'll release() this semaphore when we exit so that another thread
317 can be started up.
David James89ece422014-01-09 18:51:58 -0800318 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700319 _FetchHelper docstring for details.
320 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500321 try:
322 for project in projects:
323 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400324 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500325 break
326 finally:
327 sem.release()
David James8d201162013-10-11 17:03:19 -0700328
Xin Li745be2e2019-06-03 11:24:30 -0700329 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
330 clone_filter):
David James8d201162013-10-11 17:03:19 -0700331 """Fetch git objects for a single project.
332
David Pursehousec1b86a22012-11-14 11:36:51 +0900333 Args:
334 opt: Program options returned from optparse. See _Options().
335 project: Project object for the project to fetch.
336 lock: Lock for accessing objects that are shared amongst multiple
337 _FetchHelper() threads.
338 fetched: set object that we will add project.gitdir to when we're done
339 (with our lock held).
340 pm: Instance of a Project object. We will call pm.update() (with our
341 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900342 err_event: We'll set this event in the case of an error (after printing
343 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700344 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700345
346 Returns:
347 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900348 """
349 # We'll set to true once we've locked the lock.
350 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700351
David Pursehousec1b86a22012-11-14 11:36:51 +0900352 # Encapsulate everything in a try/except/finally so that:
353 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900354 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700355 start = time.time()
356 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900357 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700358 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900359 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900360 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500361 verbose=opt.verbose,
David Pursehouseabdf7502020-02-12 14:58:39 +0900362 current_branch_only=opt.current_branch_only,
363 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500364 clone_bundle=opt.clone_bundle,
365 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900366 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600367 retry_fetches=opt.retry_fetches,
David Pursehouseabdf7502020-02-12 14:58:39 +0900368 prune=opt.prune,
369 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900370 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700371
David Pursehousec1b86a22012-11-14 11:36:51 +0900372 # Lock around all the rest of the code, since printing, updating a set
373 # and Progress.update() are not thread safe.
374 lock.acquire()
375 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700376
David Pursehousec1b86a22012-11-14 11:36:51 +0900377 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800378 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700379 print('error: Cannot fetch %s from %s'
380 % (project.name, project.remote.url),
381 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400382 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900383 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700384
David Pursehousec1b86a22012-11-14 11:36:51 +0900385 fetched.add(project.gitdir)
Mike Frysinger3538dd22019-08-26 15:32:06 -0400386 pm.update(msg=project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900387 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800388 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400389 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900390 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900391 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900392 err_event.set()
393 raise
394 finally:
395 if did_lock:
396 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700397 finish = time.time()
398 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
399 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800400
David James8d201162013-10-11 17:03:19 -0700401 return success
402
Mike Frysinger5a033082019-09-23 19:21:20 -0400403 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700404 fetched = set()
David James89ece422014-01-09 18:51:58 -0800405 lock = _threading.Lock()
Mike Frysinger4e05f652021-02-23 16:57:56 -0500406 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800407
David James89ece422014-01-09 18:51:58 -0800408 objdir_project_map = dict()
409 for project in projects:
410 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700411
David James89ece422014-01-09 18:51:58 -0800412 threads = set()
413 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800414 for project_list in objdir_project_map.values():
415 # Check for any errors before running any more tasks.
416 # ...we'll let existing threads finish, though.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500417 if err_event.is_set() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800418 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700419
David James89ece422014-01-09 18:51:58 -0800420 sem.acquire()
421 kwargs = dict(opt=opt,
422 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500423 sem=sem,
David James89ece422014-01-09 18:51:58 -0800424 lock=lock,
425 fetched=fetched,
426 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700427 err_event=err_event,
428 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800429 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900430 t = _threading.Thread(target=self._FetchProjectList,
431 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200432 # Ensure that Ctrl-C will not freeze the repo process.
433 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800434 threads.add(t)
435 t.start()
David James89ece422014-01-09 18:51:58 -0800436 else:
437 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800438
David James89ece422014-01-09 18:51:58 -0800439 for t in threads:
440 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800441
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700442 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700443 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700444
Julien Campergue335f5ef2013-10-16 11:02:35 +0200445 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400446 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200447
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700448 return fetched
449
Xin Li745be2e2019-06-03 11:24:30 -0700450 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
451 """Main function of the fetch threads.
452
453 Delegates most of the work to _CheckoutOne.
454
455 Args:
456 opt: Program options returned from optparse. See _Options().
457 projects: Projects to fetch.
458 sem: We'll release() this semaphore when we exit so that another thread
459 can be started up.
460 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
461 _CheckoutOne docstring for details.
462 """
463 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400464 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700465 finally:
466 sem.release()
467
Vadim Bendeburydff91942019-11-06 11:05:00 -0800468 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700469 """Checkout work tree for one project
470
471 Args:
472 opt: Program options returned from optparse. See _Options().
473 project: Project object for the project to checkout.
474 lock: Lock for accessing objects that are shared amongst multiple
475 _CheckoutWorker() threads.
476 pm: Instance of a Project object. We will call pm.update() (with our
477 lock held).
478 err_event: We'll set this event in the case of an error (after printing
479 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800480 err_results: A list of strings, paths to git repos where checkout
481 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700482
483 Returns:
484 Whether the fetch was successful.
485 """
486 # We'll set to true once we've locked the lock.
487 did_lock = False
488
Xin Li745be2e2019-06-03 11:24:30 -0700489 # Encapsulate everything in a try/except/finally so that:
490 # - We always set err_event in the case of an exception.
491 # - We always make sure we unlock the lock if we locked it.
492 start = time.time()
493 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
494 detach_head=opt.detach_head)
495 success = False
496 try:
497 try:
498 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700499
500 # Lock around all the rest of the code, since printing, updating a set
501 # and Progress.update() are not thread safe.
502 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400503 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700504 did_lock = True
505
506 if not success:
507 err_event.set()
508 print('error: Cannot checkout %s' % (project.name),
509 file=sys.stderr)
510 raise _CheckoutError()
511
Mike Frysinger3538dd22019-08-26 15:32:06 -0400512 pm.update(msg=project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700513 except _CheckoutError:
514 pass
515 except Exception as e:
516 print('error: Cannot checkout %s: %s: %s' %
517 (project.name, type(e).__name__, str(e)),
518 file=sys.stderr)
519 err_event.set()
520 raise
521 finally:
522 if did_lock:
Vadim Bendeburydff91942019-11-06 11:05:00 -0800523 if not success:
524 err_results.append(project.relpath)
Xin Li745be2e2019-06-03 11:24:30 -0700525 lock.release()
526 finish = time.time()
527 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
528 start, finish, success)
529
530 return success
531
Mike Frysinger5a033082019-09-23 19:21:20 -0400532 def _Checkout(self, all_projects, opt, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700533 """Checkout projects listed in all_projects
534
535 Args:
536 all_projects: List of all projects that should be checked out.
537 opt: Program options returned from optparse. See _Options().
Mike Frysinger5a033082019-09-23 19:21:20 -0400538 err_event: We'll set this event in the case of an error (after printing
539 out info about the error).
540 err_results: A list of strings, paths to git repos where checkout
541 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700542 """
543
544 # Perform checkouts in multiple threads when we are using partial clone.
545 # Without partial clone, all needed git objects are already downloaded,
546 # in this situation it's better to use only one process because the checkout
547 # would be mostly disk I/O; with partial clone, the objects are only
548 # downloaded when demanded (at checkout time), which is similar to the
549 # Sync_NetworkHalf case and parallelism would be helpful.
550 if self.manifest.CloneFilter:
551 syncjobs = self.jobs
552 else:
553 syncjobs = 1
554
555 lock = _threading.Lock()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400556 pm = Progress('Checking out projects', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700557
558 threads = set()
559 sem = _threading.Semaphore(syncjobs)
Xin Li745be2e2019-06-03 11:24:30 -0700560
561 for project in all_projects:
562 # Check for any errors before running any more tasks.
563 # ...we'll let existing threads finish, though.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500564 if err_event.is_set() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700565 break
566
567 sem.acquire()
568 if project.worktree:
569 kwargs = dict(opt=opt,
570 sem=sem,
571 project=project,
572 lock=lock,
573 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800574 err_event=err_event,
575 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700576 if syncjobs > 1:
577 t = _threading.Thread(target=self._CheckoutWorker,
578 kwargs=kwargs)
579 # Ensure that Ctrl-C will not freeze the repo process.
580 t.daemon = True
581 threads.add(t)
582 t.start()
583 else:
584 self._CheckoutWorker(**kwargs)
585
586 for t in threads:
587 t.join()
588
589 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700590
Mike Frysinger5a033082019-09-23 19:21:20 -0400591 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700592 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700593 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500594 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500595 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900596 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100597 if not opt.quiet:
598 print('%s: Shared project %s found, disabling pruning.' %
599 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500600 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500601 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500602 else:
603 # This isn't perfect, but it's the best we can do with old git.
604 print('%s: WARNING: shared projects are unreliable when using old '
605 'versions of git; please upgrade to git-2.7.0+.'
606 % (project.relpath,),
607 file=sys.stderr)
608 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700609 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700610
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500611 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700612 cpu_count = multiprocessing.cpu_count()
613 else:
614 cpu_count = 1
615 jobs = min(self.jobs, cpu_count)
616
617 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700618 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700619 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700620 return
621
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400622 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700623
624 threads = set()
625 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700626
David James8d201162013-10-11 17:03:19 -0700627 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700628 try:
629 try:
David James8d201162013-10-11 17:03:19 -0700630 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700631 except GitError:
632 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900633 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700634 err_event.set()
635 raise
636 finally:
637 sem.release()
638
Gabe Black2ff30292014-10-09 17:54:35 -0700639 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500640 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700641 break
642 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700643 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700644 t.daemon = True
645 threads.add(t)
646 t.start()
647
648 for t in threads:
649 t.join()
650
Tim Kilbourn07669002013-03-08 15:02:49 -0800651 def _ReloadManifest(self, manifest_name=None):
652 if manifest_name:
653 # Override calls _Unload already
654 self.manifest.Override(manifest_name)
655 else:
656 self.manifest._Unload()
657
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500658 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700659 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700660 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700661 if project.relpath:
662 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700663 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500664 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700665 old_project_paths = []
666
667 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500668 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700669 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800670 # In reversed order, so subfolders are deleted before parent folder.
671 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700672 if not path:
673 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700674 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900675 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700676 gitdir = os.path.join(self.manifest.topdir, path, '.git')
677 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900678 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900679 manifest=self.manifest,
680 name=path,
681 remote=RemoteSpec('origin'),
682 gitdir=gitdir,
683 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500684 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900685 worktree=os.path.join(self.manifest.topdir, path),
686 relpath=path,
687 revisionExpr='HEAD',
688 revisionId=None,
689 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500690 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900691 quiet=opt.quiet,
692 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400693 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700694
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700695 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500696 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700697 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700698 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700699 return 0
700
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400701 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
702 if not self.manifest.manifest_server:
703 print('error: cannot smart sync: no manifest server defined in '
704 'manifest', file=sys.stderr)
705 sys.exit(1)
706
707 manifest_server = self.manifest.manifest_server
708 if not opt.quiet:
709 print('Using manifest server %s' % manifest_server)
710
David Pursehouseeeff3532020-02-12 11:24:10 +0900711 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400712 username = None
713 password = None
714 if opt.manifest_server_username and opt.manifest_server_password:
715 username = opt.manifest_server_username
716 password = opt.manifest_server_password
717 else:
718 try:
719 info = netrc.netrc()
720 except IOError:
721 # .netrc file does not exist or could not be opened
722 pass
723 else:
724 try:
725 parse_result = urllib.parse.urlparse(manifest_server)
726 if parse_result.hostname:
727 auth = info.authenticators(parse_result.hostname)
728 if auth:
729 username, _account, password = auth
730 else:
731 print('No credentials found for %s in .netrc'
732 % parse_result.hostname, file=sys.stderr)
733 except netrc.NetrcParseError as e:
734 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
735
736 if (username and password):
737 manifest_server = manifest_server.replace('://', '://%s:%s@' %
738 (username, password),
739 1)
740
741 transport = PersistentTransport(manifest_server)
742 if manifest_server.startswith('persistent-'):
743 manifest_server = manifest_server[len('persistent-'):]
744
745 try:
746 server = xmlrpc.client.Server(manifest_server, transport=transport)
747 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800748 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400749
Mike Frysinger56ce3462019-12-04 19:30:48 -0500750 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500751 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400752 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500753 elif ('TARGET_PRODUCT' in os.environ and
754 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500755 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
756 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400757 [success, manifest_str] = server.GetApprovedManifest(branch, target)
758 else:
759 [success, manifest_str] = server.GetApprovedManifest(branch)
760 else:
761 assert(opt.smart_tag)
762 [success, manifest_str] = server.GetManifest(opt.smart_tag)
763
764 if success:
765 manifest_name = os.path.basename(smart_sync_manifest_path)
766 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500767 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400768 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400769 except IOError as e:
770 print('error: cannot write manifest to %s:\n%s'
771 % (smart_sync_manifest_path, e),
772 file=sys.stderr)
773 sys.exit(1)
774 self._ReloadManifest(manifest_name)
775 else:
776 print('error: manifest server RPC call failed: %s' %
777 manifest_str, file=sys.stderr)
778 sys.exit(1)
779 except (socket.error, IOError, xmlrpc.client.Fault) as e:
780 print('error: cannot connect to manifest server %s:\n%s'
781 % (self.manifest.manifest_server, e), file=sys.stderr)
782 sys.exit(1)
783 except xmlrpc.client.ProtocolError as e:
784 print('error: cannot connect to manifest server %s:\n%d %s'
785 % (self.manifest.manifest_server, e.errcode, e.errmsg),
786 file=sys.stderr)
787 sys.exit(1)
788
789 return manifest_name
790
Mike Frysingerfb527e32019-08-27 02:34:32 -0400791 def _UpdateManifestProject(self, opt, mp, manifest_name):
792 """Fetch & update the local manifest project."""
793 if not opt.local_only:
794 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500795 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400796 current_branch_only=opt.current_branch_only,
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200797 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500798 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400799 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600800 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400801 submodules=self.manifest.HasSubmodules,
802 clone_filter=self.manifest.CloneFilter)
803 finish = time.time()
804 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
805 start, finish, success)
806
807 if mp.HasChanges:
808 syncbuf = SyncBuffer(mp.config)
809 start = time.time()
810 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
811 clean = syncbuf.Finish()
812 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
813 start, time.time(), clean)
814 if not clean:
815 sys.exit(1)
816 self._ReloadManifest(opt.manifest_name)
817 if opt.jobs is None:
818 self.jobs = self.manifest.default.sync_j
819
Mike Frysingerae6cb082019-08-27 01:10:59 -0400820 def ValidateOptions(self, opt, args):
821 if opt.force_broken:
822 print('warning: -f/--force-broken is now the default behavior, and the '
823 'options are deprecated', file=sys.stderr)
824 if opt.network_only and opt.detach_head:
825 self.OptionParser.error('cannot combine -n and -d')
826 if opt.network_only and opt.local_only:
827 self.OptionParser.error('cannot combine -n and -l')
828 if opt.manifest_name and opt.smart_sync:
829 self.OptionParser.error('cannot combine -m and -s')
830 if opt.manifest_name and opt.smart_tag:
831 self.OptionParser.error('cannot combine -m and -t')
832 if opt.manifest_server_username or opt.manifest_server_password:
833 if not (opt.smart_sync or opt.smart_tag):
834 self.OptionParser.error('-u and -p may only be combined with -s or -t')
835 if None in [opt.manifest_server_username, opt.manifest_server_password]:
836 self.OptionParser.error('both -u and -p must be given')
837
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700838 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800839 if opt.jobs:
840 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700841 if self.jobs > 1:
842 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400843 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700844
Mike Frysinger521d01b2020-02-17 01:51:49 -0500845 opt.quiet = opt.output_mode is False
846 opt.verbose = opt.output_mode is True
847
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500848 if opt.manifest_name:
849 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700850
Chirayu Desaia892b102013-06-11 14:18:46 +0530851 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900852 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900853 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530854
Xin Lid79a4bc2020-05-20 16:03:45 -0700855 if opt.clone_bundle is None:
856 opt.clone_bundle = self.manifest.CloneBundle
857
Victor Boivie08c880d2011-04-19 10:32:52 +0200858 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400859 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
860 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900861 if os.path.isfile(smart_sync_manifest_path):
862 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800863 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900864 except OSError as e:
865 print('error: failed to remove existing smart sync override manifest: %s' %
866 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700867
Mike Frysinger5a033082019-09-23 19:21:20 -0400868 err_event = _threading.Event()
869
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700870 rp = self.manifest.repoProject
871 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500872 cb = rp.CurrentBranch
873 if cb:
874 base = rp.GetBranch(cb).merge
875 if not base or not base.startswith('refs/heads/'):
876 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400877 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500878 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700879
880 mp = self.manifest.manifestProject
881 mp.PreSync()
882
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800883 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700884 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800885
Fredrik de Grootcc960972019-11-22 09:04:31 +0100886 if not opt.mp_update:
887 print('Skipping update of local manifest project.')
888 else:
889 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700890
Raman Tenneti55d6a5a2021-02-24 14:37:01 -0800891 if (opt.use_superproject or
892 self.manifest.manifestProject.config.GetBoolean(
893 'repo.superproject')):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800894 manifest_name = self._UpdateProjectsRevisionId(opt, args)
895
Simran Basib9a1b732015-08-20 12:19:28 -0700896 if self.gitc_manifest:
897 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700898 missing_ok=True)
899 gitc_projects = []
900 opened_projects = []
901 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700902 if project.relpath in self.gitc_manifest.paths and \
903 self.gitc_manifest.paths[project.relpath].old_revision:
904 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700905 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700906 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700907
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700908 if not args:
909 gitc_projects = None
910
911 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700912 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700913 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
914 if manifest_name:
915 manifest.Override(manifest_name)
916 else:
917 manifest.Override(self.manifest.manifestFile)
918 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
919 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700920 gitc_projects)
921 print('GITC client successfully synced.')
922
923 # The opened projects need to be synced as normal, therefore we
924 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700925 # TODO: make this more reliable -- if there's a project name/path overlap,
926 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900927 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
928 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700929 if not args:
930 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800931 all_projects = self.GetProjects(args,
932 missing_ok=True,
933 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700934
Mike Frysinger5a033082019-09-23 19:21:20 -0400935 err_network_sync = False
936 err_update_projects = False
937 err_checkout = False
938
Dave Borowitz67700e92012-10-23 15:00:54 -0700939 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700940 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700941 to_fetch = []
942 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700943 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700944 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900945 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700946 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700947
Mike Frysinger5a033082019-09-23 19:21:20 -0400948 fetched = self._Fetch(to_fetch, opt, err_event)
949
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500950 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700951 if opt.network_only:
952 # bail out now; the rest touches the working tree
Mike Frysingerbe24a542021-02-23 03:24:12 -0500953 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400954 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
955 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700956 return
957
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800958 # Iteratively fetch missing and/or nested unregistered submodules
959 previously_missing_set = set()
960 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100961 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800962 all_projects = self.GetProjects(args,
963 missing_ok=True,
964 submodules_ok=opt.fetch_submodules)
965 missing = []
966 for project in all_projects:
967 if project.gitdir not in fetched:
968 missing.append(project)
969 if not missing:
970 break
971 # Stop us from non-stopped fetching actually-missing repos: If set of
972 # missing repos has not been changed from last fetch, we break.
973 missing_set = set(p.name for p in missing)
974 if previously_missing_set == missing_set:
975 break
976 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400977 fetched.update(self._Fetch(missing, opt, err_event))
978
979 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500980 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400981 err_network_sync = True
982 if opt.fail_fast:
983 print('\nerror: Exited sync due to fetch errors.\n'
984 'Local checkouts *not* updated. Resolve network issues & '
985 'retry.\n'
986 '`repo sync -l` will update some local checkouts.',
987 file=sys.stderr)
988 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800989
Julien Campergue335f5ef2013-10-16 11:02:35 +0200990 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700991 # bail out now, we have no working tree
992 return
993
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500994 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400995 err_event.set()
996 err_update_projects = True
997 if opt.fail_fast:
998 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
999 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001000
Mike Frysinger5a033082019-09-23 19:21:20 -04001001 err_results = []
1002 self._Checkout(all_projects, opt, err_event, err_results)
Mike Frysingerbe24a542021-02-23 03:24:12 -05001003 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001004 err_checkout = True
1005 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001006
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001007 # If there's a notice that's supposed to print at the end of the sync, print
1008 # it now...
1009 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001010 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001011
Mike Frysinger5a033082019-09-23 19:21:20 -04001012 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001013 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001014 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1015 if err_network_sync:
1016 print('error: Downloading network changes failed.', file=sys.stderr)
1017 if err_update_projects:
1018 print('error: Updating local project lists failed.', file=sys.stderr)
1019 if err_checkout:
1020 print('error: Checking out local projects failed.', file=sys.stderr)
1021 if err_results:
1022 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1023 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1024 file=sys.stderr)
1025 sys.exit(1)
1026
Mike Frysingere19d9e12020-02-12 11:23:32 -05001027 if not opt.quiet:
1028 print('repo sync has finished successfully.')
1029
David Pursehouse819827a2020-02-12 15:20:19 +09001030
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001031def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001032 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001033 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001034 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001035 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001036 if project.Exists:
1037 project.PostRepoUpgrade()
1038
David Pursehouse819827a2020-02-12 15:20:19 +09001039
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001040def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001041 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001042 print('info: A new version of repo is available', file=sys.stderr)
1043 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001044 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001045 syncbuf = SyncBuffer(rp.config)
1046 rp.Sync_LocalHalf(syncbuf)
1047 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001048 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001049 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001050 raise RepoChangedException(['--repo-upgraded'])
1051 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001052 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001053 else:
1054 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001055 print('repo version %s is current' % rp.work_git.describe(HEAD),
1056 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001057
David Pursehouse819827a2020-02-12 15:20:19 +09001058
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001059def _VerifyTag(project):
1060 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1061 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001062 print('warning: GnuPG was not available during last "repo init"\n'
1063 'warning: Cannot automatically authenticate repo."""',
1064 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001065 return True
1066
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001067 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001068 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001069 except GitError:
1070 cur = None
1071
1072 if not cur \
1073 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001074 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075 if rev.startswith(R_HEADS):
1076 rev = rev[len(R_HEADS):]
1077
Sarah Owenscecd1d82012-11-01 22:59:27 -07001078 print(file=sys.stderr)
1079 print("warning: project '%s' branch '%s' is not signed"
1080 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001081 return False
1082
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001083 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001084 env['GIT_DIR'] = project.gitdir
1085 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001086
1087 cmd = [GIT, 'tag', '-v', cur]
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001088 result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
1089 env=env, check=False)
1090 if result.returncode:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001091 print(file=sys.stderr)
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001092 print(result.stdout, file=sys.stderr)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001093 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001094 return False
1095 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001096
David Rileye0684ad2017-04-05 00:02:59 -07001097
Dave Borowitz67700e92012-10-23 15:00:54 -07001098class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001099 _ALPHA = 0.5
1100
Dave Borowitz67700e92012-10-23 15:00:54 -07001101 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001102 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001103 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001104 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001105
1106 def Get(self, project):
1107 self._Load()
1108 return self._times.get(project.name, _ONE_DAY_S)
1109
1110 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001111 self._Load()
1112 name = project.name
1113 old = self._times.get(name, t)
1114 self._seen.add(name)
1115 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001116 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001117
1118 def _Load(self):
1119 if self._times is None:
1120 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001121 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001122 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001123 except (IOError, ValueError):
1124 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001125 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001126 except OSError:
1127 pass
1128 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001129
1130 def Save(self):
1131 if self._times is None:
1132 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001133
1134 to_delete = []
1135 for name in self._times:
1136 if name not in self._seen:
1137 to_delete.append(name)
1138 for name in to_delete:
1139 del self._times[name]
1140
Dave Borowitz67700e92012-10-23 15:00:54 -07001141 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001142 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001143 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001144 except (IOError, TypeError):
1145 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001146 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001147 except OSError:
1148 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001149
1150# This is a replacement for xmlrpc.client.Transport using urllib2
1151# and supporting persistent-http[s]. It cannot change hosts from
1152# request to request like the normal transport, the real url
1153# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001154
1155
Dan Willemsen0745bb22015-08-17 13:41:45 -07001156class PersistentTransport(xmlrpc.client.Transport):
1157 def __init__(self, orig_host):
1158 self.orig_host = orig_host
1159
1160 def request(self, host, handler, request_body, verbose=False):
1161 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1162 # Python doesn't understand cookies with the #HttpOnly_ prefix
1163 # Since we're only using them for HTTP, copy the file temporarily,
1164 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001165 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001166 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001167 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001168 try:
1169 with open(cookiefile) as f:
1170 for line in f:
1171 if line.startswith("#HttpOnly_"):
1172 line = line[len("#HttpOnly_"):]
1173 tmpcookiefile.write(line)
1174 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001175
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001176 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001177 try:
1178 cookiejar.load()
1179 except cookielib.LoadError:
1180 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001181 finally:
1182 tmpcookiefile.close()
1183 else:
1184 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001185
1186 proxyhandler = urllib.request.ProxyHandler
1187 if proxy:
1188 proxyhandler = urllib.request.ProxyHandler({
1189 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001190 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001191
1192 opener = urllib.request.build_opener(
1193 urllib.request.HTTPCookieProcessor(cookiejar),
1194 proxyhandler)
1195
1196 url = urllib.parse.urljoin(self.orig_host, handler)
1197 parse_results = urllib.parse.urlparse(url)
1198
1199 scheme = parse_results.scheme
1200 if scheme == 'persistent-http':
1201 scheme = 'http'
1202 if scheme == 'persistent-https':
1203 # If we're proxying through persistent-https, use http. The
1204 # proxy itself will do the https.
1205 if proxy:
1206 scheme = 'http'
1207 else:
1208 scheme = 'https'
1209
1210 # Parse out any authentication information using the base class
1211 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1212
1213 url = urllib.parse.urlunparse((
1214 scheme,
1215 host,
1216 parse_results.path,
1217 parse_results.params,
1218 parse_results.query,
1219 parse_results.fragment))
1220
1221 request = urllib.request.Request(url, request_body)
1222 if extra_headers is not None:
1223 for (name, header) in extra_headers:
1224 request.add_header(name, header)
1225 request.add_header('Content-Type', 'text/xml')
1226 try:
1227 response = opener.open(request)
1228 except urllib.error.HTTPError as e:
1229 if e.code == 501:
1230 # We may have been redirected through a login process
1231 # but our POST turned into a GET. Retry.
1232 response = opener.open(request)
1233 else:
1234 raise
1235
1236 p, u = xmlrpc.client.getparser()
1237 while 1:
1238 data = response.read(1024)
1239 if not data:
1240 break
1241 p.feed(data)
1242 p.close()
1243 return u.close()
1244
1245 def close(self):
1246 pass