blob: 9e4a9754c89ea6c2a275d6cc8e99c4b6f43ac0fb [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
Dave Borowitz18857212012-10-23 17:02:59 -070042try:
43 import multiprocessing
44except ImportError:
45 multiprocessing = None
46
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070047from git_command import GIT
David Pursehoused94aaef2012-09-07 09:52:04 +090048from git_refs import R_HEADS, HEAD
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070049from project import Project
50from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080051from command import Command, MirrorSafeCommand
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070052from error import RepoChangedException, GitError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070053from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070054from progress import Progress
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070055
Dave Borowitz67700e92012-10-23 15:00:54 -070056_ONE_DAY_S = 24 * 60 * 60
57
Doug Andersonfc06ced2011-03-16 15:49:18 -070058class _FetchError(Exception):
59 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
60 pass
61
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080062class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080063 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070064 common = True
65 helpSummary = "Update working tree to the latest revision"
66 helpUsage = """
67%prog [...]
68"""
69 helpDescription = """
70The '%prog' command synchronizes local project directories
71with the remote repositories specified in the manifest. If a local
72project does not yet exist, it will clone a new local directory from
73the remote repository and set up tracking branches as specified in
74the manifest. If the local project already exists, '%prog'
75will update the remote branches and rebase any new local changes
76on top of the new remote changes.
77
78'%prog' will synchronize all projects listed at the command
79line. Projects can be specified either by name, or by a relative
80or absolute path to the project's local directory. If no projects
81are specified, '%prog' will synchronize all projects listed in
82the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070083
84The -d/--detach option can be used to switch specified projects
85back to the manifest revision. This option is especially helpful
86if the project is currently on a topic branch, but the manifest
87revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070088
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070089The -s/--smart-sync option can be used to sync to a known good
90build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020091manifest. The -t/--smart-tag option is similar and allows you to
92specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070093
David Pursehousecf76b1b2012-09-14 10:31:42 +090094The -u/--manifest-server-username and -p/--manifest-server-password
95options can be used to specify a username and password to authenticate
96with the manifest server when using the -s or -t option.
97
98If -u and -p are not specified when using the -s or -t option, '%prog'
99will attempt to read authentication credentials for the manifest server
100from the user's .netrc file.
101
102'%prog' will not use authentication credentials from -u/-p or .netrc
103if the manifest server specified in the manifest file already includes
104credentials.
105
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500106The -f/--force-broken option can be used to proceed with syncing
107other projects if a project sync fails.
108
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700109The --no-clone-bundle option disables any attempt to use
110$URL/clone.bundle to bootstrap a new Git repository from a
111resumeable bundle file on a content delivery network. This
112may be necessary if there are problems with the local Python
113HTTP client or proxy configuration, but the Git binary works.
114
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700115SSH Connections
116---------------
117
118If at least one project remote URL uses an SSH connection (ssh://,
119git+ssh://, or user@host:path syntax) repo will automatically
120enable the SSH ControlMaster option when connecting to that host.
121This feature permits other projects in the same '%prog' session to
122reuse the same SSH tunnel, saving connection setup overheads.
123
124To disable this behavior on UNIX platforms, set the GIT_SSH
125environment variable to 'ssh'. For example:
126
127 export GIT_SSH=ssh
128 %prog
129
130Compatibility
131~~~~~~~~~~~~~
132
133This feature is automatically disabled on Windows, due to the lack
134of UNIX domain socket support.
135
136This feature is not compatible with url.insteadof rewrites in the
137user's ~/.gitconfig. '%prog' is currently not able to perform the
138rewrite early enough to establish the ControlMaster tunnel.
139
140If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
141later is required to fix a server side protocol bug.
142
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700143"""
144
Nico Sallembien6623b212010-05-11 12:57:01 -0700145 def _Options(self, p, show_smart=True):
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700146 self.jobs = self.manifest.default.sync_j
147
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500148 p.add_option('-f', '--force-broken',
149 dest='force_broken', action='store_true',
150 help="continue sync even if a project fails to sync")
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700151 p.add_option('-l','--local-only',
152 dest='local_only', action='store_true',
153 help="only update working tree, don't fetch")
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700154 p.add_option('-n','--network-only',
155 dest='network_only', action='store_true',
156 help="fetch only, don't update working tree")
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700157 p.add_option('-d','--detach',
158 dest='detach_head', action='store_true',
159 help='detach projects back to manifest revision')
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700160 p.add_option('-c','--current-branch',
161 dest='current_branch_only', action='store_true',
162 help='fetch only current branch from server')
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700163 p.add_option('-q','--quiet',
164 dest='quiet', action='store_true',
165 help='be more quiet')
Roy Lee18afd7f2010-05-09 04:32:08 +0800166 p.add_option('-j','--jobs',
167 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700168 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500169 p.add_option('-m', '--manifest-name',
170 dest='manifest_name',
171 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700172 p.add_option('--no-clone-bundle',
173 dest='no_clone_bundle', action='store_true',
174 help='disable use of /clone.bundle on HTTP/HTTPS')
Nico Sallembien6623b212010-05-11 12:57:01 -0700175 if show_smart:
176 p.add_option('-s', '--smart-sync',
177 dest='smart_sync', action='store_true',
178 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200179 p.add_option('-t', '--smart-tag',
180 dest='smart_tag', action='store',
181 help='smart sync using manifest from a known tag')
David Pursehousecf76b1b2012-09-14 10:31:42 +0900182 p.add_option('-u', '--manifest-server-username', action='store',
183 dest='manifest_server_username',
184 help='username to authenticate with the manifest server')
185 p.add_option('-p', '--manifest-server-password', action='store',
186 dest='manifest_server_password',
187 help='password to authenticate with the manifest server')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700188
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700189 g = p.add_option_group('repo Version options')
190 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700191 dest='no_repo_verify', action='store_true',
192 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700193 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800194 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700195 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700196
Doug Andersonfc06ced2011-03-16 15:49:18 -0700197 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
198 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800199
Doug Andersonfc06ced2011-03-16 15:49:18 -0700200 Args:
201 opt: Program options returned from optparse. See _Options().
202 project: Project object for the project to fetch.
203 lock: Lock for accessing objects that are shared amongst multiple
204 _FetchHelper() threads.
205 fetched: set object that we will add project.gitdir to when we're done
206 (with our lock held).
207 pm: Instance of a Project object. We will call pm.update() (with our
208 lock held).
209 sem: We'll release() this semaphore when we exit so that another thread
210 can be started up.
211 err_event: We'll set this event in the case of an error (after printing
212 out info about the error).
213 """
214 # We'll set to true once we've locked the lock.
215 did_lock = False
216
217 # Encapsulate everything in a try/except/finally so that:
218 # - We always set err_event in the case of an exception.
219 # - We always make sure we call sem.release().
220 # - We always make sure we unlock the lock if we locked it.
221 try:
Shawn O. Pearcee6a0eeb2011-03-22 19:04:47 -0700222 try:
Dave Borowitz67700e92012-10-23 15:00:54 -0700223 start = time.time()
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700224 success = project.Sync_NetworkHalf(
225 quiet=opt.quiet,
226 current_branch_only=opt.current_branch_only,
227 clone_bundle=not opt.no_clone_bundle)
Dave Borowitz67700e92012-10-23 15:00:54 -0700228 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700229
Shawn O. Pearcee6a0eeb2011-03-22 19:04:47 -0700230 # Lock around all the rest of the code, since printing, updating a set
231 # and Progress.update() are not thread safe.
232 lock.acquire()
233 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700234
Shawn O. Pearcee6a0eeb2011-03-22 19:04:47 -0700235 if not success:
236 print >>sys.stderr, 'error: Cannot fetch %s' % project.name
237 if opt.force_broken:
238 print >>sys.stderr, 'warn: --force-broken, continuing to sync'
239 else:
240 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700241
Shawn O. Pearcee6a0eeb2011-03-22 19:04:47 -0700242 fetched.add(project.gitdir)
243 pm.update()
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -0700244 except _FetchError:
Shawn O. Pearcee6a0eeb2011-03-22 19:04:47 -0700245 err_event.set()
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -0700246 except:
247 err_event.set()
248 raise
Doug Andersonfc06ced2011-03-16 15:49:18 -0700249 finally:
250 if did_lock:
251 lock.release()
252 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800253
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700254 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700255 fetched = set()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700256 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800257
258 if self.jobs == 1:
259 for project in projects:
260 pm.update()
Shawn O. Pearce5d0efdb2012-08-02 12:13:01 -0700261 if project.Sync_NetworkHalf(
262 quiet=opt.quiet,
263 current_branch_only=opt.current_branch_only,
264 clone_bundle=not opt.no_clone_bundle):
Roy Lee18afd7f2010-05-09 04:32:08 +0800265 fetched.add(project.gitdir)
266 else:
267 print >>sys.stderr, 'error: Cannot fetch %s' % project.name
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500268 if opt.force_broken:
269 print >>sys.stderr, 'warn: --force-broken, continuing to sync'
270 else:
271 sys.exit(1)
Roy Lee18afd7f2010-05-09 04:32:08 +0800272 else:
273 threads = set()
274 lock = _threading.Lock()
275 sem = _threading.Semaphore(self.jobs)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700276 err_event = _threading.Event()
Roy Lee18afd7f2010-05-09 04:32:08 +0800277 for project in projects:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700278 # Check for any errors before starting any new threads.
279 # ...we'll let existing threads finish, though.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400280 if err_event.isSet():
Doug Andersonfc06ced2011-03-16 15:49:18 -0700281 break
282
Roy Lee18afd7f2010-05-09 04:32:08 +0800283 sem.acquire()
284 t = _threading.Thread(target = self._FetchHelper,
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700285 args = (opt,
286 project,
287 lock,
288 fetched,
289 pm,
Doug Andersonfc06ced2011-03-16 15:49:18 -0700290 sem,
291 err_event))
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200292 # Ensure that Ctrl-C will not freeze the repo process.
293 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800294 threads.add(t)
295 t.start()
296
297 for t in threads:
298 t.join()
299
Doug Andersonfc06ced2011-03-16 15:49:18 -0700300 # If we saw an error, exit with code 1 so that other scripts can check.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400301 if err_event.isSet():
Doug Andersonfc06ced2011-03-16 15:49:18 -0700302 print >>sys.stderr, '\nerror: Exited sync due to fetch errors'
303 sys.exit(1)
304
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700305 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700306 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700307
308 self._GCProjects(projects)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700309 return fetched
310
Dave Borowitz18857212012-10-23 17:02:59 -0700311 def _GCProjects(self, projects):
312 if multiprocessing:
313 cpu_count = multiprocessing.cpu_count()
314 else:
315 cpu_count = 1
316 jobs = min(self.jobs, cpu_count)
317
318 if jobs < 2:
319 for project in projects:
320 project.bare_git.gc('--auto')
321 return
322
323 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
324
325 threads = set()
326 sem = _threading.Semaphore(jobs)
327 err_event = _threading.Event()
328
329 def GC(project):
330 try:
331 try:
332 project.bare_git.gc('--auto', config=config)
333 except GitError:
334 err_event.set()
335 except:
336 err_event.set()
337 raise
338 finally:
339 sem.release()
340
341 for project in projects:
342 if err_event.isSet():
343 break
344 sem.acquire()
345 t = _threading.Thread(target=GC, args=(project,))
346 t.daemon = True
347 threads.add(t)
348 t.start()
349
350 for t in threads:
351 t.join()
352
353 if err_event.isSet():
354 print >>sys.stderr, '\nerror: Exited sync due to gc errors'
355 sys.exit(1)
356
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700357 def UpdateProjectList(self):
358 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700359 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700360 if project.relpath:
361 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700362 file_name = 'project.list'
363 file_path = os.path.join(self.manifest.repodir, file_name)
364 old_project_paths = []
365
366 if os.path.exists(file_path):
367 fd = open(file_path, 'r')
368 try:
369 old_project_paths = fd.read().split('\n')
370 finally:
371 fd.close()
372 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700373 if not path:
374 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700375 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900376 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400377 if os.path.exists(self.manifest.topdir + '/' + path):
378 project = Project(
379 manifest = self.manifest,
380 name = path,
381 remote = RemoteSpec('origin'),
382 gitdir = os.path.join(self.manifest.topdir,
383 path, '.git'),
384 worktree = os.path.join(self.manifest.topdir, path),
385 relpath = path,
386 revisionExpr = 'HEAD',
Colin Cross5acde752012-03-28 20:15:45 -0700387 revisionId = None,
388 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400389
390 if project.IsDirty():
391 print >>sys.stderr, 'error: Cannot remove project "%s": \
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700392uncommitted changes are present' % project.relpath
Anthonyf3fdf822009-09-26 13:38:52 -0400393 print >>sys.stderr, ' commit changes, then run sync again'
394 return -1
395 else:
396 print >>sys.stderr, 'Deleting obsolete path %s' % project.worktree
397 shutil.rmtree(project.worktree)
398 # Try deleting parent subdirs if they are empty
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200399 project_dir = os.path.dirname(project.worktree)
400 while project_dir != self.manifest.topdir:
Anthonyf3fdf822009-09-26 13:38:52 -0400401 try:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200402 os.rmdir(project_dir)
Anthonyf3fdf822009-09-26 13:38:52 -0400403 except OSError:
404 break
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200405 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700406
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700407 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700408 fd = open(file_path, 'w')
409 try:
410 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700411 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700412 finally:
413 fd.close()
414 return 0
415
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700416 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800417 if opt.jobs:
418 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700419 if self.jobs > 1:
420 soft_limit, _ = _rlimit_nofile()
421 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
422
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700423 if opt.network_only and opt.detach_head:
424 print >>sys.stderr, 'error: cannot combine -n and -d'
425 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700426 if opt.network_only and opt.local_only:
427 print >>sys.stderr, 'error: cannot combine -n and -l'
428 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500429 if opt.manifest_name and opt.smart_sync:
430 print >>sys.stderr, 'error: cannot combine -m and -s'
431 sys.exit(1)
432 if opt.manifest_name and opt.smart_tag:
433 print >>sys.stderr, 'error: cannot combine -m and -t'
434 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900435 if opt.manifest_server_username or opt.manifest_server_password:
436 if not (opt.smart_sync or opt.smart_tag):
437 print >>sys.stderr, 'error: -u and -p may only be combined with ' \
438 '-s or -t'
439 sys.exit(1)
440 if None in [opt.manifest_server_username, opt.manifest_server_password]:
441 print >>sys.stderr, 'error: both -u and -p must be given'
442 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500443
444 if opt.manifest_name:
445 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700446
Victor Boivie08c880d2011-04-19 10:32:52 +0200447 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700448 if not self.manifest.manifest_server:
449 print >>sys.stderr, \
450 'error: cannot smart sync: no manifest server defined in manifest'
451 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900452
453 manifest_server = self.manifest.manifest_server
David Pursehousecf76b1b2012-09-14 10:31:42 +0900454
David Pursehouse86d973d2012-08-24 10:21:02 +0900455 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900456 username = None
457 password = None
458 if opt.manifest_server_username and opt.manifest_server_password:
459 username = opt.manifest_server_username
460 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900461 else:
462 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900463 info = netrc.netrc()
464 except IOError:
465 print >>sys.stderr, '.netrc file does not exist or could not be opened'
David Pursehouse86d973d2012-08-24 10:21:02 +0900466 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900467 try:
468 parse_result = urlparse.urlparse(manifest_server)
469 if parse_result.hostname:
470 username, _account, password = \
471 info.authenticators(parse_result.hostname)
472 except TypeError:
473 # TypeError is raised when the given hostname is not present
474 # in the .netrc file.
475 print >>sys.stderr, 'No credentials found for %s in .netrc' % \
476 parse_result.hostname
477 except netrc.NetrcParseError, e:
478 print >>sys.stderr, 'Error parsing .netrc file: %s' % e
479
480 if (username and password):
481 manifest_server = manifest_server.replace('://', '://%s:%s@' %
482 (username, password),
483 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900484
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700485 try:
David Pursehouse86d973d2012-08-24 10:21:02 +0900486 server = xmlrpclib.Server(manifest_server)
Victor Boivie08c880d2011-04-19 10:32:52 +0200487 if opt.smart_sync:
488 p = self.manifest.manifestProject
489 b = p.GetBranch(p.CurrentBranch)
490 branch = b.merge
491 if branch.startswith(R_HEADS):
492 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700493
Victor Boivie08c880d2011-04-19 10:32:52 +0200494 env = os.environ.copy()
495 if (env.has_key('TARGET_PRODUCT') and
496 env.has_key('TARGET_BUILD_VARIANT')):
497 target = '%s-%s' % (env['TARGET_PRODUCT'],
498 env['TARGET_BUILD_VARIANT'])
499 [success, manifest_str] = server.GetApprovedManifest(branch, target)
500 else:
501 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700502 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200503 assert(opt.smart_tag)
504 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700505
506 if success:
507 manifest_name = "smart_sync_override.xml"
508 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
509 manifest_name)
510 try:
511 f = open(manifest_path, 'w')
512 try:
513 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700514 finally:
515 f.close()
516 except IOError:
517 print >>sys.stderr, 'error: cannot write manifest to %s' % \
518 manifest_path
519 sys.exit(1)
Nico Sallembien719965a2010-04-20 15:28:19 -0700520 self.manifest.Override(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700521 else:
522 print >>sys.stderr, 'error: %s' % manifest_str
523 sys.exit(1)
David Pursehousebd489c42012-08-23 10:21:26 +0900524 except (socket.error, IOError, xmlrpclib.Fault), e:
525 print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%s' % (
526 self.manifest.manifest_server, e)
527 sys.exit(1)
528 except xmlrpclib.ProtocolError, e:
529 print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%d %s' % (
530 self.manifest.manifest_server, e.errcode, e.errmsg)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700531 sys.exit(1)
532
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700533 rp = self.manifest.repoProject
534 rp.PreSync()
535
536 mp = self.manifest.manifestProject
537 mp.PreSync()
538
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800539 if opt.repo_upgraded:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700540 _PostRepoUpgrade(self.manifest)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800541
Nico Sallembien9bb18162009-12-07 15:38:01 -0800542 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700543 mp.Sync_NetworkHalf(quiet=opt.quiet,
544 current_branch_only=opt.current_branch_only)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800545
546 if mp.HasChanges:
547 syncbuf = SyncBuffer(mp.config)
548 mp.Sync_LocalHalf(syncbuf)
549 if not syncbuf.Finish():
550 sys.exit(1)
551 self.manifest._Unload()
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700552 if opt.jobs is None:
553 self.jobs = self.manifest.default.sync_j
David Pursehouse8a68ff92012-09-24 12:15:13 +0900554 all_projects = self.GetProjects(args, missing_ok=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700555
Dave Borowitz67700e92012-10-23 15:00:54 -0700556 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700557 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700558 to_fetch = []
559 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700560 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700561 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900562 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700563 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700564
David Pursehouse8a68ff92012-09-24 12:15:13 +0900565 self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700566 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700567 if opt.network_only:
568 # bail out now; the rest touches the working tree
569 return
570
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700571 if self.manifest.IsMirror:
572 # bail out now, we have no working tree
573 return
574
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700575 if self.UpdateProjectList():
576 sys.exit(1)
577
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700578 syncbuf = SyncBuffer(mp.config,
579 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900580 pm = Progress('Syncing work tree', len(all_projects))
581 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700582 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800583 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700584 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700585 pm.end()
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700586 print >>sys.stderr
587 if not syncbuf.Finish():
588 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700589
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700590 # If there's a notice that's supposed to print at the end of the sync, print
591 # it now...
592 if self.manifest.notice:
593 print self.manifest.notice
594
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700595def _PostRepoUpgrade(manifest):
596 for project in manifest.projects.values():
597 if project.Exists:
598 project.PostRepoUpgrade()
599
600def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
601 if rp.HasChanges:
602 print >>sys.stderr, 'info: A new version of repo is available'
603 print >>sys.stderr, ''
604 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700605 syncbuf = SyncBuffer(rp.config)
606 rp.Sync_LocalHalf(syncbuf)
607 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700608 sys.exit(1)
609 print >>sys.stderr, 'info: Restarting repo with latest version'
610 raise RepoChangedException(['--repo-upgraded'])
611 else:
612 print >>sys.stderr, 'warning: Skipped upgrade to unverified version'
613 else:
614 if verbose:
615 print >>sys.stderr, 'repo version %s is current' % rp.work_git.describe(HEAD)
616
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700617def _VerifyTag(project):
618 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
619 if not os.path.exists(gpg_dir):
620 print >>sys.stderr,\
621"""warning: GnuPG was not available during last "repo init"
622warning: Cannot automatically authenticate repo."""
623 return True
624
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700625 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700626 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700627 except GitError:
628 cur = None
629
630 if not cur \
631 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700632 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700633 if rev.startswith(R_HEADS):
634 rev = rev[len(R_HEADS):]
635
636 print >>sys.stderr
637 print >>sys.stderr,\
638 "warning: project '%s' branch '%s' is not signed" \
639 % (project.name, rev)
640 return False
641
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800642 env = os.environ.copy()
643 env['GIT_DIR'] = project.gitdir.encode()
644 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700645
646 cmd = [GIT, 'tag', '-v', cur]
647 proc = subprocess.Popen(cmd,
648 stdout = subprocess.PIPE,
649 stderr = subprocess.PIPE,
650 env = env)
651 out = proc.stdout.read()
652 proc.stdout.close()
653
654 err = proc.stderr.read()
655 proc.stderr.close()
656
657 if proc.wait() != 0:
658 print >>sys.stderr
659 print >>sys.stderr, out
660 print >>sys.stderr, err
661 print >>sys.stderr
662 return False
663 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700664
665class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700666 _ALPHA = 0.5
667
Dave Borowitz67700e92012-10-23 15:00:54 -0700668 def __init__(self, manifest):
669 self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes')
670 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700671 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700672
673 def Get(self, project):
674 self._Load()
675 return self._times.get(project.name, _ONE_DAY_S)
676
677 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700678 self._Load()
679 name = project.name
680 old = self._times.get(name, t)
681 self._seen.add(name)
682 a = self._ALPHA
683 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700684
685 def _Load(self):
686 if self._times is None:
687 try:
688 f = open(self._path)
689 except IOError:
690 self._times = {}
691 return self._times
692 try:
693 try:
694 self._times = pickle.load(f)
695 except:
696 try:
697 os.remove(self._path)
698 except OSError:
699 pass
700 self._times = {}
701 finally:
702 f.close()
703 return self._times
704
705 def Save(self):
706 if self._times is None:
707 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700708
709 to_delete = []
710 for name in self._times:
711 if name not in self._seen:
712 to_delete.append(name)
713 for name in to_delete:
714 del self._times[name]
715
Dave Borowitz67700e92012-10-23 15:00:54 -0700716 try:
717 f = open(self._path, 'wb')
718 try:
719 pickle.dump(self._times, f)
720 except (IOError, OSError, pickle.PickleError):
721 try:
722 os.remove(self._path)
723 except OSError:
724 pass
725 finally:
726 f.close()