mirror of https://github.com/asterisk/asterisk
				
				
				
			
			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.
		
		
		
		
		
			
		
			
				
					
					
						
							502 lines
						
					
					
						
							18 KiB
						
					
					
				
			
		
		
	
	
							502 lines
						
					
					
						
							18 KiB
						
					
					
				| #
 | |
| # builder.py - PJSIP test scenarios builder
 | |
| #
 | |
| # Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
 | |
| #
 | |
| # This program is free software; you can redistribute it and/or modify
 | |
| # it under the terms of the GNU General Public License as published by
 | |
| # the Free Software Foundation; either version 2 of the License, or
 | |
| # (at your option) any later version.
 | |
| #
 | |
| # This program is distributed in the hope that it will be useful,
 | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| # GNU General Public License for more details.
 | |
| #
 | |
| # You should have received a copy of the GNU General Public License
 | |
| # along with this program; if not, write to the Free Software
 | |
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
| #
 | |
| 
 | |
| import ccdash
 | |
| import os
 | |
| import platform
 | |
| import re
 | |
| import subprocess
 | |
| import sys
 | |
| import time
 | |
| 
 | |
| class Operation:
 | |
|     """\
 | |
|     The Operation class describes the individual ccdash operation to be 
 | |
|     performed.
 | |
| 
 | |
|     """
 | |
|     # Types:
 | |
|     UPDATE = "update"           # Update operation
 | |
|     CONFIGURE = "configure"     # Configure operation
 | |
|     BUILD = "build"             # Build operation
 | |
|     TEST = "test"               # Unit test operation
 | |
| 
 | |
|     def __init__(self, type, cmdline, name="", wdir=""):
 | |
|         self.type = type
 | |
|         self.cmdline = cmdline
 | |
|         self.name = name
 | |
|         self.wdir = wdir
 | |
|         if self.type==self.TEST and not self.name:
 | |
|             raise "name required for tests"
 | |
| 
 | |
|     def encode(self, base_dir):
 | |
|         s = [self.type]
 | |
|         if self.type == self.TEST:
 | |
|             s.append(self.name)
 | |
|         if self.type != self.UPDATE:
 | |
|             s.append(self.cmdline)
 | |
|         s.append("-w")
 | |
|         if self.wdir:
 | |
|             s.append(base_dir + "/" + self.wdir)
 | |
|         else:
 | |
|             s.append(base_dir)
 | |
|         return s
 | |
| 
 | |
| 
 | |
| #
 | |
| # Update operation
 | |
| #
 | |
| update_ops = [Operation(Operation.UPDATE, "")]
 | |
| 
 | |
| #
 | |
| # The standard library tests (e.g. pjlib-test, pjsip-test, etc.)
 | |
| #
 | |
| std_test_ops= [
 | |
|     Operation(Operation.TEST, "./pjlib-test$SUFFIX", name="pjlib test",
 | |
|               wdir="pjlib/bin"),
 | |
|     Operation(Operation.TEST, "./pjlib-util-test$SUFFIX", 
 | |
|               name="pjlib-util test", wdir="pjlib-util/bin"),
 | |
|     Operation(Operation.TEST, "./pjnath-test$SUFFIX", name="pjnath test",
 | |
|               wdir="pjnath/bin"),
 | |
|     Operation(Operation.TEST, "./pjmedia-test$SUFFIX", name="pjmedia test",
 | |
|               wdir="pjmedia/bin"),
 | |
|     Operation(Operation.TEST, "./pjsip-test$SUFFIX", name="pjsip test",
 | |
|               wdir="pjsip/bin")
 | |
| ]
 | |
| 
 | |
| #
 | |
| # These are pjsua Python based unit test operations
 | |
| #
 | |
| def build_pjsua_test_ops(pjsua_exe=""):
 | |
|     ops = []
 | |
|     if pjsua_exe:
 | |
|         exe = " -e ../../pjsip-apps/bin/" + pjsua_exe
 | |
|     else:
 | |
|         exe = ""
 | |
|     cwd = os.getcwd()
 | |
|     os.chdir("../pjsua")
 | |
|     os.system("python runall.py --list > list")
 | |
|     f = open("list", "r")
 | |
|     for e in f:
 | |
|         e = e.rstrip("\r\n ")
 | |
|         (mod,param) = e.split(None,2)
 | |
|         name = mod[4:mod.find(".py")] + "_" + \
 | |
|                param[param.find("/")+1:param.find(".py")]
 | |
