circledep-finder 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. #!/usr/bin/python
  2. #
  3. # -*- coding: utf-8 -*-
  4. #
  5. # Copyright (C) 2008, TUBITAK/UEKAE
  6. #
  7. # This program is free software; you can redistribute it and/or modify it under
  8. # the terms of the GNU General Public License as published by the Free
  9. # Software Foundation; either version 2 of the License, or (at your option)
  10. # any later version.
  11. #
  12. # Please read the COPYING file.
  13. #
  14. import sys
  15. import multiprocessing
  16. import urllib2
  17. import bz2
  18. import lzma
  19. import piksemel
  20. import pisi
  21. import pisi.dependency as dependency
  22. from pisi.graph import CycleException
  23. class SourceDB:
  24. def __init__(self, index):
  25. self.__source_nodes = {}
  26. self.__pkgstosrc = {}
  27. doc = piksemel.parseString(index)
  28. self.__source_nodes, self.__pkgstosrc = self.__generate_sources(doc)
  29. def __generate_sources(self, doc):
  30. sources = {}
  31. pkgstosrc = {}
  32. for spec in doc.tags("SpecFile"):
  33. src_name = spec.getTag("Source").getTagData("Name")
  34. sources[src_name] = spec.toString()
  35. for package in spec.tags("Package"):
  36. pkgstosrc[package.getTagData("Name")] = src_name
  37. return sources, pkgstosrc
  38. def has_spec(self, name):
  39. return self.__source_nodes.has_key(name)
  40. def get_spec(self, name):
  41. src = self.__source_nodes[name]
  42. spec = pisi.specfile.SpecFile()
  43. spec.parse(src)
  44. return spec
  45. def list_specs(self):
  46. return self.__source_nodes.keys()
  47. def pkgtosrc(self, name):
  48. return self.__pkgstosrc[name]
  49. def find_circle(sourcedb, A):
  50. G_f = pisi.graph.Digraph()
  51. def get_spec(name):
  52. if sourcedb.has_spec(name):
  53. return sourcedb.get_spec(name)
  54. else:
  55. raise Exception('Cannot find source package: %s' % name)
  56. def get_src(name):
  57. return get_spec(name).source
  58. def add_src(src):
  59. if not str(src.name) in G_f.vertices():
  60. G_f.add_vertex(str(src.name), (src.version, src.release))
  61. def pkgtosrc(pkg):
  62. try:
  63. tmp = sourcedb.pkgtosrc(pkg)
  64. except KeyError, e:
  65. # this is a bad hack but after we hit a problem we need to continue
  66. tmp = "e3"
  67. print "---> borks in ", e
  68. return tmp
  69. B = A
  70. install_list = set()
  71. while len(B) > 0:
  72. Bp = set()
  73. for x in B:
  74. sf = get_spec(x)
  75. src = sf.source
  76. add_src(src)
  77. # add dependencies
  78. def process_dep(dep):
  79. srcdep = pkgtosrc(dep.package)
  80. if not srcdep in G_f.vertices():
  81. Bp.add(srcdep)
  82. add_src(get_src(srcdep))
  83. if not src.name == srcdep: # firefox - firefox-devel thing
  84. G_f.add_edge(src.name, srcdep)
  85. for builddep in src.buildDependencies:
  86. process_dep(builddep)
  87. for pkg in sf.packages:
  88. for rtdep in pkg.packageDependencies:
  89. process_dep(rtdep)
  90. B = Bp
  91. try:
  92. order_build = G_f.topological_sort()
  93. order_build.reverse()
  94. except CycleException, cycle:
  95. return str(cycle)
  96. return ""
  97. def getIndex(uri):
  98. try:
  99. if "://" in uri:
  100. rawdata = urllib2.urlopen(uri).read()
  101. else:
  102. rawdata = open(uri, "r").read()
  103. except IOError:
  104. print "could not fetch %s" % uri
  105. return None
  106. if uri.endswith("bz2"):
  107. data = bz2.decompress(rawdata)
  108. elif uri.endswith("xz") or uri.endswith("lzma"):
  109. data = lzma.decompress(rawdata)
  110. else:
  111. data = rawdata
  112. return data
  113. def processPackage(pkg, sourcesLength, counter):
  114. global sourcedb
  115. sys.stdout.write("\r(%04d/%d) Calculating build dep of %s " % (counter, sourcesLength, pkg))
  116. sys.stdout.flush()
  117. return find_circle(sourcedb, [pkg])
  118. def updateStatus(circleResult):
  119. global cycles
  120. cycles.add(circleResult)
  121. if __name__ == "__main__":
  122. if len(sys.argv) < 2:
  123. print "Usage: circlefinder.py <source repo pisi-index.xml file>"
  124. sys.exit(1)
  125. rawIndex = getIndex(sys.argv[1])
  126. sourcedb = SourceDB(rawIndex)
  127. sources = sourcedb.list_specs()
  128. sourcesLength = len(sources)
  129. counter = 0
  130. global cycles
  131. cycles = set()
  132. pool = multiprocessing.Pool()
  133. for pkg in sources:
  134. counter += 1
  135. pool.apply_async(processPackage, (pkg, sourcesLength, counter), callback=updateStatus)
  136. pool.close()
  137. pool.join()
  138. if len(cycles):
  139. print
  140. for cycle in cycles:
  141. print cycle
  142. else:
  143. print "No circular dep found"