blob: eada76a7521175abe8c48d82153a1cec9a6c69c7 [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Mike Frysinger979d5bd2020-02-09 02:28:34 -050018
19import errno
Anthony King85b24ac2014-05-06 15:57:48 +010020import json
David Pursehouse86d973d2012-08-24 10:21:02 +090021import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070022from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import os
24import re
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070025import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070026import subprocess
27import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070028import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070029import time
David Pursehouse59bbb582013-05-17 10:49:33 +090030
31from pyversion import is_python3
32if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070033 import http.cookiejar as cookielib
34 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053035 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070036 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090037 import xmlrpc.client
38else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070039 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053040 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070041 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053042 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090043 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053044 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070045 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053046 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070047 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053048 xmlrpc = imp.new_module('xmlrpc')
49 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070050
Roy Lee18afd7f2010-05-09 04:32:08 +080051try:
52 import threading as _threading
53except ImportError:
54 import dummy_threading as _threading
55
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070056try:
57 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090058
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070059 def _rlimit_nofile():
60 return resource.getrlimit(resource.RLIMIT_NOFILE)
61except ImportError:
62 def _rlimit_nofile():
63 return (256, 256)
64
Dave Borowitz18857212012-10-23 17:02:59 -070065try:
66 import multiprocessing
67except ImportError:
68 multiprocessing = None
69
David Rileye0684ad2017-04-05 00:02:59 -070070import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070071from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090072from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090073from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070074import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070075from project import Project
76from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080077from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000078from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070079import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070080from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070081from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080082from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070083from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070084
Dave Borowitz67700e92012-10-23 15:00:54 -070085_ONE_DAY_S = 24 * 60 * 60
86
David Pursehouse819827a2020-02-12 15:20:19 +090087
Doug Andersonfc06ced2011-03-16 15:49:18 -070088class _FetchError(Exception):
89 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
90 pass
91
David Pursehouse819827a2020-02-12 15:20:19 +090092
Xin Li745be2e2019-06-03 11:24:30 -070093class _CheckoutError(Exception):
94 """Internal error thrown in _CheckoutOne() when we don't want stack trace."""
95
David Pursehouse819827a2020-02-12 15:20:19 +090096
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080097class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080098 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070099 common = True
100 helpSummary = "Update working tree to the latest revision"
101 helpUsage = """
102%prog [<project>...]
103"""
104 helpDescription = """
105The '%prog' command synchronizes local project directories
106with the remote repositories specified in the manifest. If a local
107project does not yet exist, it will clone a new local directory from
108the remote repository and set up tracking branches as specified in
109the manifest. If the local project already exists, '%prog'
110will update the remote branches and rebase any new local changes
111on top of the new remote changes.
112
113'%prog' will synchronize all projects listed at the command
114line. Projects can be specified either by name, or by a relative
115or absolute path to the project's local directory. If no projects
116are specified, '%prog' will synchronize all projects listed in
117the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700118
119The -d/--detach option can be used to switch specified projects
120back to the manifest revision. This option is especially helpful
121if the project is currently on a topic branch, but the manifest
122revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700123
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700124The -s/--smart-sync option can be used to sync to a known good
125build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200126manifest. The -t/--smart-tag option is similar and allows you to
127specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700128
David Pursehousecf76b1b2012-09-14 10:31:42 +0900129The -u/--manifest-server-username and -p/--manifest-server-password
130options can be used to specify a username and password to authenticate
131with the manifest server when using the -s or -t option.
132
133If -u and -p are not specified when using the -s or -t option, '%prog'
134will attempt to read authentication credentials for the manifest server
135from the user's .netrc file.
136
137'%prog' will not use authentication credentials from -u/-p or .netrc
138if the manifest server specified in the manifest file already includes
139credentials.
140
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400141By default, all projects will be synced. The --fail-fast option can be used
142to halt syncing as soon as possible when the the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500143
Kevin Degiabaa7f32014-11-12 11:27:45 -0700144The --force-sync option can be used to overwrite existing git
145directories if they have previously been linked to a different
146object direcotry. WARNING: This may cause data to be lost since
147refs may be removed when overwriting.
148
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500149The --force-remove-dirty option can be used to remove previously used
150projects with uncommitted changes. WARNING: This may cause data to be
151lost since uncommitted changes may be removed with projects that no longer
152exist in the manifest.
153
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700154The --no-clone-bundle option disables any attempt to use
155$URL/clone.bundle to bootstrap a new Git repository from a
156resumeable bundle file on a content delivery network. This
157may be necessary if there are problems with the local Python
158HTTP client or proxy configuration, but the Git binary works.
159
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800160The --fetch-submodules option enables fetching Git submodules
161of a project from server.
162
David Pursehousef2fad612015-01-29 14:36:28 +0900163The -c/--current-branch option can be used to only fetch objects that
164are on the branch specified by a project's revision.
165
David Pursehouseb1553542014-09-04 21:28:09 +0900166The --optimized-fetch option can be used to only fetch projects that
167are fixed to a sha1 revision if the sha1 revision does not already
168exist locally.
169
David Pursehouse74cfd272015-10-14 10:50:15 +0900170The --prune option can be used to remove any refs that no longer
171exist on the remote.
172
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400173# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700174
175If at least one project remote URL uses an SSH connection (ssh://,
176git+ssh://, or user@host:path syntax) repo will automatically
177enable the SSH ControlMaster option when connecting to that host.
178This feature permits other projects in the same '%prog' session to
179reuse the same SSH tunnel, saving connection setup overheads.
180
181To disable this behavior on UNIX platforms, set the GIT_SSH
182environment variable to 'ssh'. For example:
183
184 export GIT_SSH=ssh
185 %prog
186
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400187# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700188
189This feature is automatically disabled on Windows, due to the lack
190of UNIX domain socket support.
191
192This feature is not compatible with url.insteadof rewrites in the
193user's ~/.gitconfig. '%prog' is currently not able to perform the
194rewrite early enough to establish the ControlMaster tunnel.
195
196If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
197later is required to fix a server side protocol bug.
198
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700199"""
200
Nico Sallembien6623b212010-05-11 12:57:01 -0700201 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000202 try:
203 self.jobs = self.manifest.default.sync_j
204 except ManifestParseError:
205 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700206
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500207 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200208 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400209 help='obsolete option (to be deleted in the future)')
210 p.add_option('--fail-fast',
211 dest='fail_fast', action='store_true',
212 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700213 p.add_option('--force-sync',
214 dest='force_sync', action='store_true',
215 help="overwrite an existing git directory if it needs to "
216 "point to a different object directory. WARNING: this "
217 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500218 p.add_option('--force-remove-dirty',
219 dest='force_remove_dirty', action='store_true',
220 help="force remove projects with uncommitted modifications if "
221 "projects no longer exist in the manifest. "
222 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900223 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700224 dest='local_only', action='store_true',
225 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900226 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100227 dest='mp_update', action='store_false', default='true',
228 help='use the existing manifest checkout as-is. '
229 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900230 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700231 dest='network_only', action='store_true',
232 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900233 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700234 dest='detach_head', action='store_true',
235 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900236 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700237 dest='current_branch_only', action='store_true',
238 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500239 p.add_option('-v', '--verbose',
240 dest='output_mode', action='store_true',
241 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900242 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500243 dest='output_mode', action='store_false',
244 help='only show errors')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900245 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800246 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700247 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500248 p.add_option('-m', '--manifest-name',
249 dest='manifest_name',
250 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700251 p.add_option('--no-clone-bundle',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500252 dest='clone_bundle', default=True, action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700253 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800254 p.add_option('-u', '--manifest-server-username', action='store',
255 dest='manifest_server_username',
256 help='username to authenticate with the manifest server')
257 p.add_option('-p', '--manifest-server-password', action='store',
258 dest='manifest_server_password',
259 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800260 p.add_option('--fetch-submodules',
261 dest='fetch_submodules', action='store_true',
262 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700263 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500264 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700265 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900266 p.add_option('--optimized-fetch',
267 dest='optimized_fetch', action='store_true',
268 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900269 p.add_option('--prune', dest='prune', action='store_true',
270 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700271 if show_smart:
272 p.add_option('-s', '--smart-sync',
273 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900274 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200275 p.add_option('-t', '--smart-tag',
276 dest='smart_tag', action='store',
277 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700278
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700279 g = p.add_option_group('repo Version options')
280 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500281 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700282 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700283 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800284 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700285 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700286
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500287 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700288 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800289
David James8d201162013-10-11 17:03:19 -0700290 Delegates most of the work to _FetchHelper.
291
292 Args:
293 opt: Program options returned from optparse. See _Options().
294 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500295 sem: We'll release() this semaphore when we exit so that another thread
296 can be started up.
David James89ece422014-01-09 18:51:58 -0800297 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700298 _FetchHelper docstring for details.
299 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500300 try:
301 for project in projects:
302 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400303 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500304 break
305 finally:
306 sem.release()
David James8d201162013-10-11 17:03:19 -0700307
Xin Li745be2e2019-06-03 11:24:30 -0700308 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
309 clone_filter):
David James8d201162013-10-11 17:03:19 -0700310 """Fetch git objects for a single project.
311
David Pursehousec1b86a22012-11-14 11:36:51 +0900312 Args:
313 opt: Program options returned from optparse. See _Options().
314 project: Project object for the project to fetch.
315 lock: Lock for accessing objects that are shared amongst multiple
316 _FetchHelper() threads.
317 fetched: set object that we will add project.gitdir to when we're done
318 (with our lock held).
319 pm: Instance of a Project object. We will call pm.update() (with our
320 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900321 err_event: We'll set this event in the case of an error (after printing
322 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700323 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700324
325 Returns:
326 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900327 """
328 # We'll set to true once we've locked the lock.
329 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700330
David Pursehousec1b86a22012-11-14 11:36:51 +0900331 # Encapsulate everything in a try/except/finally so that:
332 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900333 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700334 start = time.time()
335 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900336 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700337 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900338 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900339 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500340 verbose=opt.verbose,
David Pursehouseabdf7502020-02-12 14:58:39 +0900341 current_branch_only=opt.current_branch_only,
342 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500343 clone_bundle=opt.clone_bundle,
344 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900345 optimized_fetch=opt.optimized_fetch,
346 prune=opt.prune,
347 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900348 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700349
David Pursehousec1b86a22012-11-14 11:36:51 +0900350 # Lock around all the rest of the code, since printing, updating a set
351 # and Progress.update() are not thread safe.
352 lock.acquire()
353 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700354
David Pursehousec1b86a22012-11-14 11:36:51 +0900355 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800356 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700357 print('error: Cannot fetch %s from %s'
358 % (project.name, project.remote.url),
359 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400360 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900361 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700362
David Pursehousec1b86a22012-11-14 11:36:51 +0900363 fetched.add(project.gitdir)
Mike Frysinger3538dd22019-08-26 15:32:06 -0400364 pm.update(msg=project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900365 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800366 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400367 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900368 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900369 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900370 err_event.set()
371 raise
372 finally:
373 if did_lock:
374 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700375 finish = time.time()
376 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
377 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800378
David James8d201162013-10-11 17:03:19 -0700379 return success
380
Mike Frysinger5a033082019-09-23 19:21:20 -0400381 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700382 fetched = set()
David James89ece422014-01-09 18:51:58 -0800383 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200384 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200385 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800386
David James89ece422014-01-09 18:51:58 -0800387 objdir_project_map = dict()
388 for project in projects:
389 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700390
David James89ece422014-01-09 18:51:58 -0800391 threads = set()
392 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800393 for project_list in objdir_project_map.values():
394 # Check for any errors before running any more tasks.
395 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400396 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800397 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700398
David James89ece422014-01-09 18:51:58 -0800399 sem.acquire()
400 kwargs = dict(opt=opt,
401 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500402 sem=sem,
David James89ece422014-01-09 18:51:58 -0800403 lock=lock,
404 fetched=fetched,
405 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700406 err_event=err_event,
407 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800408 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900409 t = _threading.Thread(target=self._FetchProjectList,
410 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200411 # Ensure that Ctrl-C will not freeze the repo process.
412 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800413 threads.add(t)
414 t.start()
David James89ece422014-01-09 18:51:58 -0800415 else:
416 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800417
David James89ece422014-01-09 18:51:58 -0800418 for t in threads:
419 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800420
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700421 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700422 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700423
Julien Campergue335f5ef2013-10-16 11:02:35 +0200424 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400425 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200426
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700427 return fetched
428
Xin Li745be2e2019-06-03 11:24:30 -0700429 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
430 """Main function of the fetch threads.
431
432 Delegates most of the work to _CheckoutOne.
433
434 Args:
435 opt: Program options returned from optparse. See _Options().
436 projects: Projects to fetch.
437 sem: We'll release() this semaphore when we exit so that another thread
438 can be started up.
439 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
440 _CheckoutOne docstring for details.
441 """
442 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400443 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700444 finally:
445 sem.release()
446
Vadim Bendeburydff91942019-11-06 11:05:00 -0800447 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700448 """Checkout work tree for one project
449
450 Args:
451 opt: Program options returned from optparse. See _Options().
452 project: Project object for the project to checkout.
453 lock: Lock for accessing objects that are shared amongst multiple
454 _CheckoutWorker() threads.
455 pm: Instance of a Project object. We will call pm.update() (with our
456 lock held).
457 err_event: We'll set this event in the case of an error (after printing
458 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800459 err_results: A list of strings, paths to git repos where checkout
460 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700461
462 Returns:
463 Whether the fetch was successful.
464 """
465 # We'll set to true once we've locked the lock.
466 did_lock = False
467
Xin Li745be2e2019-06-03 11:24:30 -0700468 # Encapsulate everything in a try/except/finally so that:
469 # - We always set err_event in the case of an exception.
470 # - We always make sure we unlock the lock if we locked it.
471 start = time.time()
472 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
473 detach_head=opt.detach_head)
474 success = False
475 try:
476 try:
477 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700478
479 # Lock around all the rest of the code, since printing, updating a set
480 # and Progress.update() are not thread safe.
481 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400482 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700483 did_lock = True
484
485 if not success:
486 err_event.set()
487 print('error: Cannot checkout %s' % (project.name),
488 file=sys.stderr)
489 raise _CheckoutError()
490
Mike Frysinger3538dd22019-08-26 15:32:06 -0400491 pm.update(msg=project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700492 except _CheckoutError:
493 pass
494 except Exception as e:
495 print('error: Cannot checkout %s: %s: %s' %
496 (project.name, type(e).__name__, str(e)),
497 file=sys.stderr)
498 err_event.set()
499 raise
500 finally:
501 if did_lock:
Vadim Bendeburydff91942019-11-06 11:05:00 -0800502 if not success:
503 err_results.append(project.relpath)
Xin Li745be2e2019-06-03 11:24:30 -0700504 lock.release()
505 finish = time.time()
506 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
507 start, finish, success)
508
509 return success
510
Mike Frysinger5a033082019-09-23 19:21:20 -0400511 def _Checkout(self, all_projects, opt, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700512 """Checkout projects listed in all_projects
513
514 Args:
515 all_projects: List of all projects that should be checked out.
516 opt: Program options returned from optparse. See _Options().
Mike Frysinger5a033082019-09-23 19:21:20 -0400517 err_event: We'll set this event in the case of an error (after printing
518 out info about the error).
519 err_results: A list of strings, paths to git repos where checkout
520 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700521 """
522
523 # Perform checkouts in multiple threads when we are using partial clone.
524 # Without partial clone, all needed git objects are already downloaded,
525 # in this situation it's better to use only one process because the checkout
526 # would be mostly disk I/O; with partial clone, the objects are only
527 # downloaded when demanded (at checkout time), which is similar to the
528 # Sync_NetworkHalf case and parallelism would be helpful.
529 if self.manifest.CloneFilter:
530 syncjobs = self.jobs
531 else:
532 syncjobs = 1
533
534 lock = _threading.Lock()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400535 pm = Progress('Checking out projects', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700536
537 threads = set()
538 sem = _threading.Semaphore(syncjobs)
Xin Li745be2e2019-06-03 11:24:30 -0700539
540 for project in all_projects:
541 # Check for any errors before running any more tasks.
542 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400543 if err_event.isSet() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700544 break
545
546 sem.acquire()
547 if project.worktree:
548 kwargs = dict(opt=opt,
549 sem=sem,
550 project=project,
551 lock=lock,
552 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800553 err_event=err_event,
554 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700555 if syncjobs > 1:
556 t = _threading.Thread(target=self._CheckoutWorker,
557 kwargs=kwargs)
558 # Ensure that Ctrl-C will not freeze the repo process.
559 t.daemon = True
560 threads.add(t)
561 t.start()
562 else:
563 self._CheckoutWorker(**kwargs)
564
565 for t in threads:
566 t.join()
567
568 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700569
Mike Frysinger5a033082019-09-23 19:21:20 -0400570 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700571 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700572 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500573 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500574 if (not project.use_git_worktrees and
575 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500576 print('%s: Shared project %s found, disabling pruning.' %
577 (project.relpath, project.name))
578 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500579 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500580 else:
581 # This isn't perfect, but it's the best we can do with old git.
582 print('%s: WARNING: shared projects are unreliable when using old '
583 'versions of git; please upgrade to git-2.7.0+.'
584 % (project.relpath,),
585 file=sys.stderr)
586 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700587 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700588
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500589 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700590 cpu_count = multiprocessing.cpu_count()
591 else:
592 cpu_count = 1
593 jobs = min(self.jobs, cpu_count)
594
595 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700596 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700597 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700598 return
599
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400600 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700601
602 threads = set()
603 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700604
David James8d201162013-10-11 17:03:19 -0700605 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700606 try:
607 try:
David James8d201162013-10-11 17:03:19 -0700608 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700609 except GitError:
610 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900611 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700612 err_event.set()
613 raise
614 finally:
615 sem.release()
616
Gabe Black2ff30292014-10-09 17:54:35 -0700617 for bare_git in gc_gitdirs.values():
Mike Frysinger5a033082019-09-23 19:21:20 -0400618 if err_event.isSet() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700619 break
620 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700621 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700622 t.daemon = True
623 threads.add(t)
624 t.start()
625
626 for t in threads:
627 t.join()
628
Tim Kilbourn07669002013-03-08 15:02:49 -0800629 def _ReloadManifest(self, manifest_name=None):
630 if manifest_name:
631 # Override calls _Unload already
632 self.manifest.Override(manifest_name)
633 else:
634 self.manifest._Unload()
635
Dan Willemsen43507912016-09-01 16:26:02 -0700636 def _DeleteProject(self, path):
637 print('Deleting obsolete path %s' % path, file=sys.stderr)
638
639 # Delete the .git directory first, so we're less likely to have a partially
640 # working git repository around. There shouldn't be any git projects here,
641 # so rmtree works.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500642 dotgit = os.path.join(path, '.git')
643 # Try to remove plain files first in case of git worktrees. If this fails
644 # for any reason, we'll fall back to rmtree, and that'll display errors if
645 # it can't remove things either.
Dan Willemsen43507912016-09-01 16:26:02 -0700646 try:
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500647 platform_utils.remove(dotgit)
648 except OSError:
649 pass
650 try:
651 platform_utils.rmtree(dotgit)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700652 except OSError as e:
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500653 if e.errno != errno.ENOENT:
654 print('error: %s: %s' % (dotgit, str(e)), file=sys.stderr)
655 print('error: %s: Failed to delete obsolete path; remove manually, then '
656 'run sync again' % (path,), file=sys.stderr)
657 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700658
659 # Delete everything under the worktree, except for directories that contain
660 # another git project
661 dirs_to_remove = []
662 failed = False
Renaud Paquaybed8b622018-09-27 10:46:58 -0700663 for root, dirs, files in platform_utils.walk(path):
Dan Willemsen43507912016-09-01 16:26:02 -0700664 for f in files:
665 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800666 platform_utils.remove(os.path.join(root, f))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700667 except OSError as e:
668 print('Failed to remove %s (%s)' % (os.path.join(root, f), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700669 failed = True
670 dirs[:] = [d for d in dirs
671 if not os.path.lexists(os.path.join(root, d, '.git'))]
672 dirs_to_remove += [os.path.join(root, d) for d in dirs
673 if os.path.join(root, d) not in dirs_to_remove]
674 for d in reversed(dirs_to_remove):
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700675 if platform_utils.islink(d):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700676 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800677 platform_utils.remove(d)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700678 except OSError as e:
679 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700680 failed = True
Renaud Paquaybed8b622018-09-27 10:46:58 -0700681 elif len(platform_utils.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700682 try:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700683 platform_utils.rmdir(d)
684 except OSError as e:
685 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700686 failed = True
687 continue
688 if failed:
689 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
690 print(' remove manually, then run sync again', file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400691 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700692
693 # Try deleting parent dirs if they are empty
694 project_dir = path
695 while project_dir != self.manifest.topdir:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700696 if len(platform_utils.listdir(project_dir)) == 0:
697 platform_utils.rmdir(project_dir)
Dan Willemsen43507912016-09-01 16:26:02 -0700698 else:
699 break
700 project_dir = os.path.dirname(project_dir)
701
702 return 0
703
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500704 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700705 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700706 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700707 if project.relpath:
708 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700709 file_name = 'project.list'
710 file_path = os.path.join(self.manifest.repodir, file_name)
711 old_project_paths = []
712
713 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500714 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700715 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800716 # In reversed order, so subfolders are deleted before parent folder.
717 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700718 if not path:
719 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700720 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900721 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700722 gitdir = os.path.join(self.manifest.topdir, path, '.git')
723 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900724 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900725 manifest=self.manifest,
726 name=path,
727 remote=RemoteSpec('origin'),
728 gitdir=gitdir,
729 objdir=gitdir,
730 worktree=os.path.join(self.manifest.topdir, path),
731 relpath=path,
732 revisionExpr='HEAD',
733 revisionId=None,
734 groups=None)
Anthonyf3fdf822009-09-26 13:38:52 -0400735
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500736 if project.IsDirty() and opt.force_remove_dirty:
737 print('WARNING: Removing dirty project "%s": uncommitted changes '
738 'erased' % project.relpath, file=sys.stderr)
739 self._DeleteProject(project.worktree)
740 elif project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900741 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900742 'are present' % project.relpath, file=sys.stderr)
743 print(' commit changes, then run sync again',
744 file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400745 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700746 elif self._DeleteProject(project.worktree):
Mike Frysingera850ca22019-08-07 17:19:24 -0400747 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700748
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700749 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500750 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700751 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700752 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700753 return 0
754
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400755 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
756 if not self.manifest.manifest_server:
757 print('error: cannot smart sync: no manifest server defined in '
758 'manifest', file=sys.stderr)
759 sys.exit(1)
760
761 manifest_server = self.manifest.manifest_server
762 if not opt.quiet:
763 print('Using manifest server %s' % manifest_server)
764
David Pursehouseeeff3532020-02-12 11:24:10 +0900765 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400766 username = None
767 password = None
768 if opt.manifest_server_username and opt.manifest_server_password:
769 username = opt.manifest_server_username
770 password = opt.manifest_server_password
771 else:
772 try:
773 info = netrc.netrc()
774 except IOError:
775 # .netrc file does not exist or could not be opened
776 pass
777 else:
778 try:
779 parse_result = urllib.parse.urlparse(manifest_server)
780 if parse_result.hostname:
781 auth = info.authenticators(parse_result.hostname)
782 if auth:
783 username, _account, password = auth
784 else:
785 print('No credentials found for %s in .netrc'
786 % parse_result.hostname, file=sys.stderr)
787 except netrc.NetrcParseError as e:
788 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
789
790 if (username and password):
791 manifest_server = manifest_server.replace('://', '://%s:%s@' %
792 (username, password),
793 1)
794
795 transport = PersistentTransport(manifest_server)
796 if manifest_server.startswith('persistent-'):
797 manifest_server = manifest_server[len('persistent-'):]
798
799 try:
800 server = xmlrpc.client.Server(manifest_server, transport=transport)
801 if opt.smart_sync:
802 p = self.manifest.manifestProject
803 b = p.GetBranch(p.CurrentBranch)
804 branch = b.merge
805 if branch.startswith(R_HEADS):
806 branch = branch[len(R_HEADS):]
807
Mike Frysinger56ce3462019-12-04 19:30:48 -0500808 if 'SYNC_TARGET' in os.environ:
809 target = os.environ('SYNC_TARGET')
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400810 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500811 elif ('TARGET_PRODUCT' in os.environ and
812 'TARGET_BUILD_VARIANT' in os.environ):
813 target = '%s-%s' % (os.environ('TARGET_PRODUCT'),
814 os.environ('TARGET_BUILD_VARIANT'))
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400815 [success, manifest_str] = server.GetApprovedManifest(branch, target)
816 else:
817 [success, manifest_str] = server.GetApprovedManifest(branch)
818 else:
819 assert(opt.smart_tag)
820 [success, manifest_str] = server.GetManifest(opt.smart_tag)
821
822 if success:
823 manifest_name = os.path.basename(smart_sync_manifest_path)
824 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500825 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400826 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400827 except IOError as e:
828 print('error: cannot write manifest to %s:\n%s'
829 % (smart_sync_manifest_path, e),
830 file=sys.stderr)
831 sys.exit(1)
832 self._ReloadManifest(manifest_name)
833 else:
834 print('error: manifest server RPC call failed: %s' %
835 manifest_str, file=sys.stderr)
836 sys.exit(1)
837 except (socket.error, IOError, xmlrpc.client.Fault) as e:
838 print('error: cannot connect to manifest server %s:\n%s'
839 % (self.manifest.manifest_server, e), file=sys.stderr)
840 sys.exit(1)
841 except xmlrpc.client.ProtocolError as e:
842 print('error: cannot connect to manifest server %s:\n%d %s'
843 % (self.manifest.manifest_server, e.errcode, e.errmsg),
844 file=sys.stderr)
845 sys.exit(1)
846
847 return manifest_name
848
Mike Frysingerfb527e32019-08-27 02:34:32 -0400849 def _UpdateManifestProject(self, opt, mp, manifest_name):
850 """Fetch & update the local manifest project."""
851 if not opt.local_only:
852 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500853 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400854 current_branch_only=opt.current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500855 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400856 optimized_fetch=opt.optimized_fetch,
857 submodules=self.manifest.HasSubmodules,
858 clone_filter=self.manifest.CloneFilter)
859 finish = time.time()
860 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
861 start, finish, success)
862
863 if mp.HasChanges:
864 syncbuf = SyncBuffer(mp.config)
865 start = time.time()
866 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
867 clean = syncbuf.Finish()
868 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
869 start, time.time(), clean)
870 if not clean:
871 sys.exit(1)
872 self._ReloadManifest(opt.manifest_name)
873 if opt.jobs is None:
874 self.jobs = self.manifest.default.sync_j
875
Mike Frysingerae6cb082019-08-27 01:10:59 -0400876 def ValidateOptions(self, opt, args):
877 if opt.force_broken:
878 print('warning: -f/--force-broken is now the default behavior, and the '
879 'options are deprecated', file=sys.stderr)
880 if opt.network_only and opt.detach_head:
881 self.OptionParser.error('cannot combine -n and -d')
882 if opt.network_only and opt.local_only:
883 self.OptionParser.error('cannot combine -n and -l')
884 if opt.manifest_name and opt.smart_sync:
885 self.OptionParser.error('cannot combine -m and -s')
886 if opt.manifest_name and opt.smart_tag:
887 self.OptionParser.error('cannot combine -m and -t')
888 if opt.manifest_server_username or opt.manifest_server_password:
889 if not (opt.smart_sync or opt.smart_tag):
890 self.OptionParser.error('-u and -p may only be combined with -s or -t')
891 if None in [opt.manifest_server_username, opt.manifest_server_password]:
892 self.OptionParser.error('both -u and -p must be given')
893
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700894 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800895 if opt.jobs:
896 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700897 if self.jobs > 1:
898 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400899 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700900
Mike Frysinger521d01b2020-02-17 01:51:49 -0500901 opt.quiet = opt.output_mode is False
902 opt.verbose = opt.output_mode is True
903
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500904 if opt.manifest_name:
905 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700906
Chirayu Desaia892b102013-06-11 14:18:46 +0530907 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900908 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900909 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530910
Victor Boivie08c880d2011-04-19 10:32:52 +0200911 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400912 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
913 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900914 if os.path.isfile(smart_sync_manifest_path):
915 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800916 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900917 except OSError as e:
918 print('error: failed to remove existing smart sync override manifest: %s' %
919 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700920
Mike Frysinger5a033082019-09-23 19:21:20 -0400921 err_event = _threading.Event()
922
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700923 rp = self.manifest.repoProject
924 rp.PreSync()
925
926 mp = self.manifest.manifestProject
927 mp.PreSync()
928
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800929 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700930 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800931
Fredrik de Grootcc960972019-11-22 09:04:31 +0100932 if not opt.mp_update:
933 print('Skipping update of local manifest project.')
934 else:
935 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700936
Simran Basib9a1b732015-08-20 12:19:28 -0700937 if self.gitc_manifest:
938 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700939 missing_ok=True)
940 gitc_projects = []
941 opened_projects = []
942 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700943 if project.relpath in self.gitc_manifest.paths and \
944 self.gitc_manifest.paths[project.relpath].old_revision:
945 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700946 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700947 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700948
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700949 if not args:
950 gitc_projects = None
951
952 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700953 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700954 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
955 if manifest_name:
956 manifest.Override(manifest_name)
957 else:
958 manifest.Override(self.manifest.manifestFile)
959 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
960 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700961 gitc_projects)
962 print('GITC client successfully synced.')
963
964 # The opened projects need to be synced as normal, therefore we
965 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700966 # TODO: make this more reliable -- if there's a project name/path overlap,
967 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900968 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
969 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700970 if not args:
971 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800972 all_projects = self.GetProjects(args,
973 missing_ok=True,
974 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700975
Mike Frysinger5a033082019-09-23 19:21:20 -0400976 err_network_sync = False
977 err_update_projects = False
978 err_checkout = False
979
Dave Borowitz67700e92012-10-23 15:00:54 -0700980 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700981 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700982 to_fetch = []
983 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700984 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700985 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900986 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700987 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700988
Mike Frysinger5a033082019-09-23 19:21:20 -0400989 fetched = self._Fetch(to_fetch, opt, err_event)
990
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500991 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700992 if opt.network_only:
993 # bail out now; the rest touches the working tree
Mike Frysinger5a033082019-09-23 19:21:20 -0400994 if err_event.isSet():
995 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
996 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700997 return
998
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800999 # Iteratively fetch missing and/or nested unregistered submodules
1000 previously_missing_set = set()
1001 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +01001002 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001003 all_projects = self.GetProjects(args,
1004 missing_ok=True,
1005 submodules_ok=opt.fetch_submodules)
1006 missing = []
1007 for project in all_projects:
1008 if project.gitdir not in fetched:
1009 missing.append(project)
1010 if not missing:
1011 break
1012 # Stop us from non-stopped fetching actually-missing repos: If set of
1013 # missing repos has not been changed from last fetch, we break.
1014 missing_set = set(p.name for p in missing)
1015 if previously_missing_set == missing_set:
1016 break
1017 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -04001018 fetched.update(self._Fetch(missing, opt, err_event))
1019
1020 # If we saw an error, exit with code 1 so that other scripts can check.
1021 if err_event.isSet():
1022 err_network_sync = True
1023 if opt.fail_fast:
1024 print('\nerror: Exited sync due to fetch errors.\n'
1025 'Local checkouts *not* updated. Resolve network issues & '
1026 'retry.\n'
1027 '`repo sync -l` will update some local checkouts.',
1028 file=sys.stderr)
1029 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001030
Julien Campergue335f5ef2013-10-16 11:02:35 +02001031 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001032 # bail out now, we have no working tree
1033 return
1034
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001035 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001036 err_event.set()
1037 err_update_projects = True
1038 if opt.fail_fast:
1039 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1040 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001041
Mike Frysinger5a033082019-09-23 19:21:20 -04001042 err_results = []
1043 self._Checkout(all_projects, opt, err_event, err_results)
1044 if err_event.isSet():
1045 err_checkout = True
1046 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001047
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001048 # If there's a notice that's supposed to print at the end of the sync, print
1049 # it now...
1050 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001051 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001052
Mike Frysinger5a033082019-09-23 19:21:20 -04001053 # If we saw an error, exit with code 1 so that other scripts can check.
1054 if err_event.isSet():
1055 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1056 if err_network_sync:
1057 print('error: Downloading network changes failed.', file=sys.stderr)
1058 if err_update_projects:
1059 print('error: Updating local project lists failed.', file=sys.stderr)
1060 if err_checkout:
1061 print('error: Checking out local projects failed.', file=sys.stderr)
1062 if err_results:
1063 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1064 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1065 file=sys.stderr)
1066 sys.exit(1)
1067
Mike Frysingere19d9e12020-02-12 11:23:32 -05001068 if not opt.quiet:
1069 print('repo sync has finished successfully.')
1070
David Pursehouse819827a2020-02-12 15:20:19 +09001071
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001072def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001073 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001074 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001075 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001076 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001077 if project.Exists:
1078 project.PostRepoUpgrade()
1079
David Pursehouse819827a2020-02-12 15:20:19 +09001080
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001081def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001082 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001083 print('info: A new version of repo is available', file=sys.stderr)
1084 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001085 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001086 syncbuf = SyncBuffer(rp.config)
1087 rp.Sync_LocalHalf(syncbuf)
1088 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001089 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001090 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001091 raise RepoChangedException(['--repo-upgraded'])
1092 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001093 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001094 else:
1095 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001096 print('repo version %s is current' % rp.work_git.describe(HEAD),
1097 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001098
David Pursehouse819827a2020-02-12 15:20:19 +09001099
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001100def _VerifyTag(project):
1101 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1102 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001103 print('warning: GnuPG was not available during last "repo init"\n'
1104 'warning: Cannot automatically authenticate repo."""',
1105 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001106 return True
1107
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001108 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001109 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001110 except GitError:
1111 cur = None
1112
1113 if not cur \
1114 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001115 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001116 if rev.startswith(R_HEADS):
1117 rev = rev[len(R_HEADS):]
1118
Sarah Owenscecd1d82012-11-01 22:59:27 -07001119 print(file=sys.stderr)
1120 print("warning: project '%s' branch '%s' is not signed"
1121 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001122 return False
1123
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001124 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001125 env['GIT_DIR'] = project.gitdir
1126 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001127
1128 cmd = [GIT, 'tag', '-v', cur]
1129 proc = subprocess.Popen(cmd,
David Pursehousee5913ae2020-02-12 13:56:59 +09001130 stdout=subprocess.PIPE,
1131 stderr=subprocess.PIPE,
1132 env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001133 out = proc.stdout.read()
1134 proc.stdout.close()
1135
1136 err = proc.stderr.read()
1137 proc.stderr.close()
1138
1139 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001140 print(file=sys.stderr)
1141 print(out, file=sys.stderr)
1142 print(err, file=sys.stderr)
1143 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001144 return False
1145 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001146
David Rileye0684ad2017-04-05 00:02:59 -07001147
Dave Borowitz67700e92012-10-23 15:00:54 -07001148class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001149 _ALPHA = 0.5
1150
Dave Borowitz67700e92012-10-23 15:00:54 -07001151 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001152 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001153 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001154 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001155
1156 def Get(self, project):
1157 self._Load()
1158 return self._times.get(project.name, _ONE_DAY_S)
1159
1160 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001161 self._Load()
1162 name = project.name
1163 old = self._times.get(name, t)
1164 self._seen.add(name)
1165 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001166 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001167
1168 def _Load(self):
1169 if self._times is None:
1170 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001171 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001172 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001173 except (IOError, ValueError):
1174 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001175 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001176 except OSError:
1177 pass
1178 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001179
1180 def Save(self):
1181 if self._times is None:
1182 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001183
1184 to_delete = []
1185 for name in self._times:
1186 if name not in self._seen:
1187 to_delete.append(name)
1188 for name in to_delete:
1189 del self._times[name]
1190
Dave Borowitz67700e92012-10-23 15:00:54 -07001191 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001192 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001193 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001194 except (IOError, TypeError):
1195 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001196 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001197 except OSError:
1198 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001199
1200# This is a replacement for xmlrpc.client.Transport using urllib2
1201# and supporting persistent-http[s]. It cannot change hosts from
1202# request to request like the normal transport, the real url
1203# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001204
1205
Dan Willemsen0745bb22015-08-17 13:41:45 -07001206class PersistentTransport(xmlrpc.client.Transport):
1207 def __init__(self, orig_host):
1208 self.orig_host = orig_host
1209
1210 def request(self, host, handler, request_body, verbose=False):
1211 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1212 # Python doesn't understand cookies with the #HttpOnly_ prefix
1213 # Since we're only using them for HTTP, copy the file temporarily,
1214 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001215 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001216 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001217 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001218 try:
1219 with open(cookiefile) as f:
1220 for line in f:
1221 if line.startswith("#HttpOnly_"):
1222 line = line[len("#HttpOnly_"):]
1223 tmpcookiefile.write(line)
1224 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001225
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001226 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001227 try:
1228 cookiejar.load()
1229 except cookielib.LoadError:
1230 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001231 finally:
1232 tmpcookiefile.close()
1233 else:
1234 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001235
1236 proxyhandler = urllib.request.ProxyHandler
1237 if proxy:
1238 proxyhandler = urllib.request.ProxyHandler({
1239 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001240 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001241
1242 opener = urllib.request.build_opener(
1243 urllib.request.HTTPCookieProcessor(cookiejar),
1244 proxyhandler)
1245
1246 url = urllib.parse.urljoin(self.orig_host, handler)
1247 parse_results = urllib.parse.urlparse(url)
1248
1249 scheme = parse_results.scheme
1250 if scheme == 'persistent-http':
1251 scheme = 'http'
1252 if scheme == 'persistent-https':
1253 # If we're proxying through persistent-https, use http. The
1254 # proxy itself will do the https.
1255 if proxy:
1256 scheme = 'http'
1257 else:
1258 scheme = 'https'
1259
1260 # Parse out any authentication information using the base class
1261 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1262
1263 url = urllib.parse.urlunparse((
1264 scheme,
1265 host,
1266 parse_results.path,
1267 parse_results.params,
1268 parse_results.query,
1269 parse_results.fragment))
1270
1271 request = urllib.request.Request(url, request_body)
1272 if extra_headers is not None:
1273 for (name, header) in extra_headers:
1274 request.add_header(name, header)
1275 request.add_header('Content-Type', 'text/xml')
1276 try:
1277 response = opener.open(request)
1278 except urllib.error.HTTPError as e:
1279 if e.code == 501:
1280 # We may have been redirected through a login process
1281 # but our POST turned into a GET. Retry.
1282 response = opener.open(request)
1283 else:
1284 raise
1285
1286 p, u = xmlrpc.client.getparser()
1287 while 1:
1288 data = response.read(1024)
1289 if not data:
1290 break
1291 p.feed(data)
1292 p.close()
1293 return u.close()
1294
1295 def close(self):
1296 pass