blob: b83f2d4a1b95eca3c10cd582a74f130e61329cb8 [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
David Pursehouse86d973d2012-08-24 10:21:02 +090016import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070017from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070018import os
Dave Borowitz67700e92012-10-23 15:00:54 -070019import pickle
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import re
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070021import shutil
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070022import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import subprocess
24import sys
Shawn O. Pearcef6906872009-04-18 10:49:00 -070025import time
David Pursehouse86d973d2012-08-24 10:21:02 +090026import urlparse
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070027import xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070028
Roy Lee18afd7f2010-05-09 04:32:08 +080029try:
30 import threading as _threading
31except ImportError:
32 import dummy_threading as _threading
33
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070034try:
35 import resource
36 def _rlimit_nofile():
37 return resource.getrlimit(resource.RLIMIT_NOFILE)
38except ImportError:
39 def _rlimit_nofile():
40 return (256, 256)
41
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042from git_command import GIT
David Pursehoused94aaef2012-09-07 09:52:04 +090043from git_refs import R_HEADS, HEAD
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070044from project import Project
45from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080046from command import Command, MirrorSafeCommand
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070047from error import RepoChangedException, GitError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070048from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070049from progress import Progress
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070050
Dave Borowitz67700e92012-10-23 15:00:54 -070051_ONE_DAY_S = 24 * 60 * 60
52
Doug Andersonfc06ced2011-03-16 15:49:18 -070053class _FetchError(Exception):
54 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
55 pass
56
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080057class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080058 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070059 common = True
60 helpSummary = "Update working tree to the latest revision"
61 helpUsage = """
62%prog [<project>...]
63"""
64 helpDescription = """
65The '%prog' command synchronizes local project directories
66with the remote repositories specified in the manifest. If a local
67project does not yet exist, it will clone a new local directory from
68the remote repository and set up tracking branches as specified in
69the manifest. If the local project already exists, '%prog'
70will update the remote branches and rebase any new local changes
71on top of the new remote changes.
72
73'%prog' will synchronize all projects listed at the command
74line. Projects can be specified either by name, or by a relative
75or absolute path to the project's local directory. If no projects
76are specified, '%prog' will synchronize all projects listed in
77the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070078
79The -d/--detach option can be used to switch specified projects
80back to the manifest revision. This option is especially helpful
81if the project is currently on a topic branch, but the manifest
82revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070083
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070084The -s/--smart-sync option can be used to sync to a known good
85build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020086manifest. The -t/--smart-tag option is similar and allows you to
87specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070088
David Pursehousecf76b1b2012-09-14 10:31:42 +090089The -u/--manifest-server-username and -p/--manifest-server-password
90options can be used to specify a username and password to authenticate
91with the manifest server when using the -s or -t option.
92
93If -u and -p are not specified when using the -s or -t option, '%prog'
94will attempt to read authentication credentials for the manifest server
95from the user's .netrc file.
96
97'%prog' will not use authentication credentials from -u/-p or .netrc
98if the manifest server specified in the manifest file already includes
99credentials.
100
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500101The -f/--force-broken option can be used to proceed with syncing
102other projects if a project sync fails.
103
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700104The --no-clone-bundle option disables any attempt to use
105$URL/clone.bundle to bootstrap a new Git repository from a
106resumeable bundle file on a content delivery network. This
107may be necessary if there are problems with the local Python
108HTTP client or proxy configuration, but the Git binary works.
109
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700110SSH Connections
111---------------
112
113If at least one project remote URL uses an SSH connection (ssh://,
114git+ssh://, or user@host:path syntax) repo will automatically
115enable the SSH ControlMaster option when connecting to that host.
116This feature permits other projects in the same '%prog' session to
117reuse the same SSH tunnel, saving connection setup overheads.
118
119To disable this behavior on UNIX platforms, set the GIT_SSH
120environment variable to 'ssh'. For example:
121
122 export GIT_SSH=ssh
123 %prog
124
125Compatibility
126~~~~~~~~~~~~~
127
128This feature is automatically disabled on Windows, due to the lack
129of UNIX domain socket support.
130
131This feature is not compatible with url.insteadof rewrites in the
132user's ~/.gitconfig. '%prog' is currently not able to perform the
133rewrite early enough to establish the ControlMaster tunnel.
134
135If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
136later is required to fix a server side protocol bug.
137
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700138"""
139
Nico Sallembien6623b212010-05-11 12:57:01 -0700140 def _Options(self, p, show_smart=True):
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700141 self.jobs = self.manifest.default.sync_j
142
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500143 p.add_option('-f', '--force-broken',
144 dest='force_broken', action='store_true',
145 help="continue sync even if a project fails to sync")
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700146 p.add_option('-l','--local-only',
147 dest='local_only', action='store_true',
148 help="only update working tree, don't fetch")
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700149 p.add_option('-n','--network-only',
150 dest='network_only', action='store_true',
151 help="fetch only, don't update working tree")
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700152 p.add_option('-d','--detach',
153 dest='detach_head', action='store_true',
154 help='detach projects back to manifest revision')
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700155 p.add_option('-c','--current-branch',
156 dest='current_branch_only', action='store_true',
157 help='fetch only current branch from server')
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700158 p.add_option('-q','--quiet',
159 dest='quiet', action='store_true',
160 help='be more quiet')
Roy Lee18afd7f2010-05-09 04:32:08 +0800161 p.add_option('-j','--jobs',
162 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700163 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500164 p.add_option('-m', '--manifest-name',
165 dest='manifest_name',
166 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700167 p.add_option('--no-clone-bundle',
168 dest='no_clone_bundle', action='store_true',
169 help='disable use of /clone.bundle on HTTP/HTTPS')
Nico Sallembien6623b212010-05-11 12:57:01 -0700170 if show_smart:
171 p.add_option('-s', '--smart-sync',
172 dest='smart_sync', action='store_true',
173 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200174 p.add_option('-t', '--smart-tag',
175 dest='smart_tag', action='store',
176 help='smart sync using manifest from a known tag')
David Pursehousecf76b1b2012-09-14 10:31:42 +0900177 p.add_option('-u', '--manifest-server-username', action='store',
178 dest='manifest_server_username',
179 help='username to authenticate with the manifest server')
180 p.add_option('-p', '--manifest-server-password', action='store',
181 dest='manifest_server_password',
182 help='password to authenticate with the manifest server')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700183
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700184 g = p.add_option_group('repo Version options')
185 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700186 dest='no_repo_verify', action='store_true',
187 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700188 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800189 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700190 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700191
Doug Andersonfc06ced2011-03-16 15:49:18 -0700192 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
193 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800194
Doug Andersonfc06ced2011-03-16 15:49:18 -0700195 Args:
196 opt: Program options returned from optparse. See _Options().
197 project: Project object for the project to fetch.
198 lock: Lock for accessing objects that are shared amongst multiple
199 _FetchHelper() threads.
200 fetched: set object that we will add project.gitdir to when we're done
201 (with our lock held).
202 pm: Instance of a Project object. We will call pm.update() (with our
203 lock held).
204 sem: We'll release() this semaphore when we exit so that another thread
205 can be started up.
206 err_event: We'll set this event in the case of an error (after printing
207 out info about the error).
208 """
209 # We'll set to true once we've locked the lock.
210 did_lock = False
211
212 # Encapsulate everything in a try/except/finally so that:
213 # - We always set err_event in the case of an exception.
214 # - We always make sure we call sem.release().
215 # - We always make sure we unlock the lock if we locked it.
216 try:
Shawn O. Pearcee6a0eeb2011-03-22 19:04:47 -0700217 try:
Dave Borowitz67700e92012-10-23 15:00:54 -0700218 start = time.time()
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700219 success = project.Sync_NetworkHalf(
220 quiet=opt.quiet,
221 current_branch_only=opt.current_branch_only,
222 clone_bundle=not opt.no_clone_bundle)
Dave Borowitz67700e92012-10-23 15:00:54 -0700223 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700224
Shawn O. Pearcee6a0eeb2011-03-22 19:04:47 -0700225 # Lock around all the rest of the code, since printing, updating a set
226 # and Progress.update() are not thread safe.
227 lock.acquire()
228 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700229
Shawn O. Pearcee6a0eeb2011-03-22 19:04:47 -0700230 if not success:
231 print >>sys.stderr, 'error: Cannot fetch %s' % project.name
232 if opt.force_broken:
233 print >>sys.stderr, 'warn: --force-broken, continuing to sync'
234 else:
235 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700236
Shawn O. Pearcee6a0eeb2011-03-22 19:04:47 -0700237 fetched.add(project.gitdir)
238 pm.update()
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -0700239 except _FetchError:
Shawn O. Pearcee6a0eeb2011-03-22 19:04:47 -0700240 err_event.set()
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -0700241 except:
242 err_event.set()
243 raise
Doug Andersonfc06ced2011-03-16 15:49:18 -0700244 finally:
245 if did_lock:
246 lock.release()
247 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800248
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700249 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700250 fetched = set()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700251 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800252
253 if self.jobs == 1:
254 for project in projects:
255 pm.update()
Shawn O. Pearce5d0efdb2012-08-02 12:13:01 -0700256 if project.Sync_NetworkHalf(
257 quiet=opt.quiet,
258 current_branch_only=opt.current_branch_only,
259 clone_bundle=not opt.no_clone_bundle):
Roy Lee18afd7f2010-05-09 04:32:08 +0800260 fetched.add(project.gitdir)
261 else:
262 print >>sys.stderr, 'error: Cannot fetch %s' % project.name
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500263 if opt.force_broken:
264 print >>sys.stderr, 'warn: --force-broken, continuing to sync'
265 else:
266 sys.exit(1)
Roy Lee18afd7f2010-05-09 04:32:08 +0800267 else:
268 threads = set()
269 lock = _threading.Lock()
270 sem = _threading.Semaphore(self.jobs)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700271 err_event = _threading.Event()
Roy Lee18afd7f2010-05-09 04:32:08 +0800272 for project in projects:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700273 # Check for any errors before starting any new threads.
274 # ...we'll let existing threads finish, though.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400275 if err_event.isSet():
Doug Andersonfc06ced2011-03-16 15:49:18 -0700276 break
277
Roy Lee18afd7f2010-05-09 04:32:08 +0800278 sem.acquire()
279 t = _threading.Thread(target = self._FetchHelper,
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700280 args = (opt,
281 project,
282 lock,
283 fetched,
284 pm,
Doug Andersonfc06ced2011-03-16 15:49:18 -0700285 sem,
286 err_event))
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200287 # Ensure that Ctrl-C will not freeze the repo process.
288 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800289 threads.add(t)
290 t.start()
291
292 for t in threads:
293 t.join()
294
Doug Andersonfc06ced2011-03-16 15:49:18 -0700295 # If we saw an error, exit with code 1 so that other scripts can check.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400296 if err_event.isSet():
Doug Andersonfc06ced2011-03-16 15:49:18 -0700297 print >>sys.stderr, '\nerror: Exited sync due to fetch errors'
298 sys.exit(1)
299
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700300 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700301 self._fetch_times.Save()
Shawn O. Pearce0d2b61f2009-07-03 15:22:49 -0700302 for project in projects:
303 project.bare_git.gc('--auto')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700304 return fetched
305
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700306 def UpdateProjectList(self):
307 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700308 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700309 if project.relpath:
310 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700311 file_name = 'project.list'
312 file_path = os.path.join(self.manifest.repodir, file_name)
313 old_project_paths = []
314
315 if os.path.exists(file_path):
316 fd = open(file_path, 'r')
317 try:
318 old_project_paths = fd.read().split('\n')
319 finally:
320 fd.close()
321 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700322 if not path:
323 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700324 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900325 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400326 if os.path.exists(self.manifest.topdir + '/' + path):
327 project = Project(
328 manifest = self.manifest,
329 name = path,
330 remote = RemoteSpec('origin'),
331 gitdir = os.path.join(self.manifest.topdir,
332 path, '.git'),
333 worktree = os.path.join(self.manifest.topdir, path),
334 relpath = path,
335 revisionExpr = 'HEAD',
Colin Cross5acde752012-03-28 20:15:45 -0700336 revisionId = None,
337 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400338
339 if project.IsDirty():
340 print >>sys.stderr, 'error: Cannot remove project "%s": \
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700341uncommitted changes are present' % project.relpath
Anthonyf3fdf822009-09-26 13:38:52 -0400342 print >>sys.stderr, ' commit changes, then run sync again'
343 return -1
344 else:
345 print >>sys.stderr, 'Deleting obsolete path %s' % project.worktree
346 shutil.rmtree(project.worktree)
347 # Try deleting parent subdirs if they are empty
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200348 project_dir = os.path.dirname(project.worktree)
349 while project_dir != self.manifest.topdir:
Anthonyf3fdf822009-09-26 13:38:52 -0400350 try:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200351 os.rmdir(project_dir)
Anthonyf3fdf822009-09-26 13:38:52 -0400352 except OSError:
353 break
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200354 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700355
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700356 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700357 fd = open(file_path, 'w')
358 try:
359 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700360 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700361 finally:
362 fd.close()
363 return 0
364
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700365 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800366 if opt.jobs:
367 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700368 if self.jobs > 1:
369 soft_limit, _ = _rlimit_nofile()
370 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
371
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700372 if opt.network_only and opt.detach_head:
373 print >>sys.stderr, 'error: cannot combine -n and -d'
374 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700375 if opt.network_only and opt.local_only:
376 print >>sys.stderr, 'error: cannot combine -n and -l'
377 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500378 if opt.manifest_name and opt.smart_sync:
379 print >>sys.stderr, 'error: cannot combine -m and -s'
380 sys.exit(1)
381 if opt.manifest_name and opt.smart_tag:
382 print >>sys.stderr, 'error: cannot combine -m and -t'
383 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900384 if opt.manifest_server_username or opt.manifest_server_password:
385 if not (opt.smart_sync or opt.smart_tag):
386 print >>sys.stderr, 'error: -u and -p may only be combined with ' \
387 '-s or -t'
388 sys.exit(1)
389 if None in [opt.manifest_server_username, opt.manifest_server_password]:
390 print >>sys.stderr, 'error: both -u and -p must be given'
391 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500392
393 if opt.manifest_name:
394 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700395
Victor Boivie08c880d2011-04-19 10:32:52 +0200396 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700397 if not self.manifest.manifest_server:
398 print >>sys.stderr, \
399 'error: cannot smart sync: no manifest server defined in manifest'
400 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900401
402 manifest_server = self.manifest.manifest_server
David Pursehousecf76b1b2012-09-14 10:31:42 +0900403
David Pursehouse86d973d2012-08-24 10:21:02 +0900404 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900405 username = None
406 password = None
407 if opt.manifest_server_username and opt.manifest_server_password:
408 username = opt.manifest_server_username
409 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900410 else:
411 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900412 info = netrc.netrc()
413 except IOError:
414 print >>sys.stderr, '.netrc file does not exist or could not be opened'
David Pursehouse86d973d2012-08-24 10:21:02 +0900415 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900416 try:
417 parse_result = urlparse.urlparse(manifest_server)
418 if parse_result.hostname:
419 username, _account, password = \
420 info.authenticators(parse_result.hostname)
421 except TypeError:
422 # TypeError is raised when the given hostname is not present
423 # in the .netrc file.
424 print >>sys.stderr, 'No credentials found for %s in .netrc' % \
425 parse_result.hostname
426 except netrc.NetrcParseError, e:
427 print >>sys.stderr, 'Error parsing .netrc file: %s' % e
428
429 if (username and password):
430 manifest_server = manifest_server.replace('://', '://%s:%s@' %
431 (username, password),
432 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900433
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700434 try:
David Pursehouse86d973d2012-08-24 10:21:02 +0900435 server = xmlrpclib.Server(manifest_server)
Victor Boivie08c880d2011-04-19 10:32:52 +0200436 if opt.smart_sync:
437 p = self.manifest.manifestProject
438 b = p.GetBranch(p.CurrentBranch)
439 branch = b.merge
440 if branch.startswith(R_HEADS):
441 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700442
Victor Boivie08c880d2011-04-19 10:32:52 +0200443 env = os.environ.copy()
444 if (env.has_key('TARGET_PRODUCT') and
445 env.has_key('TARGET_BUILD_VARIANT')):
446 target = '%s-%s' % (env['TARGET_PRODUCT'],
447 env['TARGET_BUILD_VARIANT'])
448 [success, manifest_str] = server.GetApprovedManifest(branch, target)
449 else:
450 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700451 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200452 assert(opt.smart_tag)
453 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700454
455 if success:
456 manifest_name = "smart_sync_override.xml"
457 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
458 manifest_name)
459 try:
460 f = open(manifest_path, 'w')
461 try:
462 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700463 finally:
464 f.close()
465 except IOError:
466 print >>sys.stderr, 'error: cannot write manifest to %s' % \
467 manifest_path
468 sys.exit(1)
Nico Sallembien719965a2010-04-20 15:28:19 -0700469 self.manifest.Override(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700470 else:
471 print >>sys.stderr, 'error: %s' % manifest_str
472 sys.exit(1)
David Pursehousebd489c42012-08-23 10:21:26 +0900473 except (socket.error, IOError, xmlrpclib.Fault), e:
474 print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%s' % (
475 self.manifest.manifest_server, e)
476 sys.exit(1)
477 except xmlrpclib.ProtocolError, e:
478 print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%d %s' % (
479 self.manifest.manifest_server, e.errcode, e.errmsg)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700480 sys.exit(1)
481
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700482 rp = self.manifest.repoProject
483 rp.PreSync()
484
485 mp = self.manifest.manifestProject
486 mp.PreSync()
487
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800488 if opt.repo_upgraded:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700489 _PostRepoUpgrade(self.manifest)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800490
Nico Sallembien9bb18162009-12-07 15:38:01 -0800491 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700492 mp.Sync_NetworkHalf(quiet=opt.quiet,
493 current_branch_only=opt.current_branch_only)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800494
495 if mp.HasChanges:
496 syncbuf = SyncBuffer(mp.config)
497 mp.Sync_LocalHalf(syncbuf)
498 if not syncbuf.Finish():
499 sys.exit(1)
500 self.manifest._Unload()
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700501 if opt.jobs is None:
502 self.jobs = self.manifest.default.sync_j
David Pursehouse8a68ff92012-09-24 12:15:13 +0900503 all_projects = self.GetProjects(args, missing_ok=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700504
Dave Borowitz67700e92012-10-23 15:00:54 -0700505 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700506 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700507 to_fetch = []
508 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700509 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700510 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900511 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700512 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700513
David Pursehouse8a68ff92012-09-24 12:15:13 +0900514 self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700515 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700516 if opt.network_only:
517 # bail out now; the rest touches the working tree
518 return
519
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700520 if self.manifest.IsMirror:
521 # bail out now, we have no working tree
522 return
523
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700524 if self.UpdateProjectList():
525 sys.exit(1)
526
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700527 syncbuf = SyncBuffer(mp.config,
528 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900529 pm = Progress('Syncing work tree', len(all_projects))
530 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700531 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800532 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700533 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700534 pm.end()
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700535 print >>sys.stderr
536 if not syncbuf.Finish():
537 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700538
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700539 # If there's a notice that's supposed to print at the end of the sync, print
540 # it now...
541 if self.manifest.notice:
542 print self.manifest.notice
543
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700544def _PostRepoUpgrade(manifest):
545 for project in manifest.projects.values():
546 if project.Exists:
547 project.PostRepoUpgrade()
548
549def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
550 if rp.HasChanges:
551 print >>sys.stderr, 'info: A new version of repo is available'
552 print >>sys.stderr, ''
553 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700554 syncbuf = SyncBuffer(rp.config)
555 rp.Sync_LocalHalf(syncbuf)
556 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700557 sys.exit(1)
558 print >>sys.stderr, 'info: Restarting repo with latest version'
559 raise RepoChangedException(['--repo-upgraded'])
560 else:
561 print >>sys.stderr, 'warning: Skipped upgrade to unverified version'
562 else:
563 if verbose:
564 print >>sys.stderr, 'repo version %s is current' % rp.work_git.describe(HEAD)
565
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700566def _VerifyTag(project):
567 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
568 if not os.path.exists(gpg_dir):
569 print >>sys.stderr,\
570"""warning: GnuPG was not available during last "repo init"
571warning: Cannot automatically authenticate repo."""
572 return True
573
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700574 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700575 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700576 except GitError:
577 cur = None
578
579 if not cur \
580 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700581 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700582 if rev.startswith(R_HEADS):
583 rev = rev[len(R_HEADS):]
584
585 print >>sys.stderr
586 print >>sys.stderr,\
587 "warning: project '%s' branch '%s' is not signed" \
588 % (project.name, rev)
589 return False
590
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800591 env = os.environ.copy()
592 env['GIT_DIR'] = project.gitdir.encode()
593 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700594
595 cmd = [GIT, 'tag', '-v', cur]
596 proc = subprocess.Popen(cmd,
597 stdout = subprocess.PIPE,
598 stderr = subprocess.PIPE,
599 env = env)
600 out = proc.stdout.read()
601 proc.stdout.close()
602
603 err = proc.stderr.read()
604 proc.stderr.close()
605
606 if proc.wait() != 0:
607 print >>sys.stderr
608 print >>sys.stderr, out
609 print >>sys.stderr, err
610 print >>sys.stderr
611 return False
612 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700613
614class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700615 _ALPHA = 0.5
616
Dave Borowitz67700e92012-10-23 15:00:54 -0700617 def __init__(self, manifest):
618 self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes')
619 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700620 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700621
622 def Get(self, project):
623 self._Load()
624 return self._times.get(project.name, _ONE_DAY_S)
625
626 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700627 self._Load()
628 name = project.name
629 old = self._times.get(name, t)
630 self._seen.add(name)
631 a = self._ALPHA
632 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700633
634 def _Load(self):
635 if self._times is None:
636 try:
637 f = open(self._path)
638 except IOError:
639 self._times = {}
640 return self._times
641 try:
642 try:
643 self._times = pickle.load(f)
644 except:
645 try:
646 os.remove(self._path)
647 except OSError:
648 pass
649 self._times = {}
650 finally:
651 f.close()
652 return self._times
653
654 def Save(self):
655 if self._times is None:
656 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700657
658 to_delete = []
659 for name in self._times:
660 if name not in self._seen:
661 to_delete.append(name)
662 for name in to_delete:
663 del self._times[name]
664
Dave Borowitz67700e92012-10-23 15:00:54 -0700665 try:
666 f = open(self._path, 'wb')
667 try:
668 pickle.dump(self._times, f)
669 except (IOError, OSError, pickle.PickleError):
670 try:
671 os.remove(self._path)
672 except OSError:
673 pass
674 finally:
675 f.close()