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