|         ops.append(Operation(Operation.TEST, "python run.py" + exe + " " + \
 | |
|                              e, name=name, wdir="tests/pjsua"))
 | |
|     f.close()
 | |
|     os.remove("list") 
 | |
|     os.chdir(cwd)
 | |
|     return ops
 | |
| 
 | |
| #
 | |
| # Get gcc version
 | |
| #
 | |
| def gcc_version(gcc):
 | |
|     proc = subprocess.Popen(gcc + " -v", stdout=subprocess.PIPE,
 | |
|                             stderr=subprocess.STDOUT, shell=True)
 | |
|     ver = ""
 | |
|     while True:
 | |
|         s = proc.stdout.readline()
 | |
|         if not s:
 | |
|             break
 | |
|         if s.find("gcc version") >= 0:
 | |
|             ver = s.split(None, 3)[2]
 | |
|             break
 | |
|     proc.wait()
 | |
|     return "gcc-" + ver
 | |
| 
 | |
| #
 | |
| # Get Visual Studio version
 | |
| #
 | |
| def vs_get_version():
 | |
|     proc = subprocess.Popen("cl", stdout=subprocess.PIPE,
 | |
|                             stderr=subprocess.STDOUT)
 | |
|     while True:
 | |
|         s = proc.stdout.readline()
 | |
|         if s=="":
 | |
|             break
 | |
|         pos = s.find("Version")
 | |
|         if pos > 0:
 | |
|             proc.wait()
 | |
|             s = s[pos+8:]
 | |
|             ver = s.split(None, 1)[0]
 | |
|             major = ver[0:2]
 | |
|             if major=="12":
 | |
|                 return "vs6"
 | |
|             elif major=="13":
 | |
|                 return "vs2003"
 | |
|             elif major=="14":
 | |
|                 return "vs2005"
 | |
|             elif major=="15":
 | |
|                 return "vs2008"
 | |
|             else:
 | |
|                 return "vs-" + major
 | |
|     proc.wait()
 | |
|     return "vs-unknown"
 | |
|     
 | |
| 
 | |
| #
 | |
| # Test config
 | |
| #
 | |
| class BaseConfig:
 | |
|     def __init__(self, base_dir, url, site, group, options=None):
 | |
|         self.base_dir = base_dir
 | |
|         self.url = url
 | |
|         self.site = site
 | |
|         self.group = group
 | |
|         self.options = options
 | |
| 
 | |
| #
 | |
| # Base class for test configurator
 | |
| #
 | |
| class TestBuilder:
 | |
|     def __init__(self, config, build_config_name="",
 | |
|                  user_mak="", config_site="", exclude=[], not_exclude=[]):
 | |
|         self.config = config                        # BaseConfig instance
 | |
|         self.build_config_name = build_config_name  # Optional build suffix
 | |
|         self.user_mak = user_mak                    # To be put in user.mak
 | |
|         self.config_site = config_site              # To be put in config_s..
 | |
|         self.saved_user_mak = ""                    # To restore user.mak
 | |
|         self.saved_config_site = ""                 # To restore config_s..
 | |
|         self.exclude = exclude                      # List of exclude pattern
 | |
|         self.not_exclude = not_exclude              # List of include pattern
 | |
|         self.ccdash_args = []                       # ccdash cmd line
 | |
| 
 | |
|     def stamp(self):
 | |
|         return time.strftime("%Y%m%d-%H%M", time.localtime())
 | |
| 
 | |
|     def pre_action(self):
 | |
|         # Override user.mak
 | |
|         name = self.config.base_dir + "/user.mak"
 | |
|         if os.access(name, os.F_OK):
 | |
|             f = open(name, "r")
 | |
|             self.saved_user_mak = f.read()
 | |
|             f.close()
 | |
|         if True:
 | |
|             f = open(name, "w")
 | |
|             f.write(self.user_mak)
 | |
|             f.close()
 | |
|         # Override config_site.h
 | |
|         name = self.config.base_dir + "/pjlib/include/pj/config_site.h"
 | |
|         if os.access(name, os.F_OK):
 | |
|             f = open(name, "r")
 | |
|             self.saved_config_site= f.read()
 | |
|             f.close()
 | |
|         if True:
 | |
|             f = open(name, "wt")
 | |
|             f.write(self.config_site)
 | |
|             f.close()
 | |
| 
 | |
| 
 | |
|     def post_action(self):
 | |
|         # Restore user.mak
 | |
|         name = self.config.base_dir + "/user.mak"
 | |
|         f = open(name, "wt")
 | |
|         f.write(self.saved_user_mak)
 | |
|         f.close()
 | |
