blob: 7cf303b3c81c95d34971e99b039331041c03a261 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
LaMont Jonesbdcba7d2022-04-11 22:50:11 +000015import collections
Mike Frysingerebf04a42021-02-23 20:48:04 -050016import functools
Mike Frysingeracf63b22019-06-13 02:24:21 -040017import http.cookiejar as cookielib
Mike Frysinger7b586f22021-02-23 18:38:39 -050018import io
Anthony King85b24ac2014-05-06 15:57:48 +010019import json
Mike Frysingerebf04a42021-02-23 20:48:04 -050020import multiprocessing
David Pursehouse86d973d2012-08-24 10:21:02 +090021import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070022from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import os
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070024import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
LaMont Jones78dcd372022-10-25 22:38:07 +000028from typing import NamedTuple, List, Set
Mike Frysingeracf63b22019-06-13 02:24:21 -040029import urllib.error
30import urllib.parse
31import urllib.request
Mike Frysinger5951e302022-05-20 23:34:44 -040032import xml.parsers.expat
Mike Frysingeracf63b22019-06-13 02:24:21 -040033import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070034
Roy Lee18afd7f2010-05-09 04:32:08 +080035try:
36 import threading as _threading
37except ImportError:
38 import dummy_threading as _threading
39
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070040try:
41 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090042
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070043 def _rlimit_nofile():
44 return resource.getrlimit(resource.RLIMIT_NOFILE)
45except ImportError:
46 def _rlimit_nofile():
47 return (256, 256)
48
David Rileye0684ad2017-04-05 00:02:59 -070049import event_log
Mike Frysinger347f9ed2021-03-15 14:58:52 -040050from git_command import git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090051from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090052from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080053import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070054import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070055from project import Project
56from project import RemoteSpec
Mike Frysinger355f4392022-07-20 17:15:29 -040057from command import Command, DEFAULT_LOCAL_JOBS, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080058from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070059import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070060from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070061from progress import Progress
Joanna Wanga6c52f52022-11-03 16:51:19 -040062from repo_trace import Trace
Mike Frysinger19e409c2021-05-05 19:44:35 -040063import ssh
Conley Owens094cdbe2014-01-30 15:09:59 -080064from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070065from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070066
Dave Borowitz67700e92012-10-23 15:00:54 -070067_ONE_DAY_S = 24 * 60 * 60
68
LaMont Jonesd7935532022-12-01 20:18:46 +000069# Env var to implicitly turn auto-gc back on. This was added to allow a user to
LaMont Jones100a2142022-12-02 22:54:11 +000070# revert a change in default behavior in v2.29.9. Remove after 2023-04-01.
LaMont Jones5ed8c632022-11-10 00:10:44 +000071_REPO_AUTO_GC = 'REPO_AUTO_GC'
72_AUTO_GC = os.environ.get(_REPO_AUTO_GC) == '1'
73
David Pursehouse819827a2020-02-12 15:20:19 +090074
LaMont Jones1eddca82022-09-01 15:15:04 +000075class _FetchOneResult(NamedTuple):
76 """_FetchOne return value.
77
78 Attributes:
79 success (bool): True if successful.
80 project (Project): The fetched project.
81 start (float): The starting time.time().
82 finish (float): The ending time.time().
83 remote_fetched (bool): True if the remote was actually queried.
84 """
85 success: bool
86 project: Project
87 start: float
88 finish: float
89 remote_fetched: bool
90
91
92class _FetchResult(NamedTuple):
93 """_Fetch return value.
94
95 Attributes:
96 success (bool): True if successful.
LaMont Jones78dcd372022-10-25 22:38:07 +000097 projects (Set[str]): The names of the git directories of fetched projects.
LaMont Jones1eddca82022-09-01 15:15:04 +000098 """
99 success: bool
LaMont Jones78dcd372022-10-25 22:38:07 +0000100 projects: Set[str]
LaMont Jones1eddca82022-09-01 15:15:04 +0000101
102
103class _FetchMainResult(NamedTuple):
104 """_FetchMain return value.
105
106 Attributes:
LaMont Jones78dcd372022-10-25 22:38:07 +0000107 all_projects (List[Project]): The fetched projects.
LaMont Jones1eddca82022-09-01 15:15:04 +0000108 """
LaMont Jones78dcd372022-10-25 22:38:07 +0000109 all_projects: List[Project]
LaMont Jones1eddca82022-09-01 15:15:04 +0000110
111
112class _CheckoutOneResult(NamedTuple):
113 """_CheckoutOne return value.
114
115 Attributes:
116 success (bool): True if successful.
117 project (Project): The project.
118 start (float): The starting time.time().
119 finish (float): The ending time.time().
120 """
121 success: bool
122 project: Project
123 start: float
124 finish: float
125
126
Shawn O. Pearcec95583b2009-03-03 17:47:06 -0800127class Sync(Command, MirrorSafeCommand):
Mike Frysinger4f210542021-06-14 16:05:19 -0400128 COMMON = True
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000129 MULTI_MANIFEST_SUPPORT = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700130 helpSummary = "Update working tree to the latest revision"
131 helpUsage = """
132%prog [<project>...]
133"""
134 helpDescription = """
135The '%prog' command synchronizes local project directories
136with the remote repositories specified in the manifest. If a local
137project does not yet exist, it will clone a new local directory from
138the remote repository and set up tracking branches as specified in
139the manifest. If the local project already exists, '%prog'
140will update the remote branches and rebase any new local changes
141on top of the new remote changes.
142
143'%prog' will synchronize all projects listed at the command
144line. Projects can be specified either by name, or by a relative
145or absolute path to the project's local directory. If no projects
146are specified, '%prog' will synchronize all projects listed in
147the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700148
149The -d/--detach option can be used to switch specified projects
150back to the manifest revision. This option is especially helpful
151if the project is currently on a topic branch, but the manifest
152revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700153
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700154The -s/--smart-sync option can be used to sync to a known good
155build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200156manifest. The -t/--smart-tag option is similar and allows you to
157specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700158
David Pursehousecf76b1b2012-09-14 10:31:42 +0900159The -u/--manifest-server-username and -p/--manifest-server-password
160options can be used to specify a username and password to authenticate
161with the manifest server when using the -s or -t option.
162
163If -u and -p are not specified when using the -s or -t option, '%prog'
164will attempt to read authentication credentials for the manifest server
165from the user's .netrc file.
166
167'%prog' will not use authentication credentials from -u/-p or .netrc
168if the manifest server specified in the manifest file already includes
169credentials.
170
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400171By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400172to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500173
Kevin Degiabaa7f32014-11-12 11:27:45 -0700174The --force-sync option can be used to overwrite existing git
175directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900176object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700177refs may be removed when overwriting.
178
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500179The --force-remove-dirty option can be used to remove previously used
180projects with uncommitted changes. WARNING: This may cause data to be
181lost since uncommitted changes may be removed with projects that no longer
182exist in the manifest.
183
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700184The --no-clone-bundle option disables any attempt to use
185$URL/clone.bundle to bootstrap a new Git repository from a
186resumeable bundle file on a content delivery network. This
187may be necessary if there are problems with the local Python
188HTTP client or proxy configuration, but the Git binary works.
189
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800190The --fetch-submodules option enables fetching Git submodules
191of a project from server.
192
David Pursehousef2fad612015-01-29 14:36:28 +0900193The -c/--current-branch option can be used to only fetch objects that
194are on the branch specified by a project's revision.
195
David Pursehouseb1553542014-09-04 21:28:09 +0900196The --optimized-fetch option can be used to only fetch projects that
197are fixed to a sha1 revision if the sha1 revision does not already
198exist locally.
199
David Pursehouse74cfd272015-10-14 10:50:15 +0900200The --prune option can be used to remove any refs that no longer
201exist on the remote.
202
LaMont Jones7efab532022-09-01 15:41:12 +0000203The --auto-gc option can be used to trigger garbage collection on all
204projects. By default, repo does not run garbage collection.
205
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400206# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700207
208If at least one project remote URL uses an SSH connection (ssh://,
209git+ssh://, or user@host:path syntax) repo will automatically
210enable the SSH ControlMaster option when connecting to that host.
211This feature permits other projects in the same '%prog' session to
212reuse the same SSH tunnel, saving connection setup overheads.
213
214To disable this behavior on UNIX platforms, set the GIT_SSH
215environment variable to 'ssh'. For example:
216
217 export GIT_SSH=ssh
218 %prog
219
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400220# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700221
222This feature is automatically disabled on Windows, due to the lack
223of UNIX domain socket support.
224
225This feature is not compatible with url.insteadof rewrites in the
226user's ~/.gitconfig. '%prog' is currently not able to perform the
227rewrite early enough to establish the ControlMaster tunnel.
228
229If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
230later is required to fix a server side protocol bug.
231
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232"""
Mike Frysinger355f4392022-07-20 17:15:29 -0400233 # A value of 0 means we want parallel jobs, but we'll determine the default
234 # value later on.
235 PARALLEL_JOBS = 0
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700236
Mike Frysinger9180a072021-04-13 14:57:40 -0400237 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400238 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400239 help='number of network jobs to run in parallel (defaults to --jobs or 1)')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400240 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400241 help='number of local checkout jobs to run in parallel (defaults to --jobs or '
242 f'{DEFAULT_LOCAL_JOBS})')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400243
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500244 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200245 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400246 help='obsolete option (to be deleted in the future)')
247 p.add_option('--fail-fast',
248 dest='fail_fast', action='store_true',
249 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700250 p.add_option('--force-sync',
251 dest='force_sync', action='store_true',
252 help="overwrite an existing git directory if it needs to "
253 "point to a different object directory. WARNING: this "
254 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500255 p.add_option('--force-remove-dirty',
256 dest='force_remove_dirty', action='store_true',
257 help="force remove projects with uncommitted modifications if "
258 "projects no longer exist in the manifest. "
259 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900260 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700261 dest='local_only', action='store_true',
262 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900263 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100264 dest='mp_update', action='store_false', default='true',
265 help='use the existing manifest checkout as-is. '
266 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900267 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700268 dest='network_only', action='store_true',
269 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900270 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700271 dest='detach_head', action='store_true',
272 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900273 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700274 dest='current_branch_only', action='store_true',
275 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400276 p.add_option('--no-current-branch',
277 dest='current_branch_only', action='store_false',
278 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500279 p.add_option('-m', '--manifest-name',
280 dest='manifest_name',
281 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700282 p.add_option('--clone-bundle', action='store_true',
283 help='enable use of /clone.bundle on HTTP/HTTPS')
284 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700285 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800286 p.add_option('-u', '--manifest-server-username', action='store',
287 dest='manifest_server_username',
288 help='username to authenticate with the manifest server')
289 p.add_option('-p', '--manifest-server-password', action='store',
290 dest='manifest_server_password',
291 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800292 p.add_option('--fetch-submodules',
293 dest='fetch_submodules', action='store_true',
294 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800295 p.add_option('--use-superproject', action='store_true',
Raman Tenneti62517292021-11-01 14:49:16 -0700296 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700297 p.add_option('--no-use-superproject', action='store_false',
298 dest='use_superproject',
299 help='disable use of manifest superprojects')
Mike Frysinger2273f462021-11-05 15:10:33 -0400300 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400301 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700302 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400303 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400304 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900305 p.add_option('--optimized-fetch',
306 dest='optimized_fetch', action='store_true',
307 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600308 p.add_option('--retry-fetches',
309 default=0, action='store', type='int',
310 help='number of times to retry fetches on transient errors')
Mike Frysinger0531a622021-11-05 15:22:01 -0400311 p.add_option('--prune', action='store_true',
312 help='delete refs that no longer exist on the remote (default)')
313 p.add_option('--no-prune', dest='prune', action='store_false',
314 help='do not delete refs that no longer exist on the remote')
LaMont Jones5ed8c632022-11-10 00:10:44 +0000315 p.add_option('--auto-gc', action='store_true', default=None,
LaMont Jones7efab532022-09-01 15:41:12 +0000316 help='run garbage collection on all synced projects')
317 p.add_option('--no-auto-gc', dest='auto_gc', action='store_false',
318 help='do not run garbage collection on any projects (default)')
Nico Sallembien6623b212010-05-11 12:57:01 -0700319 if show_smart:
320 p.add_option('-s', '--smart-sync',
321 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900322 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200323 p.add_option('-t', '--smart-tag',
324 dest='smart_tag', action='store',
325 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700326
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700327 g = p.add_option_group('repo Version options')
328 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500329 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700330 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700331 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800332 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700333 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700334
LaMont Jonesa46047a2022-04-07 21:57:06 +0000335 def _GetBranch(self, manifest_project):
336 """Returns the branch name for getting the approved smartsync manifest.
337
338 Args:
339 manifest_project: the manifestProject to query.
340 """
341 b = manifest_project.GetBranch(manifest_project.CurrentBranch)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800342 branch = b.merge
343 if branch.startswith(R_HEADS):
344 branch = branch[len(R_HEADS):]
345 return branch
346
LaMont Jonesa46047a2022-04-07 21:57:06 +0000347 def _GetCurrentBranchOnly(self, opt, manifest):
Daniel Anderssond52ca422022-04-01 12:55:38 +0200348 """Returns whether current-branch or use-superproject options are enabled.
349
LaMont Jonesa46047a2022-04-07 21:57:06 +0000350 Args:
351 opt: Program options returned from optparse. See _Options().
352 manifest: The manifest to use.
353
Daniel Anderssond52ca422022-04-01 12:55:38 +0200354 Returns:
355 True if a superproject is requested, otherwise the value of the
356 current_branch option (True, False or None).
357 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000358 return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700359
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000360 def _UpdateProjectsRevisionId(self, opt, args, superproject_logging_data,
361 manifest):
362 """Update revisionId of projects with the commit hash from the superproject.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800363
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000364 This function updates each project's revisionId with the commit hash from
365 the superproject. It writes the updated manifest into a file and reloads
366 the manifest from it. When appropriate, sub manifests are also processed.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800367
368 Args:
369 opt: Program options returned from optparse. See _Options().
370 args: Arguments to pass to GetProjects. See the GetProjects
371 docstring for details.
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000372 superproject_logging_data: A dictionary of superproject data to log.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000373 manifest: The manifest to use.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800374 """
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000375 have_superproject = manifest.superproject or any(
376 m.superproject for m in manifest.all_children)
377 if not have_superproject:
378 return
379
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000380 if opt.local_only and manifest.superproject:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000381 manifest_path = manifest.superproject.manifest_path
Raman Tennetiae86a462021-07-27 08:54:59 -0700382 if manifest_path:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000383 self._ReloadManifest(manifest_path, manifest)
384 return
Raman Tennetiae86a462021-07-27 08:54:59 -0700385
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800386 all_projects = self.GetProjects(args,
387 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000388 submodules_ok=opt.fetch_submodules,
389 manifest=manifest,
390 all_manifests=not opt.this_manifest_only)
391
392 per_manifest = collections.defaultdict(list)
393 manifest_paths = {}
394 if opt.this_manifest_only:
395 per_manifest[manifest.path_prefix] = all_projects
Raman Tenneti784e16f2021-06-11 17:29:45 -0700396 else:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000397 for p in all_projects:
398 per_manifest[p.manifest.path_prefix].append(p)
399
400 superproject_logging_data = {}
401 need_unload = False
402 for m in self.ManifestList(opt):
403 if not m.path_prefix in per_manifest:
404 continue
405 use_super = git_superproject.UseSuperproject(opt.use_superproject, m)
406 if superproject_logging_data:
407 superproject_logging_data['multimanifest'] = True
408 superproject_logging_data.update(
409 superproject=use_super,
410 haslocalmanifests=bool(m.HasLocalManifests),
411 hassuperprojecttag=bool(m.superproject),
412 )
413 if use_super and (m.IsMirror or m.IsArchive):
414 # Don't use superproject, because we have no working tree.
415 use_super = False
416 superproject_logging_data['superproject'] = False
417 superproject_logging_data['noworktree'] = True
418 if opt.use_superproject is not False:
419 print(f'{m.path_prefix}: not using superproject because there is no '
420 'working tree.')
421
422 if not use_super:
423 continue
424 m.superproject.SetQuiet(opt.quiet)
425 print_messages = git_superproject.PrintMessages(opt.use_superproject, m)
426 m.superproject.SetPrintMessages(print_messages)
427 update_result = m.superproject.UpdateProjectsRevisionId(
428 per_manifest[m.path_prefix], git_event_log=self.git_event_log)
429 manifest_path = update_result.manifest_path
430 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
431 if manifest_path:
432 m.SetManifestOverride(manifest_path)
433 need_unload = True
434 else:
435 if print_messages:
436 print(f'{m.path_prefix}: warning: Update of revisionId from '
437 'superproject has failed, repo sync will not use superproject '
438 'to fetch the source. ',
439 'Please resync with the --no-use-superproject option to avoid '
440 'this repo warning.',
441 file=sys.stderr)
442 if update_result.fatal and opt.use_superproject is not None:
443 sys.exit(1)
444 if need_unload:
445 m.outer_client.manifest.Unload()
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800446
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500447 def _FetchProjectList(self, opt, projects):
448 """Main function of the fetch worker.
449
450 The projects we're given share the same underlying git object store, so we
451 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800452
David James8d201162013-10-11 17:03:19 -0700453 Delegates most of the work to _FetchHelper.
454
455 Args:
456 opt: Program options returned from optparse. See _Options().
457 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700458 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500459 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700460
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500461 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700462 """Fetch git objects for a single project.
463
David Pursehousec1b86a22012-11-14 11:36:51 +0900464 Args:
465 opt: Program options returned from optparse. See _Options().
466 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700467
468 Returns:
469 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900470 """
David Rileye0684ad2017-04-05 00:02:59 -0700471 start = time.time()
472 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500473 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900474 try:
LaMont Jones1eddca82022-09-01 15:15:04 +0000475 sync_result = project.Sync_NetworkHalf(
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500476 quiet=opt.quiet,
477 verbose=opt.verbose,
478 output_redir=buf,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000479 current_branch_only=self._GetCurrentBranchOnly(opt, project.manifest),
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500480 force_sync=opt.force_sync,
481 clone_bundle=opt.clone_bundle,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000482 tags=opt.tags, archive=project.manifest.IsArchive,
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500483 optimized_fetch=opt.optimized_fetch,
484 retry_fetches=opt.retry_fetches,
485 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400486 ssh_proxy=self.ssh_proxy,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000487 clone_filter=project.manifest.CloneFilter,
488 partial_clone_exclude=project.manifest.PartialCloneExclude)
LaMont Jones1eddca82022-09-01 15:15:04 +0000489 success = sync_result.success
Doug Andersonfc06ced2011-03-16 15:49:18 -0700490
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500491 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400492 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500493 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700494
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500495 if not success:
496 print('error: Cannot fetch %s from %s'
497 % (project.name, project.remote.url),
498 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700499 except GitError as e:
500 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500501 except Exception as e:
502 print('error: Cannot fetch %s (%s: %s)'
503 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
504 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500505
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500506 finish = time.time()
LaMont Jones1eddca82022-09-01 15:15:04 +0000507 return _FetchOneResult(success, project, start, finish,
508 sync_result.remote_fetched)
David James8d201162013-10-11 17:03:19 -0700509
Mike Frysinger339f2df2021-05-06 00:44:42 -0400510 @classmethod
511 def _FetchInitChild(cls, ssh_proxy):
512 cls.ssh_proxy = ssh_proxy
513
514 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500515 ret = True
516
Mike Frysinger355f4392022-07-20 17:15:29 -0400517 jobs = opt.jobs_network
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700518 fetched = set()
LaMont Jones1eddca82022-09-01 15:15:04 +0000519 remote_fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400520 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800521
David James89ece422014-01-09 18:51:58 -0800522 objdir_project_map = dict()
523 for project in projects:
524 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500525 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700526
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500527 def _ProcessResults(results_sets):
528 ret = True
529 for results in results_sets:
LaMont Jones1eddca82022-09-01 15:15:04 +0000530 for result in results:
531 success = result.success
532 project = result.project
533 start = result.start
534 finish = result.finish
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500535 self._fetch_times.Set(project, finish - start)
536 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
537 start, finish, success)
LaMont Jones1eddca82022-09-01 15:15:04 +0000538 if result.remote_fetched:
539 remote_fetched.add(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500540 # Check for any errors before running any more tasks.
541 # ...we'll let existing jobs finish, though.
542 if not success:
543 ret = False
544 else:
545 fetched.add(project.gitdir)
546 pm.update(msg=project.name)
547 if not ret and opt.fail_fast:
548 break
549 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700550
Mike Frysinger339f2df2021-05-06 00:44:42 -0400551 # We pass the ssh proxy settings via the class. This allows multiprocessing
552 # to pickle it up when spawning children. We can't pass it as an argument
553 # to _FetchProjectList below as multiprocessing is unable to pickle those.
554 Sync.ssh_proxy = None
555
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500556 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400557 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400558 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500559 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
560 ret = False
561 else:
562 # Favor throughput over responsiveness when quiet. It seems that imap()
563 # will yield results in batches relative to chunksize, so even as the
564 # children finish a sync, we won't see the result until one child finishes
565 # ~chunksize jobs. When using a large --jobs with large chunksize, this
566 # can be jarring as there will be a large initial delay where repo looks
567 # like it isn't doing anything and sits at 0%, but then suddenly completes
568 # a lot of jobs all at once. Since this code is more network bound, we
569 # can accept a bit more CPU overhead with a smaller chunksize so that the
570 # user sees more immediate & continuous feedback.
571 if opt.quiet:
572 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800573 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500574 pm.update(inc=0, msg='warming up')
575 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800576 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
577 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500578 results = pool.imap_unordered(
579 functools.partial(self._FetchProjectList, opt),
580 projects_list,
581 chunksize=chunksize)
582 if not _ProcessResults(results):
583 ret = False
584 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800585
Mike Frysinger339f2df2021-05-06 00:44:42 -0400586 # Cleanup the reference now that we're done with it, and we're going to
587 # release any resources it points to. If we don't, later multiprocessing
588 # usage (e.g. checkouts) will try to pickle and then crash.
589 del Sync.ssh_proxy
590
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700591 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700592 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700593
LaMont Jonesa46047a2022-04-07 21:57:06 +0000594 if not self.outer_client.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400595 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200596
LaMont Jones1eddca82022-09-01 15:15:04 +0000597 return _FetchResult(ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700598
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000599 def _FetchMain(self, opt, args, all_projects, err_event,
600 ssh_proxy, manifest):
Mike Frysingerb4429432021-05-05 20:03:26 -0400601 """The main network fetch loop.
602
603 Args:
604 opt: Program options returned from optparse. See _Options().
605 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200606 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400607 err_event: Whether an error was hit while processing.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400608 ssh_proxy: SSH manager for clients & masters.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000609 manifest: The manifest to use.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200610
611 Returns:
612 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400613 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000614 rp = manifest.repoProject
Mike Frysingerb4429432021-05-05 20:03:26 -0400615
616 to_fetch = []
617 now = time.time()
618 if _ONE_DAY_S <= (now - rp.LastFetch):
619 to_fetch.append(rp)
620 to_fetch.extend(all_projects)
621 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
622
LaMont Jones1eddca82022-09-01 15:15:04 +0000623 result = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
624 success = result.success
625 fetched = result.projects
Mike Frysingerb4429432021-05-05 20:03:26 -0400626 if not success:
627 err_event.set()
628
629 _PostRepoFetch(rp, opt.repo_verify)
630 if opt.network_only:
631 # bail out now; the rest touches the working tree
632 if err_event.is_set():
633 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
634 sys.exit(1)
LaMont Jones1eddca82022-09-01 15:15:04 +0000635 return _FetchMainResult([])
Mike Frysingerb4429432021-05-05 20:03:26 -0400636
637 # Iteratively fetch missing and/or nested unregistered submodules
638 previously_missing_set = set()
639 while True:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000640 self._ReloadManifest(None, manifest)
Mike Frysingerb4429432021-05-05 20:03:26 -0400641 all_projects = self.GetProjects(args,
642 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000643 submodules_ok=opt.fetch_submodules,
644 manifest=manifest,
645 all_manifests=not opt.this_manifest_only)
Mike Frysingerb4429432021-05-05 20:03:26 -0400646 missing = []
647 for project in all_projects:
648 if project.gitdir not in fetched:
649 missing.append(project)
650 if not missing:
651 break
652 # Stop us from non-stopped fetching actually-missing repos: If set of
653 # missing repos has not been changed from last fetch, we break.
654 missing_set = set(p.name for p in missing)
655 if previously_missing_set == missing_set:
656 break
657 previously_missing_set = missing_set
LaMont Jones1eddca82022-09-01 15:15:04 +0000658 result = self._Fetch(missing, opt, err_event, ssh_proxy)
659 success = result.success
660 new_fetched = result.projects
Mike Frysingerb4429432021-05-05 20:03:26 -0400661 if not success:
662 err_event.set()
663 fetched.update(new_fetched)
664
LaMont Jones1eddca82022-09-01 15:15:04 +0000665 return _FetchMainResult(all_projects)
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200666
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500667 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700668 """Checkout work tree for one project
669
670 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500671 detach_head: Whether to leave a detached HEAD.
672 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700673 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700674
675 Returns:
676 Whether the fetch was successful.
677 """
Xin Li745be2e2019-06-03 11:24:30 -0700678 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +0000679 syncbuf = SyncBuffer(project.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500680 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700681 success = False
682 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500683 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500684 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700685 except GitError as e:
686 print('error.GitError: Cannot checkout %s: %s' %
687 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500688 except Exception as e:
689 print('error: Cannot checkout %s: %s: %s' %
690 (project.name, type(e).__name__, str(e)),
691 file=sys.stderr)
692 raise
Xin Li745be2e2019-06-03 11:24:30 -0700693
Mike Frysingerebf04a42021-02-23 20:48:04 -0500694 if not success:
695 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
696 finish = time.time()
LaMont Jones1eddca82022-09-01 15:15:04 +0000697 return _CheckoutOneResult(success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700698
Mike Frysingerebf04a42021-02-23 20:48:04 -0500699 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700700 """Checkout projects listed in all_projects
701
702 Args:
703 all_projects: List of all projects that should be checked out.
704 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500705 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700706 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500707 # Only checkout projects with worktrees.
708 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700709
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500710 def _ProcessResults(pool, pm, results):
711 ret = True
LaMont Jones1eddca82022-09-01 15:15:04 +0000712 for result in results:
713 success = result.success
714 project = result.project
715 start = result.start
716 finish = result.finish
Mike Frysingerebf04a42021-02-23 20:48:04 -0500717 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
718 start, finish, success)
719 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500720 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500721 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500722 ret = False
LaMont Jonesbee4efb2022-09-30 17:46:52 +0000723 err_results.append(project.RelPath(local=opt.this_manifest_only))
Mike Frysingerebf04a42021-02-23 20:48:04 -0500724 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500725 if pool:
726 pool.close()
727 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500728 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500729 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700730
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500731 return self.ExecuteInParallel(
Mike Frysinger355f4392022-07-20 17:15:29 -0400732 opt.jobs_checkout,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500733 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
734 all_projects,
735 callback=_ProcessResults,
736 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500737
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000738 @staticmethod
739 def _GetPreciousObjectsState(project: Project, opt):
740 """Get the preciousObjects state for the project.
741
742 Args:
743 project (Project): the project to examine, and possibly correct.
744 opt (optparse.Values): options given to sync.
745
746 Returns:
747 Expected state of extensions.preciousObjects:
748 False: Should be disabled. (not present)
749 True: Should be enabled.
750 """
751 if project.use_git_worktrees:
752 return False
753 projects = project.manifest.GetProjectsWithName(project.name,
754 all_manifests=True)
755 if len(projects) == 1:
756 return False
757 relpath = project.RelPath(local=opt.this_manifest_only)
758 if len(projects) > 1:
759 # Objects are potentially shared with another project.
760 # See the logic in Project.Sync_NetworkHalf regarding UseAlternates.
761 # - When False, shared projects share (via symlink)
762 # .repo/project-objects/{PROJECT_NAME}.git as the one-and-only objects
763 # directory. All objects are precious, since there is no project with a
764 # complete set of refs.
765 # - When True, shared projects share (via info/alternates)
766 # .repo/project-objects/{PROJECT_NAME}.git as an alternate object store,
767 # which is written only on the first clone of the project, and is not
768 # written subsequently. (When Sync_NetworkHalf sees that it exists, it
769 # makes sure that the alternates file points there, and uses a
770 # project-local .git/objects directory for all syncs going forward.
771 # We do not support switching between the options. The environment
772 # variable is present for testing and migration only.
773 return not project.UseAlternates
774 print(f'\r{relpath}: project not found in manifest.', file=sys.stderr)
775 return False
776
LaMont Jones43549d82022-11-30 19:55:30 +0000777 def _SetPreciousObjectsState(self, project: Project, opt):
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000778 """Correct the preciousObjects state for the project.
779
780 Args:
LaMont Jones43549d82022-11-30 19:55:30 +0000781 project: the project to examine, and possibly correct.
782 opt: options given to sync.
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000783 """
784 expected = self._GetPreciousObjectsState(project, opt)
785 actual = project.config.GetBoolean('extensions.preciousObjects') or False
LaMont Jones43549d82022-11-30 19:55:30 +0000786 relpath = project.RelPath(local=opt.this_manifest_only)
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000787
LaMont Jones43549d82022-11-30 19:55:30 +0000788 if expected != actual:
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000789 # If this is unexpected, log it and repair.
790 Trace(f'{relpath} expected preciousObjects={expected}, got {actual}')
791 if expected:
792 if not opt.quiet:
793 print('\r%s: Shared project %s found, disabling pruning.' %
794 (relpath, project.name))
795 if git_require((2, 7, 0)):
796 project.EnableRepositoryExtension('preciousObjects')
797 else:
798 # This isn't perfect, but it's the best we can do with old git.
799 print('\r%s: WARNING: shared projects are unreliable when using '
800 'old versions of git; please upgrade to git-2.7.0+.'
801 % (relpath,),
802 file=sys.stderr)
803 project.config.SetString('gc.pruneExpire', 'never')
804 else:
805 if not opt.quiet:
806 print(f'\r{relpath}: not shared, disabling pruning.')
807 project.config.SetString('extensions.preciousObjects', None)
808 project.config.SetString('gc.pruneExpire', None)
809
Mike Frysinger5a033082019-09-23 19:21:20 -0400810 def _GCProjects(self, projects, opt, err_event):
LaMont Jones7efab532022-09-01 15:41:12 +0000811 """Perform garbage collection.
812
813 If We are skipping garbage collection (opt.auto_gc not set), we still want
814 to potentially mark objects precious, so that `git gc` does not discard
815 shared objects.
816 """
LaMont Jones43549d82022-11-30 19:55:30 +0000817 if not opt.auto_gc:
818 # Just repair preciousObjects state, and return.
819 for project in projects:
820 self._SetPreciousObjectsState(project, opt)
821 return
822
823 pm = Progress('Garbage collecting', len(projects), delay=False,
824 quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400825 pm.update(inc=0, msg='prescan')
826
Allen Webb4ee4a452021-10-07 10:42:38 -0500827 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700828 for project in projects:
LaMont Jones43549d82022-11-30 19:55:30 +0000829 self._SetPreciousObjectsState(project, opt)
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000830
Allen Webb669efd02021-10-01 15:25:31 -0500831 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500832 # Only call git gc once per objdir, but call pack-refs for the remainder.
833 if project.objdir not in tidy_dirs:
834 tidy_dirs[project.objdir] = (
835 True, # Run a full gc.
836 project.bare_git,
837 )
838 elif project.gitdir not in tidy_dirs:
839 tidy_dirs[project.gitdir] = (
840 False, # Do not run a full gc; just run pack-refs.
841 project.bare_git,
842 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400843
Mike Frysinger355f4392022-07-20 17:15:29 -0400844 jobs = opt.jobs
Dave Borowitz18857212012-10-23 17:02:59 -0700845
846 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500847 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400848 pm.update(msg=bare_git._project.name)
LaMont Jones891e8f72022-09-08 20:17:58 +0000849
Allen Webb4ee4a452021-10-07 10:42:38 -0500850 if run_gc:
LaMont Jones55b71252022-12-01 21:17:15 +0000851 bare_git.gc('--auto')
Allen Webb4ee4a452021-10-07 10:42:38 -0500852 else:
LaMont Jones55b71252022-12-01 21:17:15 +0000853 bare_git.pack_refs()
Mike Frysinger65af2602021-04-08 22:47:44 -0400854 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700855 return
856
Mike Frysinger355f4392022-07-20 17:15:29 -0400857 cpu_count = os.cpu_count()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400858 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700859
860 threads = set()
861 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700862
Allen Webb4ee4a452021-10-07 10:42:38 -0500863 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400864 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700865 try:
866 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500867 if run_gc:
LaMont Jones55b71252022-12-01 21:17:15 +0000868 bare_git.gc('--auto', config=config)
Allen Webb4ee4a452021-10-07 10:42:38 -0500869 else:
LaMont Jones55b71252022-12-01 21:17:15 +0000870 bare_git.pack_refs(config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700871 except GitError:
872 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900873 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700874 err_event.set()
875 raise
876 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400877 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700878 sem.release()
879
Allen Webb4ee4a452021-10-07 10:42:38 -0500880 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500881 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700882 break
883 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500884 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700885 t.daemon = True
886 threads.add(t)
887 t.start()
888
889 for t in threads:
890 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400891 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700892
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000893 def _ReloadManifest(self, manifest_name, manifest):
Raman Tennetifeb28912021-05-02 19:47:29 -0700894 """Reload the manfiest from the file specified by the |manifest_name|.
895
896 It unloads the manifest if |manifest_name| is None.
897
898 Args:
899 manifest_name: Manifest file to be reloaded.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000900 manifest: The manifest to use.
Raman Tennetifeb28912021-05-02 19:47:29 -0700901 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800902 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000903 # Override calls Unload already
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000904 manifest.Override(manifest_name)
Tim Kilbourn07669002013-03-08 15:02:49 -0800905 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000906 manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800907
LaMont Jonesa46047a2022-04-07 21:57:06 +0000908 def UpdateProjectList(self, opt, manifest):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000909 """Update the cached projects list for |manifest|
910
911 In a multi-manifest checkout, each manifest has its own project.list.
912
913 Args:
914 opt: Program options returned from optparse. See _Options().
915 manifest: The manifest to use.
916
917 Returns:
918 0: success
919 1: failure
920 """
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700921 new_project_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000922 for project in self.GetProjects(None, missing_ok=True, manifest=manifest,
923 all_manifests=False):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700924 if project.relpath:
925 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700926 file_name = 'project.list'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000927 file_path = os.path.join(manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700928 old_project_paths = []
929
930 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500931 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700932 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800933 # In reversed order, so subfolders are deleted before parent folder.
934 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700935 if not path:
936 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700937 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900938 # If the path has already been deleted, we don't need to do it
LaMont Jonesa46047a2022-04-07 21:57:06 +0000939 gitdir = os.path.join(manifest.topdir, path, '.git')
Dan Willemsen43507912016-09-01 16:26:02 -0700940 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900941 project = Project(
LaMont Jonesa46047a2022-04-07 21:57:06 +0000942 manifest=manifest,
David Pursehouseabdf7502020-02-12 14:58:39 +0900943 name=path,
944 remote=RemoteSpec('origin'),
945 gitdir=gitdir,
946 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500947 use_git_worktrees=os.path.isfile(gitdir),
LaMont Jonesa46047a2022-04-07 21:57:06 +0000948 worktree=os.path.join(manifest.topdir, path),
David Pursehouseabdf7502020-02-12 14:58:39 +0900949 relpath=path,
950 revisionExpr='HEAD',
951 revisionId=None,
952 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500953 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900954 quiet=opt.quiet,
955 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400956 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700957
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700958 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500959 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700960 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700961 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700962 return 0
963
LaMont Jonesa46047a2022-04-07 21:57:06 +0000964 def UpdateCopyLinkfileList(self, manifest):
jiajia tanga590e642021-04-25 20:02:02 +0800965 """Save all dests of copyfile and linkfile, and update them if needed.
966
967 Returns:
968 Whether update was successful.
969 """
970 new_paths = {}
971 new_linkfile_paths = []
972 new_copyfile_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000973 for project in self.GetProjects(None, missing_ok=True,
974 manifest=manifest, all_manifests=False):
jiajia tanga590e642021-04-25 20:02:02 +0800975 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
976 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
977
978 new_paths = {
979 'linkfile': new_linkfile_paths,
980 'copyfile': new_copyfile_paths,
981 }
982
983 copylinkfile_name = 'copy-link-files.json'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000984 copylinkfile_path = os.path.join(manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +0800985 old_copylinkfile_paths = {}
986
987 if os.path.exists(copylinkfile_path):
988 with open(copylinkfile_path, 'rb') as fp:
989 try:
990 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800991 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +0800992 print('error: %s is not a json formatted file.' %
993 copylinkfile_path, file=sys.stderr)
994 platform_utils.remove(copylinkfile_path)
995 return False
996
997 need_remove_files = []
998 need_remove_files.extend(
999 set(old_copylinkfile_paths.get('linkfile', [])) -
1000 set(new_linkfile_paths))
1001 need_remove_files.extend(
1002 set(old_copylinkfile_paths.get('copyfile', [])) -
1003 set(new_copyfile_paths))
1004
1005 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -04001006 # Try to remove the updated copyfile or linkfile.
1007 # So, if the file is not exist, nothing need to do.
1008 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +08001009
1010 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
1011 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
1012 json.dump(new_paths, fp)
1013 return True
1014
LaMont Jonesa46047a2022-04-07 21:57:06 +00001015 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
1016 if not manifest.manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001017 print('error: cannot smart sync: no manifest server defined in '
1018 'manifest', file=sys.stderr)
1019 sys.exit(1)
1020
LaMont Jonesa46047a2022-04-07 21:57:06 +00001021 manifest_server = manifest.manifest_server
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001022 if not opt.quiet:
1023 print('Using manifest server %s' % manifest_server)
1024
David Pursehouseeeff3532020-02-12 11:24:10 +09001025 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001026 username = None
1027 password = None
1028 if opt.manifest_server_username and opt.manifest_server_password:
1029 username = opt.manifest_server_username
1030 password = opt.manifest_server_password
1031 else:
1032 try:
1033 info = netrc.netrc()
1034 except IOError:
1035 # .netrc file does not exist or could not be opened
1036 pass
1037 else:
1038 try:
1039 parse_result = urllib.parse.urlparse(manifest_server)
1040 if parse_result.hostname:
1041 auth = info.authenticators(parse_result.hostname)
1042 if auth:
1043 username, _account, password = auth
1044 else:
1045 print('No credentials found for %s in .netrc'
1046 % parse_result.hostname, file=sys.stderr)
1047 except netrc.NetrcParseError as e:
1048 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
1049
1050 if (username and password):
1051 manifest_server = manifest_server.replace('://', '://%s:%s@' %
1052 (username, password),
1053 1)
1054
1055 transport = PersistentTransport(manifest_server)
1056 if manifest_server.startswith('persistent-'):
1057 manifest_server = manifest_server[len('persistent-'):]
1058
1059 try:
1060 server = xmlrpc.client.Server(manifest_server, transport=transport)
1061 if opt.smart_sync:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001062 branch = self._GetBranch(manifest.manifestProject)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001063
Mike Frysinger56ce3462019-12-04 19:30:48 -05001064 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -05001065 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001066 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -05001067 elif ('TARGET_PRODUCT' in os.environ and
1068 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -05001069 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
1070 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001071 [success, manifest_str] = server.GetApprovedManifest(branch, target)
1072 else:
1073 [success, manifest_str] = server.GetApprovedManifest(branch)
1074 else:
1075 assert(opt.smart_tag)
1076 [success, manifest_str] = server.GetManifest(opt.smart_tag)
1077
1078 if success:
1079 manifest_name = os.path.basename(smart_sync_manifest_path)
1080 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001081 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001082 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001083 except IOError as e:
1084 print('error: cannot write manifest to %s:\n%s'
1085 % (smart_sync_manifest_path, e),
1086 file=sys.stderr)
1087 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001088 self._ReloadManifest(manifest_name, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001089 else:
1090 print('error: manifest server RPC call failed: %s' %
1091 manifest_str, file=sys.stderr)
1092 sys.exit(1)
1093 except (socket.error, IOError, xmlrpc.client.Fault) as e:
1094 print('error: cannot connect to manifest server %s:\n%s'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001095 % (manifest.manifest_server, e), file=sys.stderr)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001096 sys.exit(1)
1097 except xmlrpc.client.ProtocolError as e:
1098 print('error: cannot connect to manifest server %s:\n%d %s'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001099 % (manifest.manifest_server, e.errcode, e.errmsg),
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001100 file=sys.stderr)
1101 sys.exit(1)
1102
1103 return manifest_name
1104
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001105 def _UpdateAllManifestProjects(self, opt, mp, manifest_name):
1106 """Fetch & update the local manifest project.
1107
1108 After syncing the manifest project, if the manifest has any sub manifests,
1109 those are recursively processed.
1110
1111 Args:
1112 opt: Program options returned from optparse. See _Options().
1113 mp: the manifestProject to query.
1114 manifest_name: Manifest file to be reloaded.
1115 """
1116 if not mp.standalone_manifest_url:
1117 self._UpdateManifestProject(opt, mp, manifest_name)
1118
1119 if mp.manifest.submanifests:
1120 for submanifest in mp.manifest.submanifests.values():
1121 child = submanifest.repo_client.manifest
1122 child.manifestProject.SyncWithPossibleInit(
1123 submanifest,
1124 current_branch_only=self._GetCurrentBranchOnly(opt, child),
1125 verbose=opt.verbose,
1126 tags=opt.tags,
1127 git_event_log=self.git_event_log,
1128 )
1129 self._UpdateAllManifestProjects(opt, child.manifestProject, None)
1130
Mike Frysingerfb527e32019-08-27 02:34:32 -04001131 def _UpdateManifestProject(self, opt, mp, manifest_name):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001132 """Fetch & update the local manifest project.
1133
1134 Args:
1135 opt: Program options returned from optparse. See _Options().
1136 mp: the manifestProject to query.
1137 manifest_name: Manifest file to be reloaded.
1138 """
Mike Frysingerfb527e32019-08-27 02:34:32 -04001139 if not opt.local_only:
1140 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -05001141 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001142 current_branch_only=self._GetCurrentBranchOnly(opt, mp.manifest),
Erwan Yvindc5c4d12019-06-18 13:49:12 +02001143 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001144 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -04001145 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001146 retry_fetches=opt.retry_fetches,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001147 submodules=mp.manifest.HasSubmodules,
1148 clone_filter=mp.manifest.CloneFilter,
1149 partial_clone_exclude=mp.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001150 finish = time.time()
1151 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
1152 start, finish, success)
1153
1154 if mp.HasChanges:
1155 syncbuf = SyncBuffer(mp.config)
1156 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +00001157 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001158 clean = syncbuf.Finish()
1159 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
1160 start, time.time(), clean)
1161 if not clean:
1162 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001163 self._ReloadManifest(manifest_name, mp.manifest)
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001164
Mike Frysingerae6cb082019-08-27 01:10:59 -04001165 def ValidateOptions(self, opt, args):
1166 if opt.force_broken:
1167 print('warning: -f/--force-broken is now the default behavior, and the '
1168 'options are deprecated', file=sys.stderr)
1169 if opt.network_only and opt.detach_head:
1170 self.OptionParser.error('cannot combine -n and -d')
1171 if opt.network_only and opt.local_only:
1172 self.OptionParser.error('cannot combine -n and -l')
1173 if opt.manifest_name and opt.smart_sync:
1174 self.OptionParser.error('cannot combine -m and -s')
1175 if opt.manifest_name and opt.smart_tag:
1176 self.OptionParser.error('cannot combine -m and -t')
1177 if opt.manifest_server_username or opt.manifest_server_password:
1178 if not (opt.smart_sync or opt.smart_tag):
1179 self.OptionParser.error('-u and -p may only be combined with -s or -t')
1180 if None in [opt.manifest_server_username, opt.manifest_server_password]:
1181 self.OptionParser.error('both -u and -p must be given')
1182
Mike Frysinger0531a622021-11-05 15:22:01 -04001183 if opt.prune is None:
1184 opt.prune = True
1185
LaMont Jones5ed8c632022-11-10 00:10:44 +00001186 if opt.auto_gc is None and _AUTO_GC:
1187 print(f"Will run `git gc --auto` because {_REPO_AUTO_GC} is set.",
LaMont Jones100a2142022-12-02 22:54:11 +00001188 f'{_REPO_AUTO_GC} is deprecated and will be removed in a future',
LaMont Jonesd7935532022-12-01 20:18:46 +00001189 'release. Use `--auto-gc` instead.', file=sys.stderr)
LaMont Jones100a2142022-12-02 22:54:11 +00001190 opt.auto_gc = True
LaMont Jones5ed8c632022-11-10 00:10:44 +00001191
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001192 def Execute(self, opt, args):
LaMont Jonesa46047a2022-04-07 21:57:06 +00001193 manifest = self.outer_manifest
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001194 if not opt.outer_manifest:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001195 manifest = self.manifest
1196
Chris Wolfee9dc3b32012-01-26 11:36:18 -05001197 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001198 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -07001199
Chirayu Desaia892b102013-06-11 14:18:46 +05301200 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +09001201 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001202 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +05301203
Xin Lid79a4bc2020-05-20 16:03:45 -07001204 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001205 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -07001206
Victor Boivie08c880d2011-04-19 10:32:52 +02001207 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001208 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001209 else:
David Pursehouse59b41742015-05-07 14:36:09 +09001210 if os.path.isfile(smart_sync_manifest_path):
1211 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001212 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +09001213 except OSError as e:
1214 print('error: failed to remove existing smart sync override manifest: %s' %
1215 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -07001216
Mike Frysingerc99322a2021-05-04 15:32:43 -04001217 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -04001218
LaMont Jonesa46047a2022-04-07 21:57:06 +00001219 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001220 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -05001221 cb = rp.CurrentBranch
1222 if cb:
1223 base = rp.GetBranch(cb).merge
1224 if not base or not base.startswith('refs/heads/'):
1225 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -04001226 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -05001227 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001228
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001229 for m in self.ManifestList(opt):
LaMont Jones4112c072022-08-24 17:32:25 +00001230 if not m.manifestProject.standalone_manifest_url:
1231 m.manifestProject.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001232
LaMont Jones4112c072022-08-24 17:32:25 +00001233 if opt.repo_upgraded:
1234 _PostRepoUpgrade(manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001235
LaMont Jones4112c072022-08-24 17:32:25 +00001236 mp = manifest.manifestProject
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001237 if opt.mp_update:
1238 self._UpdateAllManifestProjects(opt, mp, manifest_name)
1239 else:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001240 print('Skipping update of local manifest project.')
Simran Basib9a1b732015-08-20 12:19:28 -07001241
Mike Frysinger355f4392022-07-20 17:15:29 -04001242 # Now that the manifests are up-to-date, setup the jobs value.
1243 if opt.jobs is None:
1244 # User has not made a choice, so use the manifest settings.
1245 opt.jobs = mp.default.sync_j
1246 if opt.jobs is not None:
1247 # Neither user nor manifest have made a choice.
1248 if opt.jobs_network is None:
1249 opt.jobs_network = opt.jobs
1250 if opt.jobs_checkout is None:
1251 opt.jobs_checkout = opt.jobs
1252 # Setup defaults if jobs==0.
1253 if not opt.jobs:
1254 if not opt.jobs_network:
1255 opt.jobs_network = 1
1256 if not opt.jobs_checkout:
1257 opt.jobs_checkout = DEFAULT_LOCAL_JOBS
1258 opt.jobs = os.cpu_count()
1259
1260 # Try to stay under user rlimit settings.
1261 #
1262 # Since each worker requires at 3 file descriptors to run `git fetch`, use
1263 # that to scale down the number of jobs. Unfortunately there isn't an easy
1264 # way to determine this reliably as systems change, but it was last measured
1265 # by hand in 2011.
1266 soft_limit, _ = _rlimit_nofile()
1267 jobs_soft_limit = max(1, (soft_limit - 5) // 3)
1268 opt.jobs = min(opt.jobs, jobs_soft_limit)
1269 opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
1270 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1271
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001272 superproject_logging_data = {}
1273 self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
1274 manifest)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001275
Simran Basib9a1b732015-08-20 12:19:28 -07001276 if self.gitc_manifest:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001277 gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
Simran Basib9a1b732015-08-20 12:19:28 -07001278 gitc_projects = []
1279 opened_projects = []
1280 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001281 if project.relpath in self.gitc_manifest.paths and \
1282 self.gitc_manifest.paths[project.relpath].old_revision:
1283 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001284 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001285 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001286
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001287 if not args:
1288 gitc_projects = None
1289
1290 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001291 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001292 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1293 if manifest_name:
1294 manifest.Override(manifest_name)
1295 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001296 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001297 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1298 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001299 gitc_projects)
1300 print('GITC client successfully synced.')
1301
1302 # The opened projects need to be synced as normal, therefore we
1303 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001304 # TODO: make this more reliable -- if there's a project name/path overlap,
1305 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001306 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001307 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001308 if not args:
1309 return
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001310
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001311 all_projects = self.GetProjects(args,
1312 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001313 submodules_ok=opt.fetch_submodules,
1314 manifest=manifest,
1315 all_manifests=not opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001316
Mike Frysinger5a033082019-09-23 19:21:20 -04001317 err_network_sync = False
1318 err_update_projects = False
LaMont Jonesb6cfa092022-10-26 16:34:40 +00001319 err_update_linkfiles = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001320
LaMont Jonesa46047a2022-04-07 21:57:06 +00001321 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001322 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001323 with multiprocessing.Manager() as manager:
1324 with ssh.ProxyManager(manager) as ssh_proxy:
1325 # Initialize the socket dir once in the parent.
1326 ssh_proxy.sock()
LaMont Jones1eddca82022-09-01 15:15:04 +00001327 result = self._FetchMain(opt, args, all_projects, err_event,
1328 ssh_proxy, manifest)
1329 all_projects = result.all_projects
Mike Frysinger339f2df2021-05-06 00:44:42 -04001330
1331 if opt.network_only:
1332 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001333
1334 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001335 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001336 err_network_sync = True
1337 if opt.fail_fast:
1338 print('\nerror: Exited sync due to fetch errors.\n'
1339 'Local checkouts *not* updated. Resolve network issues & '
1340 'retry.\n'
1341 '`repo sync -l` will update some local checkouts.',
1342 file=sys.stderr)
1343 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001344
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001345 for m in self.ManifestList(opt):
1346 if m.IsMirror or m.IsArchive:
1347 # bail out now, we have no working tree
1348 continue
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001349
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001350 if self.UpdateProjectList(opt, m):
1351 err_event.set()
1352 err_update_projects = True
1353 if opt.fail_fast:
1354 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1355 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001356
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001357 err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
1358 if err_update_linkfiles:
1359 err_event.set()
1360 if opt.fail_fast:
1361 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1362 sys.exit(1)
jiajia tanga590e642021-04-25 20:02:02 +08001363
Mike Frysinger5a033082019-09-23 19:21:20 -04001364 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001365 # NB: We don't exit here because this is the last step.
1366 err_checkout = not self._Checkout(all_projects, opt, err_results)
1367 if err_checkout:
1368 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001369
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001370 printed_notices = set()
1371 # If there's a notice that's supposed to print at the end of the sync,
1372 # print it now... But avoid printing duplicate messages, and preserve
1373 # order.
1374 for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
1375 if m.notice and m.notice not in printed_notices:
1376 print(m.notice)
1377 printed_notices.add(m.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001378
Mike Frysinger5a033082019-09-23 19:21:20 -04001379 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001380 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001381 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1382 if err_network_sync:
1383 print('error: Downloading network changes failed.', file=sys.stderr)
1384 if err_update_projects:
1385 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001386 if err_update_linkfiles:
1387 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001388 if err_checkout:
1389 print('error: Checking out local projects failed.', file=sys.stderr)
1390 if err_results:
1391 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1392 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1393 file=sys.stderr)
1394 sys.exit(1)
1395
Raman Tenneti7954de12021-07-28 14:36:49 -07001396 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001397 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1398 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001399
1400 # Update and log with the new sync analysis state.
1401 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001402 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1403 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001404
Mike Frysingere19d9e12020-02-12 11:23:32 -05001405 if not opt.quiet:
1406 print('repo sync has finished successfully.')
1407
David Pursehouse819827a2020-02-12 15:20:19 +09001408
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001409def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001410 # Link the docs for the internal .repo/ layout for people
1411 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1412 if not platform_utils.islink(link):
1413 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1414 try:
1415 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001416 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001417 pass
1418
Conley Owens094cdbe2014-01-30 15:09:59 -08001419 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001420 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001421 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001422 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001423 if project.Exists:
1424 project.PostRepoUpgrade()
1425
David Pursehouse819827a2020-02-12 15:20:19 +09001426
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001427def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001428 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001429 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001430 wrapper = Wrapper()
1431 try:
1432 rev = rp.bare_git.describe(rp.GetRevisionId())
1433 except GitError:
1434 rev = None
1435 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1436 # See if we're held back due to missing signed tag.
1437 current_revid = rp.bare_git.rev_parse('HEAD')
1438 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1439 if current_revid != new_revid:
1440 # We want to switch to the new rev, but also not trash any uncommitted
1441 # changes. This helps with local testing/hacking.
1442 # If a local change has been made, we will throw that away.
1443 # We also have to make sure this will switch to an older commit if that's
1444 # the latest tag in order to support release rollback.
1445 try:
1446 rp.work_git.reset('--keep', new_rev)
1447 except GitError as e:
1448 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001449 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001450 raise RepoChangedException(['--repo-upgraded'])
1451 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001452 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001453 else:
1454 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001455 print('repo version %s is current' % rp.work_git.describe(HEAD),
1456 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001457
David Pursehouse819827a2020-02-12 15:20:19 +09001458
Dave Borowitz67700e92012-10-23 15:00:54 -07001459class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001460 _ALPHA = 0.5
1461
Dave Borowitz67700e92012-10-23 15:00:54 -07001462 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001463 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001464 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001465 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001466
1467 def Get(self, project):
1468 self._Load()
1469 return self._times.get(project.name, _ONE_DAY_S)
1470
1471 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001472 self._Load()
1473 name = project.name
1474 old = self._times.get(name, t)
1475 self._seen.add(name)
1476 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001477 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001478
1479 def _Load(self):
1480 if self._times is None:
1481 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001482 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001483 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001484 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001485 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001486 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001487
1488 def Save(self):
1489 if self._times is None:
1490 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001491
1492 to_delete = []
1493 for name in self._times:
1494 if name not in self._seen:
1495 to_delete.append(name)
1496 for name in to_delete:
1497 del self._times[name]
1498
Dave Borowitz67700e92012-10-23 15:00:54 -07001499 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001500 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001501 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001502 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001503 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001504
1505# This is a replacement for xmlrpc.client.Transport using urllib2
1506# and supporting persistent-http[s]. It cannot change hosts from
1507# request to request like the normal transport, the real url
1508# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001509
1510
Dan Willemsen0745bb22015-08-17 13:41:45 -07001511class PersistentTransport(xmlrpc.client.Transport):
1512 def __init__(self, orig_host):
1513 self.orig_host = orig_host
1514
1515 def request(self, host, handler, request_body, verbose=False):
1516 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1517 # Python doesn't understand cookies with the #HttpOnly_ prefix
1518 # Since we're only using them for HTTP, copy the file temporarily,
1519 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001520 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001521 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001522 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001523 try:
1524 with open(cookiefile) as f:
1525 for line in f:
1526 if line.startswith("#HttpOnly_"):
1527 line = line[len("#HttpOnly_"):]
1528 tmpcookiefile.write(line)
1529 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001530
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001531 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001532 try:
1533 cookiejar.load()
1534 except cookielib.LoadError:
1535 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001536 finally:
1537 tmpcookiefile.close()
1538 else:
1539 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001540
1541 proxyhandler = urllib.request.ProxyHandler
1542 if proxy:
1543 proxyhandler = urllib.request.ProxyHandler({
1544 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001545 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001546
1547 opener = urllib.request.build_opener(
1548 urllib.request.HTTPCookieProcessor(cookiejar),
1549 proxyhandler)
1550
1551 url = urllib.parse.urljoin(self.orig_host, handler)
1552 parse_results = urllib.parse.urlparse(url)
1553
1554 scheme = parse_results.scheme
1555 if scheme == 'persistent-http':
1556 scheme = 'http'
1557 if scheme == 'persistent-https':
1558 # If we're proxying through persistent-https, use http. The
1559 # proxy itself will do the https.
1560 if proxy:
1561 scheme = 'http'
1562 else:
1563 scheme = 'https'
1564
1565 # Parse out any authentication information using the base class
1566 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1567
1568 url = urllib.parse.urlunparse((
1569 scheme,
1570 host,
1571 parse_results.path,
1572 parse_results.params,
1573 parse_results.query,
1574 parse_results.fragment))
1575
1576 request = urllib.request.Request(url, request_body)
1577 if extra_headers is not None:
1578 for (name, header) in extra_headers:
1579 request.add_header(name, header)
1580 request.add_header('Content-Type', 'text/xml')
1581 try:
1582 response = opener.open(request)
1583 except urllib.error.HTTPError as e:
1584 if e.code == 501:
1585 # We may have been redirected through a login process
1586 # but our POST turned into a GET. Retry.
1587 response = opener.open(request)
1588 else:
1589 raise
1590
1591 p, u = xmlrpc.client.getparser()
Mike Frysinger5951e302022-05-20 23:34:44 -04001592 # Response should be fairly small, so read it all at once.
1593 # This way we can show it to the user in case of error (e.g. HTML).
1594 data = response.read()
1595 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -07001596 p.feed(data)
Mike Frysinger5951e302022-05-20 23:34:44 -04001597 except xml.parsers.expat.ExpatError as e:
1598 raise IOError(
1599 f'Parsing the manifest failed: {e}\n'
1600 f'Please report this to your manifest server admin.\n'
1601 f'Here is the full response:\n{data.decode("utf-8")}')
Dan Willemsen0745bb22015-08-17 13:41:45 -07001602 p.close()
1603 return u.close()
1604
1605 def close(self):
1606 pass