blob: 3f5ea9b22bba0b4920a741b07236aaa3e6247d08 [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#
85# These are operations to build the software on GNU/Posix systems
86#
87gnu_build_ops = [
Benny Prijono0b262932008-12-30 16:19:38 +000088 Operation(Operation.CONFIGURE, "sh ./configure"),
89 Operation(Operation.BUILD, "sh -c 'make distclean && make dep && make && cd pjsip-apps/src/python && python setup.py clean build'"),
Benny Prijono49048d92008-12-29 14:56:32 +000090 #Operation(Operation.BUILD, "python setup.py clean build",
91 # wdir="pjsip-apps/src/python")
92]
93
94#
95# These are pjsua Python based unit test operations
96#
Benny Prijono0b262932008-12-30 16:19:38 +000097def build_pjsua_test_ops(pjsua_exe=""):
Benny Prijono49048d92008-12-29 14:56:32 +000098 ops = []
Benny Prijono0b262932008-12-30 16:19:38 +000099 if pjsua_exe:
100 exe = " -e ../../pjsip-apps/bin/" + pjsua_exe
101 else:
102 exe = ""
Benny Prijono49048d92008-12-29 14:56:32 +0000103 cwd = os.getcwd()
104 os.chdir("../pjsua")
105 os.system("python runall.py --list > list")
106 f = open("list", "r")
107 for e in f:
108 e = e.rstrip("\r\n ")
109 (mod,param) = e.split(None,2)
110 name = mod[4:mod.find(".py")] + "_" + \
111 param[param.find("/")+1:param.find(".py")]
Benny Prijono0b262932008-12-30 16:19:38 +0000112 ops.append(Operation(Operation.TEST, "python run.py" + exe + " " + \
113 e, name=name, wdir="tests/pjsua"))
114 f.close()
115 os.remove("list")
Benny Prijono49048d92008-12-29 14:56:32 +0000116 os.chdir(cwd)
117 return ops
118
119#
120# Get gcc version
121#
122def gcc_version(gcc):
123 proc = subprocess.Popen(gcc + " -v", stdout=subprocess.PIPE,
124 stderr=subprocess.STDOUT, shell=True)
125 ver = ""
126 while True:
127 s = proc.stdout.readline()
128 if not s:
129 break
130 if s.find("gcc version") >= 0:
131 ver = s.split(None, 3)[2]
132 break
133 proc.wait()
134 return "gcc-" + ver
135
136#
Benny Prijono0b262932008-12-30 16:19:38 +0000137# Get Visual Studio version
138#
139def vs_get_version():
140 proc = subprocess.Popen("cl", stdout=subprocess.PIPE,
141 stderr=subprocess.STDOUT)
142 while True:
143 s = proc.stdout.readline()
144 if s=="":
145 break
146 pos = s.find("Version")
147 if pos > 0:
148 proc.wait()
149 s = s[pos+8:]
150 ver = s.split(None, 1)[0]
151 major = ver[0:2]
152 if major=="12":
153 return "vs6"
154 elif major=="13":
155 return "vs2003"
156 elif major=="14":
157 return "vs2005"
158 elif major=="15":
159 return "vs2008"
160 else:
161 return "vs-" + major
162 proc.wait()
163 return "vs-unknown"
164
165
166#
Benny Prijono49048d92008-12-29 14:56:32 +0000167# Test config
168#
169class BaseConfig:
170 def __init__(self, base_dir, url, site, group, options=None):
171 self.base_dir = base_dir
172 self.url = url
173 self.site = site
174 self.group = group
175 self.options = options
176
177#
178# Base class for test configurator
179#
180class TestBuilder:
181 def __init__(self, config, build_config_name="",
182 user_mak="", config_site="", exclude=[], not_exclude=[]):
183 self.config = config # BaseConfig instance
184 self.build_config_name = build_config_name # Optional build suffix
185 self.user_mak = user_mak # To be put in user.mak
186 self.config_site = config_site # To be put in config_s..
187 self.saved_user_mak = "" # To restore user.mak
188 self.saved_config_site = "" # To restore config_s..
189 self.exclude = exclude # List of exclude pattern
190 self.not_exclude = not_exclude # List of include pattern
191 self.ccdash_args = [] # ccdash cmd line
192
193 def stamp(self):
194 return time.strftime("%Y%m%d-%H%M", time.localtime())
195
196 def pre_action(self):
197 # Override user.mak
198 name = self.config.base_dir + "/user.mak"
199 if os.access(name, os.F_OK):
200 f = open(name, "r")
201 self.saved_user_mak = f.read()
202 f.close()
203 if True:
204 f = open(name, "wt")
205 f.write(self.user_mak)
206 f.close()
207 # Override config_site.h
208 name = self.config.base_dir + "/pjlib/include/pj/config_site.h"
209 if os.access(name, os.F_OK):
210 f = open(name, "r")
211 self.saved_config_site= f.read()
212 f.close()
213 if True:
214 f = open(name, "wt")
215 f.write(self.config_site)
216 f.close()
217
218
219 def post_action(self):
220 # Restore user.mak
221 name = self.config.base_dir + "/user.mak"
Benny Prijono0b262932008-12-30 16:19:38 +0000222 f = open(name, "wt")
223 f.write(self.saved_user_mak)
224 f.close()
Benny Prijono49048d92008-12-29 14:56:32 +0000225 # Restore config_site.h
226 name = self.config.base_dir + "/pjlib/include/pj/config_site.h"
Benny Prijono0b262932008-12-30 16:19:38 +0000227 f = open(name, "wt")
228 f.write(self.saved_config_site)
229 f.close()
Benny Prijono49048d92008-12-29 14:56:32 +0000230
231 def build_tests(self):
232 # This should be overridden by subclasses
233 pass
234
235 def execute(self):
236 if len(self.ccdash_args)==0:
237 self.build_tests()
238 self.pre_action()
239 counter = 0
240 for a in self.ccdash_args:
241 # Check if this test is in exclusion list
242 fullcmd = " ".join(a)
243 excluded = False
244 included = False
245 for pat in self.exclude:
Benny Prijono9be224f2008-12-29 17:57:13 +0000246 if pat and re.search(pat, fullcmd) != None:
Benny Prijono49048d92008-12-29 14:56:32 +0000247 excluded = True
248 break
249 if excluded:
250 for pat in self.not_exclude:
Benny Prijono9be224f2008-12-29 17:57:13 +0000251 if pat and re.search(pat, fullcmd) != None:
Benny Prijono49048d92008-12-29 14:56:32 +0000252 included = True
253 break
254 if excluded and not included:
Benny Prijono0b262932008-12-30 16:19:38 +0000255 if len(fullcmd)>60:
256 fullcmd = fullcmd[0:60] + ".."
257 print "Skipping '%s'" % (fullcmd)
Benny Prijono49048d92008-12-29 14:56:32 +0000258 continue
259
Benny Prijono49048d92008-12-29 14:56:32 +0000260 b = ["ccdash.py"]
261 b.extend(a)
262 a = b
263 #print a
264 ccdash.main(a)
265 counter = counter + 1
266 self.post_action()
267
268
269#
270# GNU test configurator
271#
272class GNUTestBuilder(TestBuilder):
Benny Prijono0b262932008-12-30 16:19:38 +0000273 """\
274 This class creates list of tests suitable for GNU targets.
275
276 """
Benny Prijono49048d92008-12-29 14:56:32 +0000277 def __init__(self, config, build_config_name="", user_mak="", \
278 config_site="", cross_compile="", exclude=[], not_exclude=[]):
Benny Prijono0b262932008-12-30 16:19:38 +0000279 """\
280 Parameters:
281 config - BaseConfig instance
282 build_config_name - Optional name to be added as suffix to the build
283 name. Sample: "min-size", "O4", "TLS", etc.
284 user_mak - Contents to be put on user.mak
285 config_site - Contents to be put on config_site.h
286 cross_compile - Optional cross-compile prefix. Must include the
287 trailing dash, e.g. "arm-unknown-linux-"
288 exclude - List of regular expression patterns for tests
289 that will be excluded from the run
290 not_exclude - List of regular expression patterns for tests
291 that will be run regardless of whether they
292 match the excluded pattern.
293
294 """
Benny Prijono49048d92008-12-29 14:56:32 +0000295 TestBuilder.__init__(self, config, build_config_name=build_config_name,
296 user_mak=user_mak, config_site=config_site,
297 exclude=exclude, not_exclude=not_exclude)
298 self.cross_compile = cross_compile
299 if self.cross_compile and self.cross_compile[-1] != '-':
300 self.cross_compile.append("-")
301
302 def build_tests(self):
303 if self.cross_compile:
Benny Prijono0b262932008-12-30 16:19:38 +0000304 suffix = "-" + self.cross_compile[0:-1]
305 build_name = self.cross_compile + \
306 gcc_version(self.cross_compile + "gcc")
Benny Prijono49048d92008-12-29 14:56:32 +0000307 else:
Benny Prijono0b262932008-12-30 16:19:38 +0000308 proc = subprocess.Popen("sh "+self.config.base_dir+"/config.guess",
309 shell=True, stdout=subprocess.PIPE)
310 sys = proc.stdout.readline().rstrip(" \r\n")
311 build_name = sys + "-"+gcc_version(self.cross_compile + "gcc")
312 suffix = "-" + sys
Benny Prijono49048d92008-12-29 14:56:32 +0000313
314 if self.build_config_name:
315 build_name = build_name + "-" + self.build_config_name
316 cmds = []
317 cmds.extend(update_ops)
318 cmds.extend(gnu_build_ops)
319 cmds.extend(std_test_ops)
320 cmds.extend(build_pjsua_test_ops())
321 self.ccdash_args = []
322 for c in cmds:
323 c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
324 args = c.encode(self.config.base_dir)
325 args.extend(["-U", self.config.url,
326 "-S", self.config.site,
327 "-T", self.stamp(),
328 "-B", build_name,
329 "-G", self.config.group])
330 args.extend(self.config.options)
331 self.ccdash_args.append(args)
332
Benny Prijono0b262932008-12-30 16:19:38 +0000333#
334# MSVC test configurator
335#
336class MSVCTestBuilder(TestBuilder):
337 """\
Benny Prijonofee946b2009-01-01 00:11:17 +0000338 This class creates list of tests suitable for Visual Studio builds.
339 You need to set the MSVC environment variables (typically by calling
340 vcvars32.bat) prior to running this class.
341
Benny Prijono0b262932008-12-30 16:19:38 +0000342 """
Benny Prijonofee946b2009-01-01 00:11:17 +0000343 def __init__(self, config, target="Release|Win32", build_config_name="",
Benny Prijono0b262932008-12-30 16:19:38 +0000344 config_site="", exclude=[], not_exclude=[]):
345 """\
346 Parameters:
347 config - BaseConfig instance
Benny Prijonofee946b2009-01-01 00:11:17 +0000348 target - Visual Studio build configuration to build.
Benny Prijono0b262932008-12-30 16:19:38 +0000349 Sample: "Debug|Win32", "Release|Win32".
350 build_config_name - Optional name to be added as suffix to the build
351 name. Sample: "Debug", "Release", "IPv6", etc.
352 config_site - Contents to be put on config_site.h
353 exclude - List of regular expression patterns for tests
354 that will be excluded from the run
355 not_exclude - List of regular expression patterns for tests
356 that will be run regardless of whether they
357 match the excluded pattern.
358
359 """
360 TestBuilder.__init__(self, config, build_config_name=build_config_name,
361 config_site=config_site, exclude=exclude,
362 not_exclude=not_exclude)
Benny Prijonofee946b2009-01-01 00:11:17 +0000363 self.target = target.lower()
Benny Prijono0b262932008-12-30 16:19:38 +0000364
365 def build_tests(self):
366
Benny Prijonofee946b2009-01-01 00:11:17 +0000367 (vsbuild,sys) = self.target.split("|",2)
Benny Prijono0b262932008-12-30 16:19:38 +0000368
369 build_name = sys + "-" + vs_get_version() + "-" + vsbuild
370
371 if self.build_config_name:
372 build_name = build_name + "-" + self.build_config_name
373
374 vccmd = "vcbuild.exe /nologo /nohtmllog /nocolor /rebuild " + \
Benny Prijonofee946b2009-01-01 00:11:17 +0000375 "pjproject-vs8.sln " + " \"" + self.target + "\""
Benny Prijono0b262932008-12-30 16:19:38 +0000376
377 suffix = "-i386-win32-vc8-" + vsbuild
378 pjsua = "pjsua_vc8"
379 if vsbuild=="debug":
380 pjsua = pjsua + "d"
381
382 cmds = []
383 cmds.extend(update_ops)
384 cmds.extend([Operation(Operation.BUILD, vccmd)])
385 cmds.extend(std_test_ops)
386 cmds.extend(build_pjsua_test_ops(pjsua))
387
388 self.ccdash_args = []
389 for c in cmds:
390 c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
391 args = c.encode(self.config.base_dir)
392 args.extend(["-U", self.config.url,
393 "-S", self.config.site,
394 "-T", self.stamp(),
395 "-B", build_name,
396 "-G", self.config.group])
397 args.extend(self.config.options)
398 self.ccdash_args.append(args)
399
Benny Prijono49048d92008-12-29 14:56:32 +0000400
Benny Prijonofee946b2009-01-01 00:11:17 +0000401#
402# Symbian test configurator
403#
404class SymbianTestBuilder(TestBuilder):
405 """\
406 This class creates list of tests suitable for Symbian builds. You need to
407 set the command line build settings prior to running this class (typically
408 that involves setting the EPOCROOT variable and current device).
409
410 """
411 def __init__(self, config, target="gcce urel", build_config_name="",
412 config_site="", exclude=[], not_exclude=[]):
413 """\
414 Parameters:
415 config - BaseConfig instance
416 target - Symbian target to build. Default is "gcce urel".
417 build_config_name - Optional name to be added as suffix to the build
418 name. Sample: "APS", "VAS", etc.
419 config_site - Contents to be put on config_site.h
420 exclude - List of regular expression patterns for tests
421 that will be excluded from the run
422 not_exclude - List of regular expression patterns for tests
423 that will be run regardless of whether they
424 match the excluded pattern.
425
426 """
427 TestBuilder.__init__(self, config, build_config_name=build_config_name,
428 config_site=config_site, exclude=exclude,
429 not_exclude=not_exclude)
430 self.target = target.lower()
431
432 def build_tests(self):
433
434 # Check that EPOCROOT is set
435 if not "EPOCROOT" in os.environ:
436 print "Error: EPOCROOT environment variable is not set"
437 sys.exit(1)
438 epocroot = os.environ["EPOCROOT"]
439 # EPOCROOT must have trailing backslash
440 if epocroot[-1] != "\\":
441 epocroot = epocroot + "\\"
442 os.environ["EPOCROOT"] = epocroot
443 sdk1 = epocroot.split("\\")[-2]
444
445 # Check that correct device is set
446 proc = subprocess.Popen("devices", stdout=subprocess.PIPE,
447 stderr=subprocess.STDOUT, shell=True)
448 sdk2 = ""
449 while True:
450 line = proc.stdout.readline()
451 if line.find("- default") > 0:
452 sdk2 = line.split(":",1)[0]
453 break
454 proc.wait()
455
456 if sdk1 != sdk2:
457 print "Error: default SDK in device doesn't match EPOCROOT"
458 print "Default device SDK =", sdk2
459 print "EPOCROOT SDK =", sdk1
460 sys.exit(1)
461
462 build_name = sdk2.replace("_", "-") + "-" + \
463 self.target.replace(" ", "-")
464
465 if self.build_config_name:
466 build_name = build_name + "-" + self.build_config_name
467
468 cmdline = "cmd /C \"cd build.symbian && bldmake bldfiles && abld build %s\"" % (self.target)
469
470 cmds = []
471 cmds.extend(update_ops)
472 cmds.extend([Operation(Operation.BUILD, cmdline)])
473
474 self.ccdash_args = []
475 suffix = ""
476 for c in cmds:
477 c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
478 args = c.encode(self.config.base_dir)
479 args.extend(["-U", self.config.url,
480 "-S", self.config.site,
481 "-T", self.stamp(),
482 "-B", build_name,
483 "-G", self.config.group])
484 args.extend(self.config.options)
485 self.ccdash_args.append(args)
486