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.

3070 lines
102 KiB

2 years ago
# @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
#
# ToDo:
#
# (*) Create a TaskStatus class and use it to simplify the logic replacing
# the simple bools.
#
#
# General scripting support
#
# NOTE: Included first to check the version of python!
#
from __future__ import print_function
import sys
import os
import time
import pprint
import re
checkinTestBasePath = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
sys.path = [checkinTestBasePath+"/../python_utils"] + sys.path
from GeneralScriptSupport import *
from CheckinTestConstants import *
from TribitsDependencies import getProjectDependenciesFromXmlFile
from TribitsDependencies import getDefaultDepsXmlInFile
from TribitsPackageFilePathUtils import *
from Python2and3 import s
import gitdist
pp = pprint.PrettyPrinter(indent=4)
# Load some default dependencies for some unit tests
projectDependenciesCache = None
def getDefaultProjectDependenices():
return projectDependenciesCache
def getGitRepoDir(srcDir, gitRepoName):
if gitRepoName:
return srcDir+"/"+gitRepoName
return srcDir
def getGitRepoFileExt(gitRepoName):
if gitRepoName:
return "."+gitRepoName
return ""
def getCommonConfigFileName():
return "COMMON.config"
def getProjectDependenciesXmlFileName(projectName):
return projectName+"PackageDependencies.xml"
def getProjectDependenciesXmlGenerateOutputFileName(projectName):
return projectName+"PackageDependencies.generate.out"
def getProjectExtraReposPythonOutFile(projectName):
return projectName+"ExtraRepos.py"
def getTribitsGetExtraReposForCheckinTestOututFile(projectName):
return projectName+"ExtraRepos.generate.out"
def getBuildSpecificConfigFileName(buildTestCaseName):
return buildTestCaseName+".config"
def getInitialPullOutputFileName(gitRepoName):
return "pullInitial"+getGitRepoFileExt(gitRepoName)+".out"
def getInitialExtraPullOutputFileName(gitRepoName):
return "pullInitialExtra"+getGitRepoFileExt(gitRepoName)+".out"
def getInitialPullSuccessFileName():
return "pullInitial.success"
def getModifiedFilesOutputFileName(gitRepoName):
return "modifiedFiles"+getGitRepoFileExt(gitRepoName)+".out"
def getFinalPullOutputFileName(gitRepoName):
return "pullFinal"+getGitRepoFileExt(gitRepoName)+".out"
def getConfigureOutputFileName():
return "configure.out"
def getConfigureSuccessFileName():
return "configure.success"
def getBuildOutputFileName():
return "make.out"
def getBuildSuccessFileName():
return "make.success"
def getTestOutputFileName():
return "ctest.out"
def getTestSuccessFileName():
return "ctest.success"
def getEmailBodyFileName():
return "email.out"
def getEmailSuccessFileName():
return "email.success"
def getFinalCommitBodyFileName(gitRepoName):
return "commitFinalBody"+getGitRepoFileExt(gitRepoName)+".out"
def getFinalCommitOutputFileName(gitRepoName):
return "commitFinal"+getGitRepoFileExt(gitRepoName)+".out"
def getCommitStatusEmailBodyFileName():
return "commitStatusEmailBody.out"
def getPushOutputFileName(gitRepoName):
return "push"+getGitRepoFileExt(gitRepoName)+".out"
def getExtraCommandOutputFileName():
return "extraCommand.out"
def getHostname():
return getCmndOutput("hostname", True)
def getEmailAddressesSpaceString(emailAddressesCommasStr):
emailAddressesList = emailAddressesCommasStr.split(',')
return ' '.join(emailAddressesList)
def performAnyBuildTestActions(inOptions):
if inOptions.doConfigure or inOptions.doBuild \
or inOptions.doTest or inOptions.doAll or inOptions.localDoAll \
:
return True
return False
def performAnyActions(inOptions):
if performAnyBuildTestActions(inOptions) or inOptions.doPull:
return True
return False
def doGenerateOutputFiles(inOptions):
return performAnyActions(inOptions)
def doRemoveOutputFiles(inOptions):
return performAnyActions(inOptions)
def assertAndSetupGit(inOptions):
gitWhich = getCmndOutput("which git", True, False)
if gitWhich == "" or re.match(".+no git.+", gitWhich):
msg = "Error, the 'git' command is not in your path! (" + gitWhich + ")"
print(msg)
raise Exception(msg)
else:
setattr(inOptions, "git", "git")
def assertGitRepoExists(inOptions, gitRepo):
gitRepoDir = getGitRepoDir(inOptions.srcDir, gitRepo.repoDir)
if not os.path.os.path.exists(gitRepoDir):
raise Exception("Error, the specified git repo '"+gitRepo.repoName+"' directory"
" '"+gitRepoDir+"' does not exist!")
def assertPackageNames(optionName, packagesListStr):
if not packagesListStr:
return
for packageName in packagesListStr.split(','):
if getDefaultProjectDependenices().packageNameToID(packageName) == -1:
validPackagesListStr = ""
for i in range(getDefaultProjectDependenices().numPackages()):
if validPackagesListStr != "":
validPackagesListStr += ", "
validPackagesListStr += getDefaultProjectDependenices().getPackageByID(i).packageName
raise Exception("Error, invalid package name "+packageName+" in " \
+optionName+"="+packagesListStr \
+". The valid package names include: "+validPackagesListStr)
def assertExtraBuildConfigFiles(extraBuilds):
if not extraBuilds:
return
for extraBuild in extraBuilds.split(','):
extraBuildConfigFile = extraBuild+".config"
if not os.path.exists(extraBuildConfigFile):
raise Exception("Error, the extra build configuration file " \
+extraBuildConfigFile+" does not exit!")
class GitdistOptions:
def __init__(self, useGit):
self.useGit = useGit
# Create a matching version of gitdist.getCmndOutout
def getCmndOutputForGitDist(cmnd, rtnCode=False):
return getCmndOutput(cmnd, rtnCode=rtnCode, throwOnError=False)
def getRepoStats(inOptions, gitRepo_inout):
gitRepoDir = getGitRepoDir(inOptions.srcDir, gitRepo_inout.repoDir)
gitdistOptions = GitdistOptions(inOptions.git)
pwd = os.getcwd()
try:
os.chdir(gitRepoDir)
gitRepo_inout.gitRepoStats = \
gitdist.getRepoStats(gitdistOptions, getCmndOutputForGitDist)
finally:
os.chdir(pwd)
def getReposStats(inOptions, tribitsGitRepos):
hasChangesToPush = False
repoStatTable = gitdist.RepoStatTable()
repoIdx = 0
for gitRepo in tribitsGitRepos.gitRepoList():
getRepoStats(inOptions, gitRepo)
if gitRepo.gitRepoStats.numCommitsInt() > 0:
hasChangesToPush = True
repoStatTableDirName = getRepoStatTableDirName(inOptions, gitRepo.repoDir)
repoStatTable.insertRepoStat(repoStatTableDirName, gitRepo.gitRepoStats, repoIdx)
repoIdx += 1
print(gitdist.createTable(repoStatTable.getTableData()))
return hasChangesToPush
# NOTE: Above, we could just call 'gitdist dist-repo-status' but by
# printing the table here with the actually gitRepoStat data, we ensure
# that it gets collected correctly and that the selection of repos is
# exactly the same.
def assertRepoHasBranchAndTrackingBranch(inOptions, gitRepo):
repoName = gitRepo.repoName
if repoName == "":
repoNameEntry = "base repo"
else:
repoNameEntry = "repo '"+repoName+"'"
gitRepoStats = gitRepo.gitRepoStats
if gitRepoStats.branch == "HEAD":
raise Exception("Error, the "+repoNameEntry+" is in a detached head state which" \
" is not allowed in this case!")
if gitRepoStats.trackingBranch == "":
raise Exception("Error, the "+repoNameEntry+" is not on a tracking branch which" \
" is not allowed in this case!")
def pushToTrackingBranchArgs(gitRepo):
(repo, trackingbranch) = gitRepo.gitRepoStats.trackingBranch.split("/")
return repo+" "+gitRepo.gitRepoStats.branch+":"+trackingbranch
def didSinglePullBringChanges(pullOutFileFullPath):
pullOutFileStr = readStrFromFile(pullOutFileFullPath)
#print("\npullOutFileStr:\n" + pullOutFileStr)
alreadyUpToDateIdx = pullOutFileStr.find("Already up-to-date")
#print("alreadyUpToDateIdx = "+str(alreadyUpToDateIdx))
return alreadyUpToDateIdx == -1
def executePull(gitRepo, inOptions, baseTestDir, outFile, pullFromRepo=None,
doRebase=False)\
:
cmnd = inOptions.git+" pull"
if pullFromRepo:
repoSpaceBranch = pullFromRepo.remoteRepo+" "+pullFromRepo.remoteBranch
print("\nPulling in updates to local repo '" + gitRepo.repoName + "'" +
" from '" + repoSpaceBranch + "' ...\n")
cmnd += " " + repoSpaceBranch
else:
print("\nPulling in updates from '" + gitRepo.gitRepoStats.trackingBranch +
"' ...")
# NOTE: If you do 'git pull <remote> <branch>', then the list of locally
# modified files will be wrong. I don't know why this is but if instead
# you do a raw 'git pull', then the right list of files shows up.
if doRebase:
cmnd += " && "+inOptions.git+" rebase "+gitRepo.gitRepoStats.trackingBranch
outFileFullPath = os.path.join(baseTestDir, outFile)
(pullRtn, pullTimings) = echoRunSysCmnd( cmnd,
workingDir=getGitRepoDir(inOptions.srcDir, gitRepo.repoDir),
outFile=outFileFullPath,
timeCmnd=True, returnTimeCmnd=True, throwExcept=False
)
if pullRtn == 0:
pullGotChanges = didSinglePullBringChanges(outFileFullPath)
if pullGotChanges:
print("\n ==> '" + gitRepo.repoName + "': Pulled changes from this repo!")
else:
print("\n ==> '" + gitRepo.repoName +
"': Did not pull any changes from this repo!")
else:
print("\n ==> '" + gitRepo.repoName + "': Pull failed!")
pullGotChanges = False
return (pullRtn, pullTimings, pullGotChanges)
class Timings:
def __init__(self):
self.pull = -1.0
self.configure = -1.0
self.build = -1.0
self.test = -1.0
def deepCopy(self):
copyTimings = Timings()
copyTimings.pull = self.pull
copyTimings.configure = self.configure
copyTimings.build = self.build
copyTimings.test = self.test
return copyTimings
def totalTime(self):
tt = 0.0
if self.pull > 0: tt += self.pull
if self.configure > 0: tt += self.configure
if self.build > 0: tt += self.build
if self.test > 0: tt += self.test
return tt
class GitRepo:
def __init__(self, repoName, repoDir='', repoType='GIT', repoHasPackages=True,
repoPrePost='POST' \
):
self.repoName = repoName
if repoDir:
self.repoDir = repoDir
else:
self.repoDir = repoName
self.repoType = repoType
self.repoHasPackages = repoHasPackages
self.repoPrePost = repoPrePost
self.hasChanges = False
self.gitRepoStats = None
if (self.repoName and self.repoHasPackages) and (self.repoName != self.repoDir):
raise Exception("ERROR! For extra repo '"+repoName+"', if repoHasPackages==True" \
+" then repoDir must be same as repo name, not '"+repoDir+"'!")
if self.repoType != 'GIT':
raise Exception("ERROR! For extra repo '"+repoName+"', the repo type" \
+" '"+self.repoType+"' is not supported by the checkin-test.py script, only 'GIT'!")
def __str__(self):
return "GitRepo{repoName='"+self.repoName+"'" \
+", repoDir='"+str(self.repoDir)+"'" \
+", repoType='"+str(self.repoType)+"'" \
+", repoHasPackages="+str(self.repoHasPackages) \
+", repoPrePost="+str(self.repoPrePost) \
+", hasChanges="+str(self.hasChanges) \
+"}"
def __rep__(self):
return str(self)
def getExtraReposFilePath(inOptions):
if inOptions.extraReposFile == "project":
extraReposFile = inOptions.srcDir+"/cmake/ExtraRepositoriesList.cmake"
else:
extraReposFile = inOptions.extraReposFile
return extraReposFile
def getExtraReposPyFileFromCmakeFile(inOptions, extraReposPythonOutFile, \
consoleOutputFile = None, verbose=False \
):
extraReposFile = getExtraReposFilePath(inOptions)
printConsoleOutputFile = False
if not consoleOutputFile:
# Need to send output to a file so that you can read it back in again and
# then print it out using the 'print' statement. This is needed so that
# the output shows up in both the STDOUT and the checkin-test.out log
# files!
consoleOutputFile = "TribitsGetExtraReposForCheckinTest.out"
printConsoleOutputFile = True
cmnd = "\""+inOptions.withCmake+"\""+ \
" -DSUPPRESS_PRINT_VAR_OUTPUT=TRUE" \
" -DPROJECT_SOURCE_DIR="+inOptions.srcDir+ \
" -DTRIBITS_BASE_DIR="+inOptions.tribitsDir+ \
" -DEXTRA_REPOS_FILE="+extraReposFile+ \
" -DENABLE_KNOWN_EXTERNAL_REPOS_TYPE="+inOptions.extraReposType+ \
" -DEXTRA_REPOS="+inOptions.extraRepos+ \
" -DEXTRA_REPOS_PYTHON_OUT_FILE="+extraReposPythonOutFile
if inOptions.ignoreMissingExtraRepos:
cmnd += " -DIGNORE_MISSING_EXTRA_REPOSITORIES=TRUE"
cmnd += \
" -P "+inOptions.tribitsDir+"/ci_support/TribitsGetExtraReposForCheckinTest.cmake"
try:
echoRunSysCmnd(cmnd, throwExcept=True, timeCmnd=True, outFile=consoleOutputFile, \
verbose=verbose)
finally:
if printConsoleOutputFile:
print("\n" + open(consoleOutputFile, 'r').read())
def translateExtraReposPyToDictGitRepo(extraReposPyDict):
repoName = extraReposPyDict['NAME']
repoDir = extraReposPyDict['DIR']
repoType = extraReposPyDict['REPOTYPE']
repoHasPackages = (extraReposPyDict['HASPKGS'] == 'HASPACKAGES')
repoPrePost = extraReposPyDict['PREPOST']
return GitRepo(repoName, repoDir, repoType, repoHasPackages, repoPrePost)
class TribitsGitRepos:
def __init__(self):
self.reset()
self.__insertMainRepo()
self.__initFinalize()
def initFromCommandlineArguments(self, inOptions, consoleOutputFile=None, verbose=True):
self.reset()
self.__insertMainRepo()
if inOptions.extraRepos!="" and \
(inOptions.extraReposFile=="" or inOptions.extraReposType=="") \
:
# Just use the listed set of extra repos with no checking
for extraRepoName in inOptions.extraRepos.split(","):
extraRepo = GitRepo(extraRepoName)
self.__gitRepoList.append(extraRepo)
elif inOptions.extraReposFile!="" and inOptions.extraReposType!="":
# Read in the extra repos from file and assert or ignore missing repos, etc.
extraReposPythonOutFile = getProjectExtraReposPythonOutFile(inOptions.projectName)
getExtraReposPyFileFromCmakeFile(inOptions, extraReposPythonOutFile, \
consoleOutputFile=consoleOutputFile, verbose=verbose)
extraReposPyTxt = readStrFromFile(extraReposPythonOutFile)
extraReposPyList = eval(extraReposPyTxt)
for extraRepoDict in extraReposPyList:
extraRepo = translateExtraReposPyToDictGitRepo(extraRepoDict)
self.__gitRepoList.append(extraRepo)
self.__initFinalize()
def gitRepoList(self):
return self.__gitRepoList
def tribitsPreRepoNamesList(self):
return self.__tribitsPreRepoNamesList
def numTribitsPreRepos(self):
return len(self.__tribitsPreRepoNamesList)
def tribitsExtraRepoNamesList(self):
return self.__tribitsExtraRepoNamesList
def numTribitsExtraRepos(self):
return len(self.__tribitsExtraRepoNamesList)
def tribitsAllExtraRepoNamesList(self):
return self.__tribitsAllExtraRepoNamesList
def numTribitsAllExtraRepos(self):
return len(self.__tribitsAllExtraRepoNamesList)
def __str__(self):
strRep = "{\n"
strRep += " gitRepoList = " + self.__printReposList(self.__gitRepoList)
strRep += " tribitsPreRepoNamesList = "+str(self.__tribitsPreRepoNamesList)+"\n"
strRep += " tribitsExtraRepoNamesList = "+str(self.__tribitsExtraRepoNamesList)+"\n"
strRep += " tribitsAllExtraRepoNamesList = "+str(self.__tribitsAllExtraRepoNamesList)+"\n"
strRep += " }\n"
return strRep
def reset(self):
self.__gitRepoList = []
self.__tribitsPreRepoNamesList = []
self.__tribitsExtraRepoNamesList = []
self.__tribitsAllRepoNamesList = []
return self
# Private
def __insertMainRepo(self):
mainRepo = GitRepo("")
self.__gitRepoList.append(mainRepo)
def __printReposList(self, reposList):
strRep = "[\n"
for gitRepo in reposList:
strRep += (" " + str(gitRepo) + ",\n")
strRep += " ]\n"
return strRep
def __initFinalize(self):
self.__tribitsPreRepoNamesList = []
self.__tribitsExtraRepoNamesList = []
self.__tribitsAllExtraRepoNamesList = []
for gitRepo in self.__gitRepoList:
if gitRepo.repoName and gitRepo.repoHasPackages:
self.__tribitsAllExtraRepoNamesList.append(gitRepo.repoName)
if gitRepo.repoPrePost == 'PRE':
self.__tribitsPreRepoNamesList.append(gitRepo.repoName)
else:
self.__tribitsExtraRepoNamesList.append(gitRepo.repoName)
def createAndGetProjectDependencies(inOptions, baseTestDir, tribitsGitRepos):
if tribitsGitRepos.numTribitsPreRepos() > 0:
print("\nPulling in packages from PRE extra repos: " +
','.join(tribitsGitRepos.tribitsPreRepoNamesList()) + " ...")
if tribitsGitRepos.numTribitsExtraRepos() > 0:
print("\nPulling in packages from POST extra repos: " +
','.join(tribitsGitRepos.tribitsExtraRepoNamesList()) + " ...")
for gitRepo in tribitsGitRepos.gitRepoList():
assertGitRepoExists(inOptions, gitRepo)
projectDepsXmlFile = baseTestDir+"/"\
+getProjectDependenciesXmlFileName(inOptions.projectName)
if not inOptions.skipDepsUpdate:
# There are extra repos so we need to build a new list of Project
# packages to include the add-on packages.
cmakeArgumentList = [
"cmake",
"-DPROJECT_NAME=%s" % inOptions.projectName,
cmakeScopedDefine(inOptions.projectName, "TRIBITS_DIR", inOptions.tribitsDir),
"-DPROJECT_SOURCE_DIR="+inOptions.srcDir,
cmakeScopedDefine(inOptions.projectName, "PRE_REPOSITORIES", "\""+\
';'.join(tribitsGitRepos.tribitsPreRepoNamesList())+"\""),
cmakeScopedDefine(inOptions.projectName, "EXTRA_REPOSITORIES", "\""+\
';'.join(tribitsGitRepos.tribitsExtraRepoNamesList())+"\""),
cmakeScopedDefine(inOptions.projectName, "DEPS_XML_OUTPUT_FILE", projectDepsXmlFile),
"-P %s/ci_support/TribitsDumpDepsXmlScript.cmake" % inOptions.tribitsDir,
]
cmnd = ' '.join(cmakeArgumentList)
echoRunSysCmnd(cmnd,
workingDir=baseTestDir,
outFile=baseTestDir+"/"\
+getProjectDependenciesXmlGenerateOutputFileName(inOptions.projectName),
timeCmnd=True)
else:
print("\nSkipping update of dependencies XML file on request!")
projectDepsXmlFileOverride = os.environ.get("CHECKIN_TEST_DEPS_XML_FILE_OVERRIDE")
if projectDepsXmlFileOverride:
print("\nprojectDepsXmlFileOverride=" + projectDepsXmlFileOverride)
projectDepsXmlFile = projectDepsXmlFileOverride
global projectDependenciesCache
projectDependenciesCache = getProjectDependenciesFromXmlFile(projectDepsXmlFile)
class RemoteRepoAndBranch:
def __init__(self, remoteRepo, remoteBranch):
self.remoteRepo = remoteRepo
self.remoteBranch = remoteBranch
def __str__(self):
return "RemoteRepoAndBranch{repoRepo='"+str(self.remoteRepo)+"'" \
+", remoteBranch='"+str(self.remoteBranch)+"'" \
+"}"
class RepoExtraRemotePulls:
def __init__(self, gitRepo, remoteRepoAndBranchList):
self.gitRepo = gitRepo
self.remoteRepoAndBranchList = remoteRepoAndBranchList
def getLocalRepoRemoteRepoAndBranchFromExtraPullArg(extraPullArg):
extraPullArgArray = extraPullArg.split(':')
localRepo = ""
remoteRepo = ""
remoteBranch = ""
matchesAllRepos = False
extraPullArgArray_len = len(extraPullArgArray)
if extraPullArgArray_len == 3:
localRepo = extraPullArgArray[0]
remoteRepo = extraPullArgArray[1]
remoteBranch = extraPullArgArray[2]
elif extraPullArgArray_len == 2:
remoteRepo = extraPullArgArray[0]
remoteBranch = extraPullArgArray[1]
matchesAllRepos = True
else:
raise ValueError(
"Error, the --extra-pull-from arg '"+extraPullArg+"' is not of the form" \
+ " <localreponame>:<remoterepo>:<remotebranch>!")
if remoteRepo == "":
raise ValueError(
"Error, the --extra-pull-from arg '"+extraPullArg+"' has an empty <remoterepo>" \
+ " field in <localreponame>:<remoterepo>:<remotebranch>!")
elif remoteBranch == "":
raise ValueError(
"Error, the --extra-pull-from arg '"+extraPullArg+"' has an empty <remotebranch>" \
+ " field in <localreponame>:<remoterepo>:<remotebranch>!")
return (localRepo, remoteRepo, remoteBranch, matchesAllRepos)
def matchExtraRepoLocalRepoMatchLocalRepo(repoName, extraRepoLocalRepoName):
if repoName == extraRepoLocalRepoName:
return True
elif repoName == "" and extraRepoLocalRepoName == "BASE_REPO":
return True
return False
def parseExtraPullFromArgs(gitRepoList, extraPullFromArgs):
# Initialize an empty set of extra pulls
repoExtraRemotePullsList = []
for gitRepo in gitRepoList:
repoExtraRemotePullsList.append(
RepoExtraRemotePulls(gitRepo, []))
# Parse the arguments and fill in the remote repos and branches
if extraPullFromArgs:
for extraPullFromArg in extraPullFromArgs.split(","):
(localRepo, remoteRepo, remoteBranch, matchesAllRepos) = \
getLocalRepoRemoteRepoAndBranchFromExtraPullArg(extraPullFromArg)
if matchesAllRepos:
for repoExtraRemotePulls in repoExtraRemotePullsList:
repoExtraRemotePulls.remoteRepoAndBranchList.append(
RemoteRepoAndBranch(remoteRepo, remoteBranch) )
else:
for repoExtraRemotePulls in repoExtraRemotePullsList:
if repoExtraRemotePulls.gitRepo.repoName == localRepo:
repoExtraRemotePulls.remoteRepoAndBranchList.append(
RemoteRepoAndBranch(remoteRepo, remoteBranch) )
return repoExtraRemotePullsList
class BuildTestCase:
def __init__(self, name, runBuildTestCase, validPackageTypesList,
isDefaultBuild, skipCaseIfNoChangeFromDefaultEnables,
extraCMakeOptions, buildIdx \
):
self.name = name
self.runBuildTestCase = runBuildTestCase
self.validPackageTypesList = validPackageTypesList
self.isDefaultBuild = isDefaultBuild
self.skipCaseIfNoChangeFromDefaultEnables = skipCaseIfNoChangeFromDefaultEnables
self.extraCMakeOptions = extraCMakeOptions
self.skippedConfigureDueToNoEnables = False
self.buildIdx = buildIdx
self.timings = Timings()
def setBuildTestCaseInList(buildTestCaseList_inout,
name, runBuildTestCase, validPackageTypesList, isDefaultBuild,
skipCaseIfNoChangeFromDefaultEnables, extraCMakeOptions \
):
buildTestCaseList_inout.append(
BuildTestCase(name, runBuildTestCase, validPackageTypesList, isDefaultBuild,
skipCaseIfNoChangeFromDefaultEnables, extraCMakeOptions,
len(buildTestCaseList_inout) ) )
def writeDefaultCommonConfigFile():
commonConfigFileName = getCommonConfigFileName()
if os.path.exists(commonConfigFileName):
print("\nThe file " + commonConfigFileName + " already exists!")
else:
print("\nCreating a default skeleton file " + commonConfigFileName + " ...")
commonConfigFileStr = \
"# Fill in the minimum CMake options that are needed to build and link\n" \
"# that are common to all builds such as the following:\n" \
"#\n" \
"#-DCMAKE_VERBOSE_MAKEFILE=ON\n" \
"#-DBUILD_SHARED_LIBS=ON\n" \
"#\n" \
"# NOTE: Please do not add any options here that would select what packages\n" \
"# get enabled or disabled.\n"
writeStrToFile(commonConfigFileName, commonConfigFileStr)
def writeDefaultBuildSpecificConfigFile(buildTestCaseName):
serialOrMpi = buildTestCaseName.split('_')[0]
buildSpecificConfigFileName = getBuildSpecificConfigFileName(buildTestCaseName)
if os.path.exists(buildSpecificConfigFileName):
print("\nThe file " + buildSpecificConfigFileName + " already exists!")
else:
# ToDo: Get rid of these! These are too specific!
print("\nCreating a default skeleton file " + buildSpecificConfigFileName +
" ...")
buildSpecificConfigFileStr = \
"# Fill in the minimum CMake options that are needed to build and link\n" \
"# that are specific to the "+serialOrMpi+" build such as:\n" \
"#\n" \
"#-DBUILD_SHARED_LIBS=ON\n" \
"#\n" \
"# NOTE: Please do not add any options here that would change what packages\n" \
"# or TPLs get enabled or disabled.\n"
writeStrToFile(buildSpecificConfigFileName, buildSpecificConfigFileStr)
def assertNoIllegalEnables(projectName, fileName, cmakeOption):
reTPlEnable = re.compile(r"-DTPL_ENABLE_.+")
reProjectEnableOn = re.compile(r"-D%s_ENABLE_[a-zA-Z]+.+=ON" % projectName)
success = True
if reTPlEnable.match(cmakeOption):
print(" ERROR: Illegal TPL enable " + cmakeOption + " in " + fileName+"!")
success = False
elif reProjectEnableOn.match(cmakeOption):
print(" ERROR: Illegal enable " + cmakeOption + " in " + fileName + "!")
success = False
return success
def readAndAppendCMakeOptions(
projectName,
fileName,
cmakeOptions_inout,
assertNoIllegalEnablesBool):
success = True
if not os.path.exists(fileName):
return
print("\nAppending options from " + fileName + ":")
cmakeOptionsFile = open(fileName, 'r')
for line in cmakeOptionsFile:
if line[0] != '#':
cmakeOption = line.strip()
if cmakeOption == "": continue
print(" Appending: " + cmakeOption)
if assertNoIllegalEnablesBool:
if not assertNoIllegalEnables(projectName, fileName, cmakeOption):
success = False
cmakeOptions_inout.append(cmakeOption)
return success
reModifiedFiles = re.compile(r"^[MAD]\t(.+)$")
def getCurrentDiffOutput(gitRepo, inOptions, baseTestDir):
if gitRepo.gitRepoStats.numCommitsInt() > 0:
echoRunSysCmnd(
inOptions.git+" diff --name-status "+gitRepo.gitRepoStats.trackingBranch,
workingDir=getGitRepoDir(inOptions.srcDir, gitRepo.repoDir),
outFile=os.path.join(baseTestDir, getModifiedFilesOutputFileName(gitRepo.repoName)),
timeCmnd=True
)
def repoHasModifiedFiles(gitRepo, baseTestDir):
if gitRepo.gitRepoStats.numCommitsInt() > 0:
modifiedFilesStr = readStrFromFile(
baseTestDir+"/"+getModifiedFilesOutputFileName(gitRepo.repoName))
if modifiedFilesStr:
return True
return False
def getCurrentDiffOutputAndLogModified(inOptions, gitRepo, baseTestDir):
getCurrentDiffOutput(gitRepo, inOptions, baseTestDir)
gitRepo.hasChanges = repoHasModifiedFiles(gitRepo, baseTestDir)
if gitRepo.hasChanges:
print("\n ==> '" + gitRepo.repoName + "': Has modified files!")
else:
print("\n ==> '" + gitRepo.repoName + "': Does *not* have any modified " +
"files!")
def extractPackageEnablesFromChangeStatus(changedFileDiffOutputStr, inOptions_inout,
gitRepo, enablePackagesList_inout, verbose=True,
projectDependenciesLocal=None, projectChangeLogic=DefaultProjectCiFileChangeLogic() ) \
:
if not projectDependenciesLocal:
projectDependenciesLocal = getDefaultProjectDependenices()
modifiedFilesList = extractFilesListMatchingPattern(
changedFileDiffOutputStr.splitlines(), reModifiedFiles )
for modifiedFileFullPath in modifiedFilesList:
# Only look for global rebuild files in the master repo (not in extra repos)
if gitRepo.repoName == '' and \
projectChangeLogic.isGlobalBuildFileRequiringGlobalRebuild(modifiedFileFullPath) \
:
if inOptions_inout.enableAllPackages == 'auto':
if verbose:
print("\nModified file: '" + modifiedFileFullPath + "'\n" +
" => Enabling all " + inOptions_inout.projectName +
" packages!")
inOptions_inout.enableAllPackages = 'on'
if gitRepo.repoDir:
modifiedFileFullPath = gitRepo.repoDir+"/"+modifiedFileFullPath
#print("\nmodifiedFileFullPath =", modifiedFileFullPath)
packageName = getPackageNameFromPath(projectDependenciesLocal, modifiedFileFullPath)
if packageName and findInSequence(enablePackagesList_inout, packageName) == -1:
if verbose:
print("\nModified file: '" + modifiedFileFullPath + "'\n" +
" => Enabling '" + packageName + "'!")
enablePackagesList_inout.append(packageName)
def createConfigureFile(cmakeOptions, baseCmnd, srcDir, configFileName):
doConfigStr = ""
doConfigStr += \
baseCmnd+ " \\\n"
for opt in cmakeOptions:
doConfigStr += opt + " \\\n"
doConfigStr += \
"\"$@\""
if srcDir:
doConfigStr += " \\\n"+srcDir
doConfigStr += "\n"
writeStrToFile(configFileName, doConfigStr)
echoRunSysCmnd('chmod a+x '+configFileName)
def formatMinutesStr(timeInMinutes):
return ("%.2f" % timeInMinutes) + " min"
def getStageStatus(stageName, stageDoBool, stagePassed, stageTiming):
stageStatusStr = stageName + ": "
if stageDoBool:
if stagePassed:
stageStatusStr += "Passed"
else:
stageStatusStr += "FAILED"
stageStatusStr += " ("+formatMinutesStr(stageTiming)+")"
else:
stageStatusStr += "Not Performed"
stageStatusStr += "\n"
return stageStatusStr
def getTotalTimeBeginStr(buildTestCaseName):
return "Total time for "+buildTestCaseName
def getTotalTimeLineStr(buildTestCaseName, timeInMin):
return getTotalTimeBeginStr(buildTestCaseName)+" = "+formatMinutesStr(timeInMin)
def getTimeInMinFromTotalTimeLine(buildTestCaseName, totalTimeLine):
if not totalTimeLine:
return -1.0
m = re.match(getTotalTimeBeginStr(buildTestCaseName)+r" = (.+) min", totalTimeLine)
if m and m.groups():
return float(m.groups()[0])
else:
return -1.0
reCtestFailTotal = re.compile(r".+, ([0-9]+) tests failed out of ([0-9]+)")
def analyzeResultsSendEmail(inOptions, buildTestCase,
enabledPackagesList, cmakeOptions, startingTime, timings ) \
:
buildTestCaseName = buildTestCase.name
print("")
print("E.1) Determine what passed and failed ...")
print("")
success = False
# Determine if the pull passed
pullPassed = None
pullOutputExists = False
if inOptions.doPull:
if os.path.exists("../"+getInitialPullOutputFileName("")):
pullOutputExists = True
if os.path.exists("../"+getInitialPullSuccessFileName()):
print("\nThe pull passed!\n")
pullPassed = True
elif pullOutputExists:
print("\nThe pull FAILED!\n")
pullPassed = False
else:
print("\nThe pull was never attempted!\n")
pullPassed = False
else:
print("\nThe pull step was not performed!\n")
# Determine if the configured passed
configurePassed = None
configureOutputExists = False
if inOptions.doConfigure:
if os.path.exists(getConfigureOutputFileName()):
configureOutputExists = True
if os.path.exists(getConfigureSuccessFileName()):
print("\nThe configure passed!\n")
configurePassed = True
elif configureOutputExists:
print("\nThe configure FAILED!\n")
configurePassed = False
else:
print("\nThe configure was never attempted!\n")
configurePassed = False
else:
print("\nThe configure step was not performed!\n")
# Determine if the build passed
buildPassed = None
buildOutputExists = False
if inOptions.doBuild:
if os.path.exists(getBuildOutputFileName()):
buildOutputExists = True
if os.path.exists(getBuildSuccessFileName()):
print("\nThe build passed!\n")
buildPassed = True
elif buildOutputExists:
print("\nThe build FAILED!\n")
buildPassed = False
else:
print("\nThe build was never attempted!\n")
buildPassed = False
else:
print("\nThe build step was not performed!\n")
# Determine if the tests passed
testsPassed = None
testOutputExists = False
if inOptions.doTest:
if os.path.exists(getTestOutputFileName()):
testOutputExists = True
if not testOutputExists:
print("\nThe tests were never even run!\n")
testsPassed = False
else: # testOutputExists
testResultsLine = getCmndOutput("grep 'tests failed out of' "+getTestOutputFileName(),
True, False)
print("testResultsLine = '" + testResultsLine + "'")
reCtestFailTotalMatch = reCtestFailTotal.match(testResultsLine)
if reCtestFailTotalMatch:
numFailedTests = int(reCtestFailTotalMatch.group(1))
numTotalTests = int(reCtestFailTotalMatch.group(2))
numPassedTests = numTotalTests - numFailedTests
else:
numTotalTests = None
numPassedTests = None
testsPassed = False
if not os.path.exists(getTestSuccessFileName()):
print("\nThe tests did not run and pass!\n")
testsPassed = False
elif numTotalTests == None:
print("\nCTest was invoked but no tests were run!\n")
testsPassed = False
elif numTotalTests == numPassedTests:
print("\nAll of the tests ran passed!\n")
testsPassed = True
else:
print("\n" + str(numTotalTests-numPassedTests) + " tests failed!\n")
testsPassed = False
else:
print("\nRunning the tests was not performed!\n")
print("")
print("E.2) Construct the email message ...")
print("")
# 2.a) Construct the subject line
overallPassed = None
buildCaseStatus = ""
selectedFinalStatus = False
if inOptions.doTest and not selectedFinalStatus:
if testOutputExists:
if numTotalTests:
buildCaseStatus += "passed="+str(numPassedTests)+",notpassed="+str(numFailedTests)
else:
buildCaseStatus += "no tests run"
if testsPassed and numTotalTests > 0:
overallPassed = True
else:
overallPassed = False
selectedFinalStatus = True
elif not inOptions.doBuild and not buildOutputExists:
buildCaseStatus += "no active build exists"
overallPassed = False
selectedFinalStatus = True
if inOptions.doBuild and not selectedFinalStatus:
if buildPassed:
buildCaseStatus += "build-only passed"
overallPassed = True
selectedFinalStatus = True
elif buildOutputExists:
buildCaseStatus += "build failed"
overallPassed = False
selectedFinalStatus = True
if inOptions.doConfigure and not selectedFinalStatus:
if configurePassed:
buildCaseStatus += "configure-only passed"
overallPassed = True
selectedFinalStatus = True
elif buildTestCase.skippedConfigureDueToNoEnables:
buildCaseStatus += "skipped configure, build, test due to no enabled packages"
overallPassed = True
selectedFinalStatus = True
elif configureOutputExists:
buildCaseStatus += "configure failed"
overallPassed = False
selectedFinalStatus = True
else:
buildCaseStatus += "pre-configure failed"
overallPassed = False
selectedFinalStatus = True
if inOptions.doPull and not selectedFinalStatus:
if pullPassed:
buildCaseStatus += "pull-only passed"
overallPassed = True
selectedFinalStatus = True
elif pullOutputExists:
buildCaseStatus += "pull FAILED"
overallPassed = False
selectedFinalStatus = True
if not selectedFinalStatus:
raise Exception("Error, final pass/fail status not found!")
subjectLine = "%s/%s: %s" % (inOptions.projectName, buildTestCaseName, buildCaseStatus)
if overallPassed:
subjectLine = "passed: " + subjectLine
else:
subjectLine = "FAILED: " + subjectLine
print("\nsubjectLine = '" + subjectLine + "'\n")
success = overallPassed
# 2.b) Construct the email body
emailBody = subjectLine + "\n\n"
emailBody += getCmndOutput("date", True) + "\n\n"
emailBody += getEnableStatusList(inOptions, enabledPackagesList)
emailBody += "Hostname: " + getHostname() + "\n"
emailBody += "Source Dir: " + inOptions.srcDir + "\n"
emailBody += "Build Dir: " + os.getcwd() + "\n"
emailBody += "\nCMake Cache Variables: " + ' '.join(cmakeOptions) + "\n"
if inOptions.extraCmakeOptions:
emailBody += "Extra CMake Options: " + inOptions.extraCmakeOptions + "\n"
if inOptions.makeOptions:
emailBody += "Make Options: " + inOptions.makeOptions + "\n"
if inOptions.ctestOptions:
emailBody += "CTest Options: " + inOptions.ctestOptions + "\n"
emailBody += "\n"
emailBody += getStageStatus("Pull", inOptions.doPull, pullPassed, timings.pull)
emailBody += getStageStatus("Configure", inOptions.doConfigure, configurePassed, timings.configure)
emailBody += getStageStatus("Build", inOptions.doBuild, buildPassed, timings.build)
emailBody += getStageStatus("Test", inOptions.doTest, testsPassed, timings.test)
emailBody += "\n"
if inOptions.doTest and testOutputExists and numTotalTests:
fullCTestOutput = readStrFromFile(getTestOutputFileName())
if inOptions.showAllTests:
emailBody += fullCTestOutput
else:
emailBody += extractLinesAfterRegex(fullCTestOutput, r".*\% tests passed.*")
else:
emailBody += "\n***\n*** WARNING: There are no test results!\n***\n\n"
endingTime = time.time()
totalTime = (endingTime - startingTime) / 60.0
emailBody += "\n"+getTotalTimeLineStr(buildTestCaseName, totalTime)+"\n"
#print("emailBody:\n\n\n\n", emailBody, "\n\n\n\n")
writeStrToFile(getEmailBodyFileName(), emailBody)
if overallPassed:
echoRunSysCmnd("touch "+getEmailSuccessFileName())
print("")
print("E.3) Send the email message ...")
print("")
if inOptions.sendEmailTo and buildTestCase.skippedConfigureDueToNoEnables \
and inOptions.abortGracefullyIfNoEnables \
:
print(buildTestCaseName + ": Skipping sending build/test case email " +
"because there were no enables and --abort-gracefully-if-no-" +
"enables was set!")
elif inOptions.sendEmailTo and inOptions.sendBuildCaseEmail=="only-on-failure" \
and overallPassed \
:
print(buildTestCaseName + ": Skipping sending build/test case email " +
"because everything passed and --send-build-case-email=only-on-"
"failure was set!")
elif inOptions.sendEmailTo and inOptions.sendBuildCaseEmail=="never" \
:
print(buildTestCaseName + ": Skipping sending build/test case email " +
"because everything passed and --send-build-case-email=never was " +
"set!")
elif inOptions.sendEmailTo and inOptions.sendEmailOnlyOnFailure and success:
print(buildTestCaseName + ": Skipping sending build/test case email " +
"because it passed and --send-email-only-on-failure was set!")
elif inOptions.sendEmailTo and buildTestCase.skippedConfigureDueToNoEnables \
and not inOptions.skipCaseSendEmail \
:
print("\nSkipping sending final status email for " + buildTestCase.name +
" because it had no packages enabled and --skip-case-no-email was " +
"set!")
elif inOptions.sendEmailTo:
emailAddresses = getEmailAddressesSpaceString(inOptions.sendEmailTo)
echoRunSysCmnd("mailx -s \""+subjectLine+"\" "+emailAddresses+" < "+getEmailBodyFileName())
else:
print("Not sending email because no email addresses were given!")
# 3) Return final result
return success
def getBuildTestCaseSummary(testCaseName, trimDown = True):
# Get the email file
absEmailBodyFileName = testCaseName+"/"+getEmailBodyFileName()
if os.path.exists(absEmailBodyFileName):
testCaseEmailStrArray = open(absEmailBodyFileName, 'r').readlines()
else:
testCaseEmailStrArray = None
# Get the first line (which is the summary)
testSummaryLine = None
if testCaseEmailStrArray:
summaryLine = testCaseEmailStrArray[0].strip()
if trimDown:
summaryLineArray = summaryLine.split(":")
testSummaryLine = summaryLineArray[0].strip() + ": " + summaryLineArray[2].strip()
else:
testSummaryLine = summaryLine
else:
testSummaryLine = \
"Error, The build/test was never completed!" \
" (the file '"+absEmailBodyFileName+"' does not exist.)"
return testSummaryLine
def getTestCaseEmailSummary(testCaseName, testCaseNum):
# Get the email file
absEmailBodyFileName = testCaseName+"/"+getEmailBodyFileName()
if os.path.exists(absEmailBodyFileName):
testCaseEmailStrArray = open(absEmailBodyFileName, 'r').readlines()
else:
testCaseEmailStrArray = None
# Write the entry
testCaseHeader = str(testCaseNum)+") "+testCaseName+" Results:"
summaryEmailSectionStr = \
"\n"+testCaseHeader+ \
"\n"+getStrUnderlineStr(len(testCaseHeader))+"\n" \
"\n"
if testCaseEmailStrArray:
for line in testCaseEmailStrArray:
summaryEmailSectionStr += " " + line
summaryEmailSectionStr += "\n"
else:
summaryEmailSectionStr += \
"Error, The build/test was never completed!" \
" (the file '"+absEmailBodyFileName+"' does not exist.)\n"
return summaryEmailSectionStr
def getSummaryEmailSectionStr(inOptions, buildTestCaseList):
summaryEmailSectionStr = ""
for buildTestCase in buildTestCaseList:
if buildTestCase.runBuildTestCase and not buildTestCase.skippedConfigureDueToNoEnables:
summaryEmailSectionStr += \
getTestCaseEmailSummary(buildTestCase.name, buildTestCase.buildIdx)
return summaryEmailSectionStr
def cmakeScopedDefine(projectName, name, value):
"""
Formats a CMake -D<projectName>_<name>=<value> argument.
"""
return '-D%s_%s=%s' % (projectName, name, value)
def getEnablesLists(inOptions, validPackageTypesList, isDefaultBuild,
skipCaseIfNoChangeFromDefaultEnables, tribitsGitRepos,
baseTestDir, verbose \
):
projectName = inOptions.projectName
cmakePkgOptions = []
enablePackagesList = []
gitRepoList = tribitsGitRepos.gitRepoList()
projectChangeLogic=getProjectCiFileChangeLogic(inOptions.srcDir)
enableAllPackages = False
if inOptions.enableAllPackages == "on":
if verbose:
print("\nEnabling all packages on request since " +
"--enable-all-packages=on! ...")
print("\nSkipping detection of changed packages since " +
"--enable-all-packages=on ...")
enableAllPackages = True
elif inOptions.enablePackages:
if verbose:
print("\nEnabling only the explicitly specified packages '" +
inOptions.enablePackages + "' ...")
enablePackagesList = inOptions.enablePackages.split(',')
else:
for gitRepo in gitRepoList:
diffOutFileName = baseTestDir+"/"+getModifiedFilesOutputFileName(gitRepo.repoName)
if verbose:
print("\nDetermining the set of packages to enable by examining " +
diffOutFileName + " ...")
if os.path.exists(diffOutFileName):
changedFileDiffOutputStr = open(diffOutFileName, 'r').read()
#print("\nchangedFileDiffOutputStr:\n", changedFileDiffOutputStr)
extractPackageEnablesFromChangeStatus(changedFileDiffOutputStr, inOptions,
gitRepo, enablePackagesList, verbose, projectChangeLogic=projectChangeLogic)
else:
if verbose:
print("\nThe file " + diffOutFileName + " does not exist!\n")
if not enableAllPackages and inOptions.enableExtraPackages:
if verbose:
print("\nEnabling extra explicitly specified packages '" +
inOptions.enableExtraPackages + "' ...")
enablePackagesList += inOptions.enableExtraPackages.split(',')
if verbose:
print("\nFull package enable list: [" + ','.join(enablePackagesList) + "]")
if inOptions.disablePackages:
if verbose:
print("\nRemoving package enables: [" + inOptions.disablePackages + "]")
for disablePackage in inOptions.disablePackages.split(","):
packageIdx = findInSequence(enablePackagesList, disablePackage)
if packageIdx >= 0:
del enablePackagesList[packageIdx]
if verbose:
print("\nFiltering the set of enabled packages according to allowed " +
"package types ...")
origEnablePackagesList = enablePackagesList[:]
enablePackagesList = getDefaultProjectDependenices().filterPackageNameList(
enablePackagesList, validPackageTypesList, verbose)
if verbose:
print("\nFinal package enable list: [" + ','.join(enablePackagesList) + "]")
if tribitsGitRepos.numTribitsAllExtraRepos() > 0:
cmakePkgOptions.extend(
[
cmakeScopedDefine(
projectName, "PRE_REPOSITORIES:STRING",
','.join(tribitsGitRepos.tribitsPreRepoNamesList())),
cmakeScopedDefine(
projectName, "EXTRA_REPOSITORIES:STRING",
','.join(tribitsGitRepos.tribitsExtraRepoNamesList())),
cmakeScopedDefine(
projectName, "ENABLE_KNOWN_EXTERNAL_REPOS_TYPE", inOptions.extraReposType),
cmakeScopedDefine(
projectName, "EXTRAREPOS_FILE", getExtraReposFilePath(inOptions)),
]
)
for pkg in enablePackagesList:
cmakePkgOptions.append(cmakeScopedDefine(projectName, "ENABLE_"+pkg+":BOOL", "ON"))
cmakePkgOptions.append(cmakeScopedDefine(projectName, "ENABLE_ALL_OPTIONAL_PACKAGES:BOOL", "ON"))
if inOptions.enableAllPackages == 'on':
cmakePkgOptions.append(cmakeScopedDefine(projectName, "ENABLE_ALL_PACKAGES:BOOL", "ON"))
if inOptions.enableFwdPackages:
if verbose:
print("\nEnabling forward packages on request!")
cmakePkgOptions.append(cmakeScopedDefine(projectName, "ENABLE_ALL_FORWARD_DEP_PACKAGES:BOOL", "ON"))
else:
cmakePkgOptions.append(cmakeScopedDefine(projectName, "ENABLE_ALL_FORWARD_DEP_PACKAGES:BOOL", "OFF"))
if inOptions.disablePackages:
if verbose:
print("\nAdding hard disables for specified packages '" +
inOptions.disablePackages + "' ...\n")
disablePackagesList = inOptions.disablePackages.split(',')
for pkg in disablePackagesList:
cmakePkgOptions.append(cmakeScopedDefine(projectName, "ENABLE_"+pkg+":BOOL", "OFF"))
if verbose:
print("\ncmakePkgOptions: " + str(cmakePkgOptions))
return (cmakePkgOptions, enablePackagesList)
def runBuildTestCase(inOptions, tribitsGitRepos, buildTestCase, timings):
success = True
startingTime = time.time()
baseTestDir = os.getcwd()
buildTestCaseName = buildTestCase.name
if not performAnyActions(inOptions):
print("\nNo other actions to perform!\n")
return success
print("\nCreating a new build directory if it does not already exist ...")
createDir(buildTestCaseName)
absBuildDir = os.path.join(baseTestDir, buildTestCaseName)
echoChDir(absBuildDir)
try:
print("")
print("A) Get the CMake configure options (" + buildTestCaseName + ") ...")
print("")
preConfigurePassed = True
projectName = inOptions.projectName
# A.1) Set the base options
cmakeBaseOptions = []
if inOptions.useNinja:
cmakeBaseOptions.append("-GNinja")
if inOptions.extraCmakeOptions:
cmakeBaseOptions.extend(commandLineOptionsToList(inOptions.extraCmakeOptions))
cmakeBaseOptions.append(cmakeScopedDefine(projectName,
"TRIBITS_DIR:PATH", inOptions.tribitsDir))
cmakeBaseOptions.append(cmakeScopedDefine(projectName,
"ENABLE_TESTS:BOOL", "ON"))
cmakeBaseOptions.append(cmakeScopedDefine(projectName,
"TEST_CATEGORIES:STRING", inOptions.testCategories))
cmakeBaseOptions.append(cmakeScopedDefine(projectName,
"ALLOW_NO_PACKAGES:BOOL", "OFF"))
if inOptions.ctestTimeOut:
cmakeBaseOptions.append(("-DDART_TESTING_TIMEOUT:STRING="+str(inOptions.ctestTimeOut)))
cmakeBaseOptions.extend(buildTestCase.extraCMakeOptions)
result = readAndAppendCMakeOptions(
inOptions.projectName,
os.path.join("..", getCommonConfigFileName()),
cmakeBaseOptions,
True)
if not result: preConfigurePassed = False
result = readAndAppendCMakeOptions(
inOptions.projectName,
os.path.join("..", getBuildSpecificConfigFileName(buildTestCaseName)),
cmakeBaseOptions,
buildTestCase.isDefaultBuild)
if not result: preConfigurePassed = False
print("\ncmakeBaseOptions: " + str(cmakeBaseOptions))
# A.2) Set the package enable options
cmakePkgOptions = []
enablePackagesList = []
if preConfigurePassed:
(cmakePkgOptions, enablePackagesList) = \
getEnablesLists(inOptions, buildTestCase.validPackageTypesList,
buildTestCase.isDefaultBuild,
buildTestCase.skipCaseIfNoChangeFromDefaultEnables, tribitsGitRepos,
baseTestDir, True)
# A.3) Set the combined options
cmakeOptions = []
if preConfigurePassed:
cmakeOptions = cmakeBaseOptions + cmakePkgOptions
print("\ncmakeOptions = " + str(cmakeOptions))
print("\nCreating base configure file do-configure.base ...")
createConfigureFile(cmakeBaseOptions, "cmake", inOptions.srcDir,
"do-configure.base")
print("\nCreating package-enabled configure file do-configure ...")
createConfigureFile(cmakePkgOptions, "./do-configure.base", None, "do-configure")
print("")
print("B) Do the configuration with CMake (" + buildTestCaseName + ") ...")
print("")
configurePassed = False
if inOptions.doConfigure and not preConfigurePassed:
print("\nSKIPPED: " + buildTestCaseName + " configure skipped because " +
"pre-configure failed (see above)!\n")
elif not (enablePackagesList or inOptions.enableAllPackages == 'on'):
print("\nSKIPPED: " + buildTestCaseName + " configure skipped because " +
"no packages are enabled!\n")
buildTestCase.skippedConfigureDueToNoEnables = True
elif inOptions.doConfigure:
removeIfExists("CMakeCache.txt")
removeDirIfExists("CMakeFiles")
cmnd = "./do-configure"
(configureRtn, timings.configure) = echoRunSysCmnd(cmnd,
outFile=getConfigureOutputFileName(),
timeCmnd=True, returnTimeCmnd=True, throwExcept=False
)
if configureRtn == 0:
print("\nConfigure passed!\n")
echoRunSysCmnd("touch "+getConfigureSuccessFileName())
configurePassed = True
else:
print("\nConfigure failed returning " + str(configureRtn) + "!\n")
raise Exception("Configure failed!")
else:
print("\nSkipping configure on request!\n")
if os.path.exists(getConfigureSuccessFileName()):
print("\nA current successful configure exists!\n")
configurePassed = True
else:
print("\nFAILED: A current successful configure does *not* exist!\n")
print("")
print("C) Do the build ("+buildTestCaseName+") ...")
print("")
buildPassed = False
if inOptions.doBuild and configurePassed:
if inOptions.useNinja:
cmnd = "ninja"
else:
cmnd = "make"
if inOptions.makeOptions:
cmnd += " " + inOptions.makeOptions
(buildRtn, timings.build) = echoRunSysCmnd(cmnd,
outFile=getBuildOutputFileName(),
timeCmnd=True, returnTimeCmnd=True, throwExcept=False
)
if buildRtn == 0:
print("\nBuild passed!\n")
echoRunSysCmnd("touch "+getBuildSuccessFileName())
buildPassed = True
else:
print("\nBuild failed returning " + str(buildRtn) + "!\n")
raise Exception("Build failed!")
elif inOptions.doBuild and not configurePassed:
print("\nSKIPPED: " + buildTestCaseName + " build skipped because " +
"configure did not pass!\n")
else:
print("\nSkipping the build on request!\n")
if os.path.exists(getBuildSuccessFileName()):
print("\nA current successful build exists!\n")
buildPassed = True
else:
print("\nFAILED: A current successful build does *not* exist!\n")
print("")
print("D) Run the tests (" + buildTestCaseName + ") ...")
print("")
testPassed = False
if inOptions.doTest and buildPassed:
cmnd = "ctest"
if inOptions.ctestOptions:
cmnd += " " + inOptions.ctestOptions
(testRtn, timings.test) = echoRunSysCmnd(cmnd,
outFile=getTestOutputFileName(),
timeCmnd=True, returnTimeCmnd=True, throwExcept=False
)
if testRtn == 0:
print("\nNo tests failed!\n")
echoRunSysCmnd("touch "+getTestSuccessFileName())
else:
errStr = "FAILED: ctest failed returning "+str(testRtn)+"!"
print("\n" + errStr + "\n")
raise Exception(errStr)
elif inOptions.doTest and buildTestCase.skippedConfigureDueToNoEnables:
print("\nSKIPPED: " + buildTestCaseName + " tests skipped because no " +
"packages are enabled!")
echoRunSysCmnd("touch "+getTestSuccessFileName())
# NOTE: We have to create this test success file because the presents of
# this file is used to determine in the build/test case is successful
# and therefore is okay to push. This is needed when the script is run
# a second time to determine if a build/test is successful and therefore
# allow a push.
else:
print("\nSkipping the tests on request!\n")
except Exception as e:
success = False
printStackTrace()
print("")
print("E) Analyze the overall results and send email notification (" +
buildTestCaseName + ") ...")
print("")
if performAnyActions(inOptions):
result = analyzeResultsSendEmail(inOptions, buildTestCase,
enablePackagesList, cmakeOptions, startingTime, timings)
if not result: success = False
else:
print("No actions performed, nothing to analyze!")
return success
def cleanBuildTestCaseOutputFiles(runBuildTestCaseBool, inOptions, baseTestDir, buildTestCaseName):
if runBuildTestCaseBool and not os.path.exists(buildTestCaseName):
print("\nSkipping cleaning build/test files for " + buildTestCaseName +
" because dir does not exist!\n")
elif runBuildTestCaseBool and os.path.exists(buildTestCaseName):
if inOptions.wipeClean:
print("\nRemoving the existing build directory " + buildTestCaseName +
" (--wipe-clean) ...")
removeDirIfExists(buildTestCaseName)
elif doRemoveOutputFiles(inOptions):
echoChDir(buildTestCaseName)
if inOptions.doConfigure or inOptions.doPull:
removeIfExists(getConfigureOutputFileName())
removeIfExists(getConfigureSuccessFileName())
if inOptions.doBuild or inOptions.doConfigure or inOptions.doPull:
removeIfExists(getBuildOutputFileName())
removeIfExists(getBuildSuccessFileName())
if inOptions.doTest or inOptions.doBuild or inOptions.doConfigure or inOptions.doPull:
removeIfExists(getTestOutputFileName())
removeIfExists(getTestSuccessFileName())
removeIfExists(getEmailBodyFileName())
removeIfExists(getEmailSuccessFileName())
echoChDir("..")
def cleanBuildTestCaseSuccessFiles(runBuildTestCaseBool, inOptions, baseTestDir, \
buildTestCaseName \
):
removeIfExists(buildTestCaseName+"/"+getConfigureSuccessFileName())
removeIfExists(buildTestCaseName+"/"+getBuildSuccessFileName())
removeIfExists(buildTestCaseName+"/"+getTestSuccessFileName())
removeIfExists(buildTestCaseName+"/"+getEmailSuccessFileName())
removeIfExists(buildTestCaseName+"/"+getEmailBodyFileName())
# NOTE: ABove, we need to delete the 'email.out' file otherwise it will get
# picked up in a later run of just a status check. But this info is not
# really last because it is duplicated in the file
# commitStatusEmailBody.out.
def cleanSuccessFiles(buildTestCaseList, inOptions, baseTestDir):
print("\nRemoving *.success files ...\n")
removeIfExists(getInitialPullSuccessFileName())
for buildTestCase in buildTestCaseList:
cleanBuildTestCaseSuccessFiles(
buildTestCase.runBuildTestCase, inOptions, baseTestDir, buildTestCase.name)
def runBuildTestCaseDriver(inOptions, tribitsGitRepos, baseTestDir, buildTestCase, timings):
success = True
buildTestCaseName = buildTestCase.name
print("\n***")
print("*** Doing build and test of "+buildTestCaseName+" ...")
print("***\n")
if buildTestCase.runBuildTestCase:
try:
echoChDir(baseTestDir)
writeDefaultBuildSpecificConfigFile(buildTestCaseName)
result = runBuildTestCase(inOptions, tribitsGitRepos, buildTestCase, timings)
if not result: success = False
except Exception as e:
success = False
printStackTrace()
else:
print("\nSkipping " + buildTestCaseName + " build/test on request!\n")
return success
def checkBuildTestCaseStatus(buildTestCase, inOptions):
runBuildTestCaseBool = buildTestCase.runBuildTestCase
buildTestCaseName = buildTestCase.name
skippedConfigureDueToNoEnables = buildTestCase.skippedConfigureDueToNoEnables
statusMsg = None
timeInMin = -1.0
if not runBuildTestCaseBool:
buildTestCaseActionsPass = True
buildTestCaseOkayToCommit = True
statusMsg = \
"Test case "+buildTestCaseName+" was not run! => Does not affect push readiness!"
return (buildTestCaseActionsPass, buildTestCaseOkayToCommit, statusMsg, timeInMin)
if skippedConfigureDueToNoEnables:
buildTestCaseActionsPass = True
buildTestCaseOkayToCommit = True
statusMsg = \
"Skipped configure, build, test due to no enabled packages! => Does not affect push readiness!"
return (buildTestCaseActionsPass, buildTestCaseOkayToCommit, statusMsg, timeInMin)
if not os.path.exists(buildTestCaseName) and not performAnyBuildTestActions(inOptions):
buildTestCaseActionsPass = True
buildTestCaseOkayToCommit = False
statusMsg = "No configure, build, or test for "+buildTestCaseName+" was requested!"
return (buildTestCaseActionsPass, buildTestCaseOkayToCommit, statusMsg, timeInMin)
if not os.path.exists(buildTestCaseName):
buildTestCaseActionsPass = False
buildTestCaseOkayToCommit = False
statusMsg = "The directory "+buildTestCaseName+" does not exist!"
emailSuccessFileName = buildTestCaseName+"/"+getEmailSuccessFileName()
if os.path.exists(emailSuccessFileName):
buildTestCaseActionsPass = True
else:
buildTestCaseActionsPass = False
testSuccessFileName = buildTestCaseName+"/"+getTestSuccessFileName()
if os.path.exists(testSuccessFileName):
buildTestCaseOkayToCommit = True
else:
buildTestCaseOkayToCommit = False
if not statusMsg:
statusMsg = getBuildTestCaseSummary(buildTestCaseName)
emailBodyFileName = buildTestCaseName+"/"+getEmailBodyFileName()
if os.path.exists(emailBodyFileName):
timeInMinLine = getCmndOutput("grep '"+getTotalTimeBeginStr(buildTestCaseName)+"' " + \
emailBodyFileName, True, False)
timeInMin = getTimeInMinFromTotalTimeLine(buildTestCaseName, timeInMinLine)
return (buildTestCaseActionsPass, buildTestCaseOkayToCommit, statusMsg, timeInMin)
def getUserCommitMessageStr(inOptions):
absCommitMsgHeaderFile = inOptions.commitMsgHeaderFile
if not os.path.isabs(absCommitMsgHeaderFile):
absCommitMsgHeaderFile = os.path.join(inOptions.srcDir, absCommitMsgHeaderFile)
print("\nExtracting commit message subject and header from the file '" +
absCommitMsgHeaderFile + "' ...\n")
commitMsgHeaderFileStr = open(absCommitMsgHeaderFile, 'r').read()
commitEmailBodyStr = commitMsgHeaderFileStr
return commitEmailBodyStr
def getAutomatedStatusSummaryHeaderKeyStr():
return "Build/Test Cases Summary"
def getAutomatedStatusSummaryHeaderStr():
commitEmailBodyStr = "\n" \
+getAutomatedStatusSummaryHeaderKeyStr()+"\n"
return commitEmailBodyStr
def getEnableStatusList(inOptions, enabledPackagesList):
enabledStatusStr = ""
enabledStatusStr += "Enabled Packages: " + ', '.join(enabledPackagesList) + "\n"
if inOptions.disablePackages:
enabledStatusStr += "Disabled Packages: " + inOptions.disablePackages + "\n"
if inOptions.enableAllPackages == "on":
enabledStatusStr += "Enabled all Packages\n"
elif inOptions.enableFwdPackages:
enabledStatusStr += "Enabled all Forward Packages\n"
return enabledStatusStr
# Extract the original log message from the output from:
#
# git cat-file -p HEAD
#
# This function strips off the git-generated header info and strips off the
# trailing build/test summary data.
#
# NOTE: This function assumes that there will be at least one blank line
# between the build/test summary data block and the original text message. If
# there is not, this function will throw!
#
def getLastCommitMessageStrFromRawCommitLogStr(rawLogOutput):
origLogStrList = []
pastHeader = False
numBlankLines = 0
lastNumBlankLines = 0
foundStatusHeader = False
for line in rawLogOutput.splitlines():
#print("\nline = '" + line + "'\n")
if pastHeader:
origLogStrList.append(line)
if line == "":
numBlankLines += 1
elif numBlankLines > 0:
lastNumBlankLines = numBlankLines
numBlankLines = 0
if line == getAutomatedStatusSummaryHeaderKeyStr():
foundStatusHeader = True
break
if line == "":
pastHeader = True
if foundStatusHeader:
#print("\nlastNumBlankLines =", lastNumBlankLines)
#print("origLogStrList[-1] = '" + origLogStrList[-1] + "'")
#print("origLogStrList[-2] = '" + origLogStrList[-2] + "'")
if origLogStrList[-2] != "":
raise Exception("Error, there must be at least one blank line before the" \
" build/test summary block! This is a corrupted commit message. Please" \
" use 'git commit --amend' and manually remove the 'Build/test Cases Summary' block.")
origLogStrList = origLogStrList[0:-lastNumBlankLines]
lastCommitMessageStr = '\n'.join(origLogStrList)
else:
lastCommitMessageStr = ('\n'.join(origLogStrList))+'\n'
lastNumBlankLines = -1 # Flag we did not find status header
return (lastCommitMessageStr, lastNumBlankLines)
def getLastCommitMessageStr(inOptions, gitRepo):
# Get the raw output from the last current commit log
rawLogOutput = getCmndOutput(
inOptions.git+" cat-file -p HEAD",
workingDir=getGitRepoDir(inOptions.srcDir, gitRepo.repoDir)
)
return getLastCommitMessageStrFromRawCommitLogStr(rawLogOutput)[0]
def trimLineToLen(lineIn, numChars):
if len(lineIn) > numChars:
return lineIn[:numChars]+".."
return lineIn
def getLocalCommitsSummariesStr(inOptions, gitRepo):
# Get the list of local commits other than this one
if gitRepo.gitRepoStats.numCommitsInt() > 0:
rawLocalCommitsStr = getCmndOutput(
inOptions.git+" log --oneline "+gitRepo.gitRepoStats.branch \
+" ^"+gitRepo.gitRepoStats.trackingBranch,
True,
workingDir=getGitRepoDir(inOptions.srcDir, gitRepo.repoDir)
)
else:
rawLocalCommitsStr = ""
if gitRepo.repoName:
repoName = gitRepo.repoName
repoNameModifier = " ("+gitRepo.repoName+")"
else:
repoName = ""
repoNameModifier = ""
print("\nLocal commits for this build/test group" + repoNameModifier + ":" +
"\n----------------------------------------" )
if rawLocalCommitsStr == "\n" or rawLocalCommitsStr == "":
localCommitsExist = False
else:
localCommitsExist = True
if localCommitsExist:
print(rawLocalCommitsStr)
else:
print("No local commits exit!")
localCommitsStr = \
"*** Commits for repo "+repoName+":"
if localCommitsExist:
for localCommitLine in rawLocalCommitsStr.splitlines():
localCommitsStr += ("\n "+trimLineToLen(localCommitLine, 90))
return localCommitsStr
def getLocalCommitsSHA1ListStr(inOptions, gitRepo):
# Get the raw output from the last current commit log
rawLocalCommitsStr = getCmndOutput(
inOptions.git+" log --pretty=format:'%h' "\
+gitRepo.gitRepoStats.branch+" ^"+gitRepo.gitRepoStats.trackingBranch,
True,
workingDir=getGitRepoDir(inOptions.srcDir, gitRepo.repoDir)
)
rawLocalCommitsArray = rawLocalCommitsStr.splitlines()
if len(rawLocalCommitsArray) > 1:
return ("Other local commits for this build/test group: "
+ (", ".join(rawLocalCommitsArray[1:]))) + "\n"
return ""
# NOTE: Above, you have to use:
#
# git log --pretty='%h' <currentbranch> ^<trackingbranch>
#
# and pop off the top commit as shown above instead of:
#
# git log --pretty='%h' <currentbranch>^ ^<trackingbranch>
#
# The latter returns nothing when the top commit is a merge commit.
def getLocalCommitsExist(inOptions, gitRepo):
if gitRepo.gitRepoStats.numCommitsInt() > 0:
return True
return False
def matchProjectName(line):
"""
Attempts to match and return the value of PROJECT_NAME in a line like
SET(PROJECT_NAME <name>)
If no match can be made, None is returned.
"""
matchRegex = r'\s*[Ss][Ee][Tt]\s*\(\s*PROJECT_NAME\s+([^\)\s]*)\s*\).*'
match = re.search(matchRegex, line)
if match:
return match.group(1)
else:
return None
def getProjectName(sourceDirectory):
"""
Reads the project name from <root>/ProjectName.cmake
"""
projectNameFile = os.path.join(sourceDirectory, 'ProjectName.cmake')
if not os.path.exists(projectNameFile):
raise Exception(
"%s is required to exist for a valid Tribits project." % projectNameFile)
content = open(projectNameFile, "r")
line = content.readline()
while line:
name = matchProjectName(line)
if name:
return name
line = content.readline()
raise Exception(
'The file %s does not set the PROJECT_NAME variable. ' +
'This is required of any Tribits project.')
def getRepoStatTableDirName(inOptions, repoDir):
if repoDir == "":
repoStatTableDirName = gitdist.getBaseRepoTblName(
gitdist.getBaseDirNameFromPath(os.path.abspath(inOptions.srcDir)))
else:
repoStatTableDirName = repoDir
return repoStatTableDirName
def checkinTest(tribitsDir, inOptions, configuration={}):
"""
Main function for checkin testing.
"""
if not inOptions.projectName:
inOptions.projectName = getProjectName(inOptions.srcDir)
print("\n**********************************************")
print("*** Performing checkin testing of %s ***" % inOptions.projectName)
print("**********************************************")
setattr(inOptions, "tribitsDir", tribitsDir)
ciSupportDir = os.path.join(tribitsDir, 'ci_support')
setattr(inOptions, "ciSupportDir", ciSupportDir)
print("\nciSupportDir = " + ciSupportDir)
print("\nsrcDir = " + inOptions.srcDir)
baseTestDir = os.getcwd()
print("\nbaseTestDir = " + baseTestDir)
if inOptions.withoutDefaultBuilds:
inOptions.defaultBuilds = ''
if inOptions.doAll:
inOptions.doPull = True
inOptions.doConfigure = True
inOptions.doBuild = True
inOptions.doTest = True
if inOptions.localDoAll:
inOptions.allowNoPull = True
inOptions.doConfigure = True
inOptions.doBuild = True
inOptions.doTest = True
assertAndSetupGit(inOptions)
if inOptions.overallNumProcs:
inOptions.makeOptions = "-j"+inOptions.overallNumProcs+" "+inOptions.makeOptions
inOptions.ctestOptions = "-j"+inOptions.overallNumProcs+" "+inOptions.ctestOptions
assertExtraBuildConfigFiles(inOptions.extraBuilds)
assertExtraBuildConfigFiles(inOptions.stExtraBuilds)
if not inOptions.skipDepsUpdate:
removeIfExists(getProjectDependenciesXmlFileName(inOptions.projectName))
removeIfExists(getProjectDependenciesXmlGenerateOutputFileName(inOptions.projectName))
removeIfExists(getProjectExtraReposPythonOutFile(inOptions.projectName))
print("\n***")
print("*** 0) Read project dependencies files and build dependencies graph ...")
print("***")
tribitsGitRepos = TribitsGitRepos()
tribitsGitRepos.initFromCommandlineArguments(inOptions)
#print("\ntribitsGitRepos =", tribitsGitRepos)
createAndGetProjectDependencies(inOptions, baseTestDir, tribitsGitRepos)
# Assert the names of packages passed in
assertPackageNames("--enable-packages", inOptions.enablePackages)
assertPackageNames("--enable-extra-packages", inOptions.enableExtraPackages)
assertPackageNames("--disable-packages", inOptions.disablePackages)
success = True
didAtLeastOnePush = False
timings = Timings()
subjectLine = None
# Set up build/test cases array
buildTestCaseList = []
cmakeConfig = configuration.get('cmake', {})
commonConfigOptions = cmakeConfig.get('common', [])
defaultBuilds = cmakeConfig.get('default-builds', [])
requestedDefaultBuilds = inOptions.defaultBuilds
for buildname, buildopts in defaultBuilds:
setBuildTestCaseInList(
buildTestCaseList,
buildname,
buildname in requestedDefaultBuilds,
["PT"],
True,
False,
commonConfigOptions \
+ [ cmakeScopedDefine(inOptions.projectName,
"ENABLE_SECONDARY_TESTED_CODE:BOOL", "OFF") ] \
+ buildopts \
)
if inOptions.stExtraBuilds:
for ssExtraBuild in inOptions.stExtraBuilds.split(','):
setBuildTestCaseInList(buildTestCaseList, ssExtraBuild, True,
["PT", "ST"], False, True, [])
allValidPackageTypesList = ["PT", "ST", "EX"]
if inOptions.extraBuilds:
for extraBuild in inOptions.extraBuilds.split(','):
setBuildTestCaseInList(buildTestCaseList, extraBuild, True,
allValidPackageTypesList, False, False, [])
try:
print("\n***")
print("*** 1) Clean old output files ...")
print("***")
if inOptions.doPull:
for gitRepo in tribitsGitRepos.gitRepoList():
removeIfExists(getInitialPullOutputFileName(gitRepo.repoName))
removeIfExists(getInitialExtraPullOutputFileName(gitRepo.repoName))
removeIfExists(getInitialPullSuccessFileName())
for gitRepo in tribitsGitRepos.gitRepoList():
removeIfExists(getFinalCommitBodyFileName(gitRepo.repoName))
removeIfExists(getFinalCommitOutputFileName(gitRepo.repoName))
removeIfExists(getCommitStatusEmailBodyFileName())
for gitRepo in tribitsGitRepos.gitRepoList():
removeIfExists(getModifiedFilesOutputFileName(gitRepo.repoName))
removeIfExists(getFinalPullOutputFileName(gitRepo.repoName))
removeIfExists(getPushOutputFileName(gitRepo.repoName))
if inOptions.executeOnReadyToPush:
removeIfExists(getExtraCommandOutputFileName())
for buildTestCase in buildTestCaseList:
cleanBuildTestCaseOutputFiles(
buildTestCase.runBuildTestCase, inOptions, baseTestDir, buildTestCase.name)
print("\n***")
print("*** 2) Get repo status")
print("***\n")
hasChangesToPush = getReposStats(inOptions, tribitsGitRepos)
# Determine if we will need to perform git diffs of
if inOptions.enableAllPackages == "on":
print("\n--enable-all-packages=on => git diffs w.r.t. tracking branch " +
"*will not* be needed to look for changed files!")
gitDiffsWrtTrackingBranchAreNeeded = False
elif (inOptions.enablePackages != "" and inOptions.enableAllPackages == "off"):
print("\n--enable-packages!='' and --enable-all-packages='off'" +
" => git diffs w.r.t. tracking branch *will not* be needed to " +
"look for changed files!")
gitDiffsWrtTrackingBranchAreNeeded = False
elif (inOptions.enablePackages == "" or inOptions.enableAllPackages == "auto"):
# If the user has not specified a set of packages to enable, or allows
# for logic that determines if all packages should be enabled (because
# base-level CMake files have changed), then we need to do git diffs to
# look for changed files. This is the default set of arguments.
print("\n--enable-packages='' or --enable-all-packages='auto'" +
" => git diffs w.r.t. tracking branch *will* be needed to look " +
"for changed files!")
gitDiffsWrtTrackingBranchAreNeeded = True
else:
# We should never get here, but just in case, let's do the diffs.
print("git diffs w.r.t. tracking branch may be needed to look for " +
"changed files?")
gitDiffsWrtTrackingBranchAreNeeded = True
# Determine if all repos must be on a branch and have a tracking branch
if gitDiffsWrtTrackingBranchAreNeeded:
print("\nNeed git diffs w.r.t. tracking branch so all repos must be on a" +
" branch and have a tracking branch!")
reposMustHaveTrackingBranch = True
elif inOptions.doPull:
print("\nDoing a pull so all repos must be on a branch and have a "
"tracking branch!")
reposMustHaveTrackingBranch = True
elif inOptions.doPush:
print("\nDoing a push so all repos must be on a branch and have a "
"tracking branch!")
reposMustHaveTrackingBranch = True
else:
print("\nNo need for repos to be on a branch with a tracking branch!")
reposMustHaveTrackingBranch = False
# Assert that all of the repos are on a branch with a tracking branch
if reposMustHaveTrackingBranch:
repoIdx = 0
for gitRepo in tribitsGitRepos.gitRepoList():
assertRepoHasBranchAndTrackingBranch(inOptions, gitRepo)
print("\n***")
print("*** 3) Pull updated commits for %s ..." % inOptions.projectName)
print("***")
reposAreClean = True
pullPassed = True
doingAtLeastOnePull = inOptions.doPull
pulledSomeChanges = False
pulledSomeExtraChanges = False
if not doingAtLeastOnePull:
print("\nSkipping all pulls on request!\n")
if doingAtLeastOnePull and pullPassed:
#
print("\n3.a) Check that there are no uncommitted and no new unknown "
"files before doing the pull(s) ...\n")
#
repoIdx = 0
print(tribitsGitRepos.gitRepoList())
for gitRepo in tribitsGitRepos.gitRepoList():
print("\n3.a." + str(repoIdx) + ") Git Repo: '" + gitRepo.repoName +
"'")
# See if the repo is clean
if gitRepo.gitRepoStats.numModifiedInt() > 0:
repoNotCleanMsg = "\nERROR: There are changed uncommitted files => cannot continue!"
reposAreClean = False
if gitRepo.gitRepoStats.numUntrackedInt() > 0:
repoNotCleanMsg = "\nERROR: There are newly created uncommitted files => Cannot continue!"
reposAreClean = False
if not reposAreClean:
print(repoNotCleanMsg)
gitStatusOutput = getCmndOutput(inOptions.git+" status", True, throwOnError=False,
workingDir=getGitRepoDir(inOptions.srcDir, gitRepo.repoDir))
print(
"\nOutput from 'git status':\n" +
"\n--------------------------------------------------------------\n" +
gitStatusOutput +
"\n--------------------------------------------------------------\n")
print(
"\nExplanation: In order to do a meaningful test to allow a push, all files\n"
"in the local repo must be committed. Otherwise, if there are changed but not\n"
"committed files or new unknown files that are used in the build or the test, then\n"
"what you are testing is *not* what you will be pushing. If you have changes that\n"
"you don't want to push, then try using 'git stash' before you run this script to\n"
"stash away all of the changes you don't want to push. That way, what you are testing\n"
"will be consistent with what you will be pushing.\n")
pullPassed = False
#print("gitRepo =", gitRepo)
repoIdx += 1
if doingAtLeastOnePull and pullPassed:
# NOTE: We want to pull first from the global repo and then from the
# extra repo so the extra repo's revisions will get rebased on top of
# the others. This is what you would want and expect for the remote
# test/push process where multiple pulls may be needed before it works.
#
print("\n3.b) Pull updates from remote tracking branch ...")
#
if inOptions.doPull and pullPassed:
repoIdx = 0
for gitRepo in tribitsGitRepos.gitRepoList():
print("\n3.b." + str(repoIdx) + ") Git Repo: " + gitRepo.repoName)
echoChDir(baseTestDir)
(pullRtn, pullTimings, pullGotChanges) = executePull(
gitRepo,
inOptions, baseTestDir,
getInitialPullOutputFileName(gitRepo.repoName))
if pullGotChanges:
pulledSomeChanges = True
timings.pull += pullTimings
if pullRtn != 0:
print("\nPull failed!\n")
pullPassed = False
break
repoIdx += 1
else:
print("\nSkipping initial pull from remote tracking branch!\n")
#
print("\n3.c) Pull extra updates for --extra-pull-from='" +
inOptions.extraPullFrom + "' ...")
#
timings.pull = 0
if inOptions.extraPullFrom and pullPassed:
repoExtraRemotePullsList = \
parseExtraPullFromArgs(tribitsGitRepos.gitRepoList(), inOptions.extraPullFrom)
repoIdx = 0
for repoExtraRemotePulls in repoExtraRemotePullsList:
gitRepo = repoExtraRemotePulls.gitRepo
remoteRepoAndBranchList = repoExtraRemotePulls.remoteRepoAndBranchList
if not remoteRepoAndBranchList:
continue
print("\n3.c." + str(repoIdx) + ") Git Repo: " + gitRepo.repoName)
echoChDir(baseTestDir)
for remoteRepoAndBranch in remoteRepoAndBranchList:
(pullRtn, pullTimings, pullGotChanges) = executePull(
gitRepo,
inOptions, baseTestDir,
getInitialExtraPullOutputFileName(gitRepo.repoName),
remoteRepoAndBranch )
if pullGotChanges:
pulledSomeChanges = True
pulledSomeExtraChanges = True
timings.pull += pullTimings
if pullRtn != 0:
print("\nPull failed!\n")
pullPassed = False
break
if pullRtn != 0:
break
repoIdx += 1
else:
print("\nSkipping extra pull from '" + inOptions.extraPullFrom + "'!\n")
# Given overall status of the pulls and determine if to abort gracefully
if pulledSomeChanges:
print("\nThere where at least some changes pulled!")
else:
print("\nNo changes were pulled!")
# Determine if extra changes were pulled and if to get repo status again
if pulledSomeExtraChanges:
print("\nExtra pull pulled new commits so need to get repo status "
"again ...\n")
if getReposStats(inOptions, tribitsGitRepos):
hasChangesToPush = True
#
print("\nDetermine overall pull pass/fail ...\n")
#
echoChDir(baseTestDir)
# Check for prior successful initial pull
currentSuccessfullPullExists = os.path.exists(getInitialPullSuccessFileName())
if inOptions.doPull:
if pullPassed:
print("\nPull passed!\n")
echoRunSysCmnd("touch "+getInitialPullSuccessFileName())
else:
print("\nPull failed!\n")
elif currentSuccessfullPullExists:
print("\nA previous pull was performed and was successful!")
pullPassed = True
elif inOptions.allowNoPull:
print("\nNot performing pull since --allow-no-pull was passed in\n")
pullPassed = True
else:
print("\nNo previous successful pull is still current!")
pullPassed = False
# Update for current successful pull
currentSuccessfullPullExists = os.path.exists(getInitialPullSuccessFileName())
print("\n***")
print("*** 4) Get the list of all the modified files ...")
print("***")
if pullPassed:
if gitDiffsWrtTrackingBranchAreNeeded:
for gitRepo in tribitsGitRepos.gitRepoList():
getCurrentDiffOutputAndLogModified(inOptions, gitRepo, baseTestDir)
else:
print("\nSkipping getting list of modified files because not "
"needed!\n")
else:
print("\nSkipping getting list of modified files because pull failed!\n")
print("\n***")
print("*** 5) Running the different build/test cases ...")
print("***")
# Determine if we will run the build/test cases or not
# Set runBuildCases flag and other logic
abortGracefullyDueToNoUpdates = False
abortGracefullyDueToNoChangesToPush = False
if not performAnyBuildTestActions(inOptions):
print("\nNot performing any build cases because no --configure, " +
"--build or --test was specified!\n")
runBuildCases = False
elif doingAtLeastOnePull:
if reposAreClean and not pulledSomeChanges and \
inOptions.abortGracefullyIfNoChangesPulled \
:
print("\nNot performing any build cases because pull did not bring "
"any *new* commits and --abort-gracefully-if-no-changes-pulled "
"was set!\n")
abortGracefullyDueToNoUpdates = True
runBuildCases = False
elif reposAreClean and not hasChangesToPush and \
inOptions.abortGracefullyIfNoChangesToPush \
:
print("\nNot performing any build cases because there are no local "
"changes to push and --abort-gracefully-if-no-changes-to-push!\n")
abortGracefullyDueToNoChangesToPush = True
runBuildCases = False
elif pullPassed:
print("\nThe pull passed, running the build/test cases ...\n")
runBuildCases = True
else:
print("\nNot running any build/test cases because the pull failed!\n")
runBuildCases = False
else:
if inOptions.allowNoPull:
print("\nNo pull was attempted but we are running the build/test cases "
"anyway because --allow-no-pull was specified ...\n")
runBuildCases = True
elif os.path.exists(getInitialPullSuccessFileName()):
print("\nA previous pull was successful, running build/test cases "
"...!\n")
runBuildCases = True
else:
print("\nNot running any build/test cases because no pull was "
"attempted!\n\nHint: Use --allow-no-pull to allow build/test "
"cases to run without having to do a pull first!")
runBuildCases = False
# Run the build/test cases
buildTestCasesPassed = True
if runBuildCases:
echoChDir(baseTestDir)
writeDefaultCommonConfigFile()
print("\nSetting up to run the build/test cases:")
for i in range(len(buildTestCaseList)):
buildTestCase = buildTestCaseList[i]
print(str(i) + ") " + buildTestCase.name + ": ", end="")
if buildTestCase.runBuildTestCase:
print("Will attempt to run!")
else:
print("Will *not* attempt to run on request!")
for buildTestCase in buildTestCaseList:
buildTestCase.timings = timings.deepCopy()
result = runBuildTestCaseDriver(
inOptions,
tribitsGitRepos,
baseTestDir,
buildTestCase,
buildTestCase.timings
)
if not result:
buildTestCasesPassed = False
success = False
print("\n***")
print("*** 6) Determine overall success and push readiness ...")
print("***")
okayToCommit = False
okayToPush = False
forcedCommitPush = False
abortedCommitPush = False
atLeastOneConfigureBuildAttemptPassed = False
if inOptions.doPushReadinessCheck:
echoChDir(baseTestDir)
okayToCommit = success
subjectLine = None
commitEmailBodyExtra = ""
shortCommitEmailBodyExtra = ""
(cmakePkgOptions, enabledPackagesList) = \
getEnablesLists(inOptions, allValidPackageTypesList, False, False,
tribitsGitRepos, baseTestDir, False)
enableStatsListStr = getEnableStatusList(inOptions, enabledPackagesList)
commitEmailBodyExtra += enableStatsListStr
shortCommitEmailBodyExtra += enableStatsListStr
commitEmailBodyExtra += \
"\nBuild test results:" \
"\n-------------------\n"
for i in range(len(buildTestCaseList)):
buildTestCase = buildTestCaseList[i]
buildTestCaseName = buildTestCase.name
(buildTestCaseActionsPass, buildTestCaseOkayToCommit, statusMsg, timeInMin) = \
checkBuildTestCaseStatus(buildTestCase, inOptions)
buildTestCaseStatusStr = str(i)+") "+buildTestCaseName+" => "+statusMsg
if not buildTestCaseOkayToCommit:
buildTestCaseStatusStr += " => Not ready to push!"
buildTestCaseStatusStr += " ("+formatMinutesStr(timeInMin)+")\n"
print(buildTestCaseStatusStr)
commitEmailBodyExtra += buildTestCaseStatusStr
shortCommitEmailBodyExtra += buildTestCaseStatusStr
#print("buildTestCaseActionsPass =", buildTestCaseActionsPass)
if not buildTestCaseActionsPass:
success = False
if not buildTestCaseOkayToCommit:
okayToCommit = False
#print("buildTestCaseOkayToCommit =", buildTestCaseOkayToCommit)
if buildTestCase.runBuildTestCase and buildTestCaseOkayToCommit \
and not buildTestCase.skippedConfigureDueToNoEnables \
:
#print("Setting atLeastOneConfigureBuildAttemptPassed=True")
atLeastOneConfigureBuildAttemptPassed = True
if not atLeastOneConfigureBuildAttemptPassed:
print("\nThere were no successful attempts to configure/build/test!")
okayToCommit = False
if not okayToCommit:
print("\nAt least one of the actions (pull, configure, built, test)"
" failed or was not performed correctly!\n")
# Determine if we should do a forced push
if inOptions.doPushReadinessCheck and not okayToCommit and inOptions.forcePush \
:
forcedPushMsg = \
"\n***" \
"\n*** WARNING: The acceptance criteria for doing a push has *not*" \
"\n*** been met, but a push is being forced anyway by --force-push!" \
"\n***\n"
print(forcedPushMsg)
okayToCommit = True
forcedCommitPush = True
# Determine if a push is ready to try or not
if okayToCommit:
if currentSuccessfullPullExists:
print("\nA current successful pull also exists => Ready for final "
"push!\n")
okayToPush = True
else:
commitEmailBodyExtra += \
"\nA current successful pull does *not* exist => Not ready for final push!\n" \
"\nExplanation: In order to safely push, the local working directory needs\n" \
"to be up-to-date with the global repo or a full integration has not been\n" \
"performed!\n"
print(commitEmailBodyExtra)
okayToPush = False
abortedCommitPush = True
else:
okayToPush = False
if okayToPush:
print("\n => A PUSH IS READY TO BE PERFORMED!")
else:
print("\n => A PUSH IS *NOT* READY TO BE PERFORMED!")
else:
print("\nSkipping push readiness check on request!")
okayToCommit = False
okayToPush = False
print("\n***")
print("*** 7) Do final push ...")
print("***")
# Attempt the final pull, commit amend, and push
pullFinalPassed = True
amendFinalCommitPassed = True
pushPassed = True
didPush = False
if not inOptions.doPush:
print("\nNot doing the push but sending an email"
" about the commit/push readiness status ...")
if okayToPush:
subjectLine = "READY TO PUSH"
else:
subjectLine = "NOT READY TO PUSH"
elif not okayToPush:
print("\nNot performing push due to prior errors\n")
pushPassed = False
else: # inOptions.doPush and okayToPush:
#
print("\n7.a) Performing a final pull to make sure there are no "
"conflicts for push ...\n")
#
if not okayToPush:
print("\nSkippng final pull due to prior errors!\n")
pullFinalPassed = False
else:
print("\nExplanation: In order to push, the local repo needs to be "
"up-to-date\nwith the global repo or the push will not be "
"allowed. Therefore, a pull\nbefore the push must be performed "
"if there are updates in the global reop\nregardless if --pull "
"was specified or not. Also, a rebase might be done in\norder "
"to get a linear history required by the hooks in the main "
"repository.\n")
doFinalRebase = inOptions.rebase
if not doFinalRebase:
print("Skipping the final rebase on request! (see --no-rebase "
"option)")
pullFinalPassed = True
repoIdx = 0
for gitRepo in tribitsGitRepos.gitRepoList():
print("\n7.a." + str(repoIdx) + ") Git Repo: '" + gitRepo.repoName +
"'")
(pull2Rtn, pull2Time, pullGotChanges) = \
executePull(gitRepo, inOptions, baseTestDir,
getFinalPullOutputFileName(gitRepo.repoName), None,
doFinalRebase )
if pull2Rtn != 0:
pullFinalPassed = False
break
repoIdx += 1
if pullFinalPassed:
print("\nFinal pull passed!\n")
else:
print("\nFinal pull failed!\n")
if not pullFinalPassed: okayToPush = False
#
print("\n7.b) Amending the final commit message by appending test "
"results ...\n")
#
if not inOptions.appendTestResults:
print("\nSkipping appending test results on request (--no-append-test-"
"results)!\n")
elif not okayToPush:
print("\nSkippng appending test results due to prior errors!\n")
amendFinalCommitPassed = False
else: # inOptions.appendTestResults and okayToPush
print("\nAttempting to amend the final commit message ...\n")
repoIdx = 0
for gitRepo in tribitsGitRepos.gitRepoList():
print("\n7.b." + str(repoIdx) + ") Git Repo: '" + gitRepo.repoName +
"'")
try:
if gitRepo.gitRepoStats.numCommitsInt() > 0:
# Get info about current commit and local commits
lastCommitMessageStr = getLastCommitMessageStr(inOptions, gitRepo)
localCommitSHA1ListStr = getLocalCommitsSHA1ListStr(inOptions, gitRepo)
# Get then final commit message
finalCommitEmailBodyStr = lastCommitMessageStr
finalCommitEmailBodyStr += getAutomatedStatusSummaryHeaderStr()
finalCommitEmailBodyStr += shortCommitEmailBodyExtra
finalCommitEmailBodyStr += localCommitSHA1ListStr
if forcedCommitPush:
finalCommitEmailBodyStr += "WARNING: Forced the push!\n"
finalCommitEmailBodyFileName = getFinalCommitBodyFileName(gitRepo.repoName)
writeStrToFile(finalCommitEmailBodyFileName, finalCommitEmailBodyStr)
# Amend the final commit message
commitAmendRtn = echoRunSysCmnd(
inOptions.git+" commit --amend" \
" -F "+os.path.join(baseTestDir, finalCommitEmailBodyFileName),
workingDir=getGitRepoDir(inOptions.srcDir, gitRepo.repoDir),
outFile=os.path.join(baseTestDir, getFinalCommitOutputFileName(gitRepo.repoName)),
timeCmnd=True, throwExcept=False
)
if commitAmendRtn != 0:
amendFinalCommitPassed = False
break
else:
print("\nSkipping amending last commit because there are no "
"local commits!\n")
except Exception as e:
success = False
amendFinalCommitPassed = False
printStackTrace()
repoIdx += 1
# end for
if amendFinalCommitPassed:
print("\nAppending test results to last commit passed!\n")
else:
print("\nAppending test results to last commit failed!\n")
if not amendFinalCommitPassed: okayToPush = False
# End final pull and amend commit message block
# Jump out if the above if block and get the list of local commits. You
# have to get this list after a final rebase and after the top commit is
# amended so that you get the right SHA1s. But you have to do this
# *before* the push or there will not be any local commits!
allLocalCommitSummariesStr = ""
if inOptions.doPushReadinessCheck:
repoIdx = 0
for gitRepo in tribitsGitRepos.gitRepoList():
localCommitSummariesStr = \
getLocalCommitsSummariesStr(inOptions, gitRepo)
if allLocalCommitSummariesStr:
allLocalCommitSummariesStr += ("\n" + localCommitSummariesStr)
else:
allLocalCommitSummariesStr = localCommitSummariesStr
repoIdx += 1
# Jump back into the push block and do the actual push
if inOptions.doPush:
#
print("\n7.c) Pushing the the local commits to the global repo ...\n")
#
if not okayToPush:
print("\nNot performing push due to prior errors!\n")
pushPassed = False
else:
print("\nAttempting to do the push ...")
debugSkipPush = os.environ.get("CHECKIN_TEST_SKIP_PUSH","")
#print("debugSkipPush =", debugSkipPush)
#debugSkipPush = True
repoIdx = 0
for gitRepo in tribitsGitRepos.gitRepoList():
print("\n7.c." + str(repoIdx) + ") Git Repo: '" + gitRepo.repoName +
"'")
if gitRepo.gitRepoStats.numCommitsInt() > 0:
if not debugSkipPush:
pushRtn = echoRunSysCmnd(
inOptions.git+" push "+pushToTrackingBranchArgs(gitRepo),
workingDir=getGitRepoDir(inOptions.srcDir, gitRepo.repoDir),
outFile=os.path.join(baseTestDir, getPushOutputFileName(gitRepo.repoName)),
throwExcept=False, timeCmnd=True )
didAtLeastOnePush = True
else:
print("\nSkipping push due to debug override ...")
pushRtn = 0
if pushRtn != 0:
pushPassed = False
break
else:
print("\nSkipping push to '" + gitRepo.repoName + "' because " +
"there are no commits!")
repoIdx += 1
# end for
if pushPassed:
if didAtLeastOnePush:
print("\nPush passed!\n")
didPush = True
else:
print("\nPush failed because the push was never attempted!")
else:
print("\nPush failed!\n")
if not pushPassed: okayToPush = False
# End push block
print("\n***")
print("*** 8) Set up to run execute extra command on ready to push ...")
print("***")
if inOptions.executeOnReadyToPush and not okayToPush:
print("\nNot executing final command (" + inOptions.executeOnReadyToPush +
") since a push is not okay to be performed!\n")
elif inOptions.executeOnReadyToPush and okayToPush:
executeCmndStr = "\nExecuting final command ("+inOptions.executeOnReadyToPush+") since" \
+" a push is okay to be performed!\n"
commitEmailBodyExtra += executeCmndStr
print(executeCmndStr)
else:
print("\nNot executing final command since none was given ...\n")
print("\n***")
print("*** 9) Create and send push (or readiness status) notification email ...")
print("***\n")
allConfiguresAbortedDueToNoEnablesGracefullAbort = False
if inOptions.doPushReadinessCheck:
#
print("\n9.a) Getting final status to send out in the summary email ...\n")
#
grepCheckinTestOutForFailed_msg = \
"\n\nTo find out more about this failure, grep the 'checkin-test.out' log" \
" file for 'failed'. In some cases, the failure will be obvious. In other" \
" cases, a system command failed and the details about the failure will be in" \
" the output file for the command that failed.\n\n"
# Determine if all configures were aborted because no package enables
allConfiguresAbortedDueToNoEnablesGracefullAbort = True
for buildTestCase in buildTestCaseList:
if not buildTestCase.skippedConfigureDueToNoEnables:
allConfiguresAbortedDueToNoEnablesGracefullAbort = False
if not pullPassed:
subjectLine = "INITIAL PULL FAILED"
commitEmailBodyExtra += "\n\nFailed because initial pull failed!" \
+grepCheckinTestOutForFailed_msg
success = False
elif abortGracefullyDueToNoUpdates:
subjectLine = "ABORTED DUE TO NO UPDATES"
commitEmailBodyExtra += "\n\nAborted because no updates and --abort-gracefully-if-no-changes-pulled was set!\n\n"
success = True
elif abortGracefullyDueToNoChangesToPush:
subjectLine = "ABORTED DUE TO NO CHANGES TO PUSH"
commitEmailBodyExtra += "\n\nAborted because no changes to push and --abort-gracefully-if-no-changes-to-push was set!\n\n"
success = True
elif allConfiguresAbortedDueToNoEnablesGracefullAbort:
subjectLine = "ABORTED DUE TO NO ENABLES"
commitEmailBodyExtra += "\n\nAborted because no enables and --abort-gracefully-if-no-enables was set!\n\n"
success = True
elif not pullFinalPassed:
subjectLine = "FINAL PULL FAILED"
commitEmailBodyExtra += "\n\nFailed because the final pull failed!" \
+grepCheckinTestOutForFailed_msg
success = False
elif not amendFinalCommitPassed:
subjectLine = "AMEND COMMIT FAILED"
commitEmailBodyExtra += "\n\nFailed because the final test commit amend failed!" \
+grepCheckinTestOutForFailed_msg
success = False
elif inOptions.doPush and pushPassed and forcedCommitPush:
subjectLine = "DID FORCED PUSH"
commitEmailBodyExtra += forcedPushMsg
success = True
commitEmailBodyExtra += forcedPushMsg
elif not buildTestCasesPassed:
subjectLine = "FAILED CONFIGURE/BUILD/TEST"
commitEmailBodyExtra += "\n\nFailed because one of the build/test cases failed!\n"
success = False
elif inOptions.doPush:
if didPush and not forcedCommitPush:
subjectLine = "DID PUSH"
elif abortedCommitPush:
subjectLine = "ABORTED COMMIT/PUSH"
commitEmailBodyExtra += "\n\nCommit/push was never attempted since commit/push" \
" criteria failed!\n\n"
success = False
else:
subjectLine = "PUSH FAILED"
commitEmailBodyExtra += "\n\nFailed because push failed!" \
+grepCheckinTestOutForFailed_msg
success = False
else:
if okayToPush:
subjectLine = "PASSED (READY TO PUSH)"
else:
if success:
subjectLine = "PASSED"
else:
subjectLine = "FAILED"
subjectLine += " (NOT READY TO PUSH)"
#
print("\n9.b) Create and send out push (or readiness status) notification email ...")
#
subjectLine += ": %s: %s" % (inOptions.projectName, getHostname())
emailBodyStr = subjectLine + "\n\n"
emailBodyStr += getCmndOutput("date", True) + "\n\n"
emailBodyStr += commitEmailBodyExtra + "\n"
emailBodyStr += allLocalCommitSummariesStr + "\n"
emailBodyStr += getSummaryEmailSectionStr(inOptions, buildTestCaseList)
print("\nCommit status email being sent:\n"
"--------------------------------\n\n\n\n" + emailBodyStr +
"\n\n\n\n")
summaryCommitEmailBodyFileName = getCommitStatusEmailBodyFileName()
writeStrToFile(summaryCommitEmailBodyFileName, emailBodyStr)
if inOptions.sendEmailTo and abortGracefullyDueToNoUpdates:
print("\nSkipping sending final email because there were no updates"
" and --abort-gracefully-if-no-changes-pulled was set!")
elif inOptions.sendEmailTo and abortGracefullyDueToNoChangesToPush:
print("\nSkipping sending final email because there are no local "
"changes to push and --abort-gracefully-if-no-changes-to-push "
"was set!")
elif inOptions.sendEmailTo and allConfiguresAbortedDueToNoEnablesGracefullAbort:
print("\nSkipping sending final email because there were no enables"
" and --abort-gracefully-if-no-enables was set!")
elif inOptions.sendEmailTo and inOptions.sendEmailOnlyOnFailure and success:
print("\nSkipping sending final email because it passed"
" and --send-email-only-on-failure was set!")
elif inOptions.sendEmailTo:
emailAddresses = getEmailAddressesSpaceString(inOptions.sendEmailTo)
if inOptions.sendEmailToOnPush and didPush:
emailAddresses += " " + getEmailAddressesSpaceString(inOptions.sendEmailToOnPush)
echoRunSysCmnd("mailx -s \""+subjectLine+"\" " \
+emailAddresses+" < "+summaryCommitEmailBodyFileName)
else:
print("\nNot sending push readiness status email because --send-email-"
"to is empty!")
else:
print("\nNot performing push or sending out push readiness status on "
"request!")
if pushPassed and didAtLeastOnePush and didPush:
cleanSuccessFiles(buildTestCaseList, inOptions, baseTestDir)
print("\n***")
print("*** 10) Run execute extra command on ready to push ...")
print("***")
if inOptions.executeOnReadyToPush and okayToPush:
print(executeCmndStr)
extraCommandRtn = echoRunSysCmnd(
inOptions.executeOnReadyToPush,
workingDir=baseTestDir,
outFile=os.path.join(baseTestDir, getExtraCommandOutputFileName()),
throwExcept=False, timeCmnd=True )
if extraCommandRtn == 0:
print("\nExtra command passed!\n")
else:
print("\nExtra command failed!\n")
success = False
else:
print("\nNot executing final command ...\n")
if not performAnyActions(inOptions) and not inOptions.doPush:
print("\n***\n"
"*** WARNING: No actions were performed!\n"
"***\n"
"*** Hint: Specify --do-all to perform full integration pull/build/test\n"
"*** or --push to push the commits for a previously run test!\n"
"***\n\n")
except Exception as e:
success = False
printStackTrace()
g_sysCmndInterceptor.assertAllCommandsRun()
# Print the final status at the very end
if subjectLine:
print("\n\n" + subjectLine + "\n\n")
return success