blob: 0ce15b74b52ccab894b3def874eaf391a45a03e6 [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 Prijonod5962672009-01-02 18:15:07 +0000255 rc = ccdash.main(a)
256 if rc!=0 and a[1] in mandatory_op:
257 print "Stopping because of error.."
258 break
Benny Prijono49048d92008-12-29 14:56:32 +0000259 counter = counter + 1
260 self.post_action()
261
262
263#
264# GNU test configurator
265#
266class GNUTestBuilder(TestBuilder):
Benny Prijono0b262932008-12-30 16:19:38 +0000267 """\
268 This class creates list of tests suitable for GNU targets.
269
270 """
Benny Prijono49048d92008-12-29 14:56:32 +0000271 def __init__(self, config, build_config_name="", user_mak="", \
272 config_site="", cross_compile="", exclude=[], not_exclude=[]):
Benny Prijono0b262932008-12-30 16:19:38 +0000273 """\
274 Parameters:
275 config - BaseConfig instance
276 build_config_name - Optional name to be added as suffix to the build
277 name. Sample: "min-size", "O4", "TLS", etc.
278 user_mak - Contents to be put on user.mak
279 config_site - Contents to be put on config_site.h
280 cross_compile - Optional cross-compile prefix. Must include the
281 trailing dash, e.g. "arm-unknown-linux-"
282 exclude - List of regular expression patterns for tests
283 that will be excluded from the run
284 not_exclude - List of regular expression patterns for tests
285 that will be run regardless of whether they
286 match the excluded pattern.
287
288 """
Benny Prijono49048d92008-12-29 14:56:32 +0000289 TestBuilder.__init__(self, config, build_config_name=build_config_name,
290 user_mak=user_mak, config_site=config_site,
291 exclude=exclude, not_exclude=not_exclude)
292 self.cross_compile = cross_compile
293 if self.cross_compile and self.cross_compile[-1] != '-':
294 self.cross_compile.append("-")
295
296 def build_tests(self):
297 if self.cross_compile:
Benny Prijono0b262932008-12-30 16:19:38 +0000298 suffix = "-" + self.cross_compile[0:-1]
299 build_name = self.cross_compile + \
300 gcc_version(self.cross_compile + "gcc")
Benny Prijono49048d92008-12-29 14:56:32 +0000301 else:
Benny Prijono0b262932008-12-30 16:19:38 +0000302 proc = subprocess.Popen("sh "+self.config.base_dir+"/config.guess",
303 shell=True, stdout=subprocess.PIPE)
Benny Prijono456e58f2009-01-02 18:51:25 +0000304 plat = proc.stdout.readline().rstrip(" \r\n")
305 build_name = plat + "-"+gcc_version(self.cross_compile + "gcc")
306 suffix = "-" + plat
Benny Prijono49048d92008-12-29 14:56:32 +0000307
308 if self.build_config_name:
309 build_name = build_name + "-" + self.build_config_name
310 cmds = []
311 cmds.extend(update_ops)
Benny Prijonod5962672009-01-02 18:15:07 +0000312 cmds.append(Operation(Operation.CONFIGURE, "sh ./configure"))
Benny Prijono456e58f2009-01-02 18:51:25 +0000313 if sys.platform=="win32":
Benny Prijonod5962672009-01-02 18:15:07 +0000314 # Don't build python module on Mingw
315 cmds.append(Operation(Operation.BUILD,
316 "sh -c 'make distclean && make dep && make'"))
317 else:
318 cmds.append(Operation(Operation.BUILD,
319 "sh -c 'make distclean && make dep && make" + \
320 " && cd pjsip-apps/src/python && " + \
321 "python setup.py clean build'"))
322
Benny Prijono49048d92008-12-29 14:56:32 +0000323 cmds.extend(std_test_ops)
324 cmds.extend(build_pjsua_test_ops())
325 self.ccdash_args = []
326 for c in cmds:
327 c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
328 args = c.encode(self.config.base_dir)
329 args.extend(["-U", self.config.url,
330 "-S", self.config.site,
331 "-T", self.stamp(),
332 "-B", build_name,
333 "-G", self.config.group])
334 args.extend(self.config.options)
335 self.ccdash_args.append(args)
336
Benny Prijono0b262932008-12-30 16:19:38 +0000337#
338# MSVC test configurator
339#
340class MSVCTestBuilder(TestBuilder):
341 """\
Benny Prijonofee946b2009-01-01 00:11:17 +0000342 This class creates list of tests suitable for Visual Studio builds.
343 You need to set the MSVC environment variables (typically by calling
344 vcvars32.bat) prior to running this class.
345
Benny Prijono0b262932008-12-30 16:19:38 +0000346 """
Benny Prijonofee946b2009-01-01 00:11:17 +0000347 def __init__(self, config, target="Release|Win32", build_config_name="",
Benny Prijono0b262932008-12-30 16:19:38 +0000348 config_site="", exclude=[], not_exclude=[]):
349 """\
350 Parameters:
351 config - BaseConfig instance
Benny Prijonofee946b2009-01-01 00:11:17 +0000352 target - Visual Studio build configuration to build.
Benny Prijono0b262932008-12-30 16:19:38 +0000353 Sample: "Debug|Win32", "Release|Win32".
354 build_config_name - Optional name to be added as suffix to the build
355 name. Sample: "Debug", "Release", "IPv6", etc.
356 config_site - Contents to be put on config_site.h
357 exclude - List of regular expression patterns for tests
358 that will be excluded from the run
359 not_exclude - List of regular expression patterns for tests
360 that will be run regardless of whether they
361 match the excluded pattern.
362
363 """
364 TestBuilder.__init__(self, config, build_config_name=build_config_name,
365 config_site=config_site, exclude=exclude,
366 not_exclude=not_exclude)
Benny Prijonofee946b2009-01-01 00:11:17 +0000367 self.target = target.lower()
Benny Prijono0b262932008-12-30 16:19:38 +0000368
369 def build_tests(self):
370
Benny Prijonofee946b2009-01-01 00:11:17 +0000371 (vsbuild,sys) = self.target.split("|",2)
Benny Prijono0b262932008-12-30 16:19:38 +0000372
373 build_name = sys + "-" + vs_get_version() + "-" + vsbuild
374
375 if self.build_config_name:
376 build_name = build_name + "-" + self.build_config_name
377
378 vccmd = "vcbuild.exe /nologo /nohtmllog /nocolor /rebuild " + \
Benny Prijonofee946b2009-01-01 00:11:17 +0000379 "pjproject-vs8.sln " + " \"" + self.target + "\""
Benny Prijono0b262932008-12-30 16:19:38 +0000380
381 suffix = "-i386-win32-vc8-" + vsbuild
382 pjsua = "pjsua_vc8"
383 if vsbuild=="debug":
384 pjsua = pjsua + "d"
385
386 cmds = []
387 cmds.extend(update_ops)
388 cmds.extend([Operation(Operation.BUILD, vccmd)])
389 cmds.extend(std_test_ops)
390 cmds.extend(build_pjsua_test_ops(pjsua))
391
392 self.ccdash_args = []
393 for c in cmds:
394 c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
395 args = c.encode(self.config.base_dir)
396 args.extend(["-U", self.config.url,
397 "-S", self.config.site,
398 "-T", self.stamp(),
399 "-B", build_name,
400 "-G", self.config.group])
401 args.extend(self.config.options)
402 self.ccdash_args.append(args)
403
Benny Prijono49048d92008-12-29 14:56:32 +0000404
Benny Prijonofee946b2009-01-01 00:11:17 +0000405#
406# Symbian test configurator
407#
408class SymbianTestBuilder(TestBuilder):
409 """\
410 This class creates list of tests suitable for Symbian builds. You need to
411 set the command line build settings prior to running this class (typically
412 that involves setting the EPOCROOT variable and current device).
413
414 """
415 def __init__(self, config, target="gcce urel", build_config_name="",
416 config_site="", exclude=[], not_exclude=[]):
417 """\
418 Parameters:
419 config - BaseConfig instance
420 target - Symbian target to build. Default is "gcce urel".
421 build_config_name - Optional name to be added as suffix to the build
422 name. Sample: "APS", "VAS", etc.
423 config_site - Contents to be put on config_site.h
424 exclude - List of regular expression patterns for tests
425 that will be excluded from the run
426 not_exclude - List of regular expression patterns for tests
427 that will be run regardless of whether they
428 match the excluded pattern.
429
430 """
431 TestBuilder.__init__(self, config, build_config_name=build_config_name,
432 config_site=config_site, exclude=exclude,
433 not_exclude=not_exclude)
434 self.target = target.lower()
435
436 def build_tests(self):
437
438 # Check that EPOCROOT is set
439 if not "EPOCROOT" in os.environ:
440 print "Error: EPOCROOT environment variable is not set"
441 sys.exit(1)
442 epocroot = os.environ["EPOCROOT"]
443 # EPOCROOT must have trailing backslash
444 if epocroot[-1] != "\\":
445 epocroot = epocroot + "\\"
446 os.environ["EPOCROOT"] = epocroot
447 sdk1 = epocroot.split("\\")[-2]
448
449 # Check that correct device is set
450 proc = subprocess.Popen("devices", stdout=subprocess.PIPE,
451 stderr=subprocess.STDOUT, shell=True)
452 sdk2 = ""
453 while True:
454 line = proc.stdout.readline()
455 if line.find("- default") > 0:
456 sdk2 = line.split(":",1)[0]
457 break
458 proc.wait()
459
460 if sdk1 != sdk2:
461 print "Error: default SDK in device doesn't match EPOCROOT"
462 print "Default device SDK =", sdk2
463 print "EPOCROOT SDK =", sdk1
464 sys.exit(1)
465
466 build_name = sdk2.replace("_", "-") + "-" + \
467 self.target.replace(" ", "-")
468
469 if self.build_config_name:
470 build_name = build_name + "-" + self.build_config_name
471
472 cmdline = "cmd /C \"cd build.symbian && bldmake bldfiles && abld build %s\"" % (self.target)
473
474 cmds = []
475 cmds.extend(update_ops)
476 cmds.extend([Operation(Operation.BUILD, cmdline)])
477
478 self.ccdash_args = []
479 suffix = ""
480 for c in cmds:
481 c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
482 args = c.encode(self.config.base_dir)
483 args.extend(["-U", self.config.url,
484 "-S", self.config.site,
485 "-T", self.stamp(),
486 "-B", build_name,
487 "-G", self.config.group])
488 args.extend(self.config.options)
489 self.ccdash_args.append(args)
490