194 lines
4.6 KiB
Python
Executable File
194 lines
4.6 KiB
Python
Executable File
#!/usr/bin/python
|
|
#
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2008, TUBITAK/UEKAE
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify it under
|
|
# the terms of the GNU General Public License as published by the Free
|
|
# Software Foundation; either version 2 of the License, or (at your option)
|
|
# any later version.
|
|
#
|
|
# Please read the COPYING file.
|
|
#
|
|
|
|
import sys
|
|
import multiprocessing
|
|
|
|
import urllib2
|
|
import bz2
|
|
import lzma
|
|
|
|
import piksemel
|
|
|
|
import pisi
|
|
import pisi.dependency as dependency
|
|
from pisi.graph import CycleException
|
|
|
|
|
|
class SourceDB:
|
|
def __init__(self, index):
|
|
|
|
self.__source_nodes = {}
|
|
self.__pkgstosrc = {}
|
|
|
|
doc = piksemel.parseString(index)
|
|
self.__source_nodes, self.__pkgstosrc = self.__generate_sources(doc)
|
|
|
|
def __generate_sources(self, doc):
|
|
sources = {}
|
|
pkgstosrc = {}
|
|
|
|
for spec in doc.tags("SpecFile"):
|
|
src_name = spec.getTag("Source").getTagData("Name")
|
|
sources[src_name] = spec.toString()
|
|
for package in spec.tags("Package"):
|
|
pkgstosrc[package.getTagData("Name")] = src_name
|
|
|
|
return sources, pkgstosrc
|
|
|
|
def has_spec(self, name):
|
|
return self.__source_nodes.has_key(name)
|
|
|
|
def get_spec(self, name):
|
|
src = self.__source_nodes[name]
|
|
spec = pisi.specfile.SpecFile()
|
|
spec.parse(src)
|
|
return spec
|
|
|
|
def list_specs(self):
|
|
return self.__source_nodes.keys()
|
|
|
|
def pkgtosrc(self, name):
|
|
return self.__pkgstosrc[name]
|
|
|
|
def find_circle(sourcedb, A):
|
|
|
|
G_f = pisi.graph.Digraph()
|
|
|
|
def get_spec(name):
|
|
if sourcedb.has_spec(name):
|
|
return sourcedb.get_spec(name)
|
|
else:
|
|
raise Exception('Cannot find source package: %s' % name)
|
|
|
|
def get_src(name):
|
|
return get_spec(name).source
|
|
|
|
def add_src(src):
|
|
if not str(src.name) in G_f.vertices():
|
|
G_f.add_vertex(str(src.name), (src.version, src.release))
|
|
|
|
def pkgtosrc(pkg):
|
|
try:
|
|
tmp = sourcedb.pkgtosrc(pkg)
|
|
except KeyError, e:
|
|
# this is a bad hack but after we hit a problem we need to continue
|
|
tmp = "e3"
|
|
print "---> borks in ", e
|
|
|
|
return tmp
|
|
|
|
B = A
|
|
|
|
install_list = set()
|
|
|
|
while len(B) > 0:
|
|
Bp = set()
|
|
for x in B:
|
|
sf = get_spec(x)
|
|
src = sf.source
|
|
add_src(src)
|
|
|
|
# add dependencies
|
|
|
|
def process_dep(dep):
|
|
srcdep = pkgtosrc(dep.package)
|
|
if not srcdep in G_f.vertices():
|
|
Bp.add(srcdep)
|
|
add_src(get_src(srcdep))
|
|
if not src.name == srcdep: # firefox - firefox-devel thing
|
|
G_f.add_edge(src.name, srcdep)
|
|
|
|
for builddep in src.buildDependencies:
|
|
process_dep(builddep)
|
|
|
|
for pkg in sf.packages:
|
|
for rtdep in pkg.packageDependencies:
|
|
process_dep(rtdep)
|
|
B = Bp
|
|
|
|
try:
|
|
order_build = G_f.topological_sort()
|
|
order_build.reverse()
|
|
except CycleException, cycle:
|
|
return str(cycle)
|
|
|
|
return ""
|
|
|
|
def getIndex(uri):
|
|
try:
|
|
if "://" in uri:
|
|
rawdata = urllib2.urlopen(uri).read()
|
|
else:
|
|
rawdata = open(uri, "r").read()
|
|
except IOError:
|
|
print "could not fetch %s" % uri
|
|
return None
|
|
|
|
if uri.endswith("bz2"):
|
|
data = bz2.decompress(rawdata)
|
|
elif uri.endswith("xz") or uri.endswith("lzma"):
|
|
data = lzma.decompress(rawdata)
|
|
else:
|
|
data = rawdata
|
|
|
|
return data
|
|
|
|
def processPackage(pkg, sourcesLength, counter):
|
|
global sourcedb
|
|
|
|
sys.stdout.write("\r(%04d/%d) Calculating build dep of %s " % (counter, sourcesLength, pkg))
|
|
sys.stdout.flush()
|
|
|
|
return find_circle(sourcedb, [pkg])
|
|
|
|
def updateStatus(circleResult):
|
|
global cycles
|
|
cycles.add(circleResult)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
if len(sys.argv) < 2:
|
|
print "Usage: circlefinder.py <source repo pisi-index.xml file>"
|
|
sys.exit(1)
|
|
|
|
rawIndex = getIndex(sys.argv[1])
|
|
sourcedb = SourceDB(rawIndex)
|
|
sources = sourcedb.list_specs()
|
|
|
|
sourcesLength = len(sources)
|
|
counter = 0
|
|
|
|
global cycles
|
|
cycles = set()
|
|
|
|
pool = multiprocessing.Pool()
|
|
|
|
for pkg in sources:
|
|
counter += 1
|
|
pool.apply_async(processPackage, (pkg, sourcesLength, counter), callback=updateStatus)
|
|
|
|
pool.close()
|
|
pool.join()
|
|
|
|
if len(cycles):
|
|
print
|
|
for cycle in cycles:
|
|
print cycle
|
|
else:
|
|
print "No circular dep found"
|
|
|
|
|