blob: 79066e8a12ef024ead477fe9eba93cf2181701e0 [file] [log] [blame]
Benny Prijono9be224f2008-12-29 17:57:13 +00001#
2# builder.py - PJSIP test scenarios builder
3#
4# Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19#
20
Benny Prijono49048d92008-12-29 14:56:32 +000021import ccdash
22import os
23import platform
24import re
25import subprocess
26import sys
27import time
28
29class Operation:
30 """\
31 The Operation class describes the individual ccdash operation to be
32 performed.
33
34 """
35 # Types:
36 UPDATE = "update" # Update operation
37 CONFIGURE = "configure" # Configure operation
38 BUILD = "build" # Build operation
39 TEST = "test" # Unit test operation
40
41 def __init__(self, type, cmdline, name="", wdir=""):
42 self.type = type
43 self.cmdline = cmdline
44 self.name = name
45 self.wdir = wdir
46 if self.type==self.TEST and not self.name:
47 raise "name required for tests"
48
49 def encode(self, base_dir):
50 s = [self.type]
51 if self.type == self.TEST:
52 s.append(self.name)
53 if self.type != self.UPDATE:
54 s.append(self.cmdline)
55 s.append("-w")
56 if self.wdir:
57 s.append(base_dir + "/" + self.wdir)
58 else:
59 s.append(base_dir)
60 return s
61
62
63#
64# Update operation
65#
66update_ops = [Operation(Operation.UPDATE, "")]
67
68#
69# The standard library tests (e.g. pjlib-test, pjsip-test, etc.)
70#
71std_test_ops= [
Benny Prijono0b262932008-12-30 16:19:38 +000072 Operation(Operation.TEST, "./pjlib-test$SUFFIX", name="pjlib test",
Benny Prijono49048d92008-12-29 14:56:32 +000073 wdir="pjlib/bin"),
Benny Prijono0b262932008-12-30 16:19:38 +000074 Operation(Operation.TEST, "./pjlib-util-test$SUFFIX",
Benny Prijono49048d92008-12-29 14:56:32 +000075 name="pjlib-util test", wdir="pjlib-util/bin"),
Benny Prijono0b262932008-12-30 16:19:38 +000076 Operation(Operation.TEST, "./pjnath-test$SUFFIX", name="pjnath test",
Benny Prijono49048d92008-12-29 14:56:32 +000077 wdir="pjnath/bin"),
Benny Prijono0b262932008-12-30 16:19:38 +000078 Operation(Operation.TEST, "./pjmedia-test$SUFFIX", name="pjmedia test",
Benny Prijono49048d92008-12-29 14:56:32 +000079 wdir="pjmedia/bin"),
Benny Prijono0b262932008-12-30 16:19:38 +000080 Operation(Operation.TEST, "./pjsip-test$SUFFIX", name="pjsip test",
Benny Prijono49048d92008-12-29 14:56:32 +000081 wdir="pjsip/bin")
82]
83
84#
Benny Prijono49048d92008-12-29 14:56:32 +000085# These are pjsua Python based unit test operations
86#
Benny Prijono0b262932008-12-30 16:19:38 +000087def build_pjsua_test_ops(pjsua_exe=""):
Benny Prijono49048d92008-12-29 14:56:32 +000088 ops = []
Benny Prijono0b262932008-12-30 16:19:38 +000089 if pjsua_exe:
90 exe = " -e ../../pjsip-apps/bin/" + pjsua_exe
91 else:
92 exe = ""
Benny Prijono49048d92008-12-29 14:56:32 +000093 cwd = os.getcwd()
94 os.chdir("../pjsua")
95 os.system("python runall.py --list > list")
96 f = open("list", "r")
97 for e in f:
98 e = e.rstrip("\r\n ")
99 (mod,param) = e.split(None,2)
100 name = mod[4:mod.find(".py")] + "_" + \
101 param[param.find("/")+1:param.find(".py")]
Benny Prijono0b262932008-12-30 16:19:38 +0000102 ops.append(Operation(Operation.TEST, "python run.py" + exe + " " + \
103 e, name=name, wdir="tests/pjsua"))
104 f.close()
105 os.remove("list")
Benny Prijono49048d92008-12-29 14:56:32 +0000106 os.chdir(cwd)
107 return ops
108
109#
110# Get gcc version
111#
112def gcc_version(gcc):
113 proc = subprocess.Popen(gcc + " -v", stdout=subprocess.PIPE,
114 stderr=subprocess.STDOUT, shell=True)
115 ver = ""
116 while True:
117 s = proc.stdout.readline()
118 if not s:
119 break
120 if s.find("gcc version") >= 0:
121 ver = s.split(None, 3)[2]
122 break
123 proc.wait()
124 return "gcc-" + ver
125
126#
Benny Prijono0b262932008-12-30 16:19:38 +0000127# Get Visual Studio version
128#
129def vs_get_version():
130 proc = subprocess.Popen("cl", stdout=subprocess.PIPE,
131 stderr=subprocess.STDOUT)
132 while True:
133 s = proc.stdout.readline()
134 if s=="":
135 break
136 pos = s.find("Version")
137 if pos > 0:
138 proc.wait()
139 s = s[pos+8:]
140 ver = s.split(None, 1)[0]
141 major = ver[0:2]
142 if major=="12":
143 return "vs6"
144 elif major=="13":
145 return "vs2003"
146 elif major=="14":
147 return "vs2005"
148 elif major=="15":
149 return "vs2008"
150 else:
151 return "vs-" + major
152 proc.wait()
153 return "vs-unknown"
154
155
156#
Benny Prijono49048d92008-12-29 14:56:32 +0000157# Test config
158#
159class BaseConfig:
160 def __init__(self, base_dir, url, site, group, options=None):
161 self.base_dir = base_dir
162 self.url = url
163 self.site = site
164 self.group = group
165 self.options = options
166
167#
168# Base class for test configurator
169#
170class TestBuilder:
171 def __init__(self, config, build_config_name="",
172 user_mak="", config_site="", exclude=[], not_exclude=[]):
173 self.config = config # BaseConfig instance
174 self.build_config_name = build_config_name # Optional build suffix
175 self.user_mak = user_mak # To be put in user.mak
176 self.config_site = config_site # To be put in config_s..
177 self.saved_user_mak = "" # To restore user.mak
178 self.saved_config_site = "" # To restore config_s..
179 self.exclude = exclude # List of exclude pattern
180 self.not_exclude = not_exclude # List of include pattern
181 self.ccdash_args = [] # ccdash cmd line
182
183 def stamp(self):
184 return time.strftime("%Y%m%d-%H%M", time.localtime())
185
186 def pre_action(self):
187 # Override user.mak
188 name = self.config.base_dir + "/user.mak"
189 if os.access(name, os.F_OK):
190 f = open(name, "r")
191 self.saved_user_mak = f.read()
192 f.close()
193 if True:
Benny Prijono456e58f2009-01-02 18:51:25 +0000194 f = open(name, "w")
Benny Prijono49048d92008-12-29 14:56:32 +0000195 f.write(self.user_mak)
196 f.close()
197 # Override config_site.h
198 name = self.config.base_dir + "/pjlib/include/pj/config_site.h"
199 if os.access(name, os.F_OK):
200 f = open(name, "r")
201 self.saved_config_site= f.read()
202 f.close()
203 if True:
204 f = open(name, "wt")
205 f.write(self.config_site)
206 f.close()
207
208
209 def post_action(self):
210 # Restore user.mak
211 name = self.config.base_dir + "/user.mak"
Benny Prijono0b262932008-12-30 16:19:38 +0000212 f = open(name, "wt")
213 f.write(self.saved_user_mak)
214 f.close()
Benny Prijono49048d92008-12-29 14:56:32 +0000215 # Restore config_site.h
216 name = self.config.base_dir + "/pjlib/include/pj/config_site.h"
Benny Prijono0b262932008-12-30 16:19:38 +0000217 f = open(name, "wt")
218 f.write(self.saved_config_site)
219 f.close()
Benny Prijono49048d92008-12-29 14:56:32 +0000220
221 def build_tests(self):
222 # This should be overridden by subclasses
223 pass
224
225 def execute(self):
226 if len(self.ccdash_args)==0:
227 self.build_tests()
228 self.pre_action()
Benny Prijonod5962672009-01-02 18:15:07 +0000229 mandatory_op = ["update", "configure", "build"]
Benny Prijono49048d92008-12-29 14:56:32 +0000230 counter = 0
231 for a in self.ccdash_args:
232 # Check if this test is in exclusion list
233 fullcmd = " ".join(a)
234 excluded = False
235 included = False
236 for pat in self.exclude:
Benny Prijono9be224f2008-12-29 17:57:13 +0000237 if pat and re.search(pat, fullcmd) != None:
Benny Prijono49048d92008-12-29 14:56:32 +0000238 excluded = True
239 break
240 if excluded:
241 for pat in self.not_exclude:
Benny Prijono9be224f2008-12-29 17:57:13 +0000242 if pat and re.search(pat, fullcmd) != None:
Benny Prijono49048d92008-12-29 14:56:32 +0000243 included = True
244 break
245 if excluded and not included:
Benny Prijono0b262932008-12-30 16:19:38 +0000246 if len(fullcmd)>60:
247 fullcmd = fullcmd[0:60] + ".."
248 print "Skipping '%s'" % (fullcmd)
Benny Prijono49048d92008-12-29 14:56:32 +0000249 continue
250
Benny Prijono49048d92008-12-29 14:56:32 +0000251 b = ["ccdash.py"]
252 b.extend(a)
253 a = b
254 #print a
Benny Prijonobf7b8422009-01-03 23:53:05 +0000255 try:
256 rc = ccdash.main(a)
257 except Exception, e:
258 errmsg = str(e)
259 print "**** Error: ccdash got exception %s ****" % errmsg
260 rc = -1
261 except:
262 print "**** Error: ccdash got unknown exception ****"
263 rc = -1
264
Benny Prijonod5962672009-01-02 18:15:07 +0000265 if rc!=0 and a[1] in mandatory_op:
266 print "Stopping because of error.."
267 break
Benny Prijono49048d92008-12-29 14:56:32 +0000268 counter = counter + 1
269 self.post_action()
270
271
272#
273# GNU test configurator
274#
275class GNUTestBuilder(TestBuilder):
Benny Prijono0b262932008-12-30 16:19:38 +0000276 """\
277 This class creates list of tests suitable for GNU targets.
278
279 """
Benny Prijono49048d92008-12-29 14:56:32 +0000280 def __init__(self, config, build_config_name="", user_mak="", \
281 config_site="", cross_compile="", exclude=[], not_exclude=[]):
Benny Prijono0b262932008-12-30 16:19:38 +0000282 """\
283 Parameters:
284 config - BaseConfig instance
285 build_config_name - Optional name to be added as suffix to the build
286 name. Sample: "min-size", "O4", "TLS", etc.
287 user_mak - Contents to be put on user.mak
288 config_site - Contents to be put on config_site.h
289 cross_compile - Optional cross-compile prefix. Must include the
290 trailing dash, e.g. "arm-unknown-linux-"
291 exclude - List of regular expression patterns for tests
292 that will be excluded from the run
293 not_exclude - List of regular expression patterns for tests
294 that will be run regardless of whether they
295 match the excluded pattern.
296
297 """
Benny Prijono49048d92008-12-29 14:56:32 +0000298 TestBuilder.__init__(self, config, build_config_name=build_config_name,
299 user_mak=user_mak, config_site=config_site,
300 exclude=exclude, not_exclude=not_exclude)
301 self.cross_compile = cross_compile
302 if self.cross_compile and self.cross_compile[-1] != '-':
303 self.cross_compile.append("-")
304
305 def build_tests(self):
306 if self.cross_compile:
Benny Prijono0b262932008-12-30 16:19:38 +0000307 suffix = "-" + self.cross_compile[0:-1]
308 build_name = self.cross_compile + \
309 gcc_version(self.cross_compile + "gcc")
Benny Prijono49048d92008-12-29 14:56:32 +0000310 else:
Benny Prijono0b262932008-12-30 16:19:38 +0000311 proc = subprocess.Popen("sh "+self.config.base_dir+"/config.guess",
312 shell=True, stdout=subprocess.PIPE)
Benny Prijono456e58f2009-01-02 18:51:25 +0000313 plat = proc.stdout.readline().rstrip(" \r\n")
314 build_name = plat + "-"+gcc_version(self.cross_compile + "gcc")
315 suffix = "-" + plat
Benny Prijono49048d92008-12-29 14:56:32 +0000316
317 if self.build_config_name:
318 build_name = build_name + "-" + self.build_config_name
319 cmds = []
320 cmds.extend(update_ops)
Benny Prijonod5962672009-01-02 18:15:07 +0000321 cmds.append(Operation(Operation.CONFIGURE, "sh ./configure"))
Benny Prijono456e58f2009-01-02 18:51:25 +0000322 if sys.platform=="win32":
Benny Prijonod5962672009-01-02 18:15:07 +0000323 # Don't build python module on Mingw
324 cmds.append(Operation(Operation.BUILD,
325 "sh -c 'make distclean && make dep && make'"))
326 else:
327 cmds.append(Operation(Operation.BUILD,
328 "sh -c 'make distclean && make dep && make" + \
329 " && cd pjsip-apps/src/python && " + \
330 "python setup.py clean build'"))
331
Benny Prijono49048d92008-12-29 14:56:32 +0000332 cmds.extend(std_test_ops)
333 cmds.extend(build_pjsua_test_ops())
334 self.ccdash_args = []
335 for c in cmds:
336 c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
337 args = c.encode(self.config.base_dir)
338 args.extend(["-U", self.config.url,
339 "-S", self.config.site,
340 "-T", self.stamp(),
341 "-B", build_name,
342 "-G", self.config.group])
343 args.extend(self.config.options)
344 self.ccdash_args.append(args)
345
Benny Prijono0b262932008-12-30 16:19:38 +0000346#
347# MSVC test configurator
348#
349class MSVCTestBuilder(TestBuilder):
350 """\
Benny Prijonofee946b2009-01-01 00:11:17 +0000351 This class creates list of tests suitable for Visual Studio builds.
352 You need to set the MSVC environment variables (typically by calling
353 vcvars32.bat) prior to running this class.
354
Benny Prijono0b262932008-12-30 16:19:38 +0000355 """
Benny Prijonofee946b2009-01-01 00:11:17 +0000356 def __init__(self, config, target="Release|Win32", build_config_name="",
Benny Prijono0b262932008-12-30 16:19:38 +0000357 config_site="", exclude=[], not_exclude=[]):
358 """\
359 Parameters:
360 config - BaseConfig instance
Benny Prijonofee946b2009-01-01 00:11:17 +0000361 target - Visual Studio build configuration to build.
Benny Prijono0b262932008-12-30 16:19:38 +0000362 Sample: "Debug|Win32", "Release|Win32".
363 build_config_name - Optional name to be added as suffix to the build
364 name. Sample: "Debug", "Release", "IPv6", etc.
365 config_site - Contents to be put on config_site.h
366 exclude - List of regular expression patterns for tests
367 that will be excluded from the run
368 not_exclude - List of regular expression patterns for tests
369 that will be run regardless of whether they
370 match the excluded pattern.
371
372 """
373 TestBuilder.__init__(self, config, build_config_name=build_config_name,
374 config_site=config_site, exclude=exclude,
375 not_exclude=not_exclude)
Benny Prijonofee946b2009-01-01 00:11:17 +0000376 self.target = target.lower()
Benny Prijono0b262932008-12-30 16:19:38 +0000377
378 def build_tests(self):
379
Benny Prijonofee946b2009-01-01 00:11:17 +0000380 (vsbuild,sys) = self.target.split("|",2)
Benny Prijono0b262932008-12-30 16:19:38 +0000381
382 build_name = sys + "-" + vs_get_version() + "-" + vsbuild
383
384 if self.build_config_name:
385 build_name = build_name + "-" + self.build_config_name
386
387 vccmd = "vcbuild.exe /nologo /nohtmllog /nocolor /rebuild " + \
Benny Prijonofee946b2009-01-01 00:11:17 +0000388 "pjproject-vs8.sln " + " \"" + self.target + "\""
Benny Prijono0b262932008-12-30 16:19:38 +0000389
390 suffix = "-i386-win32-vc8-" + vsbuild
391 pjsua = "pjsua_vc8"
392 if vsbuild=="debug":
393 pjsua = pjsua + "d"
394
395 cmds = []
396 cmds.extend(update_ops)
Benny Prijonobf7b8422009-01-03 23:53:05 +0000397 cmds.append(Operation(Operation.CONFIGURE, "CMD /C echo Nothing to do"))
398 cmds.append(Operation(Operation.BUILD, vccmd))
Benny Prijono0b262932008-12-30 16:19:38 +0000399 cmds.extend(std_test_ops)
400 cmds.extend(build_pjsua_test_ops(pjsua))
401
402 self.ccdash_args = []
403 for c in cmds:
404 c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
405 args = c.encode(self.config.base_dir)
406 args.extend(["-U", self.config.url,
407 "-S", self.config.site,
408 "-T", self.stamp(),
409 "-B", build_name,
410 "-G", self.config.group])
411 args.extend(self.config.options)
412 self.ccdash_args.append(args)
413
Benny Prijono49048d92008-12-29 14:56:32 +0000414
Benny Prijonofee946b2009-01-01 00:11:17 +0000415#
416# Symbian test configurator
417#
418class SymbianTestBuilder(TestBuilder):
419 """\
420 This class creates list of tests suitable for Symbian builds. You need to
421 set the command line build settings prior to running this class (typically
422 that involves setting the EPOCROOT variable and current device).
423
424 """
425 def __init__(self, config, target="gcce urel", build_config_name="",
426 config_site="", exclude=[], not_exclude=[]):
427 """\
428 Parameters:
429 config - BaseConfig instance
430 target - Symbian target to build. Default is "gcce urel".
431 build_config_name - Optional name to be added as suffix to the build
432 name. Sample: "APS", "VAS", etc.
433 config_site - Contents to be put on config_site.h
434 exclude - List of regular expression patterns for tests
435 that will be excluded from the run
436 not_exclude - List of regular expression patterns for tests
437 that will be run regardless of whether they
438 match the excluded pattern.
439
440 """
441 TestBuilder.__init__(self, config, build_config_name=build_config_name,
442 config_site=config_site, exclude=exclude,
443 not_exclude=not_exclude)
444 self.target = target.lower()
445
446 def build_tests(self):
447
448 # Check that EPOCROOT is set
449 if not "EPOCROOT" in os.environ:
450 print "Error: EPOCROOT environment variable is not set"
451 sys.exit(1)
452 epocroot = os.environ["EPOCROOT"]
453 # EPOCROOT must have trailing backslash
454 if epocroot[-1] != "\\":
455 epocroot = epocroot + "\\"
456 os.environ["EPOCROOT"] = epocroot
457 sdk1 = epocroot.split("\\")[-2]
458
459 # Check that correct device is set
460 proc = subprocess.Popen("devices", stdout=subprocess.PIPE,
461 stderr=subprocess.STDOUT, shell=True)
462 sdk2 = ""
463 while True:
464 line = proc.stdout.readline()
465 if line.find("- default") > 0:
466 sdk2 = line.split(":",1)[0]
467 break
468 proc.wait()
469
470 if sdk1 != sdk2:
471 print "Error: default SDK in device doesn't match EPOCROOT"
472 print "Default device SDK =", sdk2
473 print "EPOCROOT SDK =", sdk1
474 sys.exit(1)
475
476 build_name = sdk2.replace("_", "-") + "-" + \
477 self.target.replace(" ", "-")
478
479 if self.build_config_name:
480 build_name = build_name + "-" + self.build_config_name
481
482 cmdline = "cmd /C \"cd build.symbian && bldmake bldfiles && abld build %s\"" % (self.target)
483
484 cmds = []
485 cmds.extend(update_ops)
Benny Prijonobf7b8422009-01-03 23:53:05 +0000486 cmds.append(Operation(Operation.CONFIGURE, "CMD /C echo Nothing to do"))
Benny Prijonofee946b2009-01-01 00:11:17 +0000487 cmds.extend([Operation(Operation.BUILD, cmdline)])
488
489 self.ccdash_args = []
490 suffix = ""
491 for c in cmds:
492 c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
493 args = c.encode(self.config.base_dir)
494 args.extend(["-U", self.config.url,
495 "-S", self.config.site,
496 "-T", self.stamp(),
497 "-B", build_name,
498 "-G", self.config.group])
499 args.extend(self.config.options)
500 self.ccdash_args.append(args)
501