blob: d1b631ae27d846848ef412a4ca8379858bad3eda [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
Mike Frysinger7b586f22021-02-23 18:38:39 -050016import io
Anthony King85b24ac2014-05-06 15:57:48 +010017import json
David Pursehouse86d973d2012-08-24 10:21:02 +090018import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070019from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import os
21import re
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070022import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import subprocess
24import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070025import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070026import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040027import urllib.error
28import urllib.parse
29import urllib.request
30import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070031
Roy Lee18afd7f2010-05-09 04:32:08 +080032try:
33 import threading as _threading
34except ImportError:
35 import dummy_threading as _threading
36
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070037try:
38 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090039
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070040 def _rlimit_nofile():
41 return resource.getrlimit(resource.RLIMIT_NOFILE)
42except ImportError:
43 def _rlimit_nofile():
44 return (256, 256)
45
Dave Borowitz18857212012-10-23 17:02:59 -070046try:
47 import multiprocessing
48except ImportError:
49 multiprocessing = None
50
David Rileye0684ad2017-04-05 00:02:59 -070051import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070052from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090053from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090054from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080055import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070056import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070057from project import Project
58from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080059from command import Command, MirrorSafeCommand
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080060from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070061import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070062from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070063from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080064from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070065from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070066
Dave Borowitz67700e92012-10-23 15:00:54 -070067_ONE_DAY_S = 24 * 60 * 60
68
David Pursehouse819827a2020-02-12 15:20:19 +090069
Doug Andersonfc06ced2011-03-16 15:49:18 -070070class _FetchError(Exception):
71 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
Doug Andersonfc06ced2011-03-16 15:49:18 -070072
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 [...]
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"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500181 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700182
Nico Sallembien6623b212010-05-11 12:57:01 -0700183 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000184 try:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500185 self.PARALLEL_JOBS = self.manifest.default.sync_j
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000186 except ManifestParseError:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500187 pass
188 super()._Options(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700189
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500190 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200191 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400192 help='obsolete option (to be deleted in the future)')
193 p.add_option('--fail-fast',
194 dest='fail_fast', action='store_true',
195 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700196 p.add_option('--force-sync',
197 dest='force_sync', action='store_true',
198 help="overwrite an existing git directory if it needs to "
199 "point to a different object directory. WARNING: this "
200 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500201 p.add_option('--force-remove-dirty',
202 dest='force_remove_dirty', action='store_true',
203 help="force remove projects with uncommitted modifications if "
204 "projects no longer exist in the manifest. "
205 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900206 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700207 dest='local_only', action='store_true',
208 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900209 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100210 dest='mp_update', action='store_false', default='true',
211 help='use the existing manifest checkout as-is. '
212 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900213 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700214 dest='network_only', action='store_true',
215 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900216 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700217 dest='detach_head', action='store_true',
218 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900219 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700220 dest='current_branch_only', action='store_true',
221 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500222 p.add_option('-v', '--verbose',
223 dest='output_mode', action='store_true',
224 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900225 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500226 dest='output_mode', action='store_false',
227 help='only show errors')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500228 p.add_option('-m', '--manifest-name',
229 dest='manifest_name',
230 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700231 p.add_option('--clone-bundle', action='store_true',
232 help='enable use of /clone.bundle on HTTP/HTTPS')
233 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700234 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800235 p.add_option('-u', '--manifest-server-username', action='store',
236 dest='manifest_server_username',
237 help='username to authenticate with the manifest server')
238 p.add_option('-p', '--manifest-server-password', action='store',
239 dest='manifest_server_password',
240 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800241 p.add_option('--fetch-submodules',
242 dest='fetch_submodules', action='store_true',
243 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800244 p.add_option('--use-superproject', action='store_true',
245 help='use the manifest superproject to sync projects')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700246 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500247 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700248 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900249 p.add_option('--optimized-fetch',
250 dest='optimized_fetch', action='store_true',
251 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600252 p.add_option('--retry-fetches',
253 default=0, action='store', type='int',
254 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900255 p.add_option('--prune', dest='prune', action='store_true',
256 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700257 if show_smart:
258 p.add_option('-s', '--smart-sync',
259 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900260 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200261 p.add_option('-t', '--smart-tag',
262 dest='smart_tag', action='store',
263 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700264
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700265 g = p.add_option_group('repo Version options')
266 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500267 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700268 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700269 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800270 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700271 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700272
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800273 def _GetBranch(self):
274 """Returns the branch name for getting the approved manifest."""
275 p = self.manifest.manifestProject
276 b = p.GetBranch(p.CurrentBranch)
277 branch = b.merge
278 if branch.startswith(R_HEADS):
279 branch = branch[len(R_HEADS):]
280 return branch
281
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800282 def _UpdateProjectsRevisionId(self, opt, args):
283 """Update revisionId of every project with the SHA from superproject.
284
285 This function updates each project's revisionId with SHA from superproject.
286 It writes the updated manifest into a file and reloads the manifest from it.
287
288 Args:
289 opt: Program options returned from optparse. See _Options().
290 args: Arguments to pass to GetProjects. See the GetProjects
291 docstring for details.
292
293 Returns:
294 Returns path to the overriding manifest file.
295 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800296 superproject = git_superproject.Superproject(self.manifest,
297 self.repodir)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800298 all_projects = self.GetProjects(args,
299 missing_ok=True,
300 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800301 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800302 if not manifest_path:
303 print('error: Update of revsionId from superproject has failed',
304 file=sys.stderr)
305 sys.exit(1)
306 self._ReloadManifest(manifest_path)
307 return manifest_path
308
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500309 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700310 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800311
David James8d201162013-10-11 17:03:19 -0700312 Delegates most of the work to _FetchHelper.
313
314 Args:
315 opt: Program options returned from optparse. See _Options().
316 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500317 sem: We'll release() this semaphore when we exit so that another thread
318 can be started up.
David James89ece422014-01-09 18:51:58 -0800319 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700320 _FetchHelper docstring for details.
321 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500322 try:
323 for project in projects:
324 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400325 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500326 break
327 finally:
328 sem.release()
David James8d201162013-10-11 17:03:19 -0700329
Xin Li745be2e2019-06-03 11:24:30 -0700330 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
331 clone_filter):
David James8d201162013-10-11 17:03:19 -0700332 """Fetch git objects for a single project.
333
David Pursehousec1b86a22012-11-14 11:36:51 +0900334 Args:
335 opt: Program options returned from optparse. See _Options().
336 project: Project object for the project to fetch.
337 lock: Lock for accessing objects that are shared amongst multiple
338 _FetchHelper() threads.
339 fetched: set object that we will add project.gitdir to when we're done
340 (with our lock held).
341 pm: Instance of a Project object. We will call pm.update() (with our
342 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900343 err_event: We'll set this event in the case of an error (after printing
344 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700345 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700346
347 Returns:
348 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900349 """
350 # We'll set to true once we've locked the lock.
351 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700352
David Pursehousec1b86a22012-11-14 11:36:51 +0900353 # Encapsulate everything in a try/except/finally so that:
354 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900355 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700356 start = time.time()
357 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500358 buf = io.StringIO()
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500359 with lock:
360 pm.start(project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900361 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700362 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900363 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900364 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500365 verbose=opt.verbose,
Mike Frysinger7b586f22021-02-23 18:38:39 -0500366 output_redir=buf,
David Pursehouseabdf7502020-02-12 14:58:39 +0900367 current_branch_only=opt.current_branch_only,
368 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500369 clone_bundle=opt.clone_bundle,
370 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900371 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600372 retry_fetches=opt.retry_fetches,
David Pursehouseabdf7502020-02-12 14:58:39 +0900373 prune=opt.prune,
374 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900375 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700376
David Pursehousec1b86a22012-11-14 11:36:51 +0900377 # Lock around all the rest of the code, since printing, updating a set
378 # and Progress.update() are not thread safe.
379 lock.acquire()
380 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700381
Mike Frysinger7b586f22021-02-23 18:38:39 -0500382 output = buf.getvalue()
383 if opt.verbose and output:
384 pm.update(inc=0, msg=output.rstrip())
385
David Pursehousec1b86a22012-11-14 11:36:51 +0900386 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800387 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700388 print('error: Cannot fetch %s from %s'
389 % (project.name, project.remote.url),
390 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400391 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900392 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700393
David Pursehousec1b86a22012-11-14 11:36:51 +0900394 fetched.add(project.gitdir)
David Pursehousec1b86a22012-11-14 11:36:51 +0900395 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800396 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400397 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900398 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900399 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900400 err_event.set()
401 raise
402 finally:
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500403 if not did_lock:
404 lock.acquire()
405 pm.finish(project.name)
406 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700407 finish = time.time()
408 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
409 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800410
David James8d201162013-10-11 17:03:19 -0700411 return success
412
Mike Frysinger5a033082019-09-23 19:21:20 -0400413 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700414 fetched = set()
David James89ece422014-01-09 18:51:58 -0800415 lock = _threading.Lock()
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500416 pm = Progress('Fetching', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800417
David James89ece422014-01-09 18:51:58 -0800418 objdir_project_map = dict()
419 for project in projects:
420 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700421
David James89ece422014-01-09 18:51:58 -0800422 threads = set()
423 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800424 for project_list in objdir_project_map.values():
425 # Check for any errors before running any more tasks.
426 # ...we'll let existing threads finish, though.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500427 if err_event.is_set() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800428 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700429
David James89ece422014-01-09 18:51:58 -0800430 sem.acquire()
431 kwargs = dict(opt=opt,
432 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500433 sem=sem,
David James89ece422014-01-09 18:51:58 -0800434 lock=lock,
435 fetched=fetched,
436 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700437 err_event=err_event,
438 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800439 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900440 t = _threading.Thread(target=self._FetchProjectList,
441 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200442 # Ensure that Ctrl-C will not freeze the repo process.
443 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800444 threads.add(t)
445 t.start()
David James89ece422014-01-09 18:51:58 -0800446 else:
447 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800448
David James89ece422014-01-09 18:51:58 -0800449 for t in threads:
450 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800451
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700452 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700453 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700454
Julien Campergue335f5ef2013-10-16 11:02:35 +0200455 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400456 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200457
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700458 return fetched
459
Xin Li745be2e2019-06-03 11:24:30 -0700460 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
461 """Main function of the fetch threads.
462
463 Delegates most of the work to _CheckoutOne.
464
465 Args:
466 opt: Program options returned from optparse. See _Options().
467 projects: Projects to fetch.
468 sem: We'll release() this semaphore when we exit so that another thread
469 can be started up.
470 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
471 _CheckoutOne docstring for details.
472 """
473 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400474 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700475 finally:
476 sem.release()
477
Vadim Bendeburydff91942019-11-06 11:05:00 -0800478 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700479 """Checkout work tree for one project
480
481 Args:
482 opt: Program options returned from optparse. See _Options().
483 project: Project object for the project to checkout.
484 lock: Lock for accessing objects that are shared amongst multiple
485 _CheckoutWorker() threads.
486 pm: Instance of a Project object. We will call pm.update() (with our
487 lock held).
488 err_event: We'll set this event in the case of an error (after printing
489 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800490 err_results: A list of strings, paths to git repos where checkout
491 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700492
493 Returns:
494 Whether the fetch was successful.
495 """
496 # We'll set to true once we've locked the lock.
497 did_lock = False
498
Xin Li745be2e2019-06-03 11:24:30 -0700499 # Encapsulate everything in a try/except/finally so that:
500 # - We always set err_event in the case of an exception.
501 # - We always make sure we unlock the lock if we locked it.
502 start = time.time()
503 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
504 detach_head=opt.detach_head)
505 success = False
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500506 with lock:
507 pm.start(project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700508 try:
509 try:
510 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700511
512 # Lock around all the rest of the code, since printing, updating a set
513 # and Progress.update() are not thread safe.
514 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400515 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700516 did_lock = True
517
518 if not success:
519 err_event.set()
520 print('error: Cannot checkout %s' % (project.name),
521 file=sys.stderr)
522 raise _CheckoutError()
Xin Li745be2e2019-06-03 11:24:30 -0700523 except _CheckoutError:
524 pass
525 except Exception as e:
526 print('error: Cannot checkout %s: %s: %s' %
527 (project.name, type(e).__name__, str(e)),
528 file=sys.stderr)
529 err_event.set()
530 raise
531 finally:
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500532 if not did_lock:
533 lock.acquire()
534 if not success:
535 err_results.append(project.relpath)
536 pm.finish(project.name)
537 lock.release()
Xin Li745be2e2019-06-03 11:24:30 -0700538 finish = time.time()
539 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
540 start, finish, success)
541
542 return success
543
Mike Frysinger5a033082019-09-23 19:21:20 -0400544 def _Checkout(self, all_projects, opt, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700545 """Checkout projects listed in all_projects
546
547 Args:
548 all_projects: List of all projects that should be checked out.
549 opt: Program options returned from optparse. See _Options().
Mike Frysinger5a033082019-09-23 19:21:20 -0400550 err_event: We'll set this event in the case of an error (after printing
551 out info about the error).
552 err_results: A list of strings, paths to git repos where checkout
553 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700554 """
555
556 # Perform checkouts in multiple threads when we are using partial clone.
557 # Without partial clone, all needed git objects are already downloaded,
558 # in this situation it's better to use only one process because the checkout
559 # would be mostly disk I/O; with partial clone, the objects are only
560 # downloaded when demanded (at checkout time), which is similar to the
561 # Sync_NetworkHalf case and parallelism would be helpful.
562 if self.manifest.CloneFilter:
563 syncjobs = self.jobs
564 else:
565 syncjobs = 1
566
567 lock = _threading.Lock()
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500568 pm = Progress('Checking out', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700569
570 threads = set()
571 sem = _threading.Semaphore(syncjobs)
Xin Li745be2e2019-06-03 11:24:30 -0700572
573 for project in all_projects:
574 # Check for any errors before running any more tasks.
575 # ...we'll let existing threads finish, though.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500576 if err_event.is_set() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700577 break
578
579 sem.acquire()
580 if project.worktree:
581 kwargs = dict(opt=opt,
582 sem=sem,
583 project=project,
584 lock=lock,
585 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800586 err_event=err_event,
587 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700588 if syncjobs > 1:
589 t = _threading.Thread(target=self._CheckoutWorker,
590 kwargs=kwargs)
591 # Ensure that Ctrl-C will not freeze the repo process.
592 t.daemon = True
593 threads.add(t)
594 t.start()
595 else:
596 self._CheckoutWorker(**kwargs)
597
598 for t in threads:
599 t.join()
600
601 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700602
Mike Frysinger5a033082019-09-23 19:21:20 -0400603 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700604 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700605 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500606 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500607 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900608 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100609 if not opt.quiet:
610 print('%s: Shared project %s found, disabling pruning.' %
611 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500612 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500613 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500614 else:
615 # This isn't perfect, but it's the best we can do with old git.
616 print('%s: WARNING: shared projects are unreliable when using old '
617 'versions of git; please upgrade to git-2.7.0+.'
618 % (project.relpath,),
619 file=sys.stderr)
620 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700621 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700622
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500623 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700624 cpu_count = multiprocessing.cpu_count()
625 else:
626 cpu_count = 1
627 jobs = min(self.jobs, cpu_count)
628
629 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700630 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700631 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700632 return
633
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400634 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700635
636 threads = set()
637 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700638
David James8d201162013-10-11 17:03:19 -0700639 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700640 try:
641 try:
David James8d201162013-10-11 17:03:19 -0700642 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700643 except GitError:
644 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900645 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700646 err_event.set()
647 raise
648 finally:
649 sem.release()
650
Gabe Black2ff30292014-10-09 17:54:35 -0700651 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500652 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700653 break
654 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700655 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700656 t.daemon = True
657 threads.add(t)
658 t.start()
659
660 for t in threads:
661 t.join()
662
Tim Kilbourn07669002013-03-08 15:02:49 -0800663 def _ReloadManifest(self, manifest_name=None):
664 if manifest_name:
665 # Override calls _Unload already
666 self.manifest.Override(manifest_name)
667 else:
668 self.manifest._Unload()
669
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500670 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700671 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700672 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700673 if project.relpath:
674 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700675 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500676 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700677 old_project_paths = []
678
679 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500680 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700681 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800682 # In reversed order, so subfolders are deleted before parent folder.
683 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700684 if not path:
685 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700686 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900687 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700688 gitdir = os.path.join(self.manifest.topdir, path, '.git')
689 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900690 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900691 manifest=self.manifest,
692 name=path,
693 remote=RemoteSpec('origin'),
694 gitdir=gitdir,
695 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500696 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900697 worktree=os.path.join(self.manifest.topdir, path),
698 relpath=path,
699 revisionExpr='HEAD',
700 revisionId=None,
701 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500702 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900703 quiet=opt.quiet,
704 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400705 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700706
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700707 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500708 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700709 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700710 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700711 return 0
712
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400713 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
714 if not self.manifest.manifest_server:
715 print('error: cannot smart sync: no manifest server defined in '
716 'manifest', file=sys.stderr)
717 sys.exit(1)
718
719 manifest_server = self.manifest.manifest_server
720 if not opt.quiet:
721 print('Using manifest server %s' % manifest_server)
722
David Pursehouseeeff3532020-02-12 11:24:10 +0900723 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400724 username = None
725 password = None
726 if opt.manifest_server_username and opt.manifest_server_password:
727 username = opt.manifest_server_username
728 password = opt.manifest_server_password
729 else:
730 try:
731 info = netrc.netrc()
732 except IOError:
733 # .netrc file does not exist or could not be opened
734 pass
735 else:
736 try:
737 parse_result = urllib.parse.urlparse(manifest_server)
738 if parse_result.hostname:
739 auth = info.authenticators(parse_result.hostname)
740 if auth:
741 username, _account, password = auth
742 else:
743 print('No credentials found for %s in .netrc'
744 % parse_result.hostname, file=sys.stderr)
745 except netrc.NetrcParseError as e:
746 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
747
748 if (username and password):
749 manifest_server = manifest_server.replace('://', '://%s:%s@' %
750 (username, password),
751 1)
752
753 transport = PersistentTransport(manifest_server)
754 if manifest_server.startswith('persistent-'):
755 manifest_server = manifest_server[len('persistent-'):]
756
757 try:
758 server = xmlrpc.client.Server(manifest_server, transport=transport)
759 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800760 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400761
Mike Frysinger56ce3462019-12-04 19:30:48 -0500762 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500763 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400764 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500765 elif ('TARGET_PRODUCT' in os.environ and
766 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500767 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
768 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400769 [success, manifest_str] = server.GetApprovedManifest(branch, target)
770 else:
771 [success, manifest_str] = server.GetApprovedManifest(branch)
772 else:
773 assert(opt.smart_tag)
774 [success, manifest_str] = server.GetManifest(opt.smart_tag)
775
776 if success:
777 manifest_name = os.path.basename(smart_sync_manifest_path)
778 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500779 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400780 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400781 except IOError as e:
782 print('error: cannot write manifest to %s:\n%s'
783 % (smart_sync_manifest_path, e),
784 file=sys.stderr)
785 sys.exit(1)
786 self._ReloadManifest(manifest_name)
787 else:
788 print('error: manifest server RPC call failed: %s' %
789 manifest_str, file=sys.stderr)
790 sys.exit(1)
791 except (socket.error, IOError, xmlrpc.client.Fault) as e:
792 print('error: cannot connect to manifest server %s:\n%s'
793 % (self.manifest.manifest_server, e), file=sys.stderr)
794 sys.exit(1)
795 except xmlrpc.client.ProtocolError as e:
796 print('error: cannot connect to manifest server %s:\n%d %s'
797 % (self.manifest.manifest_server, e.errcode, e.errmsg),
798 file=sys.stderr)
799 sys.exit(1)
800
801 return manifest_name
802
Mike Frysingerfb527e32019-08-27 02:34:32 -0400803 def _UpdateManifestProject(self, opt, mp, manifest_name):
804 """Fetch & update the local manifest project."""
805 if not opt.local_only:
806 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500807 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400808 current_branch_only=opt.current_branch_only,
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200809 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500810 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400811 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600812 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400813 submodules=self.manifest.HasSubmodules,
814 clone_filter=self.manifest.CloneFilter)
815 finish = time.time()
816 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
817 start, finish, success)
818
819 if mp.HasChanges:
820 syncbuf = SyncBuffer(mp.config)
821 start = time.time()
822 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
823 clean = syncbuf.Finish()
824 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
825 start, time.time(), clean)
826 if not clean:
827 sys.exit(1)
828 self._ReloadManifest(opt.manifest_name)
829 if opt.jobs is None:
830 self.jobs = self.manifest.default.sync_j
831
Mike Frysingerae6cb082019-08-27 01:10:59 -0400832 def ValidateOptions(self, opt, args):
833 if opt.force_broken:
834 print('warning: -f/--force-broken is now the default behavior, and the '
835 'options are deprecated', file=sys.stderr)
836 if opt.network_only and opt.detach_head:
837 self.OptionParser.error('cannot combine -n and -d')
838 if opt.network_only and opt.local_only:
839 self.OptionParser.error('cannot combine -n and -l')
840 if opt.manifest_name and opt.smart_sync:
841 self.OptionParser.error('cannot combine -m and -s')
842 if opt.manifest_name and opt.smart_tag:
843 self.OptionParser.error('cannot combine -m and -t')
844 if opt.manifest_server_username or opt.manifest_server_password:
845 if not (opt.smart_sync or opt.smart_tag):
846 self.OptionParser.error('-u and -p may only be combined with -s or -t')
847 if None in [opt.manifest_server_username, opt.manifest_server_password]:
848 self.OptionParser.error('both -u and -p must be given')
849
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700850 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800851 if opt.jobs:
852 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700853 if self.jobs > 1:
854 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400855 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700856
Mike Frysinger521d01b2020-02-17 01:51:49 -0500857 opt.quiet = opt.output_mode is False
858 opt.verbose = opt.output_mode is True
859
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500860 if opt.manifest_name:
861 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700862
Chirayu Desaia892b102013-06-11 14:18:46 +0530863 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900864 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900865 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530866
Xin Lid79a4bc2020-05-20 16:03:45 -0700867 if opt.clone_bundle is None:
868 opt.clone_bundle = self.manifest.CloneBundle
869
Victor Boivie08c880d2011-04-19 10:32:52 +0200870 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400871 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
872 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900873 if os.path.isfile(smart_sync_manifest_path):
874 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800875 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900876 except OSError as e:
877 print('error: failed to remove existing smart sync override manifest: %s' %
878 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700879
Mike Frysinger5a033082019-09-23 19:21:20 -0400880 err_event = _threading.Event()
881
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700882 rp = self.manifest.repoProject
883 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500884 cb = rp.CurrentBranch
885 if cb:
886 base = rp.GetBranch(cb).merge
887 if not base or not base.startswith('refs/heads/'):
888 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400889 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500890 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700891
892 mp = self.manifest.manifestProject
893 mp.PreSync()
894
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800895 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700896 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800897
Fredrik de Grootcc960972019-11-22 09:04:31 +0100898 if not opt.mp_update:
899 print('Skipping update of local manifest project.')
900 else:
901 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700902
Raman Tenneti55d6a5a2021-02-24 14:37:01 -0800903 if (opt.use_superproject or
904 self.manifest.manifestProject.config.GetBoolean(
905 'repo.superproject')):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800906 manifest_name = self._UpdateProjectsRevisionId(opt, args)
907
Simran Basib9a1b732015-08-20 12:19:28 -0700908 if self.gitc_manifest:
909 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700910 missing_ok=True)
911 gitc_projects = []
912 opened_projects = []
913 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700914 if project.relpath in self.gitc_manifest.paths and \
915 self.gitc_manifest.paths[project.relpath].old_revision:
916 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700917 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700918 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700919
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700920 if not args:
921 gitc_projects = None
922
923 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700924 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700925 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
926 if manifest_name:
927 manifest.Override(manifest_name)
928 else:
929 manifest.Override(self.manifest.manifestFile)
930 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
931 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700932 gitc_projects)
933 print('GITC client successfully synced.')
934
935 # The opened projects need to be synced as normal, therefore we
936 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700937 # TODO: make this more reliable -- if there's a project name/path overlap,
938 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900939 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
940 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700941 if not args:
942 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800943 all_projects = self.GetProjects(args,
944 missing_ok=True,
945 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700946
Mike Frysinger5a033082019-09-23 19:21:20 -0400947 err_network_sync = False
948 err_update_projects = False
949 err_checkout = False
950
Dave Borowitz67700e92012-10-23 15:00:54 -0700951 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700952 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700953 to_fetch = []
954 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700955 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700956 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900957 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700958 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700959
Mike Frysinger5a033082019-09-23 19:21:20 -0400960 fetched = self._Fetch(to_fetch, opt, err_event)
961
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500962 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700963 if opt.network_only:
964 # bail out now; the rest touches the working tree
Mike Frysingerbe24a542021-02-23 03:24:12 -0500965 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400966 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
967 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700968 return
969
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800970 # Iteratively fetch missing and/or nested unregistered submodules
971 previously_missing_set = set()
972 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100973 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800974 all_projects = self.GetProjects(args,
975 missing_ok=True,
976 submodules_ok=opt.fetch_submodules)
977 missing = []
978 for project in all_projects:
979 if project.gitdir not in fetched:
980 missing.append(project)
981 if not missing:
982 break
983 # Stop us from non-stopped fetching actually-missing repos: If set of
984 # missing repos has not been changed from last fetch, we break.
985 missing_set = set(p.name for p in missing)
986 if previously_missing_set == missing_set:
987 break
988 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400989 fetched.update(self._Fetch(missing, opt, err_event))
990
991 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500992 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400993 err_network_sync = True
994 if opt.fail_fast:
995 print('\nerror: Exited sync due to fetch errors.\n'
996 'Local checkouts *not* updated. Resolve network issues & '
997 'retry.\n'
998 '`repo sync -l` will update some local checkouts.',
999 file=sys.stderr)
1000 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001001
Julien Campergue335f5ef2013-10-16 11:02:35 +02001002 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001003 # bail out now, we have no working tree
1004 return
1005
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001006 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001007 err_event.set()
1008 err_update_projects = True
1009 if opt.fail_fast:
1010 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1011 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001012
Mike Frysinger5a033082019-09-23 19:21:20 -04001013 err_results = []
1014 self._Checkout(all_projects, opt, err_event, err_results)
Mike Frysingerbe24a542021-02-23 03:24:12 -05001015 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001016 err_checkout = True
1017 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001018
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001019 # If there's a notice that's supposed to print at the end of the sync, print
1020 # it now...
1021 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001022 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001023
Mike Frysinger5a033082019-09-23 19:21:20 -04001024 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001025 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001026 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1027 if err_network_sync:
1028 print('error: Downloading network changes failed.', file=sys.stderr)
1029 if err_update_projects:
1030 print('error: Updating local project lists failed.', file=sys.stderr)
1031 if err_checkout:
1032 print('error: Checking out local projects failed.', file=sys.stderr)
1033 if err_results:
1034 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1035 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1036 file=sys.stderr)
1037 sys.exit(1)
1038
Mike Frysingere19d9e12020-02-12 11:23:32 -05001039 if not opt.quiet:
1040 print('repo sync has finished successfully.')
1041
David Pursehouse819827a2020-02-12 15:20:19 +09001042
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001043def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001044 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001045 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001046 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001047 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001048 if project.Exists:
1049 project.PostRepoUpgrade()
1050
David Pursehouse819827a2020-02-12 15:20:19 +09001051
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001052def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001053 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001054 print('info: A new version of repo is available', file=sys.stderr)
1055 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001056 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001057 syncbuf = SyncBuffer(rp.config)
1058 rp.Sync_LocalHalf(syncbuf)
1059 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001060 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001061 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001062 raise RepoChangedException(['--repo-upgraded'])
1063 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001064 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001065 else:
1066 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001067 print('repo version %s is current' % rp.work_git.describe(HEAD),
1068 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001069
David Pursehouse819827a2020-02-12 15:20:19 +09001070
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001071def _VerifyTag(project):
1072 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1073 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001074 print('warning: GnuPG was not available during last "repo init"\n'
1075 'warning: Cannot automatically authenticate repo."""',
1076 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001077 return True
1078
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001079 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001080 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001081 except GitError:
1082 cur = None
1083
1084 if not cur \
1085 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001086 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001087 if rev.startswith(R_HEADS):
1088 rev = rev[len(R_HEADS):]
1089
Sarah Owenscecd1d82012-11-01 22:59:27 -07001090 print(file=sys.stderr)
1091 print("warning: project '%s' branch '%s' is not signed"
1092 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001093 return False
1094
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001095 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001096 env['GIT_DIR'] = project.gitdir
1097 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001098
1099 cmd = [GIT, 'tag', '-v', cur]
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001100 result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
1101 env=env, check=False)
1102 if result.returncode:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001103 print(file=sys.stderr)
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001104 print(result.stdout, file=sys.stderr)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001105 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001106 return False
1107 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001108
David Rileye0684ad2017-04-05 00:02:59 -07001109
Dave Borowitz67700e92012-10-23 15:00:54 -07001110class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001111 _ALPHA = 0.5
1112
Dave Borowitz67700e92012-10-23 15:00:54 -07001113 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001114 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001115 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001116 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001117
1118 def Get(self, project):
1119 self._Load()
1120 return self._times.get(project.name, _ONE_DAY_S)
1121
1122 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001123 self._Load()
1124 name = project.name
1125 old = self._times.get(name, t)
1126 self._seen.add(name)
1127 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001128 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001129
1130 def _Load(self):
1131 if self._times is None:
1132 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001133 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001134 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001135 except (IOError, ValueError):
1136 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001137 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001138 except OSError:
1139 pass
1140 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001141
1142 def Save(self):
1143 if self._times is None:
1144 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001145
1146 to_delete = []
1147 for name in self._times:
1148 if name not in self._seen:
1149 to_delete.append(name)
1150 for name in to_delete:
1151 del self._times[name]
1152
Dave Borowitz67700e92012-10-23 15:00:54 -07001153 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001154 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001155 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001156 except (IOError, TypeError):
1157 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001158 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001159 except OSError:
1160 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001161
1162# This is a replacement for xmlrpc.client.Transport using urllib2
1163# and supporting persistent-http[s]. It cannot change hosts from
1164# request to request like the normal transport, the real url
1165# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001166
1167
Dan Willemsen0745bb22015-08-17 13:41:45 -07001168class PersistentTransport(xmlrpc.client.Transport):
1169 def __init__(self, orig_host):
1170 self.orig_host = orig_host
1171
1172 def request(self, host, handler, request_body, verbose=False):
1173 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1174 # Python doesn't understand cookies with the #HttpOnly_ prefix
1175 # Since we're only using them for HTTP, copy the file temporarily,
1176 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001177 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001178 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001179 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001180 try:
1181 with open(cookiefile) as f:
1182 for line in f:
1183 if line.startswith("#HttpOnly_"):
1184 line = line[len("#HttpOnly_"):]
1185 tmpcookiefile.write(line)
1186 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001187
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001188 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001189 try:
1190 cookiejar.load()
1191 except cookielib.LoadError:
1192 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001193 finally:
1194 tmpcookiefile.close()
1195 else:
1196 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001197
1198 proxyhandler = urllib.request.ProxyHandler
1199 if proxy:
1200 proxyhandler = urllib.request.ProxyHandler({
1201 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001202 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001203
1204 opener = urllib.request.build_opener(
1205 urllib.request.HTTPCookieProcessor(cookiejar),
1206 proxyhandler)
1207
1208 url = urllib.parse.urljoin(self.orig_host, handler)
1209 parse_results = urllib.parse.urlparse(url)
1210
1211 scheme = parse_results.scheme
1212 if scheme == 'persistent-http':
1213 scheme = 'http'
1214 if scheme == 'persistent-https':
1215 # If we're proxying through persistent-https, use http. The
1216 # proxy itself will do the https.
1217 if proxy:
1218 scheme = 'http'
1219 else:
1220 scheme = 'https'
1221
1222 # Parse out any authentication information using the base class
1223 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1224
1225 url = urllib.parse.urlunparse((
1226 scheme,
1227 host,
1228 parse_results.path,
1229 parse_results.params,
1230 parse_results.query,
1231 parse_results.fragment))
1232
1233 request = urllib.request.Request(url, request_body)
1234 if extra_headers is not None:
1235 for (name, header) in extra_headers:
1236 request.add_header(name, header)
1237 request.add_header('Content-Type', 'text/xml')
1238 try:
1239 response = opener.open(request)
1240 except urllib.error.HTTPError as e:
1241 if e.code == 501:
1242 # We may have been redirected through a login process
1243 # but our POST turned into a GET. Retry.
1244 response = opener.open(request)
1245 else:
1246 raise
1247
1248 p, u = xmlrpc.client.getparser()
1249 while 1:
1250 data = response.read(1024)
1251 if not data:
1252 break
1253 p.feed(data)
1254 p.close()
1255 return u.close()
1256
1257 def close(self):
1258 pass