blob: a4ca344ab1835d45d01f0770ea6d2bc57dace8fd [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 [<project>...]
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
Sarah Owensa5be53f2012-09-09 15:37:57 -0700477 except netrc.NetrcParseError as e:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900478 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)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700524 except (socket.error, IOError, xmlrpclib.Fault) as e:
David Pursehousebd489c42012-08-23 10:21:26 +0900525 print >>sys.stderr, 'error: cannot connect to manifest server %s:\n%s' % (
526 self.manifest.manifest_server, e)
527 sys.exit(1)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700528 except xmlrpclib.ProtocolError as e:
David Pursehousebd489c42012-08-23 10:21:26 +0900529 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
Che-Liang Chiou69998b02012-01-11 11:28:42 +0800565 fetched = 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
Che-Liang Chiou69998b02012-01-11 11:28:42 +0800571 # Iteratively fetch missing and/or nested unregistered submodules
572 previously_missing_set = set()
573 while True:
574 self.manifest._Unload()
Che-Liang Chiouab8f9112012-10-25 13:44:11 -0700575 all_projects = self.GetProjects(args, missing_ok=True)
Che-Liang Chiou69998b02012-01-11 11:28:42 +0800576 missing = []
Che-Liang Chiouab8f9112012-10-25 13:44:11 -0700577 for project in all_projects:
Che-Liang Chiou69998b02012-01-11 11:28:42 +0800578 if project.gitdir not in fetched:
579 missing.append(project)
580 if not missing:
581 break
582 # Stop us from non-stopped fetching actually-missing repos: If set of
583 # missing repos has not been changed from last fetch, we break.
584 missing_set = set(p.name for p in missing)
585 if previously_missing_set == missing_set:
586 break
587 previously_missing_set = missing_set
588 fetched.update(self._Fetch(missing, opt))
589
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700590 if self.manifest.IsMirror:
591 # bail out now, we have no working tree
592 return
593
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700594 if self.UpdateProjectList():
595 sys.exit(1)
596
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700597 syncbuf = SyncBuffer(mp.config,
598 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900599 pm = Progress('Syncing work tree', len(all_projects))
600 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700601 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800602 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700603 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700604 pm.end()
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700605 print >>sys.stderr
606 if not syncbuf.Finish():
607 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700608
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700609 # If there's a notice that's supposed to print at the end of the sync, print
610 # it now...
611 if self.manifest.notice:
612 print self.manifest.notice
613
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700614def _PostRepoUpgrade(manifest):
615 for project in manifest.projects.values():
616 if project.Exists:
617 project.PostRepoUpgrade()
618
619def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
620 if rp.HasChanges:
621 print >>sys.stderr, 'info: A new version of repo is available'
622 print >>sys.stderr, ''
623 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700624 syncbuf = SyncBuffer(rp.config)
625 rp.Sync_LocalHalf(syncbuf)
626 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700627 sys.exit(1)
628 print >>sys.stderr, 'info: Restarting repo with latest version'
629 raise RepoChangedException(['--repo-upgraded'])
630 else:
631 print >>sys.stderr, 'warning: Skipped upgrade to unverified version'
632 else:
633 if verbose:
634 print >>sys.stderr, 'repo version %s is current' % rp.work_git.describe(HEAD)
635
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700636def _VerifyTag(project):
637 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
638 if not os.path.exists(gpg_dir):
639 print >>sys.stderr,\
640"""warning: GnuPG was not available during last "repo init"
641warning: Cannot automatically authenticate repo."""
642 return True
643
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700644 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700645 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700646 except GitError:
647 cur = None
648
649 if not cur \
650 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700651 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700652 if rev.startswith(R_HEADS):
653 rev = rev[len(R_HEADS):]
654
655 print >>sys.stderr
656 print >>sys.stderr,\
657 "warning: project '%s' branch '%s' is not signed" \
658 % (project.name, rev)
659 return False
660
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800661 env = os.environ.copy()
662 env['GIT_DIR'] = project.gitdir.encode()
663 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700664
665 cmd = [GIT, 'tag', '-v', cur]
666 proc = subprocess.Popen(cmd,
667 stdout = subprocess.PIPE,
668 stderr = subprocess.PIPE,
669 env = env)
670 out = proc.stdout.read()
671 proc.stdout.close()
672
673 err = proc.stderr.read()
674 proc.stderr.close()
675
676 if proc.wait() != 0:
677 print >>sys.stderr
678 print >>sys.stderr, out
679 print >>sys.stderr, err
680 print >>sys.stderr
681 return False
682 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700683
684class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700685 _ALPHA = 0.5
686
Dave Borowitz67700e92012-10-23 15:00:54 -0700687 def __init__(self, manifest):
688 self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes')
689 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700690 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700691
692 def Get(self, project):
693 self._Load()
694 return self._times.get(project.name, _ONE_DAY_S)
695
696 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700697 self._Load()
698 name = project.name
699 old = self._times.get(name, t)
700 self._seen.add(name)
701 a = self._ALPHA
702 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700703
704 def _Load(self):
705 if self._times is None:
706 try:
707 f = open(self._path)
708 except IOError:
709 self._times = {}
710 return self._times
711 try:
712 try:
713 self._times = pickle.load(f)
714 except:
715 try:
716 os.remove(self._path)
717 except OSError:
718 pass
719 self._times = {}
720 finally:
721 f.close()
722 return self._times
723
724 def Save(self):
725 if self._times is None:
726 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700727
728 to_delete = []
729 for name in self._times:
730 if name not in self._seen:
731 to_delete.append(name)
732 for name in to_delete:
733 del self._times[name]
734
Dave Borowitz67700e92012-10-23 15:00:54 -0700735 try:
736 f = open(self._path, 'wb')
737 try:
738 pickle.dump(self._times, f)
739 except (IOError, OSError, pickle.PickleError):
740 try:
741 os.remove(self._path)
742 except OSError:
743 pass
744 finally:
745 f.close()