bvtest.py 7.32 KB
Newer Older
whitlocb's avatar
whitlocb committed
1 2
#!/usr/bin/env python
# *****************************************************************************
3
#   Script: bvtest.py
whitlocb's avatar
whitlocb committed
4 5 6 7
#
#   Purpose:
#       Provide an easy (and cron-able) way to test the build_visit script.
#   Usage:
8
#       bvtest.py [output_dir] [lib1] <lib2> ... <libn>
whitlocb's avatar
whitlocb committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
#   
#   Results:
#       Outputs a xml summary of build results to stdout.
# 
# *****************************************************************************
# Current Functionality: 
# foreach lib invoke build_visit with the following options:
#  build_visit --console --no-visit --no-thirdparty --no-hostconf --stdout \ 
#          --[libname] --thirdparty-path  [output] > [output]/bv_log_[libname]
#  Check for "Finished!" at tail of output log
#
# Possible Future Functionality:
#  Cleanup after (each?) build.
#  Figure out $VISITARCH and version of each lib &check for lib installed
#
# *****************************************************************************

26
import sys,os,time,socket,subprocess
whitlocb's avatar
whitlocb committed
27

cyrush's avatar
cyrush committed
28 29 30 31 32 33 34
def sexe(cmd):
    """
    Execute a shell command.
    """
    sys.stderr.write("[exe:%s]\n" % cmd)
    os.system(cmd)

35 36 37 38 39 40 41 42
def sub_pipe(cmd):
    """
    Execute a shell command and return the result as string.
    """
    proc = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
    res = proc.communicate()[0]
    return res.strip()
    
cyrush's avatar
cyrush committed
43 44 45 46 47
def current_time():
    """
    Helper to return a human readble string /w the current time.
    """
    return time.strftime("%Y-%m-%d %H:%M:%S")
48
    
cyrush's avatar
cyrush committed
49
class BuildResult(object):
whitlocb's avatar
whitlocb committed
50 51 52
    """
    Class that holds the results for an attemped build of a library.
    """
53
    def __init__(self,lib,cmd,log,build_start,build_end,result):
whitlocb's avatar
whitlocb committed
54 55
        self.lib = lib
        self.cmd = cmd
56
        self.log = log
whitlocb's avatar
whitlocb committed
57 58 59 60 61 62 63
        self.build_start = build_start
        self.build_end   = build_end
        self.result = result 
    def to_xml(self,space=""):
        res =  space + "<build_result>\n"
        res += space + space +"<lib>%s</lib>\n" % self.lib
        res += space + space +"<cmd>%s</cmd>\n" % self.cmd
64
        res += space + space +"<log>%s</log>\n" % self.log
whitlocb's avatar
whitlocb committed
65 66 67 68 69 70
        res += space + space +"<start>%s</start>\n" % self.build_start
        res += space + space +"<end>%s</end>\n"  % self.build_end
        res += space + space +"<result>%s</result>\n" % self.result
        res += space + "</build_result>"
        return res

cyrush's avatar
cyrush committed
71 72 73 74 75 76 77 78 79 80 81 82
class BuildTest(object):
    """
    Class executes build_visit tests and holds a set of results.
    """
    def __init__(self,output_dir,libcmds,bvexe,mode = "svn"):
        self.odir = os.path.abspath(output_dir)
        if self.odir[-1] == "/":
            self.odir = self.odir[:-1]
        self.libcmds = libcmds
        self.bvexe = bvexe
        self.mode = mode
        self.host = socket.gethostname()
83 84
        self.user = os.environ['USER']
        self.arch = self.__get_arch()
cyrush's avatar
cyrush committed
85 86 87
        self.start_time = None
        self.end_time   = None
        self.results    = []
88

cyrush's avatar
cyrush committed
89 90 91 92
    def execute(self):
        self.start_time = current_time()
        self.results    = self.build_libs(self.libcmds)
        self.end_time   = current_time()
93

cyrush's avatar
cyrush committed
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
    def build_lib(self,libcmd):
        """
        Build a library using build_visit and return a BuildResult.
        """
        # make sure output dir exists!
        if self.odir != "":
            if not os.path.exists(self.odir):
                os.mkdir(self.odir)
        libs  = self.__parse_libcmd(libcmd)
        bvc   = self.__build_visit_command(libs);
        lib   = libs[-1]
        lfile = self.__log_file_name(lib)
        bresult = "failure"
        bstart = current_time()
        sexe(bvc)
        bend = current_time()
        if self.__check_build(lib):
            bresult = "success"
        return BuildResult(lib,libcmd,lfile,bstart,bend,bresult)
113

cyrush's avatar
cyrush committed
114 115 116 117 118
    def build_libs(self,libcmds):
        """
        Build a collection of libraries using build_visit and return a BuildResult for each.
        """
        return [ self.build_lib(libcmd) for libcmd in libcmds ]
