blob: d1a06412be39d303ecae4bb180d1753cb5782ef5 [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
David Pursehouse59bbb582013-05-17 10:49:33 +090027
28from pyversion import is_python3
29if is_python3():
Chirayu Desai217ea7d2013-03-01 19:14:38 +053030 import urllib.parse
David Pursehouse59bbb582013-05-17 10:49:33 +090031 import xmlrpc.client
32else:
Chirayu Desai217ea7d2013-03-01 19:14:38 +053033 import imp
34 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090035 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053036 urllib = imp.new_module('urllib')
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053037 urllib.parse = urlparse
Chirayu Desai217ea7d2013-03-01 19:14:38 +053038 xmlrpc = imp.new_module('xmlrpc')
39 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070040
Roy Lee18afd7f2010-05-09 04:32:08 +080041try:
42 import threading as _threading
43except ImportError:
44 import dummy_threading as _threading
45
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070046try:
47 import resource
48 def _rlimit_nofile():
49 return resource.getrlimit(resource.RLIMIT_NOFILE)
50except ImportError:
51 def _rlimit_nofile():
52 return (256, 256)
53
Dave Borowitz18857212012-10-23 17:02:59 -070054try:
55 import multiprocessing
56except ImportError:
57 multiprocessing = None
58
Dave Borowitze2152672012-10-31 12:24:38 -070059from git_command import GIT, git_require
David Pursehoused94aaef2012-09-07 09:52:04 +090060from git_refs import R_HEADS, HEAD
Conley Owensc9129d92012-10-01 16:12:28 -070061from main import WrapperModule
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070062from project import Project
63from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080064from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000065from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070066from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070067from progress import Progress
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070068
Dave Borowitz67700e92012-10-23 15:00:54 -070069_ONE_DAY_S = 24 * 60 * 60
70
Doug Andersonfc06ced2011-03-16 15:49:18 -070071class _FetchError(Exception):
72 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
73 pass
74
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080075class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080076 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070077 common = True
78 helpSummary = "Update working tree to the latest revision"
79 helpUsage = """
80%prog [<project>...]
81"""
82 helpDescription = """
83The '%prog' command synchronizes local project directories
84with the remote repositories specified in the manifest. If a local
85project does not yet exist, it will clone a new local directory from
86the remote repository and set up tracking branches as specified in
87the manifest. If the local project already exists, '%prog'
88will update the remote branches and rebase any new local changes
89on top of the new remote changes.
90
91'%prog' will synchronize all projects listed at the command
92line. Projects can be specified either by name, or by a relative
93or absolute path to the project's local directory. If no projects
94are specified, '%prog' will synchronize all projects listed in
95the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070096
97The -d/--detach option can be used to switch specified projects
98back to the manifest revision. This option is especially helpful
99if the project is currently on a topic branch, but the manifest
100revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700101
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700102The -s/--smart-sync option can be used to sync to a known good
103build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200104manifest. The -t/--smart-tag option is similar and allows you to
105specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700106
David Pursehousecf76b1b2012-09-14 10:31:42 +0900107The -u/--manifest-server-username and -p/--manifest-server-password
108options can be used to specify a username and password to authenticate
109with the manifest server when using the -s or -t option.
110
111If -u and -p are not specified when using the -s or -t option, '%prog'
112will attempt to read authentication credentials for the manifest server
113from the user's .netrc file.
114
115'%prog' will not use authentication credentials from -u/-p or .netrc
116if the manifest server specified in the manifest file already includes
117credentials.
118
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500119The -f/--force-broken option can be used to proceed with syncing
120other projects if a project sync fails.
121
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700122The --no-clone-bundle option disables any attempt to use
123$URL/clone.bundle to bootstrap a new Git repository from a
124resumeable bundle file on a content delivery network. This
125may be necessary if there are problems with the local Python
126HTTP client or proxy configuration, but the Git binary works.
127
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800128The --fetch-submodules option enables fetching Git submodules
129of a project from server.
130
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700131SSH Connections
132---------------
133
134If at least one project remote URL uses an SSH connection (ssh://,
135git+ssh://, or user@host:path syntax) repo will automatically
136enable the SSH ControlMaster option when connecting to that host.
137This feature permits other projects in the same '%prog' session to
138reuse the same SSH tunnel, saving connection setup overheads.
139
140To disable this behavior on UNIX platforms, set the GIT_SSH
141environment variable to 'ssh'. For example:
142
143 export GIT_SSH=ssh
144 %prog
145
146Compatibility
147~~~~~~~~~~~~~
148
149This feature is automatically disabled on Windows, due to the lack
150of UNIX domain socket support.
151
152This feature is not compatible with url.insteadof rewrites in the
153user's ~/.gitconfig. '%prog' is currently not able to perform the
154rewrite early enough to establish the ControlMaster tunnel.
155
156If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
157later is required to fix a server side protocol bug.
158
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700159"""
160
Nico Sallembien6623b212010-05-11 12:57:01 -0700161 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000162 try:
163 self.jobs = self.manifest.default.sync_j
164 except ManifestParseError:
165 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700166
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500167 p.add_option('-f', '--force-broken',
168 dest='force_broken', action='store_true',
169 help="continue sync even if a project fails to sync")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900170 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700171 dest='local_only', action='store_true',
172 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900173 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700174 dest='network_only', action='store_true',
175 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900176 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700177 dest='detach_head', action='store_true',
178 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900179 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700180 dest='current_branch_only', action='store_true',
181 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900182 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700183 dest='quiet', action='store_true',
184 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900185 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800186 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700187 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500188 p.add_option('-m', '--manifest-name',
189 dest='manifest_name',
190 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700191 p.add_option('--no-clone-bundle',
192 dest='no_clone_bundle', action='store_true',
193 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800194 p.add_option('-u', '--manifest-server-username', action='store',
195 dest='manifest_server_username',
196 help='username to authenticate with the manifest server')
197 p.add_option('-p', '--manifest-server-password', action='store',
198 dest='manifest_server_password',
199 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800200 p.add_option('--fetch-submodules',
201 dest='fetch_submodules', action='store_true',
202 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700203 p.add_option('--no-tags',
204 dest='no_tags', action='store_true',
205 help="don't fetch tags")
Nico Sallembien6623b212010-05-11 12:57:01 -0700206 if show_smart:
207 p.add_option('-s', '--smart-sync',
208 dest='smart_sync', action='store_true',
209 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200210 p.add_option('-t', '--smart-tag',
211 dest='smart_tag', action='store',
212 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700213
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700214 g = p.add_option_group('repo Version options')
215 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700216 dest='no_repo_verify', action='store_true',
217 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700218 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800219 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700220 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700221
David James8d201162013-10-11 17:03:19 -0700222 def _FetchProjectList(self, opt, projects, *args):
David Pursehousec1b86a22012-11-14 11:36:51 +0900223 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800224
David James8d201162013-10-11 17:03:19 -0700225 Delegates most of the work to _FetchHelper.
226
227 Args:
228 opt: Program options returned from optparse. See _Options().
229 projects: Projects to fetch.
230 *args: Remaining arguments to pass to _FetchHelper. See the
231 _FetchHelper docstring for details.
232 """
233 for project in projects:
234 success = self._FetchHelper(opt, project, *args)
235 if not success and not opt.force_broken:
236 break
237
238 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
239 """Fetch git objects for a single project.
240
David Pursehousec1b86a22012-11-14 11:36:51 +0900241 Args:
242 opt: Program options returned from optparse. See _Options().
243 project: Project object for the project to fetch.
244 lock: Lock for accessing objects that are shared amongst multiple
245 _FetchHelper() threads.
246 fetched: set object that we will add project.gitdir to when we're done
247 (with our lock held).
248 pm: Instance of a Project object. We will call pm.update() (with our
249 lock held).
250 sem: We'll release() this semaphore when we exit so that another thread
251 can be started up.
252 err_event: We'll set this event in the case of an error (after printing
253 out info about the error).
David James8d201162013-10-11 17:03:19 -0700254
255 Returns:
256 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900257 """
258 # We'll set to true once we've locked the lock.
259 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700260
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530261 if not opt.quiet:
262 print('Fetching project %s' % project.name)
263
David Pursehousec1b86a22012-11-14 11:36:51 +0900264 # Encapsulate everything in a try/except/finally so that:
265 # - We always set err_event in the case of an exception.
266 # - We always make sure we call sem.release().
267 # - We always make sure we unlock the lock if we locked it.
268 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700269 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900270 start = time.time()
271 success = project.Sync_NetworkHalf(
272 quiet=opt.quiet,
273 current_branch_only=opt.current_branch_only,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700274 clone_bundle=not opt.no_clone_bundle,
275 no_tags=opt.no_tags)
David Pursehousec1b86a22012-11-14 11:36:51 +0900276 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700277
David Pursehousec1b86a22012-11-14 11:36:51 +0900278 # Lock around all the rest of the code, since printing, updating a set
279 # and Progress.update() are not thread safe.
280 lock.acquire()
281 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700282
David Pursehousec1b86a22012-11-14 11:36:51 +0900283 if not success:
284 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
285 if opt.force_broken:
286 print('warn: --force-broken, continuing to sync',
287 file=sys.stderr)
288 else:
289 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700290
David Pursehousec1b86a22012-11-14 11:36:51 +0900291 fetched.add(project.gitdir)
292 pm.update()
293 except _FetchError:
294 err_event.set()
295 except:
296 err_event.set()
297 raise
298 finally:
299 if did_lock:
300 lock.release()
301 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800302
David James8d201162013-10-11 17:03:19 -0700303 return success
304
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700305 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700306 fetched = set()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700307 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800308
309 if self.jobs == 1:
310 for project in projects:
311 pm.update()
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530312 if not opt.quiet:
313 print('Fetching project %s' % project.name)
Shawn O. Pearce5d0efdb2012-08-02 12:13:01 -0700314 if project.Sync_NetworkHalf(
315 quiet=opt.quiet,
316 current_branch_only=opt.current_branch_only,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700317 clone_bundle=not opt.no_clone_bundle,
318 no_tags=opt.no_tags):
Roy Lee18afd7f2010-05-09 04:32:08 +0800319 fetched.add(project.gitdir)
320 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700321 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500322 if opt.force_broken:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700323 print('warn: --force-broken, continuing to sync', file=sys.stderr)
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500324 else:
325 sys.exit(1)
Roy Lee18afd7f2010-05-09 04:32:08 +0800326 else:
David James8d201162013-10-11 17:03:19 -0700327 objdir_project_map = dict()
328 for project in projects:
329 objdir_project_map.setdefault(project.objdir, []).append(project)
330
Roy Lee18afd7f2010-05-09 04:32:08 +0800331 threads = set()
332 lock = _threading.Lock()
333 sem = _threading.Semaphore(self.jobs)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700334 err_event = _threading.Event()
David James8d201162013-10-11 17:03:19 -0700335 for project_list in objdir_project_map.values():
Doug Andersonfc06ced2011-03-16 15:49:18 -0700336 # Check for any errors before starting any new threads.
337 # ...we'll let existing threads finish, though.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400338 if err_event.isSet():
Doug Andersonfc06ced2011-03-16 15:49:18 -0700339 break
340
Roy Lee18afd7f2010-05-09 04:32:08 +0800341 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700342 t = _threading.Thread(target = self._FetchProjectList,
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700343 args = (opt,
David James8d201162013-10-11 17:03:19 -0700344 project_list,
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700345 lock,
346 fetched,
347 pm,
Doug Andersonfc06ced2011-03-16 15:49:18 -0700348 sem,
349 err_event))
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200350 # Ensure that Ctrl-C will not freeze the repo process.
351 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800352 threads.add(t)
353 t.start()
354
355 for t in threads:
356 t.join()
357
Doug Andersonfc06ced2011-03-16 15:49:18 -0700358 # If we saw an error, exit with code 1 so that other scripts can check.
Daniel Sandler723c5dc2011-04-04 11:15:17 -0400359 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700360 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700361 sys.exit(1)
362
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700363 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700364 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700365
366 self._GCProjects(projects)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700367 return fetched
368
Dave Borowitz18857212012-10-23 17:02:59 -0700369 def _GCProjects(self, projects):
David James8d201162013-10-11 17:03:19 -0700370 gitdirs = {}
371 for project in projects:
372 gitdirs[project.gitdir] = project.bare_git
373
Dave Borowitze2152672012-10-31 12:24:38 -0700374 has_dash_c = git_require((1, 7, 2))
375 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700376 cpu_count = multiprocessing.cpu_count()
377 else:
378 cpu_count = 1
379 jobs = min(self.jobs, cpu_count)
380
381 if jobs < 2:
David James8d201162013-10-11 17:03:19 -0700382 for bare_git in gitdirs.values():
383 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700384 return
385
386 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
387
388 threads = set()
389 sem = _threading.Semaphore(jobs)
390 err_event = _threading.Event()
391
David James8d201162013-10-11 17:03:19 -0700392 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700393 try:
394 try:
David James8d201162013-10-11 17:03:19 -0700395 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700396 except GitError:
397 err_event.set()
398 except:
399 err_event.set()
400 raise
401 finally:
402 sem.release()
403
David James8d201162013-10-11 17:03:19 -0700404 for bare_git in gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700405 if err_event.isSet():
406 break
407 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700408 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700409 t.daemon = True
410 threads.add(t)
411 t.start()
412
413 for t in threads:
414 t.join()
415
416 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700417 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700418 sys.exit(1)
419
Tim Kilbourn07669002013-03-08 15:02:49 -0800420 def _ReloadManifest(self, manifest_name=None):
421 if manifest_name:
422 # Override calls _Unload already
423 self.manifest.Override(manifest_name)
424 else:
425 self.manifest._Unload()
426
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700427 def UpdateProjectList(self):
428 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700429 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700430 if project.relpath:
431 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700432 file_name = 'project.list'
433 file_path = os.path.join(self.manifest.repodir, file_name)
434 old_project_paths = []
435
436 if os.path.exists(file_path):
437 fd = open(file_path, 'r')
438 try:
439 old_project_paths = fd.read().split('\n')
440 finally:
441 fd.close()
442 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700443 if not path:
444 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700445 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900446 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400447 if os.path.exists(self.manifest.topdir + '/' + path):
David James8d201162013-10-11 17:03:19 -0700448 gitdir = os.path.join(self.manifest.topdir, path, '.git')
David Pursehousec1b86a22012-11-14 11:36:51 +0900449 project = Project(
450 manifest = self.manifest,
451 name = path,
452 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700453 gitdir = gitdir,
454 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900455 worktree = os.path.join(self.manifest.topdir, path),
456 relpath = path,
457 revisionExpr = 'HEAD',
458 revisionId = None,
459 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400460
David Pursehousec1b86a22012-11-14 11:36:51 +0900461 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900462 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900463 'are present' % project.relpath, file=sys.stderr)
464 print(' commit changes, then run sync again',
465 file=sys.stderr)
466 return -1
467 else:
468 print('Deleting obsolete path %s' % project.worktree,
469 file=sys.stderr)
470 shutil.rmtree(project.worktree)
471 # Try deleting parent subdirs if they are empty
472 project_dir = os.path.dirname(project.worktree)
473 while project_dir != self.manifest.topdir:
474 try:
475 os.rmdir(project_dir)
476 except OSError:
477 break
478 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700479
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700480 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700481 fd = open(file_path, 'w')
482 try:
483 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700484 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700485 finally:
486 fd.close()
487 return 0
488
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700489 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800490 if opt.jobs:
491 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700492 if self.jobs > 1:
493 soft_limit, _ = _rlimit_nofile()
494 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
495
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700496 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700497 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700498 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700499 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700500 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700501 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500502 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700503 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500504 sys.exit(1)
505 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700506 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500507 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900508 if opt.manifest_server_username or opt.manifest_server_password:
509 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700510 print('error: -u and -p may only be combined with -s or -t',
511 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900512 sys.exit(1)
513 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700514 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900515 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500516
517 if opt.manifest_name:
518 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700519
Chirayu Desaia892b102013-06-11 14:18:46 +0530520 manifest_name = opt.manifest_name
521
Victor Boivie08c880d2011-04-19 10:32:52 +0200522 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700523 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900524 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700525 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700526 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900527
528 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900529 if not opt.quiet:
530 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900531
David Pursehouse86d973d2012-08-24 10:21:02 +0900532 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900533 username = None
534 password = None
535 if opt.manifest_server_username and opt.manifest_server_password:
536 username = opt.manifest_server_username
537 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900538 else:
539 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900540 info = netrc.netrc()
541 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700542 print('.netrc file does not exist or could not be opened',
543 file=sys.stderr)
David Pursehouse86d973d2012-08-24 10:21:02 +0900544 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900545 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530546 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900547 if parse_result.hostname:
548 username, _account, password = \
549 info.authenticators(parse_result.hostname)
550 except TypeError:
551 # TypeError is raised when the given hostname is not present
552 # in the .netrc file.
Sarah Owenscecd1d82012-11-01 22:59:27 -0700553 print('No credentials found for %s in .netrc'
554 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700555 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700556 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900557
558 if (username and password):
559 manifest_server = manifest_server.replace('://', '://%s:%s@' %
560 (username, password),
561 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900562
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700563 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530564 server = xmlrpc.client.Server(manifest_server)
Victor Boivie08c880d2011-04-19 10:32:52 +0200565 if opt.smart_sync:
566 p = self.manifest.manifestProject
567 b = p.GetBranch(p.CurrentBranch)
568 branch = b.merge
569 if branch.startswith(R_HEADS):
570 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700571
Victor Boivie08c880d2011-04-19 10:32:52 +0200572 env = os.environ.copy()
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530573 if 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200574 target = '%s-%s' % (env['TARGET_PRODUCT'],
575 env['TARGET_BUILD_VARIANT'])
576 [success, manifest_str] = server.GetApprovedManifest(branch, target)
577 else:
578 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700579 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200580 assert(opt.smart_tag)
581 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700582
583 if success:
584 manifest_name = "smart_sync_override.xml"
585 manifest_path = os.path.join(self.manifest.manifestProject.worktree,
586 manifest_name)
587 try:
588 f = open(manifest_path, 'w')
589 try:
590 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700591 finally:
592 f.close()
593 except IOError:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700594 print('error: cannot write manifest to %s' % manifest_path,
595 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700596 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100597 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700598 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900599 print('error: manifest server RPC call failed: %s' %
600 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700601 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530602 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700603 print('error: cannot connect to manifest server %s:\n%s'
604 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900605 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530606 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700607 print('error: cannot connect to manifest server %s:\n%d %s'
608 % (self.manifest.manifest_server, e.errcode, e.errmsg),
609 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700610 sys.exit(1)
611
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700612 rp = self.manifest.repoProject
613 rp.PreSync()
614
615 mp = self.manifest.manifestProject
616 mp.PreSync()
617
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800618 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700619 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800620
Nico Sallembien9bb18162009-12-07 15:38:01 -0800621 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700622 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700623 current_branch_only=opt.current_branch_only,
624 no_tags=opt.no_tags)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800625
626 if mp.HasChanges:
627 syncbuf = SyncBuffer(mp.config)
628 mp.Sync_LocalHalf(syncbuf)
629 if not syncbuf.Finish():
630 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100631 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700632 if opt.jobs is None:
633 self.jobs = self.manifest.default.sync_j
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800634 all_projects = self.GetProjects(args,
635 missing_ok=True,
636 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700637
Dave Borowitz67700e92012-10-23 15:00:54 -0700638 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700639 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700640 to_fetch = []
641 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700642 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700643 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900644 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700645 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700646
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800647 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700648 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700649 if opt.network_only:
650 # bail out now; the rest touches the working tree
651 return
652
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800653 # Iteratively fetch missing and/or nested unregistered submodules
654 previously_missing_set = set()
655 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100656 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800657 all_projects = self.GetProjects(args,
658 missing_ok=True,
659 submodules_ok=opt.fetch_submodules)
660 missing = []
661 for project in all_projects:
662 if project.gitdir not in fetched:
663 missing.append(project)
664 if not missing:
665 break
666 # Stop us from non-stopped fetching actually-missing repos: If set of
667 # missing repos has not been changed from last fetch, we break.
668 missing_set = set(p.name for p in missing)
669 if previously_missing_set == missing_set:
670 break
671 previously_missing_set = missing_set
672 fetched.update(self._Fetch(missing, opt))
673
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700674 if self.manifest.IsMirror:
675 # bail out now, we have no working tree
676 return
677
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700678 if self.UpdateProjectList():
679 sys.exit(1)
680
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700681 syncbuf = SyncBuffer(mp.config,
682 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900683 pm = Progress('Syncing work tree', len(all_projects))
684 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700685 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800686 if project.worktree:
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700687 project.Sync_LocalHalf(syncbuf)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700688 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700689 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700690 if not syncbuf.Finish():
691 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700692
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700693 # If there's a notice that's supposed to print at the end of the sync, print
694 # it now...
695 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700696 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700697
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700698def _PostRepoUpgrade(manifest, quiet=False):
Conley Owensc9129d92012-10-01 16:12:28 -0700699 wrapper = WrapperModule()
700 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700701 wrapper.SetupGnuPG(quiet)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700702 for project in manifest.projects.values():
703 if project.Exists:
704 project.PostRepoUpgrade()
705
706def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
707 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700708 print('info: A new version of repo is available', file=sys.stderr)
709 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700710 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700711 syncbuf = SyncBuffer(rp.config)
712 rp.Sync_LocalHalf(syncbuf)
713 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700714 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700715 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700716 raise RepoChangedException(['--repo-upgraded'])
717 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700718 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700719 else:
720 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700721 print('repo version %s is current' % rp.work_git.describe(HEAD),
722 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700723
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700724def _VerifyTag(project):
725 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
726 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700727 print('warning: GnuPG was not available during last "repo init"\n'
728 'warning: Cannot automatically authenticate repo."""',
729 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700730 return True
731
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700732 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700733 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700734 except GitError:
735 cur = None
736
737 if not cur \
738 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700739 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700740 if rev.startswith(R_HEADS):
741 rev = rev[len(R_HEADS):]
742
Sarah Owenscecd1d82012-11-01 22:59:27 -0700743 print(file=sys.stderr)
744 print("warning: project '%s' branch '%s' is not signed"
745 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700746 return False
747
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800748 env = os.environ.copy()
749 env['GIT_DIR'] = project.gitdir.encode()
750 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700751
752 cmd = [GIT, 'tag', '-v', cur]
753 proc = subprocess.Popen(cmd,
754 stdout = subprocess.PIPE,
755 stderr = subprocess.PIPE,
756 env = env)
757 out = proc.stdout.read()
758 proc.stdout.close()
759
760 err = proc.stderr.read()
761 proc.stderr.close()
762
763 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700764 print(file=sys.stderr)
765 print(out, file=sys.stderr)
766 print(err, file=sys.stderr)
767 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700768 return False
769 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700770
771class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700772 _ALPHA = 0.5
773
Dave Borowitz67700e92012-10-23 15:00:54 -0700774 def __init__(self, manifest):
775 self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes')
776 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700777 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700778
779 def Get(self, project):
780 self._Load()
781 return self._times.get(project.name, _ONE_DAY_S)
782
783 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700784 self._Load()
785 name = project.name
786 old = self._times.get(name, t)
787 self._seen.add(name)
788 a = self._ALPHA
789 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700790
791 def _Load(self):
792 if self._times is None:
793 try:
794 f = open(self._path)
795 except IOError:
796 self._times = {}
797 return self._times
798 try:
799 try:
800 self._times = pickle.load(f)
David Pursehouse1d947b32012-10-25 12:23:11 +0900801 except IOError:
Dave Borowitz67700e92012-10-23 15:00:54 -0700802 try:
803 os.remove(self._path)
804 except OSError:
805 pass
806 self._times = {}
807 finally:
808 f.close()
809 return self._times
810
811 def Save(self):
812 if self._times is None:
813 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700814
815 to_delete = []
816 for name in self._times:
817 if name not in self._seen:
818 to_delete.append(name)
819 for name in to_delete:
820 del self._times[name]
821
Dave Borowitz67700e92012-10-23 15:00:54 -0700822 try:
823 f = open(self._path, 'wb')
824 try:
825 pickle.dump(self._times, f)
826 except (IOError, OSError, pickle.PickleError):
827 try:
828 os.remove(self._path)
829 except OSError:
830 pass
831 finally:
832 f.close()