|         # Restore config_site.h
 | |
|         name = self.config.base_dir + "/pjlib/include/pj/config_site.h"
 | |
|         f = open(name, "wt")
 | |
|         f.write(self.saved_config_site)
 | |
|         f.close()
 | |
| 
 | |
|     def build_tests(self):
 | |
|         # This should be overridden by subclasses
 | |
|         pass
 | |
| 
 | |
|     def execute(self):
 | |
|         if len(self.ccdash_args)==0:
 | |
|             self.build_tests()
 | |
|         self.pre_action()
 | |
| 	mandatory_op = ["update", "configure", "build"]
 | |
|         counter = 0
 | |
|         for a in self.ccdash_args:
 | |
|             # Check if this test is in exclusion list
 | |
|             fullcmd = " ".join(a)
 | |
|             excluded = False
 | |
|             included = False
 | |
|             for pat in self.exclude:
 | |
|                 if pat and re.search(pat, fullcmd) != None:
 | |
|                     excluded = True
 | |
|                     break
 | |
|             if excluded:
 | |
|                 for pat in self.not_exclude:
 | |
|                     if pat and re.search(pat, fullcmd) != None:
 | |
|                         included = True
 | |
|                         break
 | |
|             if excluded and not included:
 | |
|                 if len(fullcmd)>60:
 | |
|                     fullcmd = fullcmd[0:60] + ".."
 | |
|                 print "Skipping '%s'" % (fullcmd)
 | |
|                 continue
 | |
| 
 | |
|             b = ["ccdash.py"]
 | |
|             b.extend(a)
 | |
|             a = b
 | |
|             #print a
 | |
|             try:
 | |
|                 rc = ccdash.main(a)
 | |
|             except Exception, e:
 | |
|                 errmsg = str(e)
 | |
|                 print "**** Error: ccdash got exception %s ****" % errmsg
 | |
|                 rc = -1
 | |
|             except:
 | |
|                 print "**** Error: ccdash got unknown exception ****"
 | |
|                 rc = -1
 | |
|                 
 | |
| 	    if rc!=0 and a[1] in mandatory_op:
 | |
| 		print "Stopping because of error.."
 | |
| 		break
 | |
|             counter = counter + 1
 | |
|         self.post_action()
 | |
| 
 | |
| 
 | |
| #
 | |
| # GNU test configurator
 | |
| #
 | |
| class GNUTestBuilder(TestBuilder):
 | |
|     """\
 | |
|     This class creates list of tests suitable for GNU targets.
 | |
| 
 | |
|     """
 | |
|     def __init__(self, config, build_config_name="", user_mak="", \
 | |
|                  config_site="", cross_compile="", exclude=[], not_exclude=[]):
 | |
|         """\
 | |
|         Parameters:
 | |
|         config              - BaseConfig instance
 | |
|         build_config_name   - Optional name to be added as suffix to the build
 | |
|                               name. Sample: "min-size", "O4", "TLS", etc.
 | |
|         user_mak            - Contents to be put on user.mak
 | |
|         config_site         - Contents to be put on config_site.h
 | |
|         cross_compile       - Optional cross-compile prefix. Must include the
 | |
|                               trailing dash, e.g. "arm-unknown-linux-"
 | |
|         exclude             - List of regular expression patterns for tests
 | |
|                               that will be excluded from the run
 | |
|         not_exclude         - List of regular expression patterns for tests
 | |
|                               that will be run regardless of whether they
 | |
|                               match the excluded pattern.
 | |
| 
 | |
|         """
 | |
|         TestBuilder.__init__(self, config, build_config_name=build_config_name,
 | |
|                              user_mak=user_mak, config_site=config_site,
 | |
|                              exclude=exclude, not_exclude=not_exclude)
 | |
|         self.cross_compile = cross_compile
 | |
|         if self.cross_compile and self.cross_compile[-1] != '-':
 | |
|             self.cross_compile.append("-")
 | |
| 
 | |
|     def build_tests(self):
 | |
|         if self.cross_compile:
 | |
|             suffix = "-" + self.cross_compile[0:-1]
 | |
|             build_name =  self.cross_compile + \
 | |
|                           gcc_version(self.cross_compile + "gcc")
 | |
|         else:
 | |
|             proc = subprocess.Popen("sh "+self.config.base_dir+"/config.guess",
 | |
|                                     shell=True, stdout=subprocess.PIPE)
 | |
|             plat = proc.stdout.readline().rstrip(" \r\n")
 | |
|             build_name =  plat + "-"+gcc_version(self.cross_compile + "gcc")
 | |