119

cyrush's avatar
cyrush committed
120 121
    def result_xml(self):
        res  = '<?xml version="1.0"?>\n'
122
        res += '<?xml-stylesheet type="text/xsl" href="bvtest.xsl"?>\n'
cyrush's avatar
cyrush committed
123
        info  = 'host="%s" '  % self.host
124 125
        info += 'user="%s" '  % self.user
        info += 'arch="%s" '  % self.arch
cyrush's avatar
cyrush committed
126 127 128 129 130 131 132 133 134 135 136
        info += 'odir="%s" '  % self.odir
        info += 'libs="%s" '  % str(self.libcmds)
        info += 'start="%s" ' % self.start_time
        info += 'end="%s" '   % self.end_time
        res += "<bvtest %s >\n" % info
        res += "  <results>\n"
        for r in self.results:
            res += r.to_xml("  ")
        res +="  </results>\n"
        res +="</bvtest>\n"
        return res
137

cyrush's avatar
cyrush committed
138 139 140 141
    def __log_file_name(self,lib):
        """
        Construct the output log file name.
        """
142
        return "bv_log_%s.txt" % (lib)
143

cyrush's avatar
cyrush committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
    def __build_visit_command(self,libs):
        """
        Construct the proper build_visit command.
        """
        options = "--console --no-visit --no-thirdparty --no-hostconf --stdout --makeflags -j4"
        if self.mode == "svn":
            options = "--svn HEAD " + options
        blibs=""
        for l in libs:
            blibs+="--%s " % l
        lib = libs[-1]
        if self.odir == "":
            cmd = "echo yes | %s %s %s >> %s"
            cmd = cmd % (self.bvexe,options,blibs,self.__log_file_name(lib))
        else:
            cmd = "echo yes | %s %s %s --thirdparty-path  %s >> %s"
            cmd = cmd % (self.bvexe,options,blibs,self.odir,self.__log_file_name(lib))
        return cmd
whitlocb's avatar
whitlocb committed
162

cyrush's avatar
cyrush committed
163 164 165 166 167 168 169 170 171 172 173
    def __check_build(self,lib):
        """
        Checks for sucessful build by examining the end of the build log.
        """
        try:
            lines = open(self.__log_file_name(lib)).readlines()
            nlines = len(lines)
            last = lines[nlines-1].strip()
            return last == "Finished!"
        except:
            return False
whitlocb's avatar
whitlocb committed
174

cyrush's avatar
cyrush committed
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
    def __parse_libcmd(self,vstr):
        """
        Parses two simple build command types:
            lib --> [lib]
            lib_3:lib_1,lib_2 -->  [lib_1,lib_2,lib_3]
        """
        res = []
        cidx = vstr.find(":")
        if cidx >0:
            pre  = vstr[cidx+1:].split(",")
            post = vstr[:cidx]
            for p in pre:
                res.append(p)
            res.append(post)
        else:
            res.append(vstr)
        return res
192 193 194 195 196 197 198 199 200 201 202 203

    def __get_arch(self):
        """
        Helper to return a human readable string describing the current platform.
        """
        opsys = sub_pipe("uname -s").lower()
        proc = sub_pipe("uname -p")
        comp = "gcc"
        comp_ver = ""
        # use gcc everwhere except AIX
        if opsys == "aix":
            comp = "xlc"
204 205
        
        res = "%s-%s-%s" % (opsys,proc,comp)
206 207
        # get gcc version
        if comp == "gcc": 
208 209
            sub_res = sub_pipe("gcc -v 2>&1")
            tok = sub_res[sub_res.find("gcc version")+len("gcc version"):].split()
210 211
            if len(tok) > 0:
                comp_ver = tok[0]
212 213 214
        if comp_ver !="":
            res += "-%s" % comp_ver;
        return res
215

whitlocb's avatar
whitlocb committed
216 217 218 219 220 221
def main():
    """
    Main driver routine.
    """
    nargs = len(sys.argv)
    if nargs < 3:
222
        sys.stderr.write("usage: bvtest.py [output_dir] [lib_1] <lib_2> .... <lib_n>\n")
whitlocb's avatar
whitlocb committed
223
        sys.exit(1)
cyrush's avatar
cyrush committed
224
    odir    = sys.argv[1]
whitlocb's avatar
whitlocb committed
225
    libcmds = sys.argv[2:]
cyrush's avatar
cyrush committed
226 227 228
    bt = BuildTest(odir,libcmds,"./build_visit")
    bt.execute()
    print bt.result_xml()
whitlocb's avatar
whitlocb committed
229 230 231 232 233
    

if __name__ == "__main__":
    main()