from Logger import Logger from collections import OrderedDict from constants import constants from sets import Set import copy from SpecData import SPECS class PackageBuildDataGenerator(object): cycleCount=0 def __init__(self,logName=None,logPath=None): if logName is None: logName = "PackageBuildDataGenerator" if logPath is None: logPath = constants.logPath self.logName=logName self.logPath=logPath self.logger=Logger.getLogger(logName,logPath) self.__mapCyclesToPackageList={} self.__mapPackageToCycle={} self.__buildDependencyGraph={} self.__runTimeDependencyGraph={} self.__sortedPackageList=[] self.__sortedBuildDependencyGraph={} def removeDuplicateEntries(self, myList): myListCopy = list(OrderedDict.fromkeys(myList)) return myListCopy def getPackageBuildData(self,listPackages): basePackages = [] for pkg in listPackages: basePackages.append(SPECS.getData().getBasePkg(pkg)) self.__readDependencyGraphAndCyclesForGivenPackages(basePackages) self.__getSortedBuildOrderListForGivenPackages() return self.__mapCyclesToPackageList, self.__mapPackageToCycle, self.__sortedPackageList #todo def findCompleteListOfPackagesRequiredToBuildGivenPackages(self): return list(self.__buildDependencyGraph.keys()) def createSortListForPkg(self,pkg): runTimeDepPkgList=list(set(self.__runTimeDependencyGraph[pkg])) runTimeDepPkgList.append(pkg) sortListForPkg=[] for p in runTimeDepPkgList: basePkg=SPECS.getData().getBasePkg(p) for bPkg in self.__sortedBuildDependencyGraph[basePkg]: if bPkg not in sortListForPkg: sortListForPkg.append(bPkg) return sortListForPkg def getCircularDependentPackages(self,pkg): circularDependentPackages=[] if self.__mapPackageToCycle.has_key(pkg): circularDependentPackages.extend(self.__mapCyclesToPackageList[self.__mapPackageToCycle[pkg]]) circularDependentPackages.remove(pkg) return circularDependentPackages def __getSortedBuildOrderListForGivenPackages(self): alreadyProcessedPackages = set() sortedList = [] completeListPackagesToBuild = self.findCompleteListOfPackagesRequiredToBuildGivenPackages() packageIndexInSortedList = 0 prevSortListLen = 0 while completeListPackagesToBuild: # find next package to process pkg = None index = -1 lenList = len(sortedList) for i in range(lenList): if sortedList[i] in alreadyProcessedPackages: continue pkg = sortedList[i] packageIndexInSortedList = i break if pkg is None: pkg = completeListPackagesToBuild.pop() packageIndexInSortedList = len(sortedList) #creating sort list for package sortListForPkg = self.createSortListForPkg(pkg) #remove any cyclic packages in sortListForPkg if they already #exists in sortedList circularDependentPackages = self.getCircularDependentPackages(pkg) for p in circularDependentPackages: if p in sortedList and p in sortListForPkg: sortListForPkg.remove(p) # insert sort list of package in global sorted list index = packageIndexInSortedList subList = [] if packageIndexInSortedList > 0: subList = sortedList[:packageIndexInSortedList] for p in sortListForPkg: if p not in subList: sortedList.insert(index, p) index = index + 1 alreadyProcessedPackages.add(p) # Remove duplicate entries in sorted list in intervals if (len(sortedList) - prevSortListLen) > 100: self.logger.debug("Removing duplicates in sortedList") sortedList = self.removeDuplicateEntries(sortedList) else: prevSortListLen = len(sortedList) self.logger.debug("Removing duplicates in sorted list") sortedList = self.removeDuplicateEntries(sortedList) self.logger.debug("Sorted list: ") self.logger.debug(sortedList) self.__sortedPackageList = sortedList def __constructBuildAndRunTimeDependencyGraph(self, basePackage): addBuildTimeGraph = True addRunTimeGraph = True if basePackage in self.__buildDependencyGraph: addBuildTimeGraph = False if basePackage in self.__runTimeDependencyGraph: addRunTimeGraph = False nextPackagesToConstructGraph = set() if addBuildTimeGraph: dependentRpmPackages = SPECS.getData().getBuildRequiresForPkg(basePackage) dependentPackages = set() for dependentPkg in dependentRpmPackages: dependentPackages.add(SPECS.getData().getBasePkg(dependentPkg)) self.__buildDependencyGraph[basePackage] = dependentPackages nextPackagesToConstructGraph.update(dependentPackages) if addRunTimeGraph: dependentPackages = set() for rpmPkg in SPECS.getData().getPackagesForPkg(basePackage): dependentRpmPackages = SPECS.getData().getRequiresAllForPkg(rpmPkg) self.__runTimeDependencyGraph[rpmPkg] = copy.copy(set(dependentRpmPackages)) for pkg in dependentRpmPackages: dependentPackages.add(SPECS.getData().getBasePkg(pkg)) nextPackagesToConstructGraph.update(dependentPackages) for pkg in nextPackagesToConstructGraph: self.__constructBuildAndRunTimeDependencyGraph(pkg) def __readDependencyGraphAndCyclesForGivenPackages(self,listPackages): self.logger.info("Reading dependency graph to check for cycles") for pkg in listPackages: self.__constructBuildAndRunTimeDependencyGraph(pkg) packagesToBUild=self.__buildDependencyGraph.keys() for pkg in packagesToBUild: sortedPackageList,circularDependentPackages = self.topologicalSortPackages(self.__buildDependencyGraph,pkg) if len(circularDependentPackages) > 0 : self.logger.error("Found circular dependency") self.logger.error(circularDependentPackages) raise Exception("Build Time Circular Dependency") self.__sortedBuildDependencyGraph[pkg]=sortedPackageList sortedPackageList,circularDependentPackages = self.topologicalSortPackages(self.__runTimeDependencyGraph) if len(circularDependentPackages) > 0 : self.__findCircularDependencies(circularDependentPackages) @staticmethod def _buildDependentPackages(dependencyGraph, package): dependentPackages = {} if package is None: dependentPackages = copy.deepcopy(dependencyGraph) else: depPkgs = set() depPkgs.add(package) while depPkgs: pkg = depPkgs.pop() if pkg in dependentPackages: continue dependentPackages[pkg] = copy.copy(dependencyGraph[pkg]) for depPkg in dependencyGraph[pkg]: depPkgs.add(depPkg) return dependentPackages @staticmethod def _buildDependentOfPackages(dependentPackages): dependentOfPackage = dict() for pkg in dependentPackages: if dependentPackages[pkg]: for depPkg in dependentPackages[pkg]: if depPkg not in dependentOfPackage: dependentOfPackage[depPkg] = {pkg} else: dependentOfPackage[depPkg].add(pkg) return dependentOfPackage @staticmethod def topologicalSortPackages(dependencyGraph, package=None): sortedPackageList = [] noDepPackages = set() dependentPackages = PackageBuildDataGenerator._buildDependentPackages( dependencyGraph, package) dependentOfPackage = PackageBuildDataGenerator._buildDependentOfPackages( dependentPackages) #Find packages with no dependencies and generate dependentof_package edge list for pkg in dependentPackages: if not dependentPackages[pkg]: noDepPackages.add(pkg) while noDepPackages: pkg = noDepPackages.pop() sortedPackageList.append(pkg) if pkg in dependentOfPackage: for childPkg in list(dependentOfPackage[pkg]): dependentOfPackage[pkg].remove(childPkg) dependentPackages[childPkg].remove(pkg) if not dependentPackages[childPkg]: noDepPackages.add(childPkg) # creating circular dependency graph for given dependency graph circularDependencyGraph = {} for pkg in dependentPackages.keys(): if dependentPackages[pkg]: circularDependencyGraph[pkg] = dependentPackages[pkg] #return (non-circular dependent package in sorted order and circular dependent #package list in a dependencyGraph) return sortedPackageList, circularDependencyGraph def __constructDependencyMap(self,cyclicDependencyGraph): self.logger.info("Constructing dependency map from circular dependency graph.....") constructDependencyMap={} listNodes=cyclicDependencyGraph.keys() for node in listNodes: tmpDepNodeList=[] tmpDepNodeList.append(node) depNodeList=[] while len(tmpDepNodeList)!=0: currentNode = tmpDepNodeList.pop() addDepNodeList = cyclicDependencyGraph[currentNode] depNodeList.append(currentNode) for depNode in addDepNodeList: if depNode in depNodeList: continue else: if depNode not in tmpDepNodeList: tmpDepNodeList.append(depNode) depNodeList.remove(node) constructDependencyMap[node]=depNodeList self.logger.info("Dependency Map:") self.logger.info(constructDependencyMap) return constructDependencyMap def __findCircularDependencies(self,cyclicDependencyGraph): self.logger.info("Looking for circular dependencies") if len(cyclicDependencyGraph) == 0: return #step1: construct dependency map from dependency graph constructDependencyMap=self.__constructDependencyMap(cyclicDependencyGraph) #step2: find cycles in dependency map self.logger.info("Finding and adding cycles using constructed dependency map......") cycleCount=0 listNodes=cyclicDependencyGraph.keys() for node in listNodes: listDepPkg=constructDependencyMap[node] cycPkgs=[] if not self.__mapPackageToCycle.has_key(node): for depPkg in listDepPkg: x = constructDependencyMap[depPkg] if node in x: cycPkgs.append(depPkg) if len(cycPkgs) != 0: cycPkgs.append(node) cycleName="cycle"+str(PackageBuildDataGenerator.cycleCount) PackageBuildDataGenerator.cycleCount=PackageBuildDataGenerator.cycleCount+1 for x in cycPkgs: self.__mapPackageToCycle[x]=cycleName self.__mapCyclesToPackageList[cycleName]=cycPkgs self.logger.info("New circular dependency found:") self.logger.info(cycleName+" "+ ",".join(cycPkgs)) cycleCount = cycleCount + 1 if cycleCount > 0 : self.logger.info("Found "+str(cycleCount) + " cycles.") self.logger.info("Successfully added all detected circular dependencies to list.") else: self.logger.info("No circular dependencies found.")