components.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. import shutil
  4. import sys
  5. import re
  6. import os
  7. from optparse import OptionParser
  8. def read_file(path):
  9. with open(path) as f:
  10. return f.read().strip()
  11. def write_file(path, content):
  12. open(path, "w").write(content)
  13. open(path, "a").write("\n")
  14. class Components():
  15. COMPONENT_XML = """<PISI>
  16. <Name>%s</Name>
  17. </PISI>
  18. """
  19. EMPTY_COMPONENT = """ <Component>
  20. <Name>%s</Name>
  21. <LocalName xml:lang="en">FIXME</LocalName>
  22. <Summary xml:lang="en">FIXME</Summary>
  23. <Description xml:lang="en">FIXME</Description>
  24. <Group>FIXME</Group>
  25. <Maintainer>
  26. <Name>PisiLinux Community</Name>
  27. <Email>admins@pisilinux.org</Email>
  28. </Maintainer>
  29. </Component>"""
  30. CBGN = """<PISI>
  31. <Components>"""
  32. CEND = """
  33. </Components>
  34. </PISI>"""
  35. LOCALNAME = ' <LocalName xml:lang="%s">%s</LocalName>\n'
  36. SUMMARY = ' <Summary xml:lang="%s">%s</Summary>\n'
  37. DESCRIPTION = ' <Description xml:lang="%s">%s</Description>\n'
  38. COMPONENT = """
  39. <Component>
  40. <Name>%s</Name>
  41. %s%s%s <Group>%s</Group>
  42. <Maintainer>
  43. <Name>%s</Name>
  44. <Email>%s</Email>
  45. </Maintainer>
  46. </Component>"""
  47. def __init__(self, path):
  48. self.path = path
  49. self.components = []
  50. self.components_xml = "%s/components.xml" % self.path
  51. self.name_ptrn = re.compile("\s*<Name>(.+?)<\/Name>\s*")
  52. self.lnamech_ptrn = re.compile("\s*<LocalName.*?>(.+?)<\/LocalName>\s*")
  53. self.lname_ptrn = re.compile("\s*<LocalName\s+xml:lang\s?=\s?[\'\"](\w\w.*?)[\'\"]\s?>(.+?)<\/LocalName>\s*")
  54. self.summarych_ptrn = re.compile("\s*<Summary.*?>(.+?)<\/Summary>\s*")
  55. self.summary_ptrn = re.compile("\s*<Summary\s+xml:lang\s?=\s?[\'\"](\w\w.*?)[\'\"]\s?>(.+?)<\/Summary>\s*")
  56. self.descch_ptrn = re.compile("\s*<Description.*?>(.+?)<\/Description>\s*")
  57. self.desc_ptrn = re.compile("\s*<Description\s+xml:lang\s?=\s?[\'\"](\w\w.*?)[\'\"]\s?>(.+?)<\/Description>\s*")
  58. self.group_ptrn = re.compile("\s*<Group>(.+?)<\/Group>\s*")
  59. self.email_ptrn = re.compile("\s*<Email>(.+?)<\/Email>\s*")
  60. self.cbgn_ptrn = re.compile("\s*<Component>\s*")
  61. self.cend_ptrn = re.compile("\s*<\/Component>\s*")
  62. self.mbgn_ptrn = re.compile("\s*<Maintainer>\s*")
  63. self.mend_ptrn = re.compile("\s*<\/Maintainer>\s*")
  64. self.csend_ptrn = re.compile("\s*<\/Components>\s*")
  65. def read_components(self):
  66. csfile = read_file(self.components_xml)
  67. cread = False
  68. mread = False
  69. component = {}
  70. for line in csfile.split("\n"):
  71. if re.search(self.cbgn_ptrn, line):
  72. cread = True
  73. component["err"] = []
  74. component["unknown"] = []
  75. component["clname"] = []
  76. component["csummary"] = []
  77. component["cdesc"] = []
  78. continue
  79. elif re.search(self.cend_ptrn, line):
  80. cread = False
  81. self.components.append(component)
  82. component = {}
  83. continue
  84. if cread:
  85. if re.search(self.mbgn_ptrn, line):
  86. mread = True
  87. continue
  88. elif re.search(self.mend_ptrn, line):
  89. mread = False
  90. continue
  91. elif re.search(self.name_ptrn, line):
  92. if mread: component["mname"] = re.sub(self.name_ptrn, r"\1", line)
  93. else: component["cname"] = re.sub(self.name_ptrn, r"\1", line)
  94. elif re.search(self.lnamech_ptrn, line):
  95. if re.search(self.lname_ptrn, line):
  96. component["clname"].append(re.sub(self.lname_ptrn, r"\1:\2", line))
  97. else:
  98. component["err"].append("LocalName")
  99. elif re.search(self.summarych_ptrn, line):
  100. if re.search(self.summary_ptrn, line):
  101. component["csummary"].append(re.sub(self.summary_ptrn, r"\1:\2", line))
  102. else:
  103. component["err"].append("Summary")
  104. elif re.search(self.descch_ptrn, line):
  105. if re.search(self.desc_ptrn, line):
  106. component["cdesc"].append(re.sub(self.desc_ptrn, r"\1:\2", line))
  107. else:
  108. component["err"].append("Description")
  109. elif re.search(self.group_ptrn, line):
  110. component["cgroup"] = re.sub(self.group_ptrn, r"\1", line)
  111. elif re.search(self.email_ptrn, line):
  112. component["memail"] = re.sub(self.email_ptrn, r"\1", line)
  113. else: component["unknown"].append(line)
  114. def sort_components(self):
  115. self.components = sorted(self.components, key=lambda k: k['cname'])
  116. def backup_components(self):
  117. files = os.walk(self.path).next()[2]
  118. last = 0
  119. for f in files:
  120. if f.startswith("components.xml.") and int(f[-3:]) > last: last = int(f[-3:])
  121. last = str(last + 1)
  122. while len(last) < 3: last = "0" + last
  123. shutil.copyfile(self.components_xml, ".".join((self.components_xml, last)))
  124. def write_components(self):
  125. self.sort_components()
  126. cs = ''
  127. for c in self.components:
  128. ln = ""
  129. for i in c["clname"]:
  130. ln += self.LOCALNAME % tuple(i.split(":"))
  131. s = ""
  132. for i in c["csummary"]:
  133. s += self.SUMMARY % tuple(i.split(":"))
  134. d = ""
  135. for i in c["cdesc"]:
  136. d += self.DESCRIPTION % tuple(i.split(":"))
  137. cs += self.COMPONENT % (c["cname"], ln, s, d, c["cgroup"], c["mname"], c["memail"])
  138. self.backup_components()
  139. open(self.components_xml, "w").write(self.CBGN)
  140. open(self.components_xml, "a").write(cs)
  141. open(self.components_xml, "a").write(self.CEND)
  142. open(self.components_xml, "a").write("\n")
  143. def edit(self):
  144. l = []
  145. mcommands = {"q": "quit",
  146. "l": "list components",
  147. "c": "choose component",
  148. "h": "help"}
  149. ecommands = {"mn": "set maintainer name",
  150. "me": "set maintainer email",
  151. "ln": "set local name (lang:name)",
  152. "s": "set summary (lang:summary)",
  153. "d": "set description (lang:description)",
  154. "m": "main menu",
  155. "w": "write",
  156. "h": "help"}
  157. def read_command(m):
  158. opts = mcommands.keys() if m == "m" else ecommands.keys()
  159. return (opts, raw_input(":".join(sorted(opts)) + " > ").split())
  160. def list_components(param, regexp = True):
  161. res = []
  162. num = 0
  163. if not param: param = ".*"
  164. for c in self.components:
  165. if regexp and re.search(param, c["cname"]):
  166. res.append(c["cname"])
  167. print num, c["cname"]
  168. num += 1
  169. elif not regexp and param == c["cname"]:
  170. res.append(c["cname"])
  171. break
  172. return res
  173. def get_component_data(name):
  174. #print self.components
  175. for c in self.components:
  176. if name == c["cname"]: return c
  177. print "Component %s not found!" % name
  178. return False
  179. def print_help(hdict):
  180. for i in iter(sorted(hdict.iteritems())):
  181. print i[0] + "\t" + i[1]
  182. def edit_loop(component):
  183. def update(key, val, data):
  184. d = val.split(":")
  185. if not len(d) == 2: print "Wrong param format! (ln lang:text)"
  186. else:
  187. lexists = -1
  188. for i in data[key]:
  189. if i.split(":")[0] == d[0]:
  190. lexists = data[key].index(i)
  191. break
  192. if lexists > -1: data[key][lexists] = val
  193. else: data[key].append(val)
  194. return data
  195. data = get_component_data(component)
  196. while True:
  197. print "-" * (len(component) + 8)
  198. print "Editing " + component
  199. print "-" * (len(component) + 8)
  200. print "LocalName:"
  201. for i in data["clname"]: print " %s" %i
  202. print "Summary:"
  203. for i in data["csummary"]: print " %s" %i
  204. print "Description:"
  205. for i in data["cdesc"]: print " %s" %i
  206. print "Group:\n %s" % data["cgroup"]
  207. print "Maintainer name:\n %s" % data["mname"]
  208. print "Email:\n %s" % data["memail"]
  209. o, c = read_command("c")
  210. if not c[0] in o: continue
  211. if len(c) == 1: c.append("")
  212. if c[0] == "m": break
  213. elif c[0] == "h": print_help(ecommands)
  214. elif c[0] == "ln": data = update("clname", c[1], data)
  215. elif c[0] == "s": data = update("csummary", c[1], data)
  216. elif c[0] == "d": data = update("cdesc", c[1], data)
  217. elif c[0] == "mn": data["mname"] = c[1]
  218. elif c[0] == "me": data["memail"] = c[1]
  219. elif c[0] == "w": self.write_components()
  220. else: print"%s is not implemented yet" % c[0]
  221. def edit_component(param, l):
  222. try:
  223. int(param)
  224. edit_loop(l[int(param)])
  225. except ValueError:
  226. l = list_components(param, regexp = False)
  227. if not len(l) == 1: print "Component %s not found" % param
  228. else: edit_loop(l[0])
  229. while True:
  230. o, c = read_command("m")
  231. if len(c) == 1: c.append("")
  232. if not c[0] in o: continue
  233. elif c[0] == "q": break
  234. elif c[0] == "l": l = list_components(c[1])
  235. elif c[0] == "c": edit_component(c[1], l)
  236. elif c[0] == "h": print_help(mcommands)
  237. else: print"%s is not implemented yet" % c[0]
  238. def check(self):
  239. cs = []
  240. for root, dirs, files in os.walk(self.path):
  241. c = root.split(self.path)[1][1:].split("/")
  242. if "files" in c or "comar" in c: continue
  243. c = ".".join(c)
  244. component_xml = "%s/component.xml" % root
  245. pspec_xml = "%s/pspec.xml" % root
  246. actions_py = "%s/actions.py" % root
  247. if not os.path.isfile(component_xml) and not os.path.isfile(pspec_xml):
  248. if os.path.isfile(actions_py): print "WARNING: %s not exists" % pspec_xml
  249. else:
  250. is_src_repo = False
  251. for r, d, f in os.walk(root):
  252. if "pspec.xml" in f:
  253. is_src_repo = True
  254. break
  255. if is_src_repo and c:
  256. print "%s not exists. creating..." % component_xml
  257. write_file(component_xml, self.COMPONENT_XML % c)
  258. if os.path.isfile(component_xml) and c: cs.append(c)
  259. mcs = cs[:]
  260. csfile = read_file(self.components_xml)
  261. maintainer = False
  262. new_file = []
  263. for line in csfile.split("\n"):
  264. new_file.append(line)
  265. if re.search(self.mbgn_ptrn, line): maintainer = True
  266. elif re.search(self.mend_ptrn, line): maintainer = False
  267. elif re.search(self.csend_ptrn, line):
  268. for m in mcs:
  269. new_file.insert(-1, self.EMPTY_COMPONENT % m)
  270. if not re.search(self.name_ptrn, line) or maintainer: continue
  271. cn = re.sub(self.name_ptrn, r"\1", line)
  272. if cn in mcs: mcs.pop(mcs.index(cn))
  273. new_file = "\n".join(new_file)
  274. write_file(self.components_xml, new_file)
  275. if __name__ == "__main__":
  276. usage = "Usage: %prog [PATH]"
  277. parser = OptionParser(usage)
  278. parser.add_option("-c", "--check", action="store_true", dest="check", help="fix missing component.xml files and entries in components.xml")
  279. parser.add_option("-e", "--edit", action="store_true", dest="edit", help="edit components.xml")
  280. (options,args) = parser.parse_args()
  281. try:
  282. root = args[0]
  283. except IndexError:
  284. print "Using ./ as PATH"
  285. root = "./"
  286. if root.endswith("/"): root = root[:-1]
  287. cs = Components(root)
  288. if not os.path.isfile(cs.components_xml):
  289. print "%s not exists!" % cs.components_xml
  290. sys.exit(1)
  291. if not os.access(cs.components_xml, os.W_OK):
  292. print "Cannot write to %s" % cs.components_xml
  293. sys.exit(1)
  294. cs.read_components()
  295. if options.check: cs.check()
  296. if options.edit:
  297. cs.edit()