@@ -255,7 +255,7 @@ class _XmlSubmanifest:
255255 project: a string, the name of the manifest project.
256256 revision: a string, the commitish.
257257 manifestName: a string, the submanifest file name.
258- groups: a list of strings, the groups to add to all projects in the
258+ groups: a set of strings, the groups to add to all projects in the
259259 submanifest.
260260 default_groups: a list of strings, the default groups to sync.
261261 path: a string, the relative path for the submanifest checkout.
@@ -281,7 +281,7 @@ def __init__(
281281 self .project = project
282282 self .revision = revision
283283 self .manifestName = manifestName
284- self .groups = groups
284+ self .groups = groups or set ()
285285 self .default_groups = default_groups
286286 self .path = path
287287 self .parent = parent
@@ -304,7 +304,7 @@ def __init__(
304304 self .repo_client = RepoClient (
305305 parent .repodir ,
306306 linkFile ,
307- parent_groups = "," . join ( groups ) or "" ,
307+ parent_groups = groups ,
308308 submanifest_path = os .path .join (parent .path_prefix , self .relpath ),
309309 outer_client = outer_client ,
310310 default_groups = default_groups ,
@@ -345,7 +345,7 @@ def ToSubmanifestSpec(self):
345345 manifestName = self .manifestName or "default.xml"
346346 revision = self .revision or self .name
347347 path = self .path or revision .split ("/" )[- 1 ]
348- groups = self .groups or []
348+ groups = self .groups
349349
350350 return SubmanifestSpec (
351351 self .name , manifestUrl , manifestName , revision , path , groups
@@ -359,9 +359,7 @@ def relpath(self):
359359
360360 def GetGroupsStr (self ):
361361 """Returns the `groups` given for this submanifest."""
362- if self .groups :
363- return "," .join (self .groups )
364- return ""
362+ return "," .join (sorted (self .groups ))
365363
366364 def GetDefaultGroupsStr (self ):
367365 """Returns the `default-groups` given for this submanifest."""
@@ -381,7 +379,7 @@ def __init__(self, name, manifestUrl, manifestName, revision, path, groups):
381379 self .manifestName = manifestName
382380 self .revision = revision
383381 self .path = path
384- self .groups = groups or []
382+ self .groups = groups
385383
386384
387385class XmlManifest :
@@ -393,7 +391,7 @@ def __init__(
393391 manifest_file ,
394392 local_manifests = None ,
395393 outer_client = None ,
396- parent_groups = "" ,
394+ parent_groups = None ,
397395 submanifest_path = "" ,
398396 default_groups = None ,
399397 ):
@@ -409,7 +407,8 @@ def __init__(
409407 manifests. This will usually be
410408 |repodir|/|LOCAL_MANIFESTS_DIR_NAME|.
411409 outer_client: RepoClient of the outer manifest.
412- parent_groups: a string, the groups to apply to this projects.
410+ parent_groups: a set of strings, the groups to apply to this
411+ manifest.
413412 submanifest_path: The submanifest root relative to the repo root.
414413 default_groups: a string, the default manifest groups to use.
415414 """
@@ -432,7 +431,7 @@ def __init__(
432431 self .manifestFileOverrides = {}
433432 self .local_manifests = local_manifests
434433 self ._load_local_manifests = True
435- self .parent_groups = parent_groups
434+ self .parent_groups = parent_groups or set ()
436435 self .default_groups = default_groups
437436
438437 if submanifest_path and not outer_client :
@@ -567,6 +566,14 @@ def _ParseList(self, field):
567566 """
568567 return [x for x in re .split (r"[,\s]+" , field ) if x ]
569568
569+ def _ParseSet (self , field ):
570+ """Parse fields that contain flattened sets.
571+
572+ These are whitespace & comma separated. Empty elements will be
573+ discarded.
574+ """
575+ return set (self ._ParseList (field ))
576+
570577 def ToXml (
571578 self ,
572579 peg_rev = False ,
@@ -725,10 +732,9 @@ def output_project(parent, parent_node, p):
725732 le .setAttribute ("dest" , lf .dest )
726733 e .appendChild (le )
727734
728- default_groups = ["all" , "name:%s" % p .name , "path:%s" % p .relpath ]
729- egroups = [g for g in p .groups if g not in default_groups ]
735+ egroups = p .groups - {"all" , f"name:{ p .name } " , f"path:{ p .relpath } " }
730736 if egroups :
731- e .setAttribute ("groups" , "," .join (egroups ))
737+ e .setAttribute ("groups" , "," .join (sorted ( egroups ) ))
732738
733739 for a in p .annotations :
734740 if a .keep == "true" :
@@ -1171,12 +1177,12 @@ def _Load(self, initial_client=None, submanifest_depth=0):
11711177 b = b [len (R_HEADS ) :]
11721178 self .branch = b
11731179
1174- parent_groups = self .parent_groups
1180+ parent_groups = self .parent_groups . copy ()
11751181 if self .path_prefix :
1176- parent_groups = (
1182+ parent_groups |= {
11771183 f"{ SUBMANIFEST_GROUP_PREFIX } :path:"
1178- f"{ self .path_prefix } , { parent_groups } "
1179- )
1184+ f"{ self .path_prefix } "
1185+ }
11801186
11811187 # The manifestFile was specified by the user which is why we
11821188 # allow include paths to point anywhere.
@@ -1202,16 +1208,16 @@ def _Load(self, initial_client=None, submanifest_depth=0):
12021208 # Since local manifests are entirely managed by
12031209 # the user, allow them to point anywhere the
12041210 # user wants.
1205- local_group = (
1211+ local_group = {
12061212 f"{ LOCAL_MANIFEST_GROUP_PREFIX } :"
12071213 f"{ local_file [:- 4 ]} "
1208- )
1214+ }
12091215 nodes .append (
12101216 self ._ParseManifestXml (
12111217 local ,
12121218 self .subdir ,
12131219 parent_groups = (
1214- f" { local_group } , { parent_groups } "
1220+ local_group | parent_groups
12151221 ),
12161222 restrict_includes = False ,
12171223 )
@@ -1262,7 +1268,7 @@ def _ParseManifestXml(
12621268 self ,
12631269 path ,
12641270 include_root ,
1265- parent_groups = "" ,
1271+ parent_groups = None ,
12661272 restrict_includes = True ,
12671273 parent_node = None ,
12681274 ):
@@ -1271,11 +1277,11 @@ def _ParseManifestXml(
12711277 Args:
12721278 path: The XML file to read & parse.
12731279 include_root: The path to interpret include "name"s relative to.
1274- parent_groups: The groups to apply to this projects .
1280+ parent_groups: The set of groups to apply to this manifest .
12751281 restrict_includes: Whether to constrain the "name" attribute of
12761282 includes.
1277- parent_node: The parent include node, to apply attribute to this
1278- projects .
1283+ parent_node: The parent include node, to apply attributes to this
1284+ manifest .
12791285
12801286 Returns:
12811287 List of XML nodes.
@@ -1307,12 +1313,10 @@ def _ParseManifestXml(
13071313 raise ManifestInvalidPathError (
13081314 f'<include> invalid "name": { name } : { msg } '
13091315 )
1310- include_groups = ""
1311- if parent_groups :
1312- include_groups = parent_groups
1316+ include_groups = (parent_groups or set ()).copy ()
13131317 if node .hasAttribute ("groups" ):
1314- include_groups = (
1315- node .getAttribute ("groups" ) + "," + include_groups
1318+ include_groups |= self . _ParseSet (
1319+ node .getAttribute ("groups" )
13161320 )
13171321 fp = os .path .join (include_root , name )
13181322 if not os .path .isfile (fp ):
@@ -1339,12 +1343,12 @@ def _ParseManifestXml(
13391343 "project" ,
13401344 "extend-project" ,
13411345 ):
1342- nodeGroups = parent_groups
1346+ nodeGroups = parent_groups . copy ()
13431347 if node .hasAttribute ("groups" ):
1344- nodeGroups = (
1345- node .getAttribute ("groups" ) + "," + nodeGroups
1348+ nodeGroups |= self . _ParseSet (
1349+ node .getAttribute ("groups" )
13461350 )
1347- node .setAttribute ("groups" , nodeGroups )
1351+ node .setAttribute ("groups" , "," . join ( sorted ( nodeGroups )) )
13481352 if (
13491353 parent_node
13501354 and node .nodeName == "project"
@@ -1461,7 +1465,7 @@ def recursively_add_projects(project):
14611465 dest_path = node .getAttribute ("dest-path" )
14621466 groups = node .getAttribute ("groups" )
14631467 if groups :
1464- groups = self ._ParseList (groups )
1468+ groups = self ._ParseSet (groups or "" )
14651469 revision = node .getAttribute ("revision" )
14661470 remote_name = node .getAttribute ("remote" )
14671471 if not remote_name :
@@ -1482,7 +1486,7 @@ def recursively_add_projects(project):
14821486 if path and p .relpath != path :
14831487 continue
14841488 if groups :
1485- p .groups . extend ( groups )
1489+ p .groups |= groups
14861490 if revision :
14871491 if base_revision :
14881492 if p .revisionExpr != base_revision :
@@ -1813,7 +1817,7 @@ def _ParseSubmanifest(self, node):
18131817 groups = ""
18141818 if node .hasAttribute ("groups" ):
18151819 groups = node .getAttribute ("groups" )
1816- groups = self ._ParseList (groups )
1820+ groups = self ._ParseSet (groups )
18171821 default_groups = self ._ParseList (node .getAttribute ("default-groups" ))
18181822 path = node .getAttribute ("path" )
18191823 if path == "" :
@@ -1922,11 +1926,6 @@ def _ParseProject(self, node, parent=None, **extra_proj_attrs):
19221926
19231927 upstream = node .getAttribute ("upstream" ) or self ._default .upstreamExpr
19241928
1925- groups = ""
1926- if node .hasAttribute ("groups" ):
1927- groups = node .getAttribute ("groups" )
1928- groups = self ._ParseList (groups )
1929-
19301929 if parent is None :
19311930 (
19321931 relpath ,
@@ -1941,8 +1940,11 @@ def _ParseProject(self, node, parent=None, **extra_proj_attrs):
19411940 parent , name , path
19421941 )
19431942
1944- default_groups = ["all" , "name:%s" % name , "path:%s" % relpath ]
1945- groups .extend (set (default_groups ).difference (groups ))
1943+ groups = ""
1944+ if node .hasAttribute ("groups" ):
1945+ groups = node .getAttribute ("groups" )
1946+ groups = self ._ParseSet (groups )
1947+ groups |= {"all" , f"name:{ name } " , f"path:{ relpath } " }
19461948
19471949 if self .IsMirror and node .hasAttribute ("force-path" ):
19481950 if XmlBool (node , "force-path" , False ):
0 commit comments