blob: 8fb948859688b5e9f4df5bd1da9a3e9f56de97ff [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
Sarah Owenscecd1d82012-11-01 22:59:27 -070016from __future__ import print_function
David Pursehouse86d973d2012-08-24 10:21:02 +090017import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070018from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
Dave Borowitz67700e92012-10-23 15:00:54 -070020import pickle
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import re
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070022import shutil
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070023import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import subprocess
25import sys
Shawn O. Pearcef6906872009-04-18 10:49:00 -070026import time
Chirayu Desai217ea7d2013-03-01 19:14:38 +053027try:
28 # For python3
29 import urllib.parse
30except ImportError:
31 # For python2
32 import imp
33 import urlparse
34 urllib = imp.new_module('urllib')
35 urllib.parse = urlparse
36try:
37 # For python3
38 import xmlrpc.client
39except ImportError:
40 # For python2
41 import imp
42 import xmlrpclib
43 xmlrpc = imp.new_module('xmlrpc')
44 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070045
Roy Lee18afd7f2010-05-09 04:32:08 +080046try:
47 import threading as _threading
48except ImportError:
49 import dummy_threading as _threading
50
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070051try:
52 import resource
53 def _rlimit_nofile():
54 return resource.getrlimit(resource.RLIMIT_NOFILE)
55except ImportError:
56 def _rlimit_nofile():
57 return (256, 256)
58
Dave Borowitz18857212012-10-23 17:02:59 -070059try:
60 import multiprocessing
61except ImportError:
62 multiprocessing = None
63
Dave Borowitze2152672012-10-31 12:24:38 -070064from git_command import GIT, git_require
David Pursehoused94aaef2012-09-07 09:52:04 +090065from git_refs import R_HEADS, HEAD
Conley Owensc9129d92012-10-01 16:12:28 -070066from main import WrapperModule
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070067from project import Project
68from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080069from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000070from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070071from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070072from progress import Progress
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070073
Dave Borowitz67700e92012-10-23 15:00:54 -070074_ONE_DAY_S = 24 * 60 * 60
75
Doug Andersonfc06ced2011-03-16 15:49:18 -070076class _FetchError(Exception):
77 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
78 pass
79
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080080class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080081 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070082 common = True
83 helpSummary = "Update working tree to the latest revision"
84 helpUsage = """
85%prog [<project>...]
86"""
87 helpDescription = """
88The '%prog' command synchronizes local project directories
89with the remote repositories specified in the manifest. If a local
90project does not yet exist, it will clone a new local directory from
91the remote repository and set up tracking branches as specified in
92the manifest. If the local project already exists, '%prog'
93will update the remote branches and rebase any new local changes
94on top of the new remote changes.
95
96'%prog' will synchronize all projects listed at the command
97line. Projects can be specified either by name, or by a relative
98or absolute path to the project's local directory. If no projects
99are specified, '%prog' will synchronize all projects listed in
100the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700101
102The -d/--detach option can be used to switch specified projects
103back to the manifest revision. This option is especially helpful
104if the project is currently on a topic branch, but the manifest
105revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700106
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700107The -s/--smart-sync option can be used to sync to a known good
108build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200109manifest. The -t/--smart-tag option is similar and allows you to
110specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700111
David Pursehousecf76b1b2012-09-14 10:31:42 +0900112The -u/--manifest-server-username and -p/--manifest-server-password
113options can be used to specify a username and password to authenticate
114with the manifest server when using the -s or -t option.
115
116If -u and -p are not specified when using the -s or -t option, '%prog'
117will attempt to read authentication credentials for the manifest server
118from the user's .netrc file.
119
120'%prog' will not use authentication credentials from -u/-p or .netrc
121if the manifest server specified in the manifest file already includes
122credentials.
123
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500124The -f/--force-broken option can be used to proceed with syncing
125other projects if a project sync fails.
126
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700127The --no-clone-bundle option disables any attempt to use
128$URL/clone.bundle to bootstrap a new Git repository from a
129resumeable bundle file on a content delivery network. This
130may be necessary if there are problems with the local Python
131HTTP client or proxy configuration, but the Git binary works.
132
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800133The --fetch-submodules option enables fetching Git submodules
134of a project from server.
135
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700136SSH Connections
137---------------
138
139If at least one project remote URL uses an SSH connection (ssh://,
140git+ssh://, or user@host:path syntax) repo will automatically
141enable the SSH ControlMaster option when connecting to that host.
142This feature permits other projects in the same '%prog' session to
143reuse the same SSH tunnel, saving connection setup overheads.
144
145To disable this behavior on UNIX platforms, set the GIT_SSH
146environment variable to 'ssh'. For example:
147
148 export GIT_SSH=ssh
149 %prog
150
151Compatibility
152~~~~~~~~~~~~~
153
154This feature is automatically disabled on Windows, due to the lack
155of UNIX domain socket support.
156
157This feature is not compatible with url.insteadof rewrites in the
158user's ~/.gitconfig. '%prog' is currently not able to perform the
159rewrite early enough to establish the ControlMaster tunnel.
160
161If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
162later is required to fix a server side protocol bug.
163
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700164"""
165
Nico Sallembien6623b212010-05-11 12:57:01 -0700166 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000167 try:
168 self.jobs = self.manifest.default.sync_j
169 except ManifestParseError:
170 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700171
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500172 p.add_option('-f', '--force-broken',
173 dest='force_broken', action='store_true',
174 help="continue sync even if a project fails to sync")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900175 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700176 dest='local_only', action='store_true',
177 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900178 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700179 dest='network_only', action='store_true',
180 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900181 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700182 dest='detach_head', action='store_true',
183 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900184 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700185 dest='current_branch_only', action='store_true',
186 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900187 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700188 dest='quiet', action='store_true',
189 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900190 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800191 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700192 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500193 p.add_option('-m', '--manifest-name',
194 dest='manifest_name',
195 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700196 p.add_option('--no-clone-bundle',
197 dest='no_clone_bundle', action='store_true',
198 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800199 p.add_option('-u', '--manifest-server-username', action='store',
200 dest='manifest_server_username',
201 help='username to authenticate with the manifest server')
202 p.add_option('-p', '--manifest-server-password', action='store',
203 dest='manifest_server_password',
204 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800205 p.add_option('--fetch-submodules',
206 dest='fetch_submodules', action='store_true',
207 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700208 p.add_option('--no-tags',
209 dest='no_tags', action='store_true',
210 help="don't fetch tags")
Nico Sallembien6623b212010-05-11 12:57:01 -0700211 if show_smart:
212 p.add_option('-s', '--smart-sync',
213 dest='smart_sync', action='store_true',
214 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200215 p.add_option('-t', '--smart-tag',
216 dest='smart_tag', action='store',
217 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700218
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700219 g = p.add_option_group('repo Version options')
220 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700221 dest='no_repo_verify', action='store_true',
222 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700223 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800224 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700225 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700226
Doug Andersonfc06ced2011-03-16 15:49:18 -0700227 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
David Pursehousec1b86a22012-11-14 11:36:51 +0900228 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800229
David Pursehousec1b86a22012-11-14 11:36:51 +0900230 Args:
231 opt: Program options returned from optparse. See _Options().
232 project: Project object for the project to fetch.
233 lock: Lock for accessing objects that are shared amongst multiple
234 _FetchHelper() threads.
235 fetched: set object that we will add project.gitdir to when we're done
236 (with our lock held).
237 pm: Instance of a Project object. We will call pm.update() (with our
238 lock held).
239 sem: We'll release() this semaphore when we exit so that another thread
240 can be started up.
241 err_event: We'll set this event in the case of an error (after printing
242 out info about the error).
243 """
244 # We'll set to true once we've locked the lock.
245 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700246
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530247 if not opt.quiet:
248 print('Fetching project %s' % project.name)
249
David Pursehousec1b86a22012-11-14 11:36:51 +0900250 # Encapsulate everything in a try/except/finally so that:
251 # - We always set err_event in the case of an exception.
252 # - We always make sure we call sem.release().
253 # - We always make sure we unlock the lock if we locked it.
254 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700255 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900256 start = time.time()
257 success = project.Sync_NetworkHalf(
258 quiet=opt.quiet,
259 current_branch_only=opt.current_branch_only,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700260 clone_bundle=not opt.no_clone_bundle,
261 no_tags=opt.no_tags)
David Pursehousec1b86a22012-11-14 11:36:51 +0900262 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700263
David Pursehousec1b86a22012-11-14 11:36:51 +0900264 # Lock around all the rest of the code, since printing, updating a set
265 # and Progress.update() are not thread safe.
266 lock.acquire()
267 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700268
David Pursehousec1b86a22012-11-14 11:36:51 +0900269 if not success:
270 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
271 if opt.force_broken:
272 print('warn: --force-broken, continuing to sync',
273 file=sys.stderr)
274 else:
275 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700276
David Pursehousec1b86a22012-11-14 11:36:51 +0900277 fetched.add(project.gitdir)
278 pm.update()
279 except _FetchError:
280 err_event.set()
281 except:
282 err_event.set()
283 raise
284 finally:
285 if did_lock:
286 lock.release()
287 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800288
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700289 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700290 fetched = set()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700291 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800292
293 if self.jobs == 1:
294 for project in projects:
295 pm.update()
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530296 if not opt.quiet:
297 print('Fetching project %s' % project.name)
Shawn O. Pearce5d0efdb2012-08-02 12:13:01 -0700298 if project.Sync_NetworkHalf(
299 quiet=opt.quiet,
300 current_branch_only=opt.current_branch_only,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700301 clone_bundle=not opt.no_clone_bundle,
302 no_tags=opt.no_tags):
Roy Lee18afd7f2010-05-09 04:32:08 +0800303 fetched.add(project.gitdir)
304 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700305 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500306 if opt.force_broken:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700307 print('warn: --force-broken, continuing to sync', file=sys.stderr)
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500308 else:
309 sys.exit(1)
Roy Lee18afd7f2010-05-09 04:32:08 +0800310 else:
311 threads = set()
312 lock = _threading.Lock()
313 sem = _threading.Semaphore(self.jobs)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700314 err_event = _threading.Event()
Roy Lee18afd7f2010-05-09 04:32:08 +0800315 for project in projects:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700316 # Check for any errors before starting any new threads.
317 # ...we'll let existing threads finish, though.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400318 if err_event.isSet():
Doug Andersonfc06ced2011-03-16 15:49:18 -0700319 break
320
Roy Lee18afd7f2010-05-09 04:32:08 +0800321 sem.acquire()
322 t = _threading.Thread(target = self._FetchHelper,
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700323 args = (opt,
324 project,
325 lock,
326 fetched,
327 pm,
Doug Andersonfc06ced2011-03-16 15:49:18 -0700328 sem,
329 err_event))
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200330 # Ensure that Ctrl-C will not freeze the repo process.
331 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800332 threads.add(t)
333 t.start()
334
335 for t in threads:
336 t.join()
337
Doug Andersonfc06ced2011-03-16 15:49:18 -0700338 # If we saw an error, exit with code 1 so that other scripts can check.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400339 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700340 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700341 sys.exit(1)
342
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700343 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700344 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700345
346 self._GCProjects(projects)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700347 return fetched
348
Dave Borowitz18857212012-10-23 17:02:59 -0700349 def _GCProjects(self, projects):
Dave Borowitze2152672012-10-31 12:24:38 -0700350 has_dash_c = git_require((1, 7, 2))
351 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700352 cpu_count = multiprocessing.cpu_count()
353 else:
354 cpu_count = 1
355 jobs = min(self.jobs, cpu_count)
356
357 if jobs < 2:
358 for project in projects:
359 project.bare_git.gc('--auto')
360 return
361
362 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
363
364 threads = set()
365 sem = _threading.Semaphore(jobs)
366 err_event = _threading.Event()
367
368 def GC(project):
369 try:
370 try:
371 project.bare_git.gc('--auto', config=config)
372 except GitError:
373 err_event.set()
374 except:
375 err_event.set()
376 raise
377 finally:
378 sem.release()
379
380 for project in projects:
381 if err_event.isSet():
382 break
383 sem.acquire()
384 t = _threading.Thread(target=GC, args=(project,))
385 t.daemon = True
386 threads.add(t)
387 t.start()
388
389 for t in threads:
390 t.join()
391
392 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700393 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700394 sys.exit(1)
395
Tim Kilbourn07669002013-03-08 15:02:49 -0800396 def _ReloadManifest(self, manifest_name=None):
397 if manifest_name:
398 # Override calls _Unload already
399 self.manifest.Override(manifest_name)
400 else:
401 self.manifest._Unload()
402
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700403 def UpdateProjectList(self):
404 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700405 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700406 if project.relpath:
407 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700408 file_name = 'project.list'
409 file_path = os.path.join(self.manifest.repodir, file_name)
410 old_project_paths = []
411
412 if os.path.exists(file_path):
413 fd = open(file_path, 'r')
414 try:
415 old_project_paths = fd.read().split('\n')
416 finally:
417 fd.close()
418 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700419 if not path:
420 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700421 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900422 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400423 if os.path.exists(self.manifest.topdir + '/' + path):
David Pursehousec1b86a22012-11-14 11:36:51 +0900424 project = Project(
425 manifest = self.manifest,
426 name = path,
427 remote = RemoteSpec('origin'),
428 gitdir = os.path.join(self.manifest.topdir,
429 path, '.git'),
430 worktree = os.path.join(self.manifest.topdir, path),
431 relpath = path,
432 revisionExpr = 'HEAD',
433 revisionId = None,
434 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400435
David Pursehousec1b86a22012-11-14 11:36:51 +0900436 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900437 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900438 'are present' % project.relpath, file=sys.stderr)
439 print(' commit changes, then run sync again',
440 file=sys.stderr)
441 return -1
442 else:
443 print('Deleting obsolete path %s' % project.worktree,
444 file=sys.stderr)
445 shutil.rmtree(project.worktree)
446 # Try deleting parent subdirs if they are empty
447 project_dir = os.path.dirname(project.worktree)
448 while project_dir != self.manifest.topdir:
449 try:
450 os.rmdir(project_dir)
451 except OSError:
452 break
453 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700454
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700455 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700456 fd = open(file_path, 'w')
457 try:
458 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700459 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700460 finally:
461 fd.close()
462 return 0
463
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700464 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800465 if opt.jobs:
466 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700467 if self.jobs > 1:
468 soft_limit, _ = _rlimit_nofile()
469 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
470
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700471 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700472 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700473 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700474 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700475 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700476 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500477 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700478 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500479 sys.exit(1)
480 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700481 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500482 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900483 if opt.manifest_server_username or opt.manifest_server_password:
484 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700485 print('error: -u and -p may only be combined with -s or -t',
486 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900487 sys.exit(1)
488 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700489 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900490 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500491
492 if opt.manifest_name:
493 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700494
Victor Boivie08c880d2011-04-19 10:32:52 +0200495 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700496 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900497 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700498 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700499 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900500
501 manifest_server = self.manifest.manifest_server
David Pursehousecf76b1b2012-09-14 10:31:42 +0900502
David Pursehouse86d973d2012-08-24 10:21:02 +0900503 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900504 username = None
505 password = None
506 if opt.manifest_server_username and opt.manifest_server_password:
507 username = opt.manifest_server_username
508 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900509 else:
510 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900511 info = netrc.netrc()
512 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700513 print('.netrc file does not exist or could not be opened',
514 file=sys.stderr)
David Pursehouse86d973d2012-08-24 10:21:02 +0900515 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900516 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530517 parse_result = urllib.parse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900518 if parse_result.hostname:
519 username, _account, password = \
520 info.authenticators(parse_result.hostname)
521 except TypeError:
522 # TypeError is raised when the given hostname is not present
523 # in the .netrc file.
Sarah Owenscecd1d82012-11-01 22:59:27 -0700524 print('No credentials found for %s in .netrc'
525 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700526 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700527 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900528
529 if (username and password):
530 manifest_server = manifest_server.replace('://', '://%s:%s@' %
531 (username, password),
532 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900533
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700534 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530535 server = xmlrpc.client.Server(manifest_server)
Victor Boivie08c880d2011-04-19 10:32:52 +0200536 if opt.smart_sync:
537 p = self.manifest.manifestProject
538 b = p.GetBranch(p.CurrentBranch)
539 branch = b.merge
540 if branch.startswith(R_HEADS):
541 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700542
Victor Boivie08c880d2011-04-19 10:32:52 +0200543 env = os.environ.copy()
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530544 if 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200545 target = '%s-%s' % (env['TARGET_PRODUCT'],
546 env['TARGET_BUILD_VARIANT'])
547 [success, manifest_str] = server.GetApprovedManifest(branch, target)
548 else:
549 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700550 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200551 assert(opt.smart_tag)
552 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700553
554 if success:
555 manifest_name = "smart_sync_override.xml"
556 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
557 manifest_name)
558 try:
559 f = open(manifest_path, 'w')
560 try:
561 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700562 finally:
563 f.close()
564 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700565 print('error: cannot write manifest to %s' % manifest_path,
566 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700567 sys.exit(1)
Nico Sallembien719965a2010-04-20 15:28:19 -0700568 self.manifest.Override(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700569 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700570 print('error: %s' % manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700571 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530572 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700573 print('error: cannot connect to manifest server %s:\n%s'
574 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900575 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530576 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700577 print('error: cannot connect to manifest server %s:\n%d %s'
578 % (self.manifest.manifest_server, e.errcode, e.errmsg),
579 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700580 sys.exit(1)
581
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700582 rp = self.manifest.repoProject
583 rp.PreSync()
584
585 mp = self.manifest.manifestProject
586 mp.PreSync()
587
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800588 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700589 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800590
Nico Sallembien9bb18162009-12-07 15:38:01 -0800591 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700592 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700593 current_branch_only=opt.current_branch_only,
594 no_tags=opt.no_tags)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800595
596 if mp.HasChanges:
597 syncbuf = SyncBuffer(mp.config)
598 mp.Sync_LocalHalf(syncbuf)
599 if not syncbuf.Finish():
600 sys.exit(1)
Tim Kilbourn07669002013-03-08 15:02:49 -0800601 self._ReloadManifest(opt.manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700602 if opt.jobs is None:
603 self.jobs = self.manifest.default.sync_j
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800604 all_projects = self.GetProjects(args,
605 missing_ok=True,
606 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700607
Dave Borowitz67700e92012-10-23 15:00:54 -0700608 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700609 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700610 to_fetch = []
611 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700612 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700613 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900614 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700615 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700616
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800617 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700618 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700619 if opt.network_only:
620 # bail out now; the rest touches the working tree
621 return
622
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800623 # Iteratively fetch missing and/or nested unregistered submodules
624 previously_missing_set = set()
625 while True:
Tim Kilbourn07669002013-03-08 15:02:49 -0800626 self._ReloadManifest(opt.manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800627 all_projects = self.GetProjects(args,
628 missing_ok=True,
629 submodules_ok=opt.fetch_submodules)
630 missing = []
631 for project in all_projects:
632 if project.gitdir not in fetched:
633 missing.append(project)
634 if not missing:
635 break
636 # Stop us from non-stopped fetching actually-missing repos: If set of
637 # missing repos has not been changed from last fetch, we break.
638 missing_set = set(p.name for p in missing)
639 if previously_missing_set == missing_set:
640 break
641 previously_missing_set = missing_set
642 fetched.update(self._Fetch(missing, opt))
643
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700644 if self.manifest.IsMirror:
645 # bail out now, we have no working tree
646 return
647
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700648 if self.UpdateProjectList():
649 sys.exit(1)
650
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700651 syncbuf = SyncBuffer(mp.config,
652 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900653 pm = Progress('Syncing work tree', len(all_projects))
654 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700655 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800656 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700657 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700658 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700659 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700660 if not syncbuf.Finish():
661 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700662
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700663 # If there's a notice that's supposed to print at the end of the sync, print
664 # it now...
665 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700666 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700667
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700668def _PostRepoUpgrade(manifest, quiet=False):
Conley Owensc9129d92012-10-01 16:12:28 -0700669 wrapper = WrapperModule()
670 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700671 wrapper.SetupGnuPG(quiet)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700672 for project in manifest.projects.values():
673 if project.Exists:
674 project.PostRepoUpgrade()
675
676def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
677 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700678 print('info: A new version of repo is available', file=sys.stderr)
679 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700680 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700681 syncbuf = SyncBuffer(rp.config)
682 rp.Sync_LocalHalf(syncbuf)
683 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700684 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700685 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700686 raise RepoChangedException(['--repo-upgraded'])
687 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700688 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700689 else:
690 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700691 print('repo version %s is current' % rp.work_git.describe(HEAD),
692 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700693
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700694def _VerifyTag(project):
695 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
696 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700697 print('warning: GnuPG was not available during last "repo init"\n'
698 'warning: Cannot automatically authenticate repo."""',
699 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700700 return True
701
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700702 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700703 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700704 except GitError:
705 cur = None
706
707 if not cur \
708 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700709 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700710 if rev.startswith(R_HEADS):
711 rev = rev[len(R_HEADS):]
712
Sarah Owenscecd1d82012-11-01 22:59:27 -0700713 print(file=sys.stderr)
714 print("warning: project '%s' branch '%s' is not signed"
715 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700716 return False
717
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800718 env = os.environ.copy()
719 env['GIT_DIR'] = project.gitdir.encode()
720 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700721
722 cmd = [GIT, 'tag', '-v', cur]
723 proc = subprocess.Popen(cmd,
724 stdout = subprocess.PIPE,
725 stderr = subprocess.PIPE,
726 env = env)
727 out = proc.stdout.read()
728 proc.stdout.close()
729
730 err = proc.stderr.read()
731 proc.stderr.close()
732
733 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700734 print(file=sys.stderr)
735 print(out, file=sys.stderr)
736 print(err, file=sys.stderr)
737 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700738 return False
739 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700740
741class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700742 _ALPHA = 0.5
743
Dave Borowitz67700e92012-10-23 15:00:54 -0700744 def __init__(self, manifest):
745 self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes')
746 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700747 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700748
749 def Get(self, project):
750 self._Load()
751 return self._times.get(project.name, _ONE_DAY_S)
752
753 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700754 self._Load()
755 name = project.name
756 old = self._times.get(name, t)
757 self._seen.add(name)
758 a = self._ALPHA
759 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700760
761 def _Load(self):
762 if self._times is None:
763 try:
764 f = open(self._path)
765 except IOError:
766 self._times = {}
767 return self._times
768 try:
769 try:
770 self._times = pickle.load(f)
David Pursehouse1d947b32012-10-25 12:23:11 +0900771 except IOError:
Dave Borowitz67700e92012-10-23 15:00:54 -0700772 try:
773 os.remove(self._path)
774 except OSError:
775 pass
776 self._times = {}
777 finally:
778 f.close()
779 return self._times
780
781 def Save(self):
782 if self._times is None:
783 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700784
785 to_delete = []
786 for name in self._times:
787 if name not in self._seen:
788 to_delete.append(name)
789 for name in to_delete:
790 del self._times[name]
791
Dave Borowitz67700e92012-10-23 15:00:54 -0700792 try:
793 f = open(self._path, 'wb')
794 try:
795 pickle.dump(self._times, f)
796 except (IOError, OSError, pickle.PickleError):
797 try:
798 os.remove(self._path)
799 except OSError:
800 pass
801 finally:
802 f.close()