|             suffix = "-" + plat
 | |
| 
 | |
|         if self.build_config_name:
 | |
|             build_name = build_name + "-" + self.build_config_name
 | |
|         cmds = []
 | |
|         cmds.extend(update_ops)
 | |
| 	cmds.append(Operation(Operation.CONFIGURE, "sh ./configure"))
 | |
| 	if sys.platform=="win32":
 | |
| 	    # Don't build python module on Mingw
 | |
| 	    cmds.append(Operation(Operation.BUILD, 
 | |
| 			    "sh -c 'make distclean && make dep && make'"))
 | |
| 	else:
 | |
| 	    cmds.append(Operation(Operation.BUILD, 
 | |
| 			    "sh -c 'make distclean && make dep && make" + \
 | |
| 			    " && cd pjsip-apps/src/python && " + \
 | |
| 			    "python setup.py clean build'"))
 | |
| 
 | |
|         cmds.extend(std_test_ops)
 | |
|         cmds.extend(build_pjsua_test_ops())
 | |
|         self.ccdash_args = []
 | |
|         for c in cmds:
 | |
|             c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
 | |
|             args = c.encode(self.config.base_dir)
 | |
|             args.extend(["-U", self.config.url, 
 | |
|                          "-S", self.config.site, 
 | |
|                          "-T", self.stamp(), 
 | |
|                          "-B", build_name, 
 | |
|                          "-G", self.config.group])
 | |
|             args.extend(self.config.options)
 | |
|             self.ccdash_args.append(args)
 | |
| 
 | |
| #
 | |
| # MSVC test configurator
 | |
| #
 | |
| class MSVCTestBuilder(TestBuilder):
 | |
|     """\
 | |
|     This class creates list of tests suitable for Visual Studio builds. 
 | |
|     You need to set the MSVC environment variables (typically by calling
 | |
|     vcvars32.bat) prior to running this class.
 | |
|     
 | |
|     """
 | |
|     def __init__(self, config, target="Release|Win32", build_config_name="", 
 | |
|                  config_site="", exclude=[], not_exclude=[]):
 | |
|         """\
 | |
|         Parameters:
 | |
|         config              - BaseConfig instance
 | |
|         target              - Visual Studio build configuration to build.
 | |
|                               Sample: "Debug|Win32", "Release|Win32".
 | |
|         build_config_name   - Optional name to be added as suffix to the build
 | |
|                               name. Sample: "Debug", "Release", "IPv6", etc.
 | |
|         config_site         - Contents to be put on config_site.h
 | |
|         exclude             - List of regular expression patterns for tests
 | |
|                               that will be excluded from the run
 | |
|         not_exclude         - List of regular expression patterns for tests
 | |
|                               that will be run regardless of whether they
 | |
|                               match the excluded pattern.
 | |
| 
 | |
|         """
 | |
|         TestBuilder.__init__(self, config, build_config_name=build_config_name,
 | |
|                              config_site=config_site, exclude=exclude, 
 | |
|                              not_exclude=not_exclude)
 | |
|         self.target = target.lower()
 | |
| 
 | |
|     def build_tests(self):
 | |
|        
 | |
|         (vsbuild,sys) = self.target.split("|",2)
 | |
|         
 | |
|         build_name = sys + "-" + vs_get_version() + "-" + vsbuild
 | |
| 
 | |
|         if self.build_config_name:
 | |
|             build_name = build_name + "-" + self.build_config_name
 | |
| 
 | |
|         vccmd = "vcbuild.exe /nologo /nohtmllog /nocolor /rebuild " + \
 | |
|                 "pjproject-vs8.sln " + " \"" + self.target + "\""
 | |
|         
 | |
|         suffix = "-i386-win32-vc8-" + vsbuild
 | |
|         pjsua = "pjsua_vc8"
 | |
|         if vsbuild=="debug":
 | |
|             pjsua = pjsua + "d"
 | |
|         
 | |
|         cmds = []
 | |
|         cmds.extend(update_ops)
 | |
|         cmds.append(Operation(Operation.CONFIGURE, "CMD /C echo Nothing to do"))
 | |
|         cmds.append(Operation(Operation.BUILD, vccmd))
 | |
|         cmds.extend(std_test_ops)
 | |
|         cmds.extend(build_pjsua_test_ops(pjsua))
 | |
| 
 | |
|         self.ccdash_args = []
 | |
|         for c in cmds:
 | |
|             c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
 | |
|             args = c.encode(self.config.base_dir)
 | |
|             args.extend(["-U", self.config.url, 
 | |
|                          "-S", self.config.site, 
 | |
|                          "-T", self.stamp(), 
 | |
|                          "-B", build_name, 
 | |
|                          "-G", self.config.group])
 | |
|             args.extend(self.config.options)
 | |
|             self.ccdash_args.append(args)
 | |
| 
 | |
| 
 | |
| #
 | |
| # Symbian test configurator
 | |
| #
 | |
| class SymbianTestBuilder(TestBuilder):
 | |
|     """\
 | |
|     This class creates list of tests suitable for Symbian builds. You need to
 | |
|     set the command line build settings prior to running this class (typically
 | |
|     that involves setting the EPOCROOT variable and current device).
 | |
|     
 | |
|     """
 | |
|     def __init__(self, config, target="gcce urel", build_config_name="", 
 | |
|                  config_site="", exclude=[], not_exclude=[]):
 | |
|         """\
 | |
|         Parameters:
 | |
|         config              - BaseConfig instance
 | |
|         target              - Symbian target to build. Default is "gcce urel".
 | |
|         build_config_name   - Optional name to be added as suffix to the build
 | |
|                               name. Sample: "APS", "VAS", etc.
 | |
|         config_site         - Contents to be put on config_site.h
 | |
|         exclude             - List of regular expression patterns for tests
 | |
|                               that will be excluded from the run
 | |
|         not_exclude         - List of regular expression patterns for tests
 | |
|                               that will be run regardless of whether they
 | |
|                               match the excluded pattern.
 | |
| 
 | |
|         """
 | |
|         TestBuilder.__init__(self, config, build_config_name=build_config_name,
 | |
|                              config_site=config_site, exclude=exclude, 
 | |
|                              not_exclude=not_exclude)
 | |
|         self.target = target.lower()
 | |
|         
 | |
|     def build_tests(self):
 | |
|        
 | |
|         # Check that EPOCROOT is set
 | |
|         if not "EPOCROOT" in os.environ:
 | |
|             print "Error: EPOCROOT environment variable is not set"
 | |
|             sys.exit(1)
 | |
|         epocroot = os.environ["EPOCROOT"]
 | |
|         # EPOCROOT must have trailing backslash
 | |
|         if epocroot[-1] != "\\":
 | |
|             epocroot = epocroot + "\\"
 | |
|             os.environ["EPOCROOT"] = epocroot
 | |
|         sdk1 = epocroot.split("\\")[-2]
 | |
| 
 | |
|         # Check that correct device is set
 | |
|         proc = subprocess.Popen("devices", stdout=subprocess.PIPE,
 | |
|                                 stderr=subprocess.STDOUT, shell=True)
 | |
|         sdk2 = ""
 | |
|         while True:
 | |
|             line = proc.stdout.readline()
 | |
|             if line.find("- default") > 0:
 | |
|                 sdk2 = line.split(":",1)[0]
 | |
|                 break
 | |
|         proc.wait()
 | |
| 
 | |
|         if sdk1 != sdk2:
 | |
|             print "Error: default SDK in device doesn't match EPOCROOT"
 | |
|             print "Default device SDK =", sdk2
 | |
|             print "EPOCROOT SDK =", sdk1
 | |
|             sys.exit(1)
 | |
| 
 | |
|         build_name = sdk2.replace("_", "-") + "-" + \
 | |
|                      self.target.replace(" ", "-")
 | |
| 
 | |
|         if self.build_config_name:
 | |
|             build_name = build_name + "-" + self.build_config_name
 | |
| 
 | |
|         cmdline = "cmd /C \"cd build.symbian && bldmake bldfiles && abld build %s\"" % (self.target)
 | |
|         
 | |
|         cmds = []
 | |
|         cmds.extend(update_ops)
 | |
|         cmds.append(Operation(Operation.CONFIGURE, "CMD /C echo Nothing to do"))
 | |
|         cmds.extend([Operation(Operation.BUILD, cmdline)])
 | |
| 
 | |
|         self.ccdash_args = []
 | |
|         suffix = ""
 | |
|         for c in cmds:
 | |
|             c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
 | |
|             args = c.encode(self.config.base_dir)
 | |
|             args.extend(["-U", self.config.url, 
 | |
|                          "-S", self.config.site, 
 | |
|                          "-T", self.stamp(), 
 | |
|                          "-B", build_name, 
 | |
|                          "-G", self.config.group])
 | |
|             args.extend(self.config.options)
 | |
|             self.ccdash_args.append(args)
 | |
| 
 |