Cloned SEACAS for EXODUS library with extra build files for internal package management.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

693 lines
21 KiB

#!/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 = "<p><b>Packages:</b> " + \
", ".join( \
[ "P%02d"%(i+1)+") "+self.__packagesList[i].packageName \
for i in range(self.numPackages())] \
) + \
"</p>"
return htmlText
def createHtmlFromTable(self, rawTable):
numPackages = self.numPackages()
htmlText = \
"<TABLE BORDER=4>\n"+\
"\n"
for i in range(numPackages+2):
htmlText += "<COL ALIGN=LEFT>\n"
topRow = rawTable[0]
htmlText += "\n<TR>\n"
for j in range(numPackages+1):
htmlText += " <TD><b>"+topRow[j]+"</b></TD>\n"
htmlText += " <TD><b>Packages</b></TD>\n"
htmlText += "</TR>\n"
for package_i in range(numPackages):
row = rawTable[package_i+1]
htmlText += "\n<TR>\n"
htmlText += " <TD><b>"+row[0]+"</b></TD>\n"
for j in range(numPackages):
entry = row[j+1]
if not entry: entry = "."
htmlText += " <TD>"+entry+"</TD>\n"
htmlText += " <TD><b>"+row[0]+"</b></TD>\n"
htmlText += "</TR>\n"
htmlText += "\n<TR>\n"
htmlText += " <TD><b>Packages</b></TD>\n"
for j in range(numPackages):
htmlText += " <TD><b>P%02d"%(j+1)+"</b></TD>\n"
htmlText += " <TD><b>Packages</b></TD>\n"
htmlText += "</TR>\n"
htmlText += "</TABLE>\n"
return htmlText
def createHtmlTableLegend(self, libsOnly):
htmlText =\
"\n"+\
"<ul>\n"+\
"<li> <b>X</b>: Diagonal entry for the package itself\n"+\
"<li> <b>LR</b>: Direct library required dependency\n"+\
"<li> <b>ILR</b>: Indirect library required dependency\n"+\
"<li> <b>LO</b>: Direct library optional dependency\n"+\
"<li> <b>ILO</b>: Indirect library optional dependency\n"
if not libsOnly:
htmlText +=\
"<li> <b>TR</b>: Direct test/example required dependency\n"+\
"<li> <b>ITR</b>: Indirect test/example required dependency\n"+\
"<li> <b>TO</b>: Direct test/example optional dependency\n"+\
"<li> <b>ITO</b>: Indirect test/example optional dependency\n"
htmlText +=\
"</ul>\n"+\
"\n"+\
"NOTE: When more than one type of dependency is present for any cell"+\
" the final selection is determined in the following order:\n"+\
"<ul>\n"+\
"<li> A required dependency trumps an optional dependency\n"+\
"<li> A direct dependency trumps an indirect dependency\n"+\
"<li> A library dependency trumps a test/example dependency\n"+\
"</ul>\n"
return htmlText
def createFullHtmlForTables(self):
packagesListHtml = self.createProjectPackagesNumberedList()
htmlText = \
"<p><huge><b>"+self.getProjectName()+" Test/Example and Library Package Dependencies</b></huge></p>\n"+\
"\n"+\
self.createHtmlFromTable(self.createRawTable(False))+\
"\n"+\
packagesListHtml+"\n"+\
"\n"+\
"<p><b>Legend</b></p>\n"+\
"\n"+\
self.createHtmlTableLegend(False)+\
"\n"+\
"<p><b><huge>"+self.getProjectName()+" Libary-Only Package Dependencies</huge></b></p>\n"+\
"\n"+\
self.createHtmlFromTable(self.createRawTable(True))+\
"\n"+\
packagesListHtml+"\n"+\
"\n"+\
"<p><b>Legend</b></p>\n"+\
"\n"+\
self.createHtmlTableLegend(True)
return htmlText
def createFullHtmlPage(self):
htmlText = \
"<html>\n"+\
"<head>\n"+\
"<title>"+self.getProjectName()+" Package Dependencies</title>\n"+\
"</head>\n"+\
"\n"+\
"<body>\n"+\
"\n"+\
self.createFullHtmlForTables()+\
"\n"+\
"</body>\n"+\
"\n"+\
"</html>\n"
return htmlText
def writeFullHtmlPage(self, htmlFileName):
htmlString = self.createFullHtmlPage()
htmlFile = open(htmlFileName, 'w')
htmlFile.write(htmlString)
htmlFile.close()
#
# CDash stuff
#
def createCDashXML(self):
xmlText = ""
xmlText += "<Project name=\""+self.getProjectName()+"\">\n"
projectBaseDirName = self.getProjectBaseDirName()
numPackages = self.numPackages()
for package_i in range(numPackages):
packageDeps = self.__packagesList[package_i]
packageName = packageDeps.packageName
packagePath = packageDeps.packageDir
if packageDeps.parentPackage == "":
xmlText += (" <SubProject name=\""+packageName+"\">\n")
xmlText += (" <Path>"+packagePath+"</Path>\n")
xmlText += \
" <EmailAddresses>\n"+\
" <Email address=\""+packageDeps.emailAddresses.regression+"\"/>\n"+\
" </EmailAddresses>\n"
xmlText += (" </SubProject>\n")
else:
# We don't want to bother CDash with subpackages. We want CDash testing
# to operate on the package level!
None
# end if
# end for
xmlText += "</Project>\n"
return xmlText
def writeCDashXmlDepsFile(self, xmlDepsFile):
xmlString = self.createCDashXML()
xmlFile = open(xmlDepsFile, 'w')
xmlFile.write(xmlString)
xmlFile.close()
#
# Read in the dependencies from XML
#
def getDependenciesByType(packageEle, typeName):
packageDepsStr = packageEle.getElementsByTagName(typeName)[0].getAttribute('value');
if len(packageDepsStr) == 0:
return []
return packageDepsStr.split(',')
def getSingleEmailAddress(emailEle, emailType):
singleEmailEle = emailEle.getElementsByTagName(emailType)[0]
singleEmailAddress = singleEmailEle.getAttribute('address');
return singleEmailAddress
def getPackageEmailAddresses(packageEle):
emailEle = packageEle.getElementsByTagName("EmailAddresses")[0]
regressionEmail = getSingleEmailAddress(emailEle, "Regression")
return PackageEmailAddresses(regressionEmail)
def getParentPackage(packageEle):
parentPackageEle = packageEle.getElementsByTagName("ParentPackage")[0]
parentPackage = parentPackageEle.getAttribute('value');
return parentPackage
def getProjectDependenciesFromXmlFile(xmlFile):
packageDepXmlDom = xml.dom.minidom.parse(xmlFile)
#print("\npackageDepXmlDom =", dir(packageDepXmlDom))
#print("\npackageDepXmlDom.documentElement =", dir(packageDepXmlDom.documentElement))
projectDependencies = TribitsDependencies()
projectDependencies.setProjectName(
packageDepXmlDom.documentElement.getAttribute('project'))
projectDependencies.setProjectBaseDirName(
packageDepXmlDom.documentElement.getAttribute('baseDirName'))
for ele in packageDepXmlDom.childNodes[0].childNodes:
if ele.nodeType == ele.ELEMENT_NODE:
packageName = ele.getAttribute('name')
packageDir = ele.getAttribute('dir')
packageType = ele.getAttribute('type')
#print("\npackageName =", packageName)
packageDeps = PackageDependencies(packageName, packageDir, packageType,
getDependenciesByType(ele, "LIB_REQUIRED_DEP_PACKAGES"),
getDependenciesByType(ele, "LIB_OPTIONAL_DEP_PACKAGES"),
getDependenciesByType(ele, "TEST_REQUIRED_DEP_PACKAGES"),
getDependenciesByType(ele, "TEST_OPTIONAL_DEP_PACKAGES"),
getPackageEmailAddresses(ele),
getParentPackage(ele)
)
#print("\npackageDeps =", str(packageDeps))
projectDependencies.addPackageDependencies(packageDeps)
return projectDependencies