blob: 79cfaaa5bd81c12a6abd26fded273a88ab1e8019 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
2# Copyright (C) 2008 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
Sarah Owenscecd1d82012-11-01 22:59:27 -070016from __future__ import print_function
Anthony King85b24ac2014-05-06 15:57:48 +010017import json
David Pursehouse86d973d2012-08-24 10:21:02 +090018import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070019from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import os
21import re
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070022import shutil
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070023import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import subprocess
25import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
David Pursehouse59bbb582013-05-17 10:49:33 +090028
29from pyversion import is_python3
30if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070031 import http.cookiejar as cookielib
32 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053033 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070034 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090035 import xmlrpc.client
36else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070037 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053038 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070039 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053040 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090041 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053042 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070043 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053044 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070045 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053046 xmlrpc = imp.new_module('xmlrpc')
47 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070048
Roy Lee18afd7f2010-05-09 04:32:08 +080049try:
50 import threading as _threading
51except ImportError:
52 import dummy_threading as _threading
53
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070054try:
55 import resource
56 def _rlimit_nofile():
57 return resource.getrlimit(resource.RLIMIT_NOFILE)
58except ImportError:
59 def _rlimit_nofile():
60 return (256, 256)
61
Dave Borowitz18857212012-10-23 17:02:59 -070062try:
63 import multiprocessing
64except ImportError:
65 multiprocessing = None
66
Dave Borowitze2152672012-10-31 12:24:38 -070067from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090068from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090069from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070070import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070071from project import Project
72from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080073from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000074from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070075from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070076from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080077from wrapper import Wrapper
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070078
Dave Borowitz67700e92012-10-23 15:00:54 -070079_ONE_DAY_S = 24 * 60 * 60
80
Doug Andersonfc06ced2011-03-16 15:49:18 -070081class _FetchError(Exception):
82 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
83 pass
84
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080085class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080086 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070087 common = True
88 helpSummary = "Update working tree to the latest revision"
89 helpUsage = """
90%prog [<project>...]
91"""
92 helpDescription = """
93The '%prog' command synchronizes local project directories
94with the remote repositories specified in the manifest. If a local
95project does not yet exist, it will clone a new local directory from
96the remote repository and set up tracking branches as specified in
97the manifest. If the local project already exists, '%prog'
98will update the remote branches and rebase any new local changes
99on top of the new remote changes.
100
101'%prog' will synchronize all projects listed at the command
102line. Projects can be specified either by name, or by a relative
103or absolute path to the project's local directory. If no projects
104are specified, '%prog' will synchronize all projects listed in
105the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700106
107The -d/--detach option can be used to switch specified projects
108back to the manifest revision. This option is especially helpful
109if the project is currently on a topic branch, but the manifest
110revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700111
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700112The -s/--smart-sync option can be used to sync to a known good
113build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200114manifest. The -t/--smart-tag option is similar and allows you to
115specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700116
David Pursehousecf76b1b2012-09-14 10:31:42 +0900117The -u/--manifest-server-username and -p/--manifest-server-password
118options can be used to specify a username and password to authenticate
119with the manifest server when using the -s or -t option.
120
121If -u and -p are not specified when using the -s or -t option, '%prog'
122will attempt to read authentication credentials for the manifest server
123from the user's .netrc file.
124
125'%prog' will not use authentication credentials from -u/-p or .netrc
126if the manifest server specified in the manifest file already includes
127credentials.
128
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500129The -f/--force-broken option can be used to proceed with syncing
130other projects if a project sync fails.
131
Kevin Degiabaa7f32014-11-12 11:27:45 -0700132The --force-sync option can be used to overwrite existing git
133directories if they have previously been linked to a different
134object direcotry. WARNING: This may cause data to be lost since
135refs may be removed when overwriting.
136
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700137The --no-clone-bundle option disables any attempt to use
138$URL/clone.bundle to bootstrap a new Git repository from a
139resumeable bundle file on a content delivery network. This
140may be necessary if there are problems with the local Python
141HTTP client or proxy configuration, but the Git binary works.
142
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800143The --fetch-submodules option enables fetching Git submodules
144of a project from server.
145
David Pursehousef2fad612015-01-29 14:36:28 +0900146The -c/--current-branch option can be used to only fetch objects that
147are on the branch specified by a project's revision.
148
David Pursehouseb1553542014-09-04 21:28:09 +0900149The --optimized-fetch option can be used to only fetch projects that
150are fixed to a sha1 revision if the sha1 revision does not already
151exist locally.
152
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700153SSH Connections
154---------------
155
156If at least one project remote URL uses an SSH connection (ssh://,
157git+ssh://, or user@host:path syntax) repo will automatically
158enable the SSH ControlMaster option when connecting to that host.
159This feature permits other projects in the same '%prog' session to
160reuse the same SSH tunnel, saving connection setup overheads.
161
162To disable this behavior on UNIX platforms, set the GIT_SSH
163environment variable to 'ssh'. For example:
164
165 export GIT_SSH=ssh
166 %prog
167
168Compatibility
169~~~~~~~~~~~~~
170
171This feature is automatically disabled on Windows, due to the lack
172of UNIX domain socket support.
173
174This feature is not compatible with url.insteadof rewrites in the
175user's ~/.gitconfig. '%prog' is currently not able to perform the
176rewrite early enough to establish the ControlMaster tunnel.
177
178If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
179later is required to fix a server side protocol bug.
180
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700181"""
182
Nico Sallembien6623b212010-05-11 12:57:01 -0700183 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000184 try:
185 self.jobs = self.manifest.default.sync_j
186 except ManifestParseError:
187 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700188
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500189 p.add_option('-f', '--force-broken',
190 dest='force_broken', action='store_true',
191 help="continue sync even if a project fails to sync")
Kevin Degiabaa7f32014-11-12 11:27:45 -0700192 p.add_option('--force-sync',
193 dest='force_sync', action='store_true',
194 help="overwrite an existing git directory if it needs to "
195 "point to a different object directory. WARNING: this "
196 "may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900197 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700198 dest='local_only', action='store_true',
199 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900200 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700201 dest='network_only', action='store_true',
202 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900203 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700204 dest='detach_head', action='store_true',
205 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900206 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700207 dest='current_branch_only', action='store_true',
208 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900209 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700210 dest='quiet', action='store_true',
211 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900212 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800213 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700214 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500215 p.add_option('-m', '--manifest-name',
216 dest='manifest_name',
217 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700218 p.add_option('--no-clone-bundle',
219 dest='no_clone_bundle', action='store_true',
220 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800221 p.add_option('-u', '--manifest-server-username', action='store',
222 dest='manifest_server_username',
223 help='username to authenticate with the manifest server')
224 p.add_option('-p', '--manifest-server-password', action='store',
225 dest='manifest_server_password',
226 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800227 p.add_option('--fetch-submodules',
228 dest='fetch_submodules', action='store_true',
229 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700230 p.add_option('--no-tags',
231 dest='no_tags', action='store_true',
232 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900233 p.add_option('--optimized-fetch',
234 dest='optimized_fetch', action='store_true',
235 help='only fetch projects fixed to sha1 if revision does not exist locally')
Nico Sallembien6623b212010-05-11 12:57:01 -0700236 if show_smart:
237 p.add_option('-s', '--smart-sync',
238 dest='smart_sync', action='store_true',
239 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200240 p.add_option('-t', '--smart-tag',
241 dest='smart_tag', action='store',
242 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700243
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700244 g = p.add_option_group('repo Version options')
245 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700246 dest='no_repo_verify', action='store_true',
247 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700248 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800249 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700250 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251
David James89ece422014-01-09 18:51:58 -0800252 def _FetchProjectList(self, opt, projects, *args, **kwargs):
David Pursehousec1b86a22012-11-14 11:36:51 +0900253 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800254
David James8d201162013-10-11 17:03:19 -0700255 Delegates most of the work to _FetchHelper.
256
257 Args:
258 opt: Program options returned from optparse. See _Options().
259 projects: Projects to fetch.
David James89ece422014-01-09 18:51:58 -0800260 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700261 _FetchHelper docstring for details.
262 """
263 for project in projects:
David James89ece422014-01-09 18:51:58 -0800264 success = self._FetchHelper(opt, project, *args, **kwargs)
David James8d201162013-10-11 17:03:19 -0700265 if not success and not opt.force_broken:
266 break
267
268 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
269 """Fetch git objects for a single project.
270
David Pursehousec1b86a22012-11-14 11:36:51 +0900271 Args:
272 opt: Program options returned from optparse. See _Options().
273 project: Project object for the project to fetch.
274 lock: Lock for accessing objects that are shared amongst multiple
275 _FetchHelper() threads.
276 fetched: set object that we will add project.gitdir to when we're done
277 (with our lock held).
278 pm: Instance of a Project object. We will call pm.update() (with our
279 lock held).
280 sem: We'll release() this semaphore when we exit so that another thread
281 can be started up.
282 err_event: We'll set this event in the case of an error (after printing
283 out info about the error).
David James8d201162013-10-11 17:03:19 -0700284
285 Returns:
286 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900287 """
288 # We'll set to true once we've locked the lock.
289 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700290
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530291 if not opt.quiet:
292 print('Fetching project %s' % project.name)
293
David Pursehousec1b86a22012-11-14 11:36:51 +0900294 # Encapsulate everything in a try/except/finally so that:
295 # - We always set err_event in the case of an exception.
296 # - We always make sure we call sem.release().
297 # - We always make sure we unlock the lock if we locked it.
298 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700299 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900300 start = time.time()
301 success = project.Sync_NetworkHalf(
302 quiet=opt.quiet,
303 current_branch_only=opt.current_branch_only,
Kevin Degiabaa7f32014-11-12 11:27:45 -0700304 force_sync=opt.force_sync,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700305 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900306 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
307 optimized_fetch=opt.optimized_fetch)
David Pursehousec1b86a22012-11-14 11:36:51 +0900308 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700309
David Pursehousec1b86a22012-11-14 11:36:51 +0900310 # Lock around all the rest of the code, since printing, updating a set
311 # and Progress.update() are not thread safe.
312 lock.acquire()
313 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700314
David Pursehousec1b86a22012-11-14 11:36:51 +0900315 if not success:
316 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
317 if opt.force_broken:
318 print('warn: --force-broken, continuing to sync',
319 file=sys.stderr)
320 else:
321 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700322
David Pursehousec1b86a22012-11-14 11:36:51 +0900323 fetched.add(project.gitdir)
324 pm.update()
325 except _FetchError:
326 err_event.set()
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400327 except Exception as e:
328 print('error: Cannot fetch %s (%s: %s)' \
329 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900330 err_event.set()
331 raise
332 finally:
333 if did_lock:
334 lock.release()
335 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800336
David James8d201162013-10-11 17:03:19 -0700337 return success
338
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700339 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700340 fetched = set()
David James89ece422014-01-09 18:51:58 -0800341 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700342 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800343
David James89ece422014-01-09 18:51:58 -0800344 objdir_project_map = dict()
345 for project in projects:
346 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700347
David James89ece422014-01-09 18:51:58 -0800348 threads = set()
349 sem = _threading.Semaphore(self.jobs)
350 err_event = _threading.Event()
351 for project_list in objdir_project_map.values():
352 # Check for any errors before running any more tasks.
353 # ...we'll let existing threads finish, though.
354 if err_event.isSet() and not opt.force_broken:
355 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700356
David James89ece422014-01-09 18:51:58 -0800357 sem.acquire()
358 kwargs = dict(opt=opt,
359 projects=project_list,
360 lock=lock,
361 fetched=fetched,
362 pm=pm,
363 sem=sem,
364 err_event=err_event)
365 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700366 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800367 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200368 # Ensure that Ctrl-C will not freeze the repo process.
369 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800370 threads.add(t)
371 t.start()
David James89ece422014-01-09 18:51:58 -0800372 else:
373 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800374
David James89ece422014-01-09 18:51:58 -0800375 for t in threads:
376 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800377
David James89ece422014-01-09 18:51:58 -0800378 # If we saw an error, exit with code 1 so that other scripts can check.
379 if err_event.isSet():
380 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
381 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700382
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700383 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700384 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700385
Julien Campergue335f5ef2013-10-16 11:02:35 +0200386 if not self.manifest.IsArchive:
387 self._GCProjects(projects)
388
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700389 return fetched
390
Dave Borowitz18857212012-10-23 17:02:59 -0700391 def _GCProjects(self, projects):
David James8d201162013-10-11 17:03:19 -0700392 gitdirs = {}
393 for project in projects:
394 gitdirs[project.gitdir] = project.bare_git
395
Dave Borowitze2152672012-10-31 12:24:38 -0700396 has_dash_c = git_require((1, 7, 2))
397 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700398 cpu_count = multiprocessing.cpu_count()
399 else:
400 cpu_count = 1
401 jobs = min(self.jobs, cpu_count)
402
403 if jobs < 2:
David James8d201162013-10-11 17:03:19 -0700404 for bare_git in gitdirs.values():
405 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700406 return
407
408 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
409
410 threads = set()
411 sem = _threading.Semaphore(jobs)
412 err_event = _threading.Event()
413
David James8d201162013-10-11 17:03:19 -0700414 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700415 try:
416 try:
David James8d201162013-10-11 17:03:19 -0700417 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700418 except GitError:
419 err_event.set()
420 except:
421 err_event.set()
422 raise
423 finally:
424 sem.release()
425
David James8d201162013-10-11 17:03:19 -0700426 for bare_git in gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700427 if err_event.isSet():
428 break
429 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700430 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700431 t.daemon = True
432 threads.add(t)
433 t.start()
434
435 for t in threads:
436 t.join()
437
438 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700439 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700440 sys.exit(1)
441
Tim Kilbourn07669002013-03-08 15:02:49 -0800442 def _ReloadManifest(self, manifest_name=None):
443 if manifest_name:
444 # Override calls _Unload already
445 self.manifest.Override(manifest_name)
446 else:
447 self.manifest._Unload()
448
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700449 def UpdateProjectList(self):
450 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700451 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700452 if project.relpath:
453 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700454 file_name = 'project.list'
455 file_path = os.path.join(self.manifest.repodir, file_name)
456 old_project_paths = []
457
458 if os.path.exists(file_path):
459 fd = open(file_path, 'r')
460 try:
461 old_project_paths = fd.read().split('\n')
462 finally:
463 fd.close()
464 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700465 if not path:
466 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700467 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900468 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400469 if os.path.exists(self.manifest.topdir + '/' + path):
David James8d201162013-10-11 17:03:19 -0700470 gitdir = os.path.join(self.manifest.topdir, path, '.git')
David Pursehousec1b86a22012-11-14 11:36:51 +0900471 project = Project(
472 manifest = self.manifest,
473 name = path,
474 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700475 gitdir = gitdir,
476 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900477 worktree = os.path.join(self.manifest.topdir, path),
478 relpath = path,
479 revisionExpr = 'HEAD',
480 revisionId = None,
481 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400482
David Pursehousec1b86a22012-11-14 11:36:51 +0900483 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900484 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900485 'are present' % project.relpath, file=sys.stderr)
486 print(' commit changes, then run sync again',
487 file=sys.stderr)
488 return -1
489 else:
490 print('Deleting obsolete path %s' % project.worktree,
491 file=sys.stderr)
492 shutil.rmtree(project.worktree)
493 # Try deleting parent subdirs if they are empty
494 project_dir = os.path.dirname(project.worktree)
495 while project_dir != self.manifest.topdir:
496 try:
497 os.rmdir(project_dir)
498 except OSError:
499 break
500 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700501
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700502 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700503 fd = open(file_path, 'w')
504 try:
505 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700506 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700507 finally:
508 fd.close()
509 return 0
510
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700511 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800512 if opt.jobs:
513 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700514 if self.jobs > 1:
515 soft_limit, _ = _rlimit_nofile()
516 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
517
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700518 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700519 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700520 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700521 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700522 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700523 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500524 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700525 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500526 sys.exit(1)
527 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700528 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500529 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900530 if opt.manifest_server_username or opt.manifest_server_password:
531 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700532 print('error: -u and -p may only be combined with -s or -t',
533 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900534 sys.exit(1)
535 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700536 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900537 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500538
539 if opt.manifest_name:
540 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700541
Chirayu Desaia892b102013-06-11 14:18:46 +0530542 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900543 smart_sync_manifest_name = "smart_sync_override.xml"
544 smart_sync_manifest_path = os.path.join(
545 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530546
Victor Boivie08c880d2011-04-19 10:32:52 +0200547 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700548 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900549 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700550 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700551 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900552
553 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900554 if not opt.quiet:
555 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900556
David Pursehouse86d973d2012-08-24 10:21:02 +0900557 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900558 username = None
559 password = None
560 if opt.manifest_server_username and opt.manifest_server_password:
561 username = opt.manifest_server_username
562 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900563 else:
564 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900565 info = netrc.netrc()
566 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900567 # .netrc file does not exist or could not be opened
568 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900569 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900570 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530571 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900572 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900573 auth = info.authenticators(parse_result.hostname)
574 if auth:
575 username, _account, password = auth
576 else:
577 print('No credentials found for %s in .netrc'
578 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700579 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700580 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900581
582 if (username and password):
583 manifest_server = manifest_server.replace('://', '://%s:%s@' %
584 (username, password),
585 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900586
Dan Willemsen0745bb22015-08-17 13:41:45 -0700587 transport = PersistentTransport(manifest_server)
588 if manifest_server.startswith('persistent-'):
589 manifest_server = manifest_server[len('persistent-'):]
590
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700591 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700592 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200593 if opt.smart_sync:
594 p = self.manifest.manifestProject
595 b = p.GetBranch(p.CurrentBranch)
596 branch = b.merge
597 if branch.startswith(R_HEADS):
598 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700599
Victor Boivie08c880d2011-04-19 10:32:52 +0200600 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700601 if 'SYNC_TARGET' in env:
602 target = env['SYNC_TARGET']
603 [success, manifest_str] = server.GetApprovedManifest(branch, target)
604 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200605 target = '%s-%s' % (env['TARGET_PRODUCT'],
606 env['TARGET_BUILD_VARIANT'])
607 [success, manifest_str] = server.GetApprovedManifest(branch, target)
608 else:
609 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700610 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200611 assert(opt.smart_tag)
612 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700613
614 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900615 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700616 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900617 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700618 try:
619 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700620 finally:
621 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900622 except IOError as e:
623 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900624 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700625 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700626 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100627 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700628 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900629 print('error: manifest server RPC call failed: %s' %
630 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700631 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530632 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700633 print('error: cannot connect to manifest server %s:\n%s'
634 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900635 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530636 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700637 print('error: cannot connect to manifest server %s:\n%d %s'
638 % (self.manifest.manifest_server, e.errcode, e.errmsg),
639 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700640 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900641 else: # Not smart sync or smart tag mode
642 if os.path.isfile(smart_sync_manifest_path):
643 try:
644 os.remove(smart_sync_manifest_path)
645 except OSError as e:
646 print('error: failed to remove existing smart sync override manifest: %s' %
647 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700648
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700649 rp = self.manifest.repoProject
650 rp.PreSync()
651
652 mp = self.manifest.manifestProject
653 mp.PreSync()
654
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800655 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700656 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800657
Nico Sallembien9bb18162009-12-07 15:38:01 -0800658 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700659 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700660 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900661 no_tags=opt.no_tags,
662 optimized_fetch=opt.optimized_fetch)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800663
664 if mp.HasChanges:
665 syncbuf = SyncBuffer(mp.config)
666 mp.Sync_LocalHalf(syncbuf)
667 if not syncbuf.Finish():
668 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100669 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700670 if opt.jobs is None:
671 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700672
Simran Basib9a1b732015-08-20 12:19:28 -0700673 if self.gitc_manifest:
674 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700675 missing_ok=True)
676 gitc_projects = []
677 opened_projects = []
678 for project in gitc_manifest_projects:
Dan Willemsen250303b2015-09-08 13:27:20 -0700679 if project.relpath in self.gitc_manifest.paths and \
680 self.gitc_manifest.paths[project.relpath].old_revision:
681 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700682 else:
Dan Willemsen250303b2015-09-08 13:27:20 -0700683 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700684
Dan Willemsen250303b2015-09-08 13:27:20 -0700685 if not args:
686 gitc_projects = None
687
688 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700689 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen250303b2015-09-08 13:27:20 -0700690 gitc_utils.generate_gitc_manifest(self.repodir,
691 self.gitc_manifest.gitc_client_name,
Simran Basib9a1b732015-08-20 12:19:28 -0700692 self.gitc_manifest,
Dan Willemsen250303b2015-09-08 13:27:20 -0700693 self.manifest.manifestFile,
Simran Basib9a1b732015-08-20 12:19:28 -0700694 gitc_projects)
695 print('GITC client successfully synced.')
696
697 # The opened projects need to be synced as normal, therefore we
698 # generate a new args list to represent the opened projects.
Dan Willemsen250303b2015-09-08 13:27:20 -0700699 # TODO: make this more reliable -- if there's a project name/path overlap,
700 # this may choose the wrong project.
701 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
702 for p in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700703 if not args:
704 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800705 all_projects = self.GetProjects(args,
706 missing_ok=True,
707 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700708
Dave Borowitz67700e92012-10-23 15:00:54 -0700709 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700710 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700711 to_fetch = []
712 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700713 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700714 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900715 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700716 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700717
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800718 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700719 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700720 if opt.network_only:
721 # bail out now; the rest touches the working tree
722 return
723
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800724 # Iteratively fetch missing and/or nested unregistered submodules
725 previously_missing_set = set()
726 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100727 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800728 all_projects = self.GetProjects(args,
729 missing_ok=True,
730 submodules_ok=opt.fetch_submodules)
731 missing = []
732 for project in all_projects:
733 if project.gitdir not in fetched:
734 missing.append(project)
735 if not missing:
736 break
737 # Stop us from non-stopped fetching actually-missing repos: If set of
738 # missing repos has not been changed from last fetch, we break.
739 missing_set = set(p.name for p in missing)
740 if previously_missing_set == missing_set:
741 break
742 previously_missing_set = missing_set
743 fetched.update(self._Fetch(missing, opt))
744
Julien Campergue335f5ef2013-10-16 11:02:35 +0200745 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700746 # bail out now, we have no working tree
747 return
748
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700749 if self.UpdateProjectList():
750 sys.exit(1)
751
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700752 syncbuf = SyncBuffer(mp.config,
753 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900754 pm = Progress('Syncing work tree', len(all_projects))
755 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700756 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800757 if project.worktree:
Kevin Degiabaa7f32014-11-12 11:27:45 -0700758 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700759 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700760 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700761 if not syncbuf.Finish():
762 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700763
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700764 # If there's a notice that's supposed to print at the end of the sync, print
765 # it now...
766 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700767 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700768
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700769def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800770 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700771 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700772 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800773 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700774 if project.Exists:
775 project.PostRepoUpgrade()
776
777def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
778 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700779 print('info: A new version of repo is available', file=sys.stderr)
780 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700781 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700782 syncbuf = SyncBuffer(rp.config)
783 rp.Sync_LocalHalf(syncbuf)
784 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700785 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700786 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700787 raise RepoChangedException(['--repo-upgraded'])
788 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700789 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700790 else:
791 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700792 print('repo version %s is current' % rp.work_git.describe(HEAD),
793 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700794
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700795def _VerifyTag(project):
796 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
797 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700798 print('warning: GnuPG was not available during last "repo init"\n'
799 'warning: Cannot automatically authenticate repo."""',
800 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700801 return True
802
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700803 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700804 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700805 except GitError:
806 cur = None
807
808 if not cur \
809 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700810 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700811 if rev.startswith(R_HEADS):
812 rev = rev[len(R_HEADS):]
813
Sarah Owenscecd1d82012-11-01 22:59:27 -0700814 print(file=sys.stderr)
815 print("warning: project '%s' branch '%s' is not signed"
816 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700817 return False
818
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800819 env = os.environ.copy()
820 env['GIT_DIR'] = project.gitdir.encode()
821 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700822
823 cmd = [GIT, 'tag', '-v', cur]
824 proc = subprocess.Popen(cmd,
825 stdout = subprocess.PIPE,
826 stderr = subprocess.PIPE,
827 env = env)
828 out = proc.stdout.read()
829 proc.stdout.close()
830
831 err = proc.stderr.read()
832 proc.stderr.close()
833
834 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700835 print(file=sys.stderr)
836 print(out, file=sys.stderr)
837 print(err, file=sys.stderr)
838 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700839 return False
840 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700841
842class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700843 _ALPHA = 0.5
844
Dave Borowitz67700e92012-10-23 15:00:54 -0700845 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100846 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700847 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700848 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700849
850 def Get(self, project):
851 self._Load()
852 return self._times.get(project.name, _ONE_DAY_S)
853
854 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700855 self._Load()
856 name = project.name
857 old = self._times.get(name, t)
858 self._seen.add(name)
859 a = self._ALPHA
860 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700861
862 def _Load(self):
863 if self._times is None:
864 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100865 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700866 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100867 self._times = json.load(f)
868 finally:
869 f.close()
870 except (IOError, ValueError):
871 try:
872 os.remove(self._path)
873 except OSError:
874 pass
875 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700876
877 def Save(self):
878 if self._times is None:
879 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700880
881 to_delete = []
882 for name in self._times:
883 if name not in self._seen:
884 to_delete.append(name)
885 for name in to_delete:
886 del self._times[name]
887
Dave Borowitz67700e92012-10-23 15:00:54 -0700888 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100889 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700890 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100891 json.dump(self._times, f, indent=2)
892 finally:
893 f.close()
894 except (IOError, TypeError):
895 try:
896 os.remove(self._path)
897 except OSError:
898 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700899
900# This is a replacement for xmlrpc.client.Transport using urllib2
901# and supporting persistent-http[s]. It cannot change hosts from
902# request to request like the normal transport, the real url
903# is passed during initialization.
904class PersistentTransport(xmlrpc.client.Transport):
905 def __init__(self, orig_host):
906 self.orig_host = orig_host
907
908 def request(self, host, handler, request_body, verbose=False):
909 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
910 # Python doesn't understand cookies with the #HttpOnly_ prefix
911 # Since we're only using them for HTTP, copy the file temporarily,
912 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700913 if cookiefile:
914 tmpcookiefile = tempfile.NamedTemporaryFile()
915 try:
916 with open(cookiefile) as f:
917 for line in f:
918 if line.startswith("#HttpOnly_"):
919 line = line[len("#HttpOnly_"):]
920 tmpcookiefile.write(line)
921 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700922
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700923 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
924 cookiejar.load()
925 finally:
926 tmpcookiefile.close()
927 else:
928 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700929
930 proxyhandler = urllib.request.ProxyHandler
931 if proxy:
932 proxyhandler = urllib.request.ProxyHandler({
933 "http": proxy,
934 "https": proxy })
935
936 opener = urllib.request.build_opener(
937 urllib.request.HTTPCookieProcessor(cookiejar),
938 proxyhandler)
939
940 url = urllib.parse.urljoin(self.orig_host, handler)
941 parse_results = urllib.parse.urlparse(url)
942
943 scheme = parse_results.scheme
944 if scheme == 'persistent-http':
945 scheme = 'http'
946 if scheme == 'persistent-https':
947 # If we're proxying through persistent-https, use http. The
948 # proxy itself will do the https.
949 if proxy:
950 scheme = 'http'
951 else:
952 scheme = 'https'
953
954 # Parse out any authentication information using the base class
955 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
956
957 url = urllib.parse.urlunparse((
958 scheme,
959 host,
960 parse_results.path,
961 parse_results.params,
962 parse_results.query,
963 parse_results.fragment))
964
965 request = urllib.request.Request(url, request_body)
966 if extra_headers is not None:
967 for (name, header) in extra_headers:
968 request.add_header(name, header)
969 request.add_header('Content-Type', 'text/xml')
970 try:
971 response = opener.open(request)
972 except urllib.error.HTTPError as e:
973 if e.code == 501:
974 # We may have been redirected through a login process
975 # but our POST turned into a GET. Retry.
976 response = opener.open(request)
977 else:
978 raise
979
980 p, u = xmlrpc.client.getparser()
981 while 1:
982 data = response.read(1024)
983 if not data:
984 break
985 p.feed(data)
986 p.close()
987 return u.close()
988
989 def close(self):
990 pass
991