Sync with 5.4.0
[deliverable/titan.core.git] / etc / autotest / titan_builder.py
CommitLineData
970ed795 1###############################################################################
3abe9331 2# Copyright (c) 2000-2015 Ericsson Telecom AB
970ed795
EL
3# All rights reserved. This program and the accompanying materials
4# are made available under the terms of the Eclipse Public License v1.0
5# which accompanies this distribution, and is available at
6# http://www.eclipse.org/legal/epl-v10.html
7###############################################################################
8#!/usr/bin/env python
9
10import logging, optparse, os, re, sys, time, fnmatch
11import socket, traceback, types
12# Never import everything! E.g. enumerate() can be redefined somewhere.
13# Check out: http://www.phidgets.com/phorum/viewtopic.php?f=26&t=3315.
14import product_handler, titan_builder_cfg, titan_publisher, utils, threading
15
16LOG_FILENAME = './titan_builder.log'
17TRACE_FILENAME = './titan_builder.err-log'
18
19vobtest_lock = threading.Lock()
20
21class config_handler:
22 """ Class to process the semi-configuration file and provide easy access to
23 the configuration data read. This very same configuration file will be
24 reused by each slave.
25
26 Or simply include the parts of the current shell script setting all
27 environment variables?
28 """
29 def __init__(self, logger):
30 self.logger = logger
31 self.products = titan_builder_cfg.products
32 self.recipients = titan_builder_cfg.recipients
33 self.configs = titan_builder_cfg.configs
34 self.slaves = titan_builder_cfg.slaves
35 self.common = titan_builder_cfg.common
36
37 self.validate_config_data()
38
39 def __str__(self):
40 """ For debugging purposes only. """
41 results = (str(self.configs), str(self.products), str(self.recipients), \
42 str(self.slaves))
43 return '\n'.join(results)
44
45 def is_used_config(self, config):
46 """ Check if the given build configuration is used by any of the slaves.
47 It is assumed that the configuration file is already validated.
48
49 Arguments:
50 config: The name of the build configuration.
51 """
52 for slave in self.slaves:
53 if config in self.slaves[slave]['configs']:
54 return True
55 # The build configuration will be skipped from the build process.
56 return False
57
58 def validate_config_data(self):
59 """ We have only one configuration file. The wrong addresses are filtered
60 out automatically. Add more checks. Rewrite `installdir' in case of
61 a FOA build.
62 """
63 self.recipients = dict([(key, self.recipients[key]) \
64 for key in self.recipients \
65 if re.match('^<[\w\-\.]+@(\w[\w\-]+\.)+\w+>$', self.recipients[key])])
66# elif current_section == 'slaves':
67# row_data = [data.strip() for data in line.split()]
68# if len(row_data) != 4:
69# elif not re.match('^\w+\s*(\d{1,3}.){3}\d{1,3}\s*\w+\s*[\w/]+$', line):
70# else: # 100% correct data all over.
71# self.slaves[row_data[0]] = row_data[1:]
72 for config_name, config_data in self.configs.iteritems():
73 if 'foa' in config_data and config_data['foa'] and (not 'foadir' in config_data or len(config_data['foadir']) == 0):
74 config_data['foadir'] = config_data['installdir'] # The final build directory, it'll be linked.
75 config_data['installdir'] = "%s/temporary_foa_builds/TTCNv3-%s" % ('/'.join(config_data['foadir'].split('/')[0:-1]), utils.get_time(True))
76
77class MasterThread(threading.Thread):
78 def __init__(self, titan_builder, config, config_name, slave_list, log_dir, build_dir, tests):
79 threading.Thread.__init__(self)
80 self.titan_builder = titan_builder
81 self.config = config
82 self.config_name = config_name
83 self.slave_list = slave_list
84 self.log_dir = log_dir
85 self.build_dir = build_dir
86 self.tests = tests
87
88 def run(self):
89 self.slave_list.extend(self.titan_builder.master(self.config, self.config_name, self.log_dir, self.build_dir, self.tests))
90
91class RegtestThread(threading.Thread):
92 def __init__(self, titan_builder, config, slave_name):
93 threading.Thread.__init__(self)
94 self.titan_builder = titan_builder
95 self.config = config
96 self.slave_name = slave_name
97
98 def run(self):
99 self.titan_builder.pass_regtest(self.config, self.slave_name)
100
101class FunctestThread(threading.Thread):
102 def __init__(self, titan_builder, config, slave_name):
103 threading.Thread.__init__(self)
104 self.titan_builder = titan_builder
105 self.config = config
106 self.slave_name = slave_name
107
108 def run(self):
109 self.titan_builder.pass_functest(self.config, self.slave_name)
110
111class PerftestThread(threading.Thread):
112 def __init__(self, titan_builder, config, slave_name):
113 threading.Thread.__init__(self)
114 self.titan_builder = titan_builder
115 self.config = config
116 self.slave_name = slave_name
117
118 def run(self):
119 self.titan_builder.pass_perftest(self.config, self.slave_name)
120
121class EclipseThread(threading.Thread):
122 def __init__(self, titan_builder, config, slave_name):
123 threading.Thread.__init__(self)
124 self.titan_builder = titan_builder
125 self.config = config
126 self.slave_name = slave_name
127
128 def run(self):
129 self.titan_builder.pass_eclipse(self.config, self.slave_name)
130
131class VobtestThread(threading.Thread):
132 def __init__(self, titan_builder, config, slave_name):
133 threading.Thread.__init__(self)
134 self.titan_builder = titan_builder
135 self.config = config
136 self.slave_name = slave_name
137
138 def run(self):
139 self.titan_builder.pass_vobtest(self.config, self.slave_name)
140
141class titan_builder:
142 def __init__(self):
143 self.logger = None
144 self.logger = self.create_logger()
145 self.config = None
146 self.config = self.create_config()
147 self.publisher = None
148 self.publisher = self.create_publisher()
149
150 def create_logger(self):
151 if self.logger:
152 return self.logger
153 logger = logging.getLogger('titan_logger')
154 logger.setLevel(logging.DEBUG)
155 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
156 handler = logging.FileHandler(LOG_FILENAME)
157 handler.setFormatter(formatter)
158 logger.addHandler(handler)
159 sth = logging.StreamHandler()
160 sth.setLevel(logging.DEBUG)
161 logger.addHandler(sth)
162 return logger # Just like in singleton.
163
164 def create_config(self):
165 """ Create the configuration file handler class. If it's already created
166 the existing one will be returned. The configuration cannot be
167 changed during operation. It cannot be reloaded etc.
168 """
169 if self.config:
170 return self.config
171 return config_handler(self.logger)
172
173 def create_publisher(self):
174 if self.publisher:
175 return self.publisher
176 return titan_publisher.titan_publisher(self.logger, self.config)
177
178 def remove_dups(self, list = []):
179 """ Remove duplicates from a list.
180 """
181 tmp_list = []
182 if len(list) > 0:
183 [tmp_list.append(elem) for elem in list if not elem in tmp_list]
184 return tmp_list
185
186 def build(self, config, slave_name, reset, set_addressees, tests, path):
187 """ Build the specified build configurations. The configurations are
188 built sequentially. For the slaves a single build configuration should
189 be specified in the command line. The slave will abort build execution
190 if there are more build configurations specified. It's a limitation
191 and should be relaxed later.
192
193 Arguments:
194 config: The list of build configurations specified in the command line.
195 slave_name: The name of the slave if `--slave-mode' is on.
196 reset: Reset statistics.
197 set_addressees: List of recipients.
198 tests: Tests to run for all configurations.
199
200 Returns:
201 Nothing. It's the main driver of the whole build.
202 """
203 config_list = []
204 if not config:
205 self.logger.warning('Running all available build configurations from ' \
206 'the configuration file...')
207 config_list.extend(self.config.configs.keys())
208 elif not re.match('^\w+(,\w+)*$', config):
209 self.logger.error('Invalid build configuration list: `%s\'' % config)
210 else:
211 config = self.remove_dups(config.split(','))
212 for config_elem in config:
213 if not config_elem in self.config.configs.keys():
214 self.logger.error('Build configuration `%s\' not found' \
215 % config_elem)
216 else:
217 config_list.append(config_elem)
218 if not len(config_list) > 0:
219 self.logger.error('No valid build configurations were found, ' \
220 'exiting...')
221 return
222 if set_addressees:
223 self.config.recipients = {}
224 addressees = set_addressees.split(',')
225 for addressee in addressees:
226 self.config.recipients[' '.join(addressee.split(' ')[:-1])] = addressee.split(' ')[-1];
227 if not slave_name:
228 everything_started_here = utils.get_time()
229 utils.run_cmd('/bin/rm -rf %s %s && mkdir -p %s %s' \
230 % (self.config.common['builddir'], self.config.common['logdir'], self.config.common['builddir'], self.config.common['logdir']), None, 1800, self.logger)
231 slave_list = []
232 master_threads = []
233 for config_name in config_list:
234 if not self.config.is_used_config(config_name):
235 self.logger.warning('Skipping unused build configuration: `%s\'' \
236 % config_name)
237 else:
238 # Create the slave and configuration specific log directory. If the
239 # logs haven't arrived yet from the given slave, that slave should
240 # be considered lost.
241 build_dir = os.path.join(self.config.common['builddir'], config_name)
242 log_dir = os.path.join(self.config.common['logdir'], config_name)
243 utils.run_cmd('/bin/rm -rf %s %s && mkdir -p %s %s' % (build_dir, log_dir, build_dir, log_dir), None, 1800, self.logger)
244 master_thread = MasterThread(self, self.config.configs[config_name], config_name, slave_list, log_dir, build_dir, tests)
245 master_thread.start()
246 master_threads.append((config_name, master_thread))
247 for config_name, master_thread in master_threads:
248 master_thread.join()
249 self.logger.debug('Master thread for `%s\' joined successfully' % config_name)
250 everything_ended_here = utils.get_time()
251 self.gather_all_stuff_together_and_present_to_the_public( \
252 everything_started_here, everything_ended_here, slave_list, reset)
253 else:
254 # Run the tests on the given slave of each assigned build configuration.
255 # It may cause problems if several configurations are run one after
256 # another, but otherwise it's not possible assign multiple build
257 # configurations at all.
258 for config_name in config_list:
259 self.logger.debug('Hello, from a slave `%s\' running build ' \
260 'configuration `%s\'' \
261 % (slave_name, config_name))
262 if tests and len(tests) > 0:
263 self.config.configs[config_name]['functest'] = tests.find('f') != -1
264 self.config.configs[config_name]['perftest'] = tests.find('p') != -1
265 self.config.configs[config_name]['regtest'] = tests.find('r') != -1
266 self.slave(self.config.configs[config_name], config_name, slave_name)
267
268 def get_titan(self, config, config_name, log_dir, build_dir):
269 """ Get the TITAN sources from the CVS repository. It can do checkouts by
270 tag and date only. If the version string is omitted HEAD will be
271 used. The checkout will be made into the build directory. The output
272 is not handled by the output handler yet.
273
274 Arguments:
275 The build configuration to get TITAN sources for. Log/build
276 directories.
277
278 Returns:
279 0 on success. 1 if the checkout failed for some reason. It's most
280 probably a timeout, since the parameters are validated and the
281 existence of `cvs' is required. So, it's safe to abort the build if
282 1 is returned.
283 """
284 command_line = 'cd %s && cvs get TTCNv3' % build_dir
285 if re.match('^v\d\-\d\-pl\d$', config['version']):
286 command_line = 'cd %s && cvs co -r%s TTCNv3' \
287 % (build_dir, config['version'])
288 elif re.match('2\d{7}', config['version']):
289 command_line = 'cd %s && cvs co -D%s TTCNv3' \
290 % (build_dir, config['version'])
291 command_line += ' 1>%s/cvs-%s.stdout 2>%s/cvs-%s.stderr' \
292 % (log_dir, config_name, log_dir, config_name)
293 self.logger.debug('CVS checkout starting for config `%s\'', config_name)
294 (retval, stdout, stderr) = utils.run_cmd(command_line, None, 10800)
295 if retval:
296 self.logger.error('The CVS checkout failed with command: `%s\', exit status: `%d\', stdout: `%s\', stderr: `%s\'' \
297 % (command_line, retval, stdout, stderr))
298 return 1 # `retval' is not handled yet.
299 self.logger.debug('CVS checkout finished for config `%s\'', config_name)
300 return 0
301
302 def master(self, config, config_name, log_dir, build_dir, tests):
303 """ Prepare the packages for the slaves. The regression tests and
304 function tests are part of TITAN, hence the preparations regarding
305 those tests are done together with TITAN. It seems to make sense.
306 Delete only the `TTCNv3' directory when switching between build
307 configurations. It's advised to use a different global build
308 directory for the master and slaves.
309
310 Arguments:
311 The current build configuration and its name.
312 """
313 slave_list = []
314 for slave_name in self.config.slaves:
315 slave = self.config.slaves[slave_name]
316 if not config_name in slave['configs']:
317 continue
318 slave_url = '%s@%s' % (slave['user'], slave['ip'])
319 # Need more robust IP address checking. It doesn't work on my Debian
320 # laptop. It can return simply `127.0.0.1' and fool this check
321 # completely.
322 is_localhost = socket.gethostbyname(socket.gethostname()) == slave['ip']
323 if self.pass_prepare_titan(config, config_name, slave_name, log_dir, build_dir):
324 continue # Configuration for a given slave is failed.
325 # The slave list is needed for the last pass.
326 slave_list.append((slave_name, config_name, is_localhost))
327
328 self.logger.debug('Removing old build `%s\' and log `%s\' ' \
329 'directories for slave `%s\' and build configuration `%s\'' \
330 % (config['builddir'], config['logdir'], slave_name, config_name))
331 if is_localhost: # Cleanup first.
332 utils.run_cmd('/bin/rm -rf %s %s && mkdir -p %s %s' \
333 % (config['builddir'], config['logdir'],
334 config['builddir'], config['logdir']), None, 1800, self.logger)
335 else:
336 utils.run_cmd('ssh %s \'/bin/rm -rf %s %s && mkdir -p %s %s\'' \
337 % (slave_url, config['builddir'], config['logdir'],
338 config['builddir'], config['logdir']), None, 1800, self.logger)
339
340 if config['perftest']:
341 self.logger.debug('Copying performance tests for slave `%s\'' % slave_name)
342 self.pass_prepare_perftest(config, config_name, slave, slave_name, slave_url, \
343 is_localhost)
344 if config['vobtest']:
345 self.logger.debug('Copying VOB product tests for slave `%s\'' % slave_name)
346 self.pass_prepare_vobtest(config, config_name, slave, slave_name, slave_url, \
347 is_localhost)
348
349 if is_localhost: # Optimize local builds.
350 self.logger.debug('It\'s a local build for slave `%s\' and build ' \
351 'configuration `%s\', working locally' \
352 % (slave_name, config_name))
353 utils.run_cmd('cp %s/TTCNv3-%s.tar.bz2 %s' % \
354 (build_dir, config_name, config['builddir']), None, 1800)
355 utils.run_cmd('cp ./*.py ./*.sh %s' % config['builddir'])
356 utils.run_cmd('cd %s && %s/titan_builder.sh -s %s -c %s %s' \
357 % (config['builddir'], config['builddir'], \
358 slave_name, config_name, ((tests and len(tests) > 0) and ('-t %s' % tests) or '')), None, 21600)
359 utils.run_cmd('cp -r %s/%s/* %s' \
360 % (config['logdir'], slave_name, log_dir))
361 else:
362 self.logger.debug('It\'s a remote build for slave `%s\' and ' \
363 'build configuration `%s\', doing remote build' \
364 % (slave_name, config_name))
365 (retval, stdout, stderr) = \
366 utils.run_cmd('scp %s/TTCNv3-%s.tar.bz2 %s:%s' \
367 % (build_dir, config_name, slave_url,
368 config['builddir']), None, 1800)
369 if not retval:
370 self.logger.debug('The TITAN package is ready and distributed ' \
371 'for slave `%s\'' % slave_name)
372 else:
373 self.logger.error('Unable to distribute the TITAN package for ' \
374 'slave `%s\', it will be skipped from build ' \
375 'configuration `%s\'' % (slave_name, config_name))
376 continue
377 utils.run_cmd('scp ./*.py %s:%s' % (slave_url, config['builddir']))
378 utils.run_cmd('scp ./*.sh %s:%s' % (slave_url, config['builddir']))
379 utils.run_cmd('ssh %s \'cd %s && %s/titan_builder.sh -s %s -c ' \
380 '%s %s\'' % (slave_url, config['builddir'], \
381 config['builddir'], slave_name, config_name, ((tests and len(tests) > 0) and ('-t %s' % tests) or '') ), None, 21600)
382 utils.run_cmd('scp -r %s:%s/%s/* %s' \
383 % (slave_url, config['logdir'], slave_name, log_dir))
384
385 return slave_list
386
387 def gather_all_stuff_together_and_present_to_the_public(self, build_start, \
388 build_end, slave_list, reset):
389 """ Collect and process all logs. Only the CVS logs are coming from the
390 master. If the CSV output is not arrived from a slave, then the slave
391 will be considered lost.
392 """
393 build_root = utils.get_time(True)
394 html_root = os.path.join(self.config.common['htmldir'], build_root)
395 utils.run_cmd('mkdir -p %s' % html_root, None, 1800, self.logger)
396 utils.run_cmd('cd %s && /bin/rm -f latest && ln -s %s latest' % (self.config.common['htmldir'], build_root))
397 utils.run_cmd('cp -r %s/* %s' % (self.config.common['logdir'], html_root))
398 email_file = '%s/report.txt' % html_root
399 self.publisher.publish_csv2email(build_start, build_end, email_file, \
400 slave_list, build_root, self.config.configs, reset)
401 self.publisher.publish_html(build_root)
402 utils.send_email(self.logger, self.config.recipients, email_file)
403
404 def pass_prepare_titan(self, config, config_name, slave_name, log_dir, build_dir):
405 """ Get TITAN from the CVS and configure it for the actual slave. Then
406 TITAN archive is created. The archive is not copied to the actual
407 slave, because this function can be a showstopper for the whole build
408 process for the actual slave.
409
410 Arguments:
411 The build configuration and its name, the actual slave's name,
412 log/build directories.
413
414 Returns:
415 0 if everything went fine. 1 is returned when e.g. the CVS was
416 unreachable or the TITAN configuration failed for some reason.
417 Returning 1 should stop the build process for the actual slave.
418 """
419 if self.get_titan(config, config_name, log_dir, build_dir):
420 self.logger.error('The CVS checkout failed for slave `%s\' and ' \
421 'build configuration `%s\'' \
422 % (slave_name, config_name))
423 return 1
424 if self.config_titan(config, build_dir):
425 self.logger.error('Configuring TITAN failed for slave `%s\' ' \
426 'and build configuration `%s\'' \
427 % (slave_name, config_name))
428 return 1
429 utils.run_cmd('cd %s && tar cf TTCNv3-%s.tar ./TTCNv3' \
430 % (build_dir, config_name), None, 1800)
431 utils.run_cmd('cd %s && bzip2 TTCNv3-%s.tar' \
432 % (build_dir, config_name), None, 1800)
433 utils.run_cmd('/bin/rm -rf %s/TTCNv3' % build_dir, None, 1800)
434 return 0
435
436 def pass_prepare_perftest(self, config, config_name, slave, slave_name, slave_url, \
437 is_localhost):
438 """ Copy the performance test package to the actual slave. It's a simple
439 archive. Its location is defined in the configuration file.
440
441 Arguments:
442 The build configuration and its name with the actual slave and its
443 name. Nothing is returned.
444 """
445 if os.path.isfile(config['perftestdir']):
446 if is_localhost:
447 utils.run_cmd('cp -f %s %s' % (config['perftestdir'], \
448 config['builddir']))
449 else:
450 (retval, stdout, stderr) = utils.run_cmd('scp %s %s:%s' \
451 % (config['perftestdir'], slave_url, config['builddir']))
452 if retval:
453 self.logger.error('Unable to copy performance test package ' \
454 'to slave `%s\'' % slave_name)
455 else:
456 self.logger.error('The performance test package cannot be found at ' \
457 '`%s\'' % config['perftestdir'])
458
459 def pass_prepare_vobtest(self, config, config_name, slave, slave_name, slave_url, \
460 is_localhost):
461 """ Collect and configure the VOB products. The products will be
462 collected only if there's no file matching the `vobtest-*.tar.bz2'
463 pattern in the build directory. The resulting archive is copied to
464 the given slave only if it's a remote slave. The errors are reported
465 to the local error log. The URL of the slave is calculated locally.
466
467 Arguments:
468 The build configuration and its name with the actual slave and its
469 name. Nothing is returned.
470 """
471 vobtest_lock.acquire()
472 really_collect_products = len([file for file in os.listdir(self.config.common['builddir']) \
473 if fnmatch.fnmatch(file, 'vobtest\-.*\.tar\.bz2')]) == 0
474 if really_collect_products:
475 handler = product_handler.product_handler(self.logger, self.config)
476 if handler.config_products('%s/vobtest' % self.config.common['builddir']):
477 self.logger.error('Configuring VOB products failed for slave: ' \
478 '`%s\' and build configuration: `%s\'' \
479 % (slave_name, config_name))
480 return
481 utils.run_cmd('cd %s && tar cf vobtest-%s.tar ./vobtest' \
482 % (self.config.common['builddir'], \
483 time.strftime('%Y%m%d')), None, 1800)
484 utils.run_cmd('cd %s && bzip2 vobtest-*.tar' \
485 % self.config.common['builddir'], None, 1800)
486 utils.run_cmd('/bin/rm -rf %s/vobtest %s/vobtest-*.tar' \
487 % (self.config.common['builddir'], \
488 self.config.common['builddir']), None, 1800)
489 else:
490 self.logger.debug('VOB products don\'t need to be configured again')
491 vobtest_lock.release()
492 if not is_localhost:
493 (retval, stdout, stderr) = \
494 utils.run_cmd('scp %s/vobtest-*.tar.bz2 %s:%s' \
495 % (self.config.common['builddir'], slave_url, \
496 config['builddir']))
497 if retval:
498 self.logger.error('Copying the VOB package to slave: ' \
499 '`%s\' failed for build configuration: `%s\'' \
500 % (slave_name, config_name))
501 else:
502 utils.run_cmd('cp %s/vobtest-*.tar.bz2 %s' \
503 % (self.config.common['builddir'], config['builddir']))
504
505 def slave(self, config, config_name, slave_name):
506 """ Run the build passes sequentially. If the TITAN build fails, the
507 remaining passes are skipped. Log everything. All the results will
508 be written in all supported formats. It should be configurable.
509 """
510 self.logger.debug('Setting environment variables from `pass_setenv()\'')
511 self.pass_setenv(config, slave_name)
512 self.logger.debug('Building TITAN from `pass_titan()\'')
513 stamp_old = utils.get_time()
514 if not self.pass_titan(config, config_name, slave_name):
515 test_threads = []
516 if config['regtest']:
517 regtest_thread = RegtestThread(self, config, slave_name)
518 regtest_thread.start()
519 test_threads.append(('regression tests', regtest_thread))
520 self.logger.debug('Running regression tests from `pass_regtest()\'')
521 if config['functest']:
522 functest_thread = FunctestThread(self, config, slave_name)
523 functest_thread.start()
524 test_threads.append(('function tests', functest_thread))
525 self.logger.debug('Running function tests from `pass_functest()\'')
526 if config['perftest']:
527 perftest_thread = PerftestThread(self, config, slave_name)
528 perftest_thread.start()
529 test_threads.append(('performance tests', perftest_thread))
530 self.logger.debug('Running performance tests from `pass_perftest()\'')
531 if 'eclipse' in config and config['eclipse']:
532 eclipse_thread = EclipseThread(self, config, slave_name)
533 eclipse_thread.start()
534 test_threads.append(('eclipse tests', eclipse_thread))
535 self.logger.debug('Running Eclipse build from `pass_eclipse()\'')
536 if config['vobtest']:
537 vobtest_thread = VobtestThread(self, config, slave_name)
538 vobtest_thread.start()
539 test_threads.append(('VOB product tests', vobtest_thread))
540 self.logger.debug('Running VOB product tests from `pass_vobtest()\'')
541 for test_thread_name, test_thread in test_threads:
542 test_thread.join()
543 self.logger.debug('Thread for `%s\' joined successfully' % test_thread_name)
544 self.publisher.dump_csv(stamp_old, utils.get_time(), config, config_name, slave_name)
545 self.publisher.dump_txt(stamp_old, utils.get_time(), config, config_name, slave_name)
546 self.publisher.dump_html(stamp_old, utils.get_time(), config, config_name, slave_name)
547 self.logger.debug('Finalizing build from using `pass_slave_postprocess()\'')
548 self.pass_slave_postprocess(config, config_name, slave_name)
549
550 def pass_slave_postprocess(self, config, config_name, slave_name):
551 """ Archive stuff and make everything available for the master. The
552 master will copy all necessary stuff. The build directory is
553 available until the next build. Do the cleanup here. The installation
554 directory is never removed.
555
556 Arguments:
557 The current build configuration.
558 """
559 utils.run_cmd('cd %s && tar cf TTCNv3-%s-bin.tar ./TTCNv3' \
560 % (config['builddir'], config_name), None, 1800)
561 utils.run_cmd('bzip2 %s/TTCNv3-%s-bin.tar' \
562 % (config['builddir'], config_name), None, 1800)
563 utils.run_cmd('/bin/rm -rf %s/TTCNv3' % config['builddir'], None, 1800)
564 utils.run_cmd('/bin/rm -f %s/TTCNv3-%s.tar.bz2' \
565 % (config['builddir'], config_name))
566 utils.run_cmd('cd %s && /bin/rm -f *.py *.pyc *.sh' % config['builddir'])
567 utils.run_cmd('mv -f %s %s %s/%s' % (LOG_FILENAME, TRACE_FILENAME, \
568 config['logdir'], slave_name))
569
570 def pass_titan(self, config, config_name, slave_name):
571 """ Build pass for TITAN itself. It is assumed that the master have
572 already copied the TITAN package to the build directory. It's the
573 only requirement here. If the installation fails the TITAN build is
574 considered as a failure. Only the `make install' is taken into
575 consideration.
576
577 Arguments:
578 The current build configuration of the slave and its name.
579
580 Returns:
581 1 on error, e.g. if the TITAN package is not present. 0 if the
582 TITAN package was found and the full build completed successfully.
583 """
584 stamp_begin = utils.get_time()
585 utils.run_cmd('mkdir -p %s/%s' % (config['logdir'], slave_name), None, 1800, self.logger)
586 utils.run_cmd('bunzip2 %s/TTCNv3-%s.tar.bz2' \
587 % (config['builddir'], config_name), None, 1800, self.logger)
588 utils.run_cmd('cd %s && tar xf TTCNv3-%s.tar && bzip2 %s/TTCNv3-*.tar' \
589 % (config['builddir'], config_name, config['builddir']), \
590 None, 1800, self.logger)
591 if not os.path.isdir('%s/TTCNv3' % config['builddir']):
592 self.logger.error('The `%s/TTCNv3\' directory is not found' \
593 % config['builddir'])
594 self.publisher.titan_out(config, slave_name, \
595 (stamp_begin, utils.get_time(), None))
596 return 1
597 utils.run_cmd('cd %s && find . -exec touch {} \;' % config['builddir'], None, 1800)
598 (ret_val_dep, stdout_dep, stderr_dep) = \
599 utils.run_cmd('cd %s/TTCNv3 && make dep 2>&1' \
600 % config['builddir'], None, 1800)
601 (ret_val_make, stdout_make, stderr_make) = \
602 utils.run_cmd('cd %s/TTCNv3 && make -j4 2>&1' \
603 % config['builddir'], None, 1800)
604 (ret_val_install, stdout_install, stderr_install) = \
605 utils.run_cmd('cd %s/TTCNv3 && make install 2>&1' \
606 % config['builddir'], None, 1800)
607 if ret_val_make or ret_val_install:
608 self.logger.error('TITAN build failed for slave `%s\', please check ' \
609 'the logs for further investigation, stopping slave ' \
610 % slave_name)
611 output = (stamp_begin, utils.get_time(), \
612 ((ret_val_dep, stdout_dep, stderr_dep), \
613 (ret_val_make, stdout_make, stderr_make), \
614 (ret_val_install, stdout_install, stderr_install)))
615 self.publisher.titan_out(config, slave_name, output)
616 if ret_val_dep or ret_val_make or ret_val_install:
617 return 1
618 else:
619 if 'foa' in config and config['foa'] and 'foadir' in config and config['foadir'] != config['installdir']:
620 # The `installdir' must be removed by hand after a FOA period. Cannot
621 # be automated in a sane way. For FOA `installdir' shall be a unique
622 # directory. E.g. date based. Otherwise, the builds are always
623 # overwritten.
624 self.logger.debug('Linking directories for FOA build to `%s\'' % config['foadir'])
625 (ret_val_rm, stdout_rm, stderr_rm) = utils.run_cmd('/bin/rm -rvf %s' % config['foadir'])
626 if ret_val_rm: # Sometimes it doesn't work.
627 self.logger.error('Unable to remove `%s\': `%s\'' % (config['foadir'], ''.join(stderr_rm)))
628 utils.run_cmd('ln -s %s %s' % (config['installdir'], config['foadir']))
629 return 0
630
631 def pass_setenv(self, config, slave_name):
632 """ Set some environment variables needed to run the TITAN tests. Don't
633 use uppercase latters in directory names. The GCC is added as well.
634 Always check if an environment variable exists before reading it.
635
636 Arguments:
637 The current build configuration of the slave and its name.
638 """
639 path = os.environ.get('PATH')
640 ld_library_path = os.environ.get('LD_LIBRARY_PATH')
641 os.environ['PATH'] = '%s/bin:%s/bin:%s' % (config['installdir'], config['gccdir'], path and path or '')
642 os.environ['LD_LIBRARY_PATH'] = '%s/lib:%s/lib:%s' % (config['installdir'], config['gccdir'], ld_library_path and ld_library_path or '')
643 os.environ['TTCN3_DIR'] = config['installdir']
644 os.environ['TTCN3_LICENSE_FILE'] = config['license']
645
646 def pass_regtest_helper(self, config, slave_name, runtime):
647 """ Run the regression tests with `make' and then `make run'. The output
648 is sent to the publisher as well. At the end, `make clean' is done to
649 save some bytes. Don't use `tee', since its exit code will always be
650 0. Only `stdout' is used.
651
652 Arguments:
653 config: The current build configuration.
654 slave_name: The name of the slave.
655 runtime: 0 for the load-test run-time, 1 for the function-test
656 runtime.
657 """
658 utils.run_cmd('cd %s/TTCNv3/regression_test && make distclean' \
659 % config['builddir'], None, 1800)
660 (ret_val_make, stdout_make, stderr_make) = \
661 utils.run_cmd('cd %s/TTCNv3/regression_test && %s make 2>&1' \
662 % (config['builddir'], runtime and 'RT2=1' or ''), None, 3600)
663 if ret_val_make:
664 self.logger.error('The regression test failed to build for the ' \
665 '`%s\' runtime' % (runtime and 'function-test' or 'load-test'))
666 (ret_val_run, stdout_run, stderr_run) = \
667 utils.run_cmd('cd %s/TTCNv3/regression_test && %s make run 2>&1' \
668 % (config['builddir'], runtime and 'RT2=1' or ''), None, 1800)
669 failed_lines = []
670 all_fine = True
671 for index, line in globals()['__builtins__'].enumerate(stdout_run):
672 matched_line = re.search('Verdict stat.*pass \((\d+)\..*', line)
673 if matched_line and int(matched_line.group(1)) != 100:
674 if all_fine and not failed_lines:
675 failed_lines.append('\nThe failed tests were the following:\n\n')
676 if not re.search('TverdictOper', stdout_run[index - 1]):
677 all_fine = False
678 failed_lines.append(stdout_run[index - 1])
679 failed_lines.append(line)
680 stdout_run.extend(failed_lines)
681 if ret_val_run or not all_fine:
682 self.logger.error('The regression test failed to run for the ' \
683 '`%s\' runtime' % (runtime and 'function-test' or 'load-test'))
684 ret_val_run = 1
685 utils.run_cmd('cd %s/TTCNv3/regression_test && make clean' \
686 % config['builddir'], None, 1800)
687 return ((ret_val_make, stdout_make, stderr_make), \
688 (ret_val_run, stdout_run, stderr_run))
689
690 def pass_regtest(self, config, slave_name):
691 """ Build and run the regression tests and publish the results. The
692 `pass_regtest_helper()' does the dirty job.
693
694 Arguments:
695 config: The current build configuration.
696 slave_name: The name of the slave.
697 """
698 output = {}
699 stamp_begin = utils.get_time()
700 rt1_results = self.pass_regtest_helper(config, slave_name, 0)
701 output['rt1'] = (stamp_begin, utils.get_time(), rt1_results)
702 if config['rt2']:
703 stamp_begin = utils.get_time()
704 rt2_results = self.pass_regtest_helper(config, slave_name, 1)
705 output['rt2'] = (stamp_begin, utils.get_time(), rt2_results)
706 self.publisher.regtest_out(config, slave_name, output)
707
708 def pass_eclipse(self, config, slave_name):
709 """ Build Eclipse plugins and publish them to an update site.
710
711 Arguments:
712 config: The current build configuration.
713 slave_name: The name of the slave.
714 """
715 output = {}
716 stamp_begin = utils.get_time()
717 results = utils.run_cmd('cd %s/TTCNv3/eclipse/automatic_build && ant -d -l mylog.log -f build_main.xml updatesite.experimental 2>&1' \
718 % config['builddir'], None, 1800)
719 log_dir = os.path.join(config['logdir'], slave_name)
720 utils.run_cmd('cp %s/TTCNv3/eclipse/automatic_build/mylog.log %s/eclipse-mylog.log' \
721 % (config['builddir'], log_dir))
722 output = (stamp_begin, utils.get_time(), os.path.join(log_dir, 'eclipse-mylog.log'), results)
723 self.publisher.eclipse_out(config, slave_name, output)
724
725 def pass_perftest_helper(self, config, slave_name, runtime):
726 """ Build the performance test and run it for some predefined CPS values.
727 These CPS values should come from the build configurations instead.
728 Obviously, if the build fails all test runs are skipped. It handles
729 its own tarball as well. It's unpacked at the beginning and removed
730 at the end. The results are also published.
731
732 Arguments:
733 The actual build configuration and the name of the slave. The
734 function returns nothing.
735 """
736 perftest_out = {}
737 utils.run_cmd('cd %s/perftest && ttcn3_makefilegen -e titansim %s ' \
738 '*.ttcnpp *.ttcnin *.ttcn *.cc *.cfg' \
739 % (config['builddir'], (runtime and '-fpgR' or '-fpg')))
740 # Avoid infinite recursion.
741 utils.run_cmd('sed \'s/^-include $(DEPFILES)$//\' Makefile >Makefile-tmp && mv Makefile-tmp Makefile',
742 os.path.join(config['builddir'], 'perftest'))
743 utils.run_cmd('cd %s/perftest && make clean' % config['builddir'])
744 (ret_val_dep, stdout_dep, stderr_dep) = \
745 utils.run_cmd('cd %s/perftest && find . -exec touch {} \; && make %s dep 2>&1' \
746 % (config['builddir'], (runtime and 'RT2=1' or '')), \
747 None, 900)
748 (ret_val_make, stdout_make, stderr_make) = \
749 utils.run_cmd('cd %s/perftest && make %s 2>&1' \
750 % (config['builddir'], (runtime and 'RT2=1' or '')), \
751 None, 1800)
752 perftest_out['dep'] = (ret_val_dep, stdout_dep, stderr_dep)
753 perftest_out['make'] = (ret_val_make, stdout_make, stderr_make)
754 perftest_out['run'] = []
755 if not ret_val_make:
756 cps_min = config.get('cpsmin', 1000)
757 cps_max = config.get('cpsmax', 2000)
758 cps_diff = abs(cps_max - cps_min) / 5
759 for cps in range(cps_min, cps_max + cps_diff, cps_diff):
760 # These numbers should be platform dependent. Lower on slow
761 # machines and high on fast machines.
762 (ret_val_run, stdout_run, stderr_run) = \
763 utils.run_cmd('cd %s/perftest && cpp -DTSP_CPS_CPP=%d.0 config.cfg >config.cfg-tmp && ' \
764 'ttcn3_start ./titansim ./config.cfg-tmp 2>&1' \
765 % (config['builddir'], cps), None, 900)
766 for line in stdout_run:
767 matched_line = re.search('Verdict stat.*pass \((\d+)\..*', line)
768 if matched_line and int(matched_line.group(1)) != 100:
769 self.logger.error('Performance test failed to run for `%d\' CPSs' % cps)
770 ret_val_run = 1
771 perftest_out['run'].append((cps, (ret_val_run, stdout_run, stderr_run)))
772 else:
773 self.logger.error('Performance test compilation failed for the ' \
774 '`%s\' runtime' % (runtime and 'function-test' or 'load-test'))
775 return perftest_out
776
777 def pass_perftest(self, config, slave_name):
778 """ Build and run the performance tests and publish the results. The
779 `pass_perftest_helper()' does the dirty job.
780
781 Arguments:
782 config: The current build configuration.
783 slave_name: The name of the slave.
784 """
785 utils.run_cmd('bunzip2 perftest-*.tar.bz2', config['builddir'], 1800)
786 utils.run_cmd('tar xf ./perftest-*.tar && bzip2 ./perftest-*.tar', \
787 config['builddir'], 1800)
788 if not os.path.isdir('%s/perftest' % config['builddir']):
789 self.logger.error('The performance test is not available at ' \
790 '`%s/perftest\'' % config['builddir'])
791 else:
792 output = {}
793 stamp_begin = utils.get_time()
794 rt1_results = self.pass_perftest_helper(config, slave_name, 0)
795 output['rt1'] = (stamp_begin, utils.get_time(), rt1_results)
796 if config['rt2']:
797 stamp_begin = utils.get_time()
798 rt2_results = self.pass_perftest_helper(config, slave_name, 1)
799 output['rt2'] = (stamp_begin, utils.get_time(), rt2_results)
800 self.publisher.perftest_out(config, slave_name, output)
801 utils.run_cmd('/bin/rm -rf %s/perftest*' % config['builddir'], None, 1800)
802
803 def pass_functest_helper(self, config, slave_name, runtime):
804 function_test_prefix = '%s/TTCNv3/function_test' % config['builddir']
805 function_test_prefixes = ('%s/BER_EncDec' % function_test_prefix, \
806 '%s/Config_Parser' % function_test_prefix, \
807 '%s/RAW_EncDec' % function_test_prefix, \
808 '%s/Semantic_Analyser' % function_test_prefix, \
809 '%s/Text_EncDec' % function_test_prefix, \
810 '%s/Semantic_Analyser/float' % function_test_prefix, \
811 '%s/Semantic_Analyser/import_of_iports' % function_test_prefix, \
812 '%s/Semantic_Analyser/options' % function_test_prefix, \
813 '%s/Semantic_Analyser/ver' % function_test_prefix, \
814 '%s/Semantic_Analyser/xer' % function_test_prefix)
815 log_dir = os.path.join(config['logdir'], slave_name)
816 functest_out = {}
817 stamp_old = utils.get_time()
818 for function_test in function_test_prefixes:
819 utils.run_cmd('ln -s %s %s' % (config['perl'], function_test))
820 function_test_name = function_test.split('/')[-1]
821 ber_or_raw_or_text = not (function_test_name == 'Config_Parser' or function_test_name == 'Semantic_Analyser')
822 utils.run_cmd('cd %s && %s ./%s %s 2>&1 | tee %s/functest-%s.%s' \
823 % (function_test, (runtime and 'RT2=1' or ''),
824 (os.path.isfile('%s/run_test_all' % function_test) \
825 and 'run_test_all' or 'run_test'), \
826 ((runtime and not ber_or_raw_or_text) and '-rt2' or ''), \
827 log_dir, function_test_name, \
828 (runtime and 'rt2' or 'rt1')), None, 3600)
829 error_target = os.path.join(log_dir, 'functest-%s-error.%s' % (function_test_name, (runtime and 'rt2' or 'rt1')))
830 if ber_or_raw_or_text:
831 utils.run_cmd('cp %s/%s_TD.script_error %s' \
832 % (function_test, function_test_name, error_target))
833 utils.run_cmd('cp %s/%s_TD.fast_script_error %s' \
834 % (function_test, function_test_name, error_target))
835 functest_out[function_test_name] = \
836 ('%s/functest-%s.%s' % (log_dir, function_test_name, (runtime and 'rt2' or 'rt1')), \
837 (ber_or_raw_or_text and error_target or ''))
838 return functest_out
839
840 def pass_functest(self, config, slave_name):
841 """ Build pass to build and run the function tests. The
842 `pass_functest_helper()' does the direty job.
843 """
844 output = {}
845 stamp_begin = utils.get_time()
846 rt1_results = self.pass_functest_helper(config, slave_name, 0)
847 output['rt1'] = (stamp_begin, utils.get_time(), rt1_results)
848 if config['rt2']:
849 stamp_begin = utils.get_time()
850 rt2_results = self.pass_functest_helper(config, slave_name, 1)
851 output['rt2'] = (stamp_begin, utils.get_time(), rt2_results)
852 self.publisher.functest_out(config, slave_name, output)
853
854 def pass_vobtest(self, config, slave_name):
855 """ Build pass for the VOB products. Currently, the VOB products are
856 compiled only due to the lack of usable tests written for them. The
857 output is stored here by the publisher. The normal runtime should
858 always be the first, it's a restriction of the publisher.
859
860 Arguments:
861 The actual build configuration and its name.
862 """
863 utils.run_cmd('bunzip2 %s/vobtest-*.tar.bz2' \
864 % config['builddir'], None, 1800)
865 utils.run_cmd('cd %s && tar xf ./vobtest-*.tar && bzip2 ./vobtest-*.tar' \
866 % config['builddir'], None, 1800)
867 if not os.path.isdir('%s/vobtest' % config['builddir']):
868 self.logger.error('The products are not available at `%s/vobtest\'' \
869 % config['builddir'])
870 self.publisher.vobtest_out(utils.get_time(), utils.get_time(), {})
871 else:
872 output = {}
873 stamp_begin = utils.get_time()
874 handler = product_handler.product_handler(self.logger, self.config)
875 log_dir = '%s/%s/products' % (config['logdir'], slave_name)
876 results = handler.build_products('%s/vobtest' % config['builddir'], \
877 log_dir, config, False)
878 output['rt1'] = (stamp_begin, utils.get_time(), results)
879 if config['rt2']:
880 stamp_begin = utils.get_time()
881 results = handler.build_products('%s/vobtest' \
882 % config['builddir'], log_dir, config, True)
883 output['rt2'] = (stamp_begin, utils.get_time(), results)
884 self.publisher.vobtest_out(config, slave_name, output)
885 utils.run_cmd('/bin/rm -rf %s/vobtest*' % config['builddir'], None, 1800)
886
887 def config_titan(self, config, build_dir):
888 """ Modify TITAN configuration files to create a platform-specific source
889 package. The original files are always preserved in an `*.orig' file.
890 `sed' would be shorter, but this way everything is under control.
891 Improve file handling. It is assumed, that there's a `TTCNv3' directory
892 in the build directory.
893
894 Arguments:
895 config: The build configuration we're configuring for.
896
897 Returns:
898 If everything went fine 0 is returned. Otherwise 1 is returned and
899 the error messages will be logged. The screen always stays intact.
900 """
901 if not os.path.isdir('%s/TTCNv3' % build_dir):
902 self.logger.error('The `%s/TTCNv3\' directory is not found' % build_dir)
903 return 1 # It's a fatal error, no way out.
904 # Prepare all function tests. Add links to the `perl' interpreter and
905 # modify some some Makefiles containing the platform string.
906 if config['functest']:
907 function_test_prefix = '%s/TTCNv3/function_test' % build_dir
908 for function_test in ('%s/BER_EncDec' % function_test_prefix, \
909 '%s/Config_Parser' % function_test_prefix, \
910 '%s/RAW_EncDec' % function_test_prefix, \
911 '%s/Semantic_Analyser' % function_test_prefix, \
912 '%s/Text_EncDec' % function_test_prefix):
913 if os.path.isdir(function_test):
914 if function_test.endswith('BER_EncDec') or \
915 function_test.endswith('RAW_EncDec') or \
916 function_test.endswith('Text_EncDec'):
917 utils.run_cmd('mv %s/Makefile %s/Makefile.orig' \
918 % (function_test, function_test))
919 berrawtext_makefile = open('%s/Makefile.orig' % function_test, 'rt')
920 berrawtext_makefile_new = open('%s/Makefile' % function_test, 'wt')
921 for line in berrawtext_makefile:
922 if re.match('^PLATFORM\s*:?=\s*\w+$', line): # Platform
923 # autodetect later. It is hard-coded into the build
924 # configuration.
925 berrawtext_makefile_new.write('PLATFORM = %s\n' % config['platform'])
926 elif re.match('^CXX\s*:?=\s*.*$', line) and ('cxx' in config and len(config['cxx']) > 0):
927 berrawtext_makefile_new.write('CXX = %s\n' % config['cxx'])
928 else:
929 berrawtext_makefile_new.write(line)
930 if function_test.endswith('BER_EncDec'):
931 utils.run_cmd('mv %s/run_test %s/run_test.orig' \
932 % (function_test, function_test))
933 utils.run_cmd('cat %s/run_test.orig | ' \
934 'sed s/TD.script/TD.fast_script/ >%s/run_test ' \
935 '&& chmod 755 %s/run_test' \
936 % (function_test, function_test, function_test)) # Make it fast.
937 else:
938 self.logger.warning('Function test directory `%s\' is not found'
939 % function_test)
940 # Add `-lncurses' for all `LINUX' targets. It's not always needed, hence
941 # platform autodetect won't help in this.
942 if config['platform'] == 'LINUX':
943 mctr_makefile_name = '%s/TTCNv3/mctr2/mctr/Makefile' % build_dir
944 if os.path.isfile(mctr_makefile_name):
945 utils.run_cmd('mv %s %s.orig' % (mctr_makefile_name, mctr_makefile_name))
946 mctr_makefile = open('%s.orig' % mctr_makefile_name, 'rt')
947 mctr_makefile_new = open(mctr_makefile_name, 'wt')
948 for line in mctr_makefile:
949 if re.match('^LINUX_CLI_LIBS\s*:?=\s*$', line):
950 mctr_makefile_new.write('LINUX_CLI_LIBS := -lncurses\n')
951 else:
952 mctr_makefile_new.write(line)
953 mctr_makefile.close()
954 mctr_makefile_new.close()
955 else:
956 self.logger.warning('The `%s\' is not found' % mctr_makefile_name)
957 # Prepare the main configuration file.
958 makefile_cfg_name = '%s/TTCNv3/Makefile.cfg' % build_dir
959 if os.path.isfile(makefile_cfg_name):
960 utils.run_cmd('mv %s %s.orig' % (makefile_cfg_name, makefile_cfg_name))
961 makefile_cfg = open('%s.orig' % makefile_cfg_name, 'rt')
962 makefile_cfg_new = open(makefile_cfg_name, 'wt')
963 for line in makefile_cfg:
964 if re.match('^TTCN3_DIR\s*:?=\s*.*$', line):
965 # Use the environment.
966 continue
967 elif re.match('^DEBUG\s*:?=\s*.*$', line):
968 makefile_cfg_new.write('DEBUG := %s\n' % (config['debug'] and 'yes' or 'no'))
969 elif re.match('^# PLATFORM\s*:?=\s*.*$', line) and len(config['platform']) > 0:
970 # Automatic platform detection doesn't seem to work very well, so the
971 # platform should always be set explicitly.
972 makefile_cfg_new.write('PLATFORM := %s\n' % config['platform'])
973 elif re.match('^JNI\s*:?=\s*.*$', line):
974 # It's the so called `and-or' trick from http://diveintopython.org/
975 # power_of_introspection/and_or.html.
976 makefile_cfg_new.write('JNI := %s\n' % (config['jni'] and 'yes' or 'no'))
977 elif re.match('^GUI\s*:?=\s*.*$', line):
978 makefile_cfg_new.write('GUI := %s\n' % (config['gui'] and 'yes' or 'no'))
979 elif re.match('^FLEX\s*:?=\s*.*$', line) and len(config['flex']) > 0:
980 makefile_cfg_new.write('FLEX := %s\n' % config['flex'])
981 elif re.match('^BISON\s*:?=\s*.*$', line) and len(config['bison']) > 0:
982 makefile_cfg_new.write('BISON := %s\n' % config['bison'])
983 elif re.match('^CC\s*:?=\s*.*$', line) and len(config['gccdir']) > 0:
984 makefile_cfg_new.write('CC := %s/bin/%s\n' % (config['gccdir'], (('cc' in config and len(config['cc']) > 0) and config['cc'] or 'gcc')))
985 elif re.match('^CXX\s*:?=\s*.*$', line) and len(config['gccdir']) > 0:
986 makefile_cfg_new.write('CXX := %s/bin/%s\n' % (config['gccdir'], (('cxx' in config and len(config['cxx']) > 0) and config['cxx'] or 'g++')))
987 elif re.match('^JDKDIR\s*:?=\s*.*$', line) and len(config['jdkdir']) > 0:
988 makefile_cfg_new.write('JDKDIR := %s\n' % config['jdkdir'])
989 elif re.match('^QTDIR\s*:?=\s*.*$', line) and len(config['qtdir']) > 0:
990 makefile_cfg_new.write('QTDIR = %s\n' % config['qtdir'])
991 elif re.match('^XMLDIR\s*:?=\s*.*$', line) and len(config['xmldir']) > 0:
992 makefile_cfg_new.write('XMLDIR = %s\n' % config['xmldir'])
993 elif re.match('^OPENSSL_DIR\s*:?=\s*.*$', line) and len(config['openssldir']) > 0:
994 makefile_cfg_new.write('OPENSSL_DIR = %s\n' % config['openssldir'])
995 elif re.match('^LDFLAGS\s*:?=\s*.*$', line) and len(config['ldflags']) > 0:
996 makefile_cfg_new.write('LDFLAGS = %s\n' % config['ldflags'])
997 elif re.match('^COMPILERFLAGS\s*:?=\s*.*$', line) and len(config['compilerflags']) > 0:
998 makefile_cfg_new.write('COMPILERFLAGS = %s\n' % config['compilerflags'])
999 else:
1000 makefile_cfg_new.write(line)
1001 makefile_cfg.close()
1002 makefile_cfg_new.close()
1003 else:
1004 self.logger.error('The `%s\' is not found, it seems to be a fake ' \
1005 'installation' % makefile_cfg_name)
1006 return 1 # It's essential, exit immediately.
1007 if config['regtest']:
1008 regtest_makefile_name = '%s/TTCNv3/regression_test/Makefile.regression' % build_dir
1009 if os.path.isfile(regtest_makefile_name):
1010 utils.run_cmd('mv %s %s.orig' \
1011 % (regtest_makefile_name, regtest_makefile_name))
1012 regtest_makefile = open('%s.orig' % regtest_makefile_name, 'rt')
1013 regtest_makefile_new = open(regtest_makefile_name, 'wt')
1014 for line in regtest_makefile:
1015 if re.match('^TTCN3_DIR\s*:?=\s*.*$', line):
1016 # Use the environment.
1017 continue
1018 elif re.match('^CC\s*:?=\s*.*$', line) and len(config['gccdir']) > 0:
1019 regtest_makefile_new.write('CC := %s/bin/%s\n' % (config['gccdir'], (('cc' in config and len(config['cc']) > 0) and config['cc'] or 'gcc')))
1020 elif re.match('^CXX\s*:?=\s*.*$', line) and len(config['gccdir']) > 0:
1021 regtest_makefile_new.write('CXX := %s/bin/%s\n' % (config['gccdir'], (('cxx' in config and len(config['cxx']) > 0) and config['cxx'] or 'g++')))
1022 elif re.match('^XMLDIR\s*:?=\s*.*$', line) and len(config['xmldir']) > 0:
1023 regtest_makefile_new.write('XMLDIR = %s\n' % config['xmldir'])
1024 else:
1025 regtest_makefile_new.write(line)
1026 regtest_makefile.close()
1027 regtest_makefile_new.close()
1028 else:
1029 self.logger.warning('Regression test configuration file `%s\' is ' \
1030 'not found' % regtest_makefile_name)
1031 if 'xsdtests' in config and not config['xsdtests']:
1032 self.logger.warning('Disabling `xsd2ttcn\' tests to save some time')
1033 utils.run_cmd('mv %s %s.orig' \
1034 % (regtest_makefile_name.split('.')[0], \
1035 regtest_makefile_name.split('.')[0]))
1036 utils.run_cmd('cat %s.orig | sed s/\'xsdConverter\'/\'\'/ >%s' \
1037 % (regtest_makefile_name.split('.')[0], \
1038 regtest_makefile_name.split('.')[0]))
1039 self.config_pdfs(config, build_dir)
1040 self.logger.debug('`%s/TTCNv3\' was configured and ready to build, all ' \
1041 'Makefiles were modified successfully' % build_dir)
1042 return 0 # Allow the caller to catch errors. Use exceptions later
1043 # instead. Only `./TTCNv3' and `./TTCNv3/Makefile.cfg' is necessary for a
1044 # successful configuration. Other Makefiles can be missing.
1045
1046 def config_pdfs(self, config, build_dir):
1047 """ Optionally, copy .pdf files to the documentation directory or create
1048 fake .pdf files to make the installation successful. If the build
1049 configuration doesn't have the appropriate key nothing is done with
1050 .pdf files. If the directory of .pdf files doesn't exists the .pdf
1051 files will be faked.
1052
1053 Arguments:
1054 The actual build configuration.
1055 """
1056 if 'pdfdir' in config:
1057 if not os.path.isdir(config['pdfdir']):
1058 self.logger.debug('Creating fake .pdf files in %s/TTCNv3/usrguide' % build_dir)
1059 for file in os.listdir('%s/TTCNv3/usrguide' % build_dir):
1060 if file.endswith('.doc'):
1061 utils.run_cmd('touch %s/TTCNv3/usrguide/%s.pdf' \
1062 % (build_dir, file.split('.')[0]))
1063 else:
1064 self.logger.debug('Copying .pdf files from %s' % config['pdfdir'])
1065 utils.run_cmd('cp %s/*.pdf %s/TTCNv3/usrguide' % (config['pdfdir'], build_dir))
1066 else:
1067 self.logger.debug('The .pdf files are not in place, your ' \
1068 'installation will fail if you haven\'t fixed the ' \
1069 'Makefile...')
1070
1071 def dump_addressees(self):
1072 for addressee in self.config.recipients:
1073 print('%s %s' % (addressee, self.config.recipients[addressee]))
1074
1075 def dump_configs(self):
1076 configs = self.config.configs
1077 slaves = self.config.slaves
1078 for config_name, config_data in configs.iteritems():
1079 slave_list = []
1080 for slave_name, slave_data in slaves.iteritems():
1081 if config_name in slave_data['configs']:
1082 slave_list.append(slave_name)
1083 print('%s %s' % (config_name, ', '.join(slave_list)))
1084
1085def main(argv = None):
1086 if argv is None:
1087 argv = sys.argv
1088 usage = 'Usage: %prog [options]'
1089 version = '%prog 0.0.5'
1090 parser = optparse.OptionParser(usage = usage, version = version)
1091 parser.add_option('-a', '--addressees', action = 'store_true', dest = 'addressees', help = 'dump all addressees')
1092 parser.add_option('-A', '--set-addressees', action = 'store', type = 'string', dest = 'set_addressees', help = 'set addressees from command line')
1093 parser.add_option('-c', '--config-list', action = 'store', type = 'string', dest = 'config_list', help = 'list of build configurations')
1094 parser.add_option('-d', '--dump', action = 'store_true', dest = 'dump', help = 'dump build configurations and the attached slaves', default = False)
1095 parser.add_option('-p', '--source-path', action = 'store', type = 'string', dest = 'source_path', help = 'instead of CVS use the given path')
1096 parser.add_option('-r', '--reset', action = 'store_true', dest = 'reset', help = 'reset statistics', default = False)
1097 parser.add_option('-s', '--slave-mode', action = 'store', type = 'string', dest = 'slave_mode', help = 'enable slave mode', default = None)
1098 parser.add_option('-t', '--tests', action = 'store', type = 'string', dest = 'tests', help = 'tests to run')
1099 (options, args) = parser.parse_args()
1100 # The slaves are always executing a specific build configuration.
1101 if not options.config_list and options.slave_mode:
1102 parser.print_help()
1103 elif options.addressees:
1104 titan_builder().dump_addressees()
1105 elif options.dump:
1106 titan_builder().dump_configs()
1107 else:
1108 builder = titan_builder()
1109 builder.build(options.config_list, options.slave_mode, options.reset, options.set_addressees, options.tests, options.source_path)
1110 return 0
1111
1112if __name__ == '__main__':
1113 ret_val = 1
1114 try:
1115 ret_val = main()
1116 except SystemExit, e:
1117 ret_val = e
1118 except:
1119 print('Exception caught, writing traceback info to log file `%s\'' \
1120 % TRACE_FILENAME)
1121 traceback.print_exc(file = open(TRACE_FILENAME, 'at'))
1122 sys.exit(1) # Don't fall through.
1123 sys.exit(ret_val)
This page took 0.157479 seconds and 5 git commands to generate.