blob: 49867a978bf2e741b24f90bbb972a0b2403ee23b [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)):
579 project.config.SetString('core.repositoryFormatVersion', '1')
580 project.config.SetString('extensions.preciousObjects', 'true')
581 else:
582 # This isn't perfect, but it's the best we can do with old git.
583 print('%s: WARNING: shared projects are unreliable when using old '
584 'versions of git; please upgrade to git-2.7.0+.'
585 % (project.relpath,),
586 file=sys.stderr)
587 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700588 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700589
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500590 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700591 cpu_count = multiprocessing.cpu_count()
592 else:
593 cpu_count = 1
594 jobs = min(self.jobs, cpu_count)
595
596 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700597 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700598 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700599 return
600
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400601 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700602
603 threads = set()
604 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700605
David James8d201162013-10-11 17:03:19 -0700606 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700607 try:
608 try:
David James8d201162013-10-11 17:03:19 -0700609 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700610 except GitError:
611 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900612 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700613 err_event.set()
614 raise
615 finally:
616 sem.release()
617
Gabe Black2ff30292014-10-09 17:54:35 -0700618 for bare_git in gc_gitdirs.values():
Mike Frysinger5a033082019-09-23 19:21:20 -0400619 if err_event.isSet() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700620 break
621 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700622 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700623 t.daemon = True
624 threads.add(t)
625 t.start()
626
627 for t in threads:
628 t.join()
629
Tim Kilbourn07669002013-03-08 15:02:49 -0800630 def _ReloadManifest(self, manifest_name=None):
631 if manifest_name:
632 # Override calls _Unload already
633 self.manifest.Override(manifest_name)
634 else:
635 self.manifest._Unload()
636
Dan Willemsen43507912016-09-01 16:26:02 -0700637 def _DeleteProject(self, path):
638 print('Deleting obsolete path %s' % path, file=sys.stderr)
639
640 # Delete the .git directory first, so we're less likely to have a partially
641 # working git repository around. There shouldn't be any git projects here,
642 # so rmtree works.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500643 dotgit = os.path.join(path, '.git')
644 # Try to remove plain files first in case of git worktrees. If this fails
645 # for any reason, we'll fall back to rmtree, and that'll display errors if
646 # it can't remove things either.
Dan Willemsen43507912016-09-01 16:26:02 -0700647 try:
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500648 platform_utils.remove(dotgit)
649 except OSError:
650 pass
651 try:
652 platform_utils.rmtree(dotgit)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700653 except OSError as e:
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500654 if e.errno != errno.ENOENT:
655 print('error: %s: %s' % (dotgit, str(e)), file=sys.stderr)
656 print('error: %s: Failed to delete obsolete path; remove manually, then '
657 'run sync again' % (path,), file=sys.stderr)
658 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700659
660 # Delete everything under the worktree, except for directories that contain
661 # another git project
662 dirs_to_remove = []
663 failed = False
Renaud Paquaybed8b622018-09-27 10:46:58 -0700664 for root, dirs, files in platform_utils.walk(path):
Dan Willemsen43507912016-09-01 16:26:02 -0700665 for f in files:
666 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800667 platform_utils.remove(os.path.join(root, f))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700668 except OSError as e:
669 print('Failed to remove %s (%s)' % (os.path.join(root, f), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700670 failed = True
671 dirs[:] = [d for d in dirs
672 if not os.path.lexists(os.path.join(root, d, '.git'))]
673 dirs_to_remove += [os.path.join(root, d) for d in dirs
674 if os.path.join(root, d) not in dirs_to_remove]
675 for d in reversed(dirs_to_remove):
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700676 if platform_utils.islink(d):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700677 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800678 platform_utils.remove(d)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700679 except OSError as e:
680 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700681 failed = True
Renaud Paquaybed8b622018-09-27 10:46:58 -0700682 elif len(platform_utils.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700683 try:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700684 platform_utils.rmdir(d)
685 except OSError as e:
686 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700687 failed = True
688 continue
689 if failed:
690 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
691 print(' remove manually, then run sync again', file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400692 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700693
694 # Try deleting parent dirs if they are empty
695 project_dir = path
696 while project_dir != self.manifest.topdir:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700697 if len(platform_utils.listdir(project_dir)) == 0:
698 platform_utils.rmdir(project_dir)
Dan Willemsen43507912016-09-01 16:26:02 -0700699 else:
700 break
701 project_dir = os.path.dirname(project_dir)
702
703 return 0
704
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500705 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700706 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700707 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700708 if project.relpath:
709 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700710 file_name = 'project.list'
711 file_path = os.path.join(self.manifest.repodir, file_name)
712 old_project_paths = []
713
714 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500715 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700716 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800717 # In reversed order, so subfolders are deleted before parent folder.
718 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700719 if not path:
720 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700721 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900722 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700723 gitdir = os.path.join(self.manifest.topdir, path, '.git')
724 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900725 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900726 manifest=self.manifest,
727 name=path,
728 remote=RemoteSpec('origin'),
729 gitdir=gitdir,
730 objdir=gitdir,
731 worktree=os.path.join(self.manifest.topdir, path),
732 relpath=path,
733 revisionExpr='HEAD',
734 revisionId=None,
735 groups=None)
Anthonyf3fdf822009-09-26 13:38:52 -0400736
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500737 if project.IsDirty() and opt.force_remove_dirty:
738 print('WARNING: Removing dirty project "%s": uncommitted changes '
739 'erased' % project.relpath, file=sys.stderr)
740 self._DeleteProject(project.worktree)
741 elif project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900742 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900743 'are present' % project.relpath, file=sys.stderr)
744 print(' commit changes, then run sync again',
745 file=sys.stderr)
Mike Frysingera850ca22019-08-07 17:19:24 -0400746 return 1
Dan Willemsen43507912016-09-01 16:26:02 -0700747 elif self._DeleteProject(project.worktree):
Mike Frysingera850ca22019-08-07 17:19:24 -0400748 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700749
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700750 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500751 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700752 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700753 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700754 return 0
755
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400756 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
757 if not self.manifest.manifest_server:
758 print('error: cannot smart sync: no manifest server defined in '
759 'manifest', file=sys.stderr)
760 sys.exit(1)
761
762 manifest_server = self.manifest.manifest_server
763 if not opt.quiet:
764 print('Using manifest server %s' % manifest_server)
765
David Pursehouseeeff3532020-02-12 11:24:10 +0900766 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400767 username = None
768 password = None
769 if opt.manifest_server_username and opt.manifest_server_password:
770 username = opt.manifest_server_username
771 password = opt.manifest_server_password
772 else:
773 try:
774 info = netrc.netrc()
775 except IOError:
776 # .netrc file does not exist or could not be opened
777 pass
778 else:
779 try:
780 parse_result = urllib.parse.urlparse(manifest_server)
781 if parse_result.hostname:
782 auth = info.authenticators(parse_result.hostname)
783 if auth:
784 username, _account, password = auth
785 else:
786 print('No credentials found for %s in .netrc'
787 % parse_result.hostname, file=sys.stderr)
788 except netrc.NetrcParseError as e:
789 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
790
791 if (username and password):
792 manifest_server = manifest_server.replace('://', '://%s:%s@' %
793 (username, password),
794 1)
795
796 transport = PersistentTransport(manifest_server)
797 if manifest_server.startswith('persistent-'):
798 manifest_server = manifest_server[len('persistent-'):]
799
800 try:
801 server = xmlrpc.client.Server(manifest_server, transport=transport)
802 if opt.smart_sync:
803 p = self.manifest.manifestProject
804 b = p.GetBranch(p.CurrentBranch)
805 branch = b.merge
806 if branch.startswith(R_HEADS):
807 branch = branch[len(R_HEADS):]
808
Mike Frysinger56ce3462019-12-04 19:30:48 -0500809 if 'SYNC_TARGET' in os.environ:
810 target = os.environ('SYNC_TARGET')
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400811 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500812 elif ('TARGET_PRODUCT' in os.environ and
813 'TARGET_BUILD_VARIANT' in os.environ):
814 target = '%s-%s' % (os.environ('TARGET_PRODUCT'),
815 os.environ('TARGET_BUILD_VARIANT'))
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400816 [success, manifest_str] = server.GetApprovedManifest(branch, target)
817 else:
818 [success, manifest_str] = server.GetApprovedManifest(branch)
819 else:
820 assert(opt.smart_tag)
821 [success, manifest_str] = server.GetManifest(opt.smart_tag)
822
823 if success:
824 manifest_name = os.path.basename(smart_sync_manifest_path)
825 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500826 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400827 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400828 except IOError as e:
829 print('error: cannot write manifest to %s:\n%s'
830 % (smart_sync_manifest_path, e),
831 file=sys.stderr)
832 sys.exit(1)
833 self._ReloadManifest(manifest_name)
834 else:
835 print('error: manifest server RPC call failed: %s' %
836 manifest_str, file=sys.stderr)
837 sys.exit(1)
838 except (socket.error, IOError, xmlrpc.client.Fault) as e:
839 print('error: cannot connect to manifest server %s:\n%s'
840 % (self.manifest.manifest_server, e), file=sys.stderr)
841 sys.exit(1)
842 except xmlrpc.client.ProtocolError as e:
843 print('error: cannot connect to manifest server %s:\n%d %s'
844 % (self.manifest.manifest_server, e.errcode, e.errmsg),
845 file=sys.stderr)
846 sys.exit(1)
847
848 return manifest_name
849
Mike Frysingerfb527e32019-08-27 02:34:32 -0400850 def _UpdateManifestProject(self, opt, mp, manifest_name):
851 """Fetch & update the local manifest project."""
852 if not opt.local_only:
853 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500854 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400855 current_branch_only=opt.current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500856 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400857 optimized_fetch=opt.optimized_fetch,
858 submodules=self.manifest.HasSubmodules,
859 clone_filter=self.manifest.CloneFilter)
860 finish = time.time()
861 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
862 start, finish, success)
863
864 if mp.HasChanges:
865 syncbuf = SyncBuffer(mp.config)
866 start = time.time()
867 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
868 clean = syncbuf.Finish()
869 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
870 start, time.time(), clean)
871 if not clean:
872 sys.exit(1)
873 self._ReloadManifest(opt.manifest_name)
874 if opt.jobs is None:
875 self.jobs = self.manifest.default.sync_j
876
Mike Frysingerae6cb082019-08-27 01:10:59 -0400877 def ValidateOptions(self, opt, args):
878 if opt.force_broken:
879 print('warning: -f/--force-broken is now the default behavior, and the '
880 'options are deprecated', file=sys.stderr)
881 if opt.network_only and opt.detach_head:
882 self.OptionParser.error('cannot combine -n and -d')
883 if opt.network_only and opt.local_only:
884 self.OptionParser.error('cannot combine -n and -l')
885 if opt.manifest_name and opt.smart_sync:
886 self.OptionParser.error('cannot combine -m and -s')
887 if opt.manifest_name and opt.smart_tag:
888 self.OptionParser.error('cannot combine -m and -t')
889 if opt.manifest_server_username or opt.manifest_server_password:
890 if not (opt.smart_sync or opt.smart_tag):
891 self.OptionParser.error('-u and -p may only be combined with -s or -t')
892 if None in [opt.manifest_server_username, opt.manifest_server_password]:
893 self.OptionParser.error('both -u and -p must be given')
894
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700895 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800896 if opt.jobs:
897 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700898 if self.jobs > 1:
899 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400900 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700901
Mike Frysinger521d01b2020-02-17 01:51:49 -0500902 opt.quiet = opt.output_mode is False
903 opt.verbose = opt.output_mode is True
904
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500905 if opt.manifest_name:
906 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700907
Chirayu Desaia892b102013-06-11 14:18:46 +0530908 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900909 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900910 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530911
Victor Boivie08c880d2011-04-19 10:32:52 +0200912 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400913 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
914 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900915 if os.path.isfile(smart_sync_manifest_path):
916 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800917 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900918 except OSError as e:
919 print('error: failed to remove existing smart sync override manifest: %s' %
920 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700921
Mike Frysinger5a033082019-09-23 19:21:20 -0400922 err_event = _threading.Event()
923
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700924 rp = self.manifest.repoProject
925 rp.PreSync()
926
927 mp = self.manifest.manifestProject
928 mp.PreSync()
929
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800930 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700931 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800932
Fredrik de Grootcc960972019-11-22 09:04:31 +0100933 if not opt.mp_update:
934 print('Skipping update of local manifest project.')
935 else:
936 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700937
Simran Basib9a1b732015-08-20 12:19:28 -0700938 if self.gitc_manifest:
939 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700940 missing_ok=True)
941 gitc_projects = []
942 opened_projects = []
943 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700944 if project.relpath in self.gitc_manifest.paths and \
945 self.gitc_manifest.paths[project.relpath].old_revision:
946 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700947 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700948 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700949
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700950 if not args:
951 gitc_projects = None
952
953 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700954 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700955 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
956 if manifest_name:
957 manifest.Override(manifest_name)
958 else:
959 manifest.Override(self.manifest.manifestFile)
960 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
961 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700962 gitc_projects)
963 print('GITC client successfully synced.')
964
965 # The opened projects need to be synced as normal, therefore we
966 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700967 # TODO: make this more reliable -- if there's a project name/path overlap,
968 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900969 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
970 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700971 if not args:
972 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800973 all_projects = self.GetProjects(args,
974 missing_ok=True,
975 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700976
Mike Frysinger5a033082019-09-23 19:21:20 -0400977 err_network_sync = False
978 err_update_projects = False
979 err_checkout = False
980
Dave Borowitz67700e92012-10-23 15:00:54 -0700981 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700982 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700983 to_fetch = []
984 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700985 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700986 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900987 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700988 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700989
Mike Frysinger5a033082019-09-23 19:21:20 -0400990 fetched = self._Fetch(to_fetch, opt, err_event)
991
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500992 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700993 if opt.network_only:
994 # bail out now; the rest touches the working tree
Mike Frysinger5a033082019-09-23 19:21:20 -0400995 if err_event.isSet():
996 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
997 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700998 return
999
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001000 # Iteratively fetch missing and/or nested unregistered submodules
1001 previously_missing_set = set()
1002 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +01001003 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001004 all_projects = self.GetProjects(args,
1005 missing_ok=True,
1006 submodules_ok=opt.fetch_submodules)
1007 missing = []
1008 for project in all_projects:
1009 if project.gitdir not in fetched:
1010 missing.append(project)
1011 if not missing:
1012 break
1013 # Stop us from non-stopped fetching actually-missing repos: If set of
1014 # missing repos has not been changed from last fetch, we break.
1015 missing_set = set(p.name for p in missing)
1016 if previously_missing_set == missing_set:
1017 break
1018 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -04001019 fetched.update(self._Fetch(missing, opt, err_event))
1020
1021 # If we saw an error, exit with code 1 so that other scripts can check.
1022 if err_event.isSet():
1023 err_network_sync = True
1024 if opt.fail_fast:
1025 print('\nerror: Exited sync due to fetch errors.\n'
1026 'Local checkouts *not* updated. Resolve network issues & '
1027 'retry.\n'
1028 '`repo sync -l` will update some local checkouts.',
1029 file=sys.stderr)
1030 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001031
Julien Campergue335f5ef2013-10-16 11:02:35 +02001032 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001033 # bail out now, we have no working tree
1034 return
1035
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001036 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001037 err_event.set()
1038 err_update_projects = True
1039 if opt.fail_fast:
1040 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1041 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001042
Mike Frysinger5a033082019-09-23 19:21:20 -04001043 err_results = []
1044 self._Checkout(all_projects, opt, err_event, err_results)
1045 if err_event.isSet():
1046 err_checkout = True
1047 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001048
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001049 # If there's a notice that's supposed to print at the end of the sync, print
1050 # it now...
1051 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001052 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001053
Mike Frysinger5a033082019-09-23 19:21:20 -04001054 # If we saw an error, exit with code 1 so that other scripts can check.
1055 if err_event.isSet():
1056 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1057 if err_network_sync:
1058 print('error: Downloading network changes failed.', file=sys.stderr)
1059 if err_update_projects:
1060 print('error: Updating local project lists failed.', file=sys.stderr)
1061 if err_checkout:
1062 print('error: Checking out local projects failed.', file=sys.stderr)
1063 if err_results:
1064 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1065 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1066 file=sys.stderr)
1067 sys.exit(1)
1068
Mike Frysingere19d9e12020-02-12 11:23:32 -05001069 if not opt.quiet:
1070 print('repo sync has finished successfully.')
1071
David Pursehouse819827a2020-02-12 15:20:19 +09001072
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001073def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001074 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001075 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001076 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001077 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001078 if project.Exists:
1079 project.PostRepoUpgrade()
1080
David Pursehouse819827a2020-02-12 15:20:19 +09001081
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001082def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001083 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001084 print('info: A new version of repo is available', file=sys.stderr)
1085 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001086 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001087 syncbuf = SyncBuffer(rp.config)
1088 rp.Sync_LocalHalf(syncbuf)
1089 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001090 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001091 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001092 raise RepoChangedException(['--repo-upgraded'])
1093 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001094 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001095 else:
1096 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001097 print('repo version %s is current' % rp.work_git.describe(HEAD),
1098 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001099
David Pursehouse819827a2020-02-12 15:20:19 +09001100
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001101def _VerifyTag(project):
1102 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1103 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001104 print('warning: GnuPG was not available during last "repo init"\n'
1105 'warning: Cannot automatically authenticate repo."""',
1106 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001107 return True
1108
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001109 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001110 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001111 except GitError:
1112 cur = None
1113
1114 if not cur \
1115 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001116 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001117 if rev.startswith(R_HEADS):
1118 rev = rev[len(R_HEADS):]
1119
Sarah Owenscecd1d82012-11-01 22:59:27 -07001120 print(file=sys.stderr)
1121 print("warning: project '%s' branch '%s' is not signed"
1122 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001123 return False
1124
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001125 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001126 env['GIT_DIR'] = project.gitdir
1127 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001128
1129 cmd = [GIT, 'tag', '-v', cur]
1130 proc = subprocess.Popen(cmd,
David Pursehousee5913ae2020-02-12 13:56:59 +09001131 stdout=subprocess.PIPE,
1132 stderr=subprocess.PIPE,
1133 env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001134 out = proc.stdout.read()
1135 proc.stdout.close()
1136
1137 err = proc.stderr.read()
1138 proc.stderr.close()
1139
1140 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001141 print(file=sys.stderr)
1142 print(out, file=sys.stderr)
1143 print(err, file=sys.stderr)
1144 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001145 return False
1146 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001147
David Rileye0684ad2017-04-05 00:02:59 -07001148
Dave Borowitz67700e92012-10-23 15:00:54 -07001149class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001150 _ALPHA = 0.5
1151
Dave Borowitz67700e92012-10-23 15:00:54 -07001152 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001153 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001154 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001155 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001156
1157 def Get(self, project):
1158 self._Load()
1159 return self._times.get(project.name, _ONE_DAY_S)
1160
1161 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001162 self._Load()
1163 name = project.name
1164 old = self._times.get(name, t)
1165 self._seen.add(name)
1166 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001167 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001168
1169 def _Load(self):
1170 if self._times is None:
1171 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001172 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001173 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001174 except (IOError, ValueError):
1175 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001176 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001177 except OSError:
1178 pass
1179 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001180
1181 def Save(self):
1182 if self._times is None:
1183 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001184
1185 to_delete = []
1186 for name in self._times:
1187 if name not in self._seen:
1188 to_delete.append(name)
1189 for name in to_delete:
1190 del self._times[name]
1191
Dave Borowitz67700e92012-10-23 15:00:54 -07001192 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001193 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001194 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001195 except (IOError, TypeError):
1196 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001197 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001198 except OSError:
1199 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001200
1201# This is a replacement for xmlrpc.client.Transport using urllib2
1202# and supporting persistent-http[s]. It cannot change hosts from
1203# request to request like the normal transport, the real url
1204# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001205
1206
Dan Willemsen0745bb22015-08-17 13:41:45 -07001207class PersistentTransport(xmlrpc.client.Transport):
1208 def __init__(self, orig_host):
1209 self.orig_host = orig_host
1210
1211 def request(self, host, handler, request_body, verbose=False):
1212 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1213 # Python doesn't understand cookies with the #HttpOnly_ prefix
1214 # Since we're only using them for HTTP, copy the file temporarily,
1215 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001216 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001217 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001218 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001219 try:
1220 with open(cookiefile) as f:
1221 for line in f:
1222 if line.startswith("#HttpOnly_"):
1223 line = line[len("#HttpOnly_"):]
1224 tmpcookiefile.write(line)
1225 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001226
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001227 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001228 try:
1229 cookiejar.load()
1230 except cookielib.LoadError:
1231 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001232 finally:
1233 tmpcookiefile.close()
1234 else:
1235 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001236
1237 proxyhandler = urllib.request.ProxyHandler
1238 if proxy:
1239 proxyhandler = urllib.request.ProxyHandler({
1240 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001241 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001242
1243 opener = urllib.request.build_opener(
1244 urllib.request.HTTPCookieProcessor(cookiejar),
1245 proxyhandler)
1246
1247 url = urllib.parse.urljoin(self.orig_host, handler)
1248 parse_results = urllib.parse.urlparse(url)
1249
1250 scheme = parse_results.scheme
1251 if scheme == 'persistent-http':
1252 scheme = 'http'
1253 if scheme == 'persistent-https':
1254 # If we're proxying through persistent-https, use http. The
1255 # proxy itself will do the https.
1256 if proxy:
1257 scheme = 'http'
1258 else:
1259 scheme = 'https'
1260
1261 # Parse out any authentication information using the base class
1262 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1263
1264 url = urllib.parse.urlunparse((
1265 scheme,
1266 host,
1267 parse_results.path,
1268 parse_results.params,
1269 parse_results.query,
1270 parse_results.fragment))
1271
1272 request = urllib.request.Request(url, request_body)
1273 if extra_headers is not None:
1274 for (name, header) in extra_headers:
1275 request.add_header(name, header)
1276 request.add_header('Content-Type', 'text/xml')
1277 try:
1278 response = opener.open(request)
1279 except urllib.error.HTTPError as e:
1280 if e.code == 501:
1281 # We may have been redirected through a login process
1282 # but our POST turned into a GET. Retry.
1283 response = opener.open(request)
1284 else:
1285 raise
1286
1287 p, u = xmlrpc.client.getparser()
1288 while 1:
1289 data = response.read(1024)
1290 if not data:
1291 break
1292 p.feed(data)
1293 p.close()
1294 return u.close()
1295
1296 def close(self):
1297 pass