123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- #
- # 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)
|