blob: a8022d9d58ef98c6ea814cc308022acc43186824 [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.
194
195 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()
Roy Lee18afd7f2010-05-09 04:32:08 +0800236
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:
240 err_event.set()
241 except:
Shawn O. Pearcee6a0eeb2011-03-22 19:04:47 -0700242 err_event.set()
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -0700243 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)
513 self._fetch_times.Clear()
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700514
David Pursehouse8a68ff92012-09-24 12:15:13 +0900515 self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700516 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700517 if opt.network_only:
518 # bail out now; the rest touches the working tree
519 return
520
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700521 if self.manifest.IsMirror:
522 # bail out now, we have no working tree
523 return
524
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700525 if self.UpdateProjectList():
526 sys.exit(1)
527
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700528 syncbuf = SyncBuffer(mp.config,
529 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900530 pm = Progress('Syncing work tree', len(all_projects))
531 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700532 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800533 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700534 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700535 pm.end()
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700536 print >>sys.stderr
537 if not syncbuf.Finish():
538 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700539
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700540 # If there's a notice that's supposed to print at the end of the sync, print
541 # it now...
542 if self.manifest.notice:
543 print self.manifest.notice
544
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700545def _PostRepoUpgrade(manifest):
546 for project in manifest.projects.values():
547 if project.Exists:
548 project.PostRepoUpgrade()
549
550def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
551 if rp.HasChanges:
552 print >>sys.stderr, 'info: A new version of repo is available'
553 print >>sys.stderr, ''
554 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700555 syncbuf = SyncBuffer(rp.config)
556 rp.Sync_LocalHalf(syncbuf)
557 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700558 sys.exit(1)
559 print >>sys.stderr, 'info: Restarting repo with latest version'
560 raise RepoChangedException(['--repo-upgraded'])
561 else:
562 print >>sys.stderr, 'warning: Skipped upgrade to unverified version'
563 else:
564 if verbose:
565 print >>sys.stderr, 'repo version %s is current' % rp.work_git.describe(HEAD)
566
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700567def _VerifyTag(project):
568 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
569 if not os.path.exists(gpg_dir):
570 print >>sys.stderr,\
571"""warning: GnuPG was not available during last "repo init"
572warning: Cannot automatically authenticate repo."""
573 return True
574
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700575 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700576 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700577 except GitError:
578 cur = None
579
580 if not cur \
581 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700582 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700583 if rev.startswith(R_HEADS):
584 rev = rev[len(R_HEADS):]
585
586 print >>sys.stderr
587 print >>sys.stderr,\
588 "warning: project '%s' branch '%s' is not signed" \
589 % (project.name, rev)
590 return False
591
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800592 env = os.environ.copy()
593 env['GIT_DIR'] = project.gitdir.encode()
594 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700595
596 cmd = [GIT, 'tag', '-v', cur]
597 proc = subprocess.Popen(cmd,
598 stdout = subprocess.PIPE,
599 stderr = subprocess.PIPE,
600 env = env)
601 out = proc.stdout.read()
602 proc.stdout.close()
603
604 err = proc.stderr.read()
605 proc.stderr.close()
606
607 if proc.wait() != 0:
608 print >>sys.stderr
609 print >>sys.stderr, out
610 print >>sys.stderr, err
611 print >>sys.stderr
612 return False
613 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700614
615class _FetchTimes(object):
616 def __init__(self, manifest):
617 self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes')
618 self._times = None
619
620 def Clear(self):
621 self._times = {}
622
623 def Get(self, project):
624 self._Load()
625 return self._times.get(project.name, _ONE_DAY_S)
626
627 def Set(self, project, t):
628 self._times[project.name] = t
629
630 def _Load(self):
631 if self._times is None:
632 try:
633 f = open(self._path)
634 except IOError:
635 self._times = {}
636 return self._times
637 try:
638 try:
639 self._times = pickle.load(f)
640 except:
641 try:
642 os.remove(self._path)
643 except OSError:
644 pass
645 self._times = {}
646 finally:
647 f.close()
648 return self._times
649
650 def Save(self):
651 if self._times is None:
652 return
653 try:
654 f = open(self._path, 'wb')
655 try:
656 pickle.dump(self._times, f)
657 except (IOError, OSError, pickle.PickleError):
658 try:
659 os.remove(self._path)
660 except OSError:
661 pass
662 finally:
663 f.close()