3 from copy
import copy
, deepcopy
8 def __init__(self
, name
, is_multivalued
=False, allow_arch_extensions
=False):
10 self
.is_multivalued
= is_multivalued
11 self
.allow_arch_extensions
= allow_arch_extensions
13 PKGBUILD_ATTRIBUTES
= {
14 'arch': Attr('arch', True),
15 'backup': Attr('backup', True),
16 'changelog': Attr('changelog', False),
17 'checkdepends': Attr('checkdepends', True),
18 'conflicts': Attr('conflicts', True, True),
19 'depends': Attr('depends', True, True),
20 'epoch': Attr('epoch', False),
21 'groups': Attr('groups', True),
22 'install': Attr('install', False),
23 'license': Attr('license', True),
24 'makedepends': Attr('makedepends', True, True),
25 'md5sums': Attr('md5sums', True, True),
26 'noextract': Attr('noextract', True),
27 'optdepends': Attr('optdepends', True, True),
28 'options': Attr('options', True),
29 'pkgname': Attr('pkgname', False),
30 'pkgrel': Attr('pkgrel', False),
31 'pkgver': Attr('pkgver', False),
32 'provides': Attr('provides', True, True),
33 'replaces': Attr('replaces', True, True),
34 'sha1sums': Attr('sha1sums', True, True),
35 'sha224sums': Attr('sha224sums', True, True),
36 'sha256sums': Attr('sha256sums', True, True),
37 'sha384sums': Attr('sha384sums', True, True),
38 'sha512sums': Attr('sha512sums', True, True),
39 'source': Attr('source', True, True),
40 'url': Attr('url', False),
41 'validpgpkeys': Attr('validpgpkeys', True),
44 def find_attr(attrname
):
46 attr
= PKGBUILD_ATTRIBUTES
.get(attrname
, None)
51 # XXX: this could break in the future if PKGBUILD(5) ever
52 # introduces a key which is a subset of another.
53 for k
in PKGBUILD_ATTRIBUTES
.keys():
54 if attrname
.startswith(k
+ '_'):
55 return PKGBUILD_ATTRIBUTES
[k
]
57 def IsMultiValued(attrname
):
58 attr
= find_attr(attrname
)
59 return attr
and attr
.is_multivalued
61 class AurInfo(object):
66 def GetPackageNames(self
):
67 return self
._packages
.keys()
69 def GetMergedPackage(self
, pkgname
):
70 package
= deepcopy(self
._pkgbase
)
71 package
['pkgname'] = pkgname
72 for k
, v
in self
._packages
.get(pkgname
).items():
73 package
[k
] = deepcopy(v
)
76 def AddPackage(self
, pkgname
):
77 self
._packages
[pkgname
] = {}
78 return self
._packages
[pkgname
]
80 def SetPkgbase(self
, pkgbasename
):
81 self
._pkgbase
= {'pkgname' : pkgbasename
}
85 class StderrECatcher(object):
86 def Catch(self
, lineno
, error
):
87 print('ERROR[{:d}]: {:s}'.format(lineno
, error
), file=sys
.stderr
)
90 class CollectionECatcher(object):
94 def Catch(self
, lineno
, error
):
95 self
._errors
.append((lineno
, error
))
98 return len(self
._errors
) > 0
101 return copy(self
._errors
)
104 def ParseAurinfoFromIterable(iterable
, ecatcher
=None):
108 ecatcher
= StderrECatcher()
110 current_package
= None
113 for line
in iterable
:
116 if line
.startswith('#'):
121 current_package
= None
124 if not (line
.startswith('\t') or line
.startswith(' ')):
125 # start of new package
127 key
, value
= map(str.strip
, line
.split('=', 1))
129 ecatcher
.Catch(lineno
, 'unexpected header format in section={:s}'.format(
130 current_package
['pkgname']))
134 current_package
= aurinfo
.SetPkgbase(value
)
135 elif key
== 'pkgname':
136 current_package
= aurinfo
.AddPackage(value
)
138 ecatcher
.Catch(lineno
, 'unexpected new section not starting '
139 'with \'pkgname\' found')
143 if current_package
is None:
144 ecatcher
.Catch(lineno
, 'package attribute found outside of '
149 key
, value
= map(str.strip
, line
.split('=', 1))
151 ecatcher
.Catch(lineno
, 'unexpected attribute format in '
152 'section={:s}'.format(current_package
['pkgname']))
154 if IsMultiValued(key
):
155 if not current_package
.get(key
):
156 current_package
[key
] = []
158 current_package
[key
].append(value
)
160 if not current_package
.get(key
):
161 current_package
[key
] = value
163 ecatcher
.Catch(lineno
, 'overwriting attribute '
164 '{:s}: {:s} -> {:s}'.format(key
,
165 current_package
[key
], value
))
170 def ParseAurinfo(filename
='.AURINFO', ecatcher
=None):
171 with
open(filename
) as f
:
172 return ParseAurinfoFromIterable(f
, ecatcher
)
175 def ValidateAurinfo(filename
='.AURINFO'):
176 ecatcher
= CollectionECatcher()
177 ParseAurinfo(filename
, ecatcher
)
178 errors
= ecatcher
.Errors()
180 print('error on line {:d}: {:s}'.format(error
), file=sys
.stderr
)
184 if __name__
== '__main__':
185 pp
= pprint
.PrettyPrinter(indent
=4)
187 if len(sys
.argv
) == 1:
188 print('error: not enough arguments')
190 elif len(sys
.argv
) == 2:
192 filename
= '.AURINFO'
194 action
, filename
= sys
.argv
[1:3]
196 if action
== 'parse':
197 aurinfo
= ParseAurinfo(filename
)
198 for pkgname
in aurinfo
.GetPackageNames():
199 print(">>> merged package: {:s}".format(pkgname
))
200 pp
.pprint(aurinfo
.GetMergedPackage(pkgname
))
202 elif action
== 'validate':
203 sys
.exit(not ValidateAurinfo(filename
))
205 print('unknown action: {:s}'.format(action
))
208 # vim: set et ts=4 sw=4: