#!/usr/bin/env python # @HEADER # ************************************************************************ # # TriBITS: Tribal Build, Integrate, and Test System # Copyright 2013 Sandia Corporation # # Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, # the U.S. Government retains certain rights in this software. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # 3. Neither the name of the Corporation nor the names of the # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # ************************************************************************ # @HEADER import xml.dom.minidom import os import sys from FindGeneralScriptSupport import * from GeneralScriptSupport import * # # Default file locations # def getDefaultDepsXmlDirectory(rootDir): return os.path.join(rootDir, 'cmake', 'dependencies') def getDefaultDepsXmlInFile(rootDir, projectName): return os.path.join( getDefaultDepsXmlDirectory(rootDir), '%sPackageDependencies.xml' % projectName) def getDefaultDepsHtmlOutFile(rootDir, projectName): return os.path.join( getDefaultDepsXmlDirectory(rootDir), '%sPackageDependenciesTable.html' % projectName) def getDefaultCDashDepsXmlFile(rootDir): return os.path.join( getDefaultDepsXmlDirectory(rootDir), 'CDashSubprojectDependencies.xml') # # Store and manipulate the dependencies # class PackageEmailAddresses: def __init__(self, regression_in): self.regression = regression_in def __str__(self): return "{regression="+self.regression+"}" class PackageDependencies: def __init__(self, packageName_in, packageDir_in, packageType_in, libRequiredDepPackages_in, libOptionalDepPackages_in, testRequiredDepPackages_in, testOptionalDepPackages_in, emailAddresses_in, parentPackage_in ): self.packageName = packageName_in self.packageDir = packageDir_in self.packageType = packageType_in self.packageID = -1 self.libRequiredDepPackages = libRequiredDepPackages_in self.libOptionalDepPackages = libOptionalDepPackages_in self.testRequiredDepPackages = testRequiredDepPackages_in self.testOptionalDepPackages = testOptionalDepPackages_in self.emailAddresses = emailAddresses_in self.parentPackage = parentPackage_in def __str__(self): return "{\n"+\ " packageName="+self.packageName+",\n"+\ " packageID="+str(self.packageID)+",\n"+\ " packageDir="+str(self.packageDir)+",\n"+\ " libRequiredDepPackages="+str(self.libRequiredDepPackages)+",\n"+\ " libOptionalDepPackages="+str(self.libOptionalDepPackages)+",\n"+\ " testRequiredDepPackages="+str(self.testRequiredDepPackages)+",\n"+\ " testOptionalDepPackages="+str(self.testOptionalDepPackages)+" \n"+\ " emailAddresses="+str(self.emailAddresses)+"\n"+\ " parentPackage="+str(self.parentPackage)+"\n"+\ "}\n" def isRequiredDep(dep): return (dep[-1] == 'R') def isDirectDep(dep): return (dep[0] != 'I') def isLibDep(dep): return (dep[0] == 'L' or dep[1] == 'L') # # (dep1, dep2) => newDep # # (*) Required dependencies trump optional dependencies # (*) Direct dependencies trump indirect dependencies # (*) Library dependicnes trump test dependencies # def updatePackageDep(dep1, dep2): #print("\n updatePackageDep("+dep1+", "+dep2+") ...") dep1_required = isRequiredDep(dep1) dep1_direct = isDirectDep(dep1) dep1_lib = isLibDep(dep1) dep2_required = isRequiredDep(dep2) dep2_direct = isDirectDep(dep2) dep2_lib = isLibDep(dep2) selectedDep = False if dep1 == dep2: newDep = dep1 selectedDep = True # Required trumps optional if not selectedDep: if dep1_required and not dep2_required: newDep = dep1 selectedDep = True elif not dep1_required and dep2_required: newDep = dep2 selectedDep = True # Direct trumps indirect if not selectedDep: if dep1_direct and not dep2_direct: newDep = dep1 selectedDep = True elif not dep1_direct and dep2_direct: newDep = dep2 selectedDep = True # Library trumps test if not selectedDep: if dep1_lib and not dep2_lib: newDep = dep1 selectedDep = True elif not dep1_lib and dep2_lib: newDep = dep2 selectedDep = True assert(selectedDep) #print("\n newDep =", newDep) return newDep class DepStats: isDirect = None isRequired = None isTestDepChain = None def __init__(self, isDirect, isRequired, isTestDepChain): self.isDirect = isDirect self.isRequired = isRequired self.isTestDepChain = isTestDepChain class TribitsDependencies: def __init__(self): self.__projectName = None self.__projectBaseDirName = None self.__packagesList = [] self.__packagesNameToID = {} self.__packagesDirToID = {} def setProjectName(self, projectName): self.__projectName = projectName def getProjectName(self): return self.__projectName def setProjectBaseDirName(self, projectBaseDirName): self.__projectBaseDirName = projectBaseDirName def getProjectBaseDirName(self): return self.__projectBaseDirName def addPackageDependencies(self, packageDeps): packageName = packageDeps.packageName packageDir = packageDeps.packageDir self.__packagesList.append(packageDeps) packageDeps.packageID = len(self.__packagesList)-1 self.__packagesNameToID.update( { packageName : packageDeps.packageID } ) self.__packagesDirToID.update( { packageDir : packageDeps.packageID } ) def numPackages(self): return len(self.__packagesList) def getPackagesNamesList(self, onlyTopLevelPackages=True): packagesNamesList = [] for packageDep in self.__packagesList: #print ("packageDep.packageName = "+packageDep.packageName) #print ("packageDep.parentPackage = "+packageDep.parentPackage) if packageDep.parentPackage == "": addPackage = True elif not onlyTopLevelPackages: addPackage = True else: addPackage = False if addPackage: packagesNamesList.append(packageDep.packageName) return packagesNamesList def packageNameToID(self, packageName): return self.__packagesNameToID.get(packageName, -1) def getPackageByID(self, packageID): return self.__packagesList[packageID] def getPackageByName(self, packageName): return self.getPackageByID(self.__packagesNameToID[packageName]) def getPackageByDir(self, packageDir): packageID = self.__packagesDirToID.get(packageDir, -1) #print("\ngetPackageByDir: packageDir="+packageDir+", packageID="+str(packageID)) if packageID >= 0: return self.__packagesList[packageID] return None # Note: Path must contain ending "/" def getPackageNameFromPath(self, fullPath): for packageDep in self.__packagesList: regexFilePath = packageDep.packageDir+"/" #print("\nregexFilePath="+regexFilePath) #print("fullPath="+fullPath) if re.match(regexFilePath, fullPath): #print("MATCH!") return packageDep.packageName return u"" # NOTE: The above loop with match subpackages before it matches # packages because subpackages are listed before packages! # Returns the paraent package name given a test name def getPackageNameFromTestName(self, testName): for packageDep in self.__packagesList: startTestName = packageDep.packageName+"_" #print("\nstartTestName="+startTestName) testNameStartIdx = testName.find(startTestName, 0) if testNameStartIdx == 0: #print("MATCH!") if packageDep.parentPackage: #print("Subpackage match!") return self.getPackageByName(packageDep.parentPackage).packageName # Else, is not a subpackage return packageDep.packageName return u"" def filterPackageNameList(self, inputPackagesList, keepTypesList, verbose=False): if len(inputPackagesList)==1 and inputPackagesList[0]=='': return [] i = 0 outputPackagesList = [] for packageName in inputPackagesList: #print("packageName = " + packageName) if packageName == "ALL_PACKAGES": outputPackagesList.append(packageName) continue packageDep = self.getPackageByName(packageName) packageType = packageDep.packageType #print("packageType = " + packageType) if findInSequence(keepTypesList, packageType) >= 0: outputPackagesList.append(packageName) else: if verbose: print(packageName + " of type " + packageType + " is being excluded because it is not in the valid list of " + "package types [" + ','.join(keepTypesList) + "]") return outputPackagesList def __str__(self): strRep = "" for packageDep in self.__packagesList: strRep += str(packageDep) return strRep def updateDepCell(self, packageRow, packageID, depStats, depCategoryName): currentDepName = packageRow[packageID+1] newDepName = depCategoryName # If we are in a test dependency chain, we must change library # dependencies to test dependencies. if depStats.isTestDepChain: newDepName = 'T'+newDepName[1:] if depStats.isDirect: newDepName = newDepName else: newDepName = "I"+newDepName if not depStats.isRequired: newDepName = newDepName[0:-1]+"O" if currentDepName: #print("\n updateDepCell: depStats.isDirect="+str(depStats.isDirect)+", depStats.isRequired="+str(depStats.isRequired)+", depCategoryName="+depCategoryName) newDepName = updatePackageDep(currentDepName, newDepName) packageRow[packageID+1] = newDepName def updatePackageDepsCategory(self, libsOnly, packageRowID, packageID, depCategory, depCategoryName, depStats, projectDepsTable ): packageRow = projectDepsTable[packageRowID+1] #print("\npackageRow =", packageRow) depList = getattr(self.__packagesList[packageID], depCategory) #print("\ndepList =", depList) for dep in depList: depPackage = self.getPackageByName(dep) #print("\n depPackageName =", depPackage.packageName) dep_i = depPackage.packageID self.updateDepCell(packageRow, dep_i, depStats, depCategoryName) if not depStats.isRequired: isRequiredDep = False elif depCategoryName[-1]=="R": isRequiredDep = True else: isRequiredDep = False childDepStats = DepStats(False, isRequiredDep, depStats.isTestDepChain) self.updatePackageDeps(libsOnly, packageRowID, dep_i, childDepStats, projectDepsTable) def updatePackageDeps(self, libsOnly, packageRowID, packageID, depStats, projectDepsTable ): self.updatePackageDepsCategory(libsOnly, packageRowID, packageID, "libRequiredDepPackages", "LR", depStats, projectDepsTable) self.updatePackageDepsCategory(libsOnly, packageRowID, packageID, "libOptionalDepPackages", "LO", depStats, projectDepsTable) # Only process the test dependencies if we are asked to do so # (i.e. libsOnly=True) or if this is the top-level package. The tests for # dependent packages are not any kind of dependency for tests for the # top-level package. However, we need to record that these are test # dependencies so that any package libraries that get recursed are # recorded as 'ITR' or 'ITO' and not as library dependencies. if not libsOnly and depStats.isDirect: libDepStats = DepStats(True, depStats.isRequired, True) self.updatePackageDepsCategory(False, packageRowID, packageID, "testRequiredDepPackages", "TR", libDepStats, projectDepsTable) self.updatePackageDepsCategory(False, packageRowID, packageID, "testOptionalDepPackages", "TO", libDepStats, projectDepsTable) def createRawTable(self, libsOnly): numPackages = self.numPackages() #print("\nnumPackages =", numPackages) projectDepsTable = [] topRow = [ "Packages" ] topRow.extend(["P%02d"%(i+1) for i in range(numPackages)] ) projectDepsTable.append(topRow) for packageDeps in self.__packagesList: i = packageDeps.packageID row = ["P%02d"%(i+1)+") "+packageDeps.packageName] row.extend(["" for i in range(numPackages)]) projectDepsTable.append(row) for packageDeps in self.__packagesList: #print("\npackageName =", packageDeps.packageName) i = packageDeps.packageID projectDepsTable[i+1][i+1] = "X" self.updatePackageDeps(libsOnly, i, i, DepStats(True, True, False), projectDepsTable) return projectDepsTable def createProjectPackagesNumberedList(self): numPackages = self.numPackages() htmlText = "
Packages: " + \ ", ".join( \ [ "P%02d"%(i+1)+") "+self.__packagesList[i].packageName \ for i in range(self.numPackages())] \ ) + \ "
" return htmlText def createHtmlFromTable(self, rawTable): numPackages = self.numPackages() htmlText = \ ""+topRow[j]+" | \n" htmlText += "Packages | \n" htmlText += "|
"+row[0]+" | \n" for j in range(numPackages): entry = row[j+1] if not entry: entry = "." htmlText += ""+entry+" | \n" htmlText += ""+row[0]+" | \n" htmlText += "
Packages | \n" for j in range(numPackages): htmlText += "P%02d"%(j+1)+" | \n" htmlText += "Packages | \n" htmlText += "
Legend
\n"+\ "\n"+\ self.createHtmlTableLegend(False)+\ "\n"+\ "Legend
\n"+\ "\n"+\ self.createHtmlTableLegend(True) return htmlText def createFullHtmlPage(self): htmlText = \ "\n"+\ "\n"+\ "