Changeset 37a0843 in rtems-tools


Ignore:
Timestamp:
May 24, 2017, 2:27:38 AM (2 years ago)
Author:
Chris Johns <chrisj@…>
Branches:
master
Children:
718b230
Parents:
4c24f40
Message:

rtems-bsp-builder: Refactor to add jobs.

This change adds job support to the rtems-bsp-builder so builds can
run in parallel.

Some options have changed so they make sense and are more useful.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • tester/rt/check.py

    r4c24f40 r37a0843  
    3939import sys
    4040import textwrap
     41import threading
     42import time
     43import traceback
    4144
    4245import pprint
     
    5053from rtemstoolkit import textbox
    5154from rtemstoolkit import version
     55
     56#
     57# Group loggin entries together.
     58#
     59log_lock = threading.Lock()
     60
     61#
     62# The max build label size in the jobs list.
     63#
     64max_build_label = 0
    5265
    5366def rtems_version():
     
    8093    return s
    8194
     95def comma_split(options):
     96    if options is None:
     97        return None
     98    return [o.strip() for o in options.split(',')]
     99
    82100def title():
    83101    return 'RTEMS Tools Project - RTEMS Kernel BSP Builder, %s' % (version.str())
     
    86104    return wrap(('command: ', ' '.join(sys.argv)), lineend = '\\')
    87105
     106def jobs_option_parse(jobs_option):
     107    try:
     108        if '/' not in jobs_option:
     109            return 1, int(jobs_option)
     110        jos = jobs_option.split('/')
     111        if len(jos) != 2:
     112            raise error.general('invalid jobs option: %s' % (jobs_option))
     113        return int(jos[0]), int(jos[1])
     114    except:
     115        pass
     116    raise error.general('invalid jobs option: %s' % (jobs_option))
     117
     118def arch_bsp_build_parse(build):
     119    if type(build) is str:
     120        build_key = build
     121    else:
     122        build_key = build.key()
     123    abb = build_key.split('.')
     124    if len(abb) != 2:
     125        raise error.general('invalid build key: %s' % (build_key))
     126    ab = abb[0].split('/')
     127    if len(ab) != 2:
     128        raise error.general('invalid build key: %s' % (build_key))
     129    return ab[0], ab[1], abb[1]
     130
     131def set_max_build_label(jobs):
     132    global max_build_label
     133    for job in jobs:
     134        if len(job.build.key()) > max_build_label:
     135            max_build_label = len(job.build.key())
     136    max_build_label += 2
     137
     138class arch_bsp_build:
     139
     140    def __init__(self, arch, bsp, build, build_config):
     141        self.arch = arch
     142        self.bsp = bsp
     143        self.build = build
     144        self.build_config = build_config
     145        self.start_time = None
     146        self.stop_time = None
     147
     148    def __str__(self):
     149        return self.key() + ': ' + self.build_config
     150
     151    def key(self):
     152        return '%s/%s.%s' % (self.arch, self.bsp, self.build)
     153
     154    def get_arch_bsp(self):
     155        return self.arch, self.bsp
     156
     157    def start(self):
     158        self.start_time = datetime.datetime.now()
     159
     160    def stop(self):
     161        self.stop_time = datetime.datetime.now()
     162
     163    def duration(self):
     164        return self.stop_time - self.start_time
     165
     166class output_worker:
     167
     168    def __init__(self, we, build):
     169        self.text = []
     170        self.warnings_errors = we
     171        self.build = build
     172
     173    def output(self, text):
     174        self.warnings_errors.process_output(text, self.build)
     175        self.text += text.splitlines()
     176
     177    def log_output(self, heading):
     178        log_lock.acquire()
     179        try:
     180            log.output(heading + self.text)
     181        except:
     182            raise
     183        finally:
     184            log_lock.release()
     185
    88186class warnings_errors:
    89187
    90     def __init__(self, rtems):
    91         self.rtems = path.host(rtems)
     188    def __init__(self, source_base, groups):
     189        self.lock = threading.Lock()
     190        self.source_base = path.host(source_base)
     191        self.groups = groups
    92192        self.reset()
    93         self.groups = { 'groups'  : ['Shared', 'BSP', 'Network', 'Tests',
    94                                      'LibCPU', 'CPU Kit'],
    95                         'exclude' : '.*Makefile.*',
    96                         'CPU Kit' : '.*cpukit/.*',
    97                         'Network' : '.*libnetworking/.*|.*librpc/.*',
    98                         'Tests'   : '.*testsuites/.*',
    99                         'BSP'     : '.*libbsp/.*',
    100                         'LibCPU'  : '.*libcpu/.*',
    101                         'Shared'  : '.*shared/.*' }
    102         self.arch = None
    103         self.bsp = None
    104         self.build = None
    105 
    106     def _opts(self, arch = None, bsp = None, build = None):
    107         if arch is None:
    108             arch = self.arch
    109         if bsp is None:
    110             bsp = self.bsp
    111         if build is None:
    112             build = self.build
    113         return arch, bsp, build
    114 
    115     def _key(self, arch, bsp, build):
    116         arch, bsp, build = self._opts(arch, bsp, build)
    117         return '%s/%s-%s' % (arch, bsp, build)
    118 
    119     def _get_warnings(self, arch = None, bsp = None, build = None):
    120         arch, bsp, build = self._opts(arch = arch, bsp = bsp, build = build)
    121         if arch is None:
    122             arch = '.*'
    123         if bsp is None:
    124             bsp = '.*'
    125         if build is None:
    126             build = '.*'
    127         selector = re.compile('^%s/%s-%s$' % (arch, bsp, build))
    128         warnings = [w for w in self.warnings if selector.match(w)]
     193
     194    def _get_warnings(self, build):
     195        self.lock.acquire()
     196        warnings = [w for w in self.warnings]
     197        self.lock.release()
    129198        return sorted(warnings)
    130199
     
    140209                data['groups'] = { }
    141210            if category not in data['groups']:
    142                 data['groups'][category] = { 'totals' : { } }
     211                data['groups'][category] = { }
     212            if 'totals' not in data['groups'][category]:
     213                data['groups'][category]['totals'] = { }
    143214            if name not in data['groups'][category]:
    144215                data['groups'][category][name] = { }
     
    163234            _group(data, category, name,  w, count, groups, group_regx)
    164235
    165         data = { }
     236        categories = ['arch', 'arch_bsp', 'build']
     237        data = { 'groups': { } }
     238        for category in categories:
     239            data[category] = { }
     240            data['groups'][category] = { }
    166241        group_regx = { }
    167242        for group in self.groups['groups']:
    168243            group_regx[group] = re.compile(self.groups[group])
    169244        exclude_regx = re.compile(exclude)
    170         for warning in warnings:
    171             arch = warning.split('/', 1)[0]
    172             arch_bsp = warning.split('-', 1)[0]
    173             build = warning.split('-', 1)[1]
     245        for warning in self.warnings:
     246            arch, bsp, build = arch_bsp_build_parse(warning)
     247            arch_bsp = '%s/%s' % (arch, bsp)
    174248            for w in self.warnings[warning]:
    175249                if not exclude_regx.match(w):
    176250                    count = self.warnings[warning][w]
    177                     _update(data, 'arch',     arch,    w, count,
     251                    _update(data, 'arch', arch, w, count,
    178252                           self.groups['groups'], group_regx)
    179253                    _update(data, 'arch_bsp', arch_bsp, w, count,
    180254                           self.groups['groups'], group_regx)
    181                     _update(data, 'build',  build, w, count,
     255                    _update(data, 'build', build, w, count,
    182256                           self.groups['groups'], group_regx)
    183         for category in ['arch', 'arch_bsp', 'build']:
     257        for category in categories:
    184258            common = {}
    185259            for name in data[category]:
     
    268342        return s
    269343
    270     def report(self):
    271         arch, bsp, build = self._opts()
    272         warnings = self._get_warnings(arch, bsp, build)
    273         total = 0
    274         for build in warnings:
    275             total += self._total(self.warnings[build])
    276         if total == 0:
    277             s = ' No warnings'
    278         else:
    279             data = self._analyze(warnings, self.groups['exclude'])
    280             s = self._report_category('By Architecture (total : %d)' % (total),
    281                                       data['arch'], data['groups']['arch'])
    282             s += os.linesep
    283             s += self._report_category('By BSP (total : %d)' % (total),
    284                                        data['arch_bsp'], data['groups']['arch_bsp'])
    285             s += os.linesep
    286             s += self._report_category('By Build (total : %d)' % (total),
    287                                        data['build'], data['groups']['build'])
    288             s += os.linesep
    289             s += self._report_warning_map()
    290             s += os.linesep
    291 
     344    def warnings_report(self):
     345        self.lock.acquire()
     346        s = ' No warnings'
     347        try:
     348            total = 0
     349            for build in self.warnings:
     350                total += self._total(self.warnings[build])
     351            if total != 0:
     352                data = self._analyze(self.warnings, self.groups['exclude'])
     353                s = self._report_category('By Architecture (total : %d)' % (total),
     354                                          data['arch'], data['groups']['arch'])
     355                s += os.linesep
     356                s += self._report_category('By BSP (total : %d)' % (total),
     357                                           data['arch_bsp'], data['groups']['arch_bsp'])
     358                s += os.linesep
     359                s += self._report_category('By Build (total : %d)' % (total),
     360                                           data['build'], data['groups']['build'])
     361                s += os.linesep
     362                s += self._report_warning_map()
     363                s += os.linesep
     364        finally:
     365            self.lock.release()
    292366        return s
    293367
    294     def set_build(self, arch, bsp, build):
    295         self.arch = arch
    296         self.bsp = bsp
    297         self.build = build
    298         self.build_key = '%s/%s-%s' % (arch, bsp, build)
    299         if self.build_key not in self.warnings:
    300             self.warnings[self.build_key] = {}
    301         if self.build_key not in self.errors:
    302             self.errors[self.build_key] = {}
    303 
    304     def clear_build(self):
    305         self.arch = None
    306         self.bsp = None
    307         self.build = None
    308         self.build_key = None
     368    def clear_build(self, build):
     369        self.lock.acquire()
     370        self.warnings[build.key()] = {}
     371        self.errors[build.key()] = {}
     372        self.lock.release()
    309373
    310374    def get_warning_count(self):
    311         return self.warning_count
     375        self.lock.acquire()
     376        count = self.warning_count
     377        self.lock.release()
     378        return count
    312379
    313380    def get_error_count(self):
    314         return self.error_count
     381        self.lock.acquire()
     382        count = self.error_count
     383        self.lock.release()
     384        return count
    315385
    316386    def reset(self):
     387        self.lock.acquire()
    317388        self.warnings = { }
    318389        self.warning_count = 0
     
    320391        self.error_count = 0
    321392        self.messages = { 'warnings' : { }, 'errors' : { } }
    322 
    323     def get_warning_messages(self, arch = None, bsp = None, build = None):
    324         key = self._key(arch, bsp, build)
    325         if key not in self.messages['warnings']:
    326             return []
    327         messages = self.messages['warnings'][key]
    328         return ['%s %s' % (m, messages[m]) for m in messages]
    329 
    330     def get_error_messages(self, arch = None, bsp = None, build = None):
    331         key = self._key(arch, bsp, build)
    332         if key not in self.messages['errors']:
    333             return []
    334         messages = self.messages['errors'][key]
    335         return ['%s %s' % (m, messages[m]) for m in messages]
    336 
    337     def output(self, text):
     393        self.lock.release()
     394
     395    def _get_messages(self, build, key):
     396        self.lock.acquire()
     397        if type(build) is str:
     398            build_key = build
     399        else:
     400            build_key = build.key()
     401        if build_key not in self.messages[key]:
     402            messages = []
     403        else:
     404            messages = self.messages[key][build_key]
     405        messages = ['%s %s' % (m, messages[m]) for m in messages]
     406        self.lock.release()
     407        return messages
     408
     409    def get_warning_messages(self, build):
     410        return self._get_messages(build, 'warning')
     411
     412    def get_error_messages(self, build):
     413        return self._get_messages(build, 'errors')
     414
     415    def process_output(self, text, build):
    338416        def _line_split(line, source_base):
     417            if line.count(':') < 2:
     418                return None
    339419            ls = line.split(' ', 1)
    340             fname = ls[0].split(':')
    341             #
    342             # Ignore compiler option warnings.
    343             #
    344             if len(fname) < 4:
     420            fname = ls[0].strip().split(':', 2)
     421            if len(fname) != 3:
    345422                return None
    346423            p = path.abspath(fname[0])
     
    348425            if path.isabspath(p):
    349426                p = p[1:]
    350             return p, fname[1], fname[2], ls[1]
    351 
    352         if self.build_key is not None and \
    353            (' warning:' in text or ' error:' in text):
    354             for l in text.splitlines():
    355                 if ' warning:' in l:
    356                     self.warning_count += 1
    357                     archive = self.warnings[self.build_key]
    358                     messages = 'warnings'
    359                 elif ' error:' in l:
    360                     self.error_count += 1
    361                     archive = self.errors[self.build_key]
    362                     messages = 'errors'
    363                 else:
    364                     continue
    365                 line_parts = _line_split(l, self.rtems)
    366                 if line_parts is not None:
    367                     src, line, pos, msg = line_parts
    368                     where = '%s:%s:%s' % (src, line, pos)
    369                     if where not in archive:
    370                         archive[where] = 1
     427            if len(fname[2]) == 0:
     428                pos = None
     429            else:
     430                pos = fname[2]
     431            return p, fname[1], pos, ls[1]
     432
     433        def _create_build_errors(build, archive):
     434            if build.key() not in archive:
     435                archive[build.key()] = { }
     436            return archive[build.key()]
     437
     438        #
     439        # The GNU linker does not supply 'error' in error messages. There is no
     440        # line information which is understandable. Look for `bin/ld:` and
     441        # `collect2:` in the output and then create the error when `collect2:`
     442        # is seen.
     443        #
     444        # The order we inspect each line is important.
     445        #
     446        if ' warning:' in text or \
     447           ' error:' in text or \
     448           ' Error:' in text or \
     449           'bin/ld:' in text:
     450            self.lock.acquire()
     451            try:
     452                for l in text.splitlines():
     453                    if 'bin/ld:' in l:
     454                        archive =_create_build_errors(build, self.errors)
     455                        if 'linker' not in archive:
     456                            archive['linker'] = []
     457                        archive['linker'] += [l.split(':', 1)[1].strip()]
     458                    elif l.startswith('collect2:'):
     459                        archive =_create_build_errors(build, self.errors)
     460                        l = '/ld/collect2:0: error: '
     461                        if 'linker' not in archive or len(archive['linker']) == 0:
     462                            l += 'no error message found!'
     463                        else:
     464                            l += '; '.join(archive['linker'])
     465                            archive['linker'] = []
     466                        messages = 'errors'
     467                    elif ' warning:' in l:
     468                        self.warning_count += 1
     469                        archive = _create_build_errors(build, self.warnings)
     470                        messages = 'warnings'
     471                    elif ' error:' in l or ' Error:' in l:
     472                        self.error_count += 1
     473                        archive =_create_build_errors(build, self.errors)
     474                        messages = 'errors'
    371475                    else:
    372                         archive[where] += 1
    373                     if self.build_key not in self.messages[messages]:
    374                         self.messages[messages][self.build_key] = { }
    375                     self.messages[messages][self.build_key][where] = msg
    376 
    377         log.output(text)
     476                        continue
     477                    line_parts = _line_split(l, self.source_base)
     478                    if line_parts is not None:
     479                        src, line, pos, msg = line_parts
     480                        if pos is not None:
     481                            where = '%s:%s:%s' % (src, line, pos)
     482                        else:
     483                            where = '%s:%s' % (src, line)
     484                        if where not in archive:
     485                            archive[where] = 1
     486                        else:
     487                            archive[where] += 1
     488                        if build.key() not in self.messages[messages]:
     489                            self.messages[messages][build.key()] = { }
     490                        self.messages[messages][build.key()][where] = msg
     491            finally:
     492                self.lock.release()
    378493
    379494class results:
    380495
    381     def __init__(self):
    382         self.passes = []
    383         self.fails = []
     496    def __init__(self, source_base, groups):
     497        self.lock = threading.Lock()
     498        self.errors = { 'pass':      0,
     499                        'configure': 0,
     500                        'build':     0,
     501                        'tests':     0,
     502                        'passes':    { },
     503                        'fails':     { } }
     504        self.counts = { 'h'        : 0,
     505                        'exes'     : 0,
     506                        'objs'     : 0,
     507                        'libs'     : 0 }
     508        self.warnings_errors = warnings_errors(source_base, groups)
    384509
    385510    def _arch_bsp(self, arch, bsp):
    386511        return '%s/%s' % (arch, bsp)
    387512
    388     def add(self, good, arch, bsp, configure, warnings, error_messages):
    389         if good:
    390             self.passes += [(arch, bsp, configure, warnings, None)]
     513    def _arch_bsp_passes(self, build):
     514        if build.key() not in self.errors['passes']:
     515            self.errors['passes'][build.key()] = []
     516        return self.errors['passes'][build.key()]
     517
     518    def _arch_bsp_fails(self, build):
     519        if build.key() not in self.errors['fails']:
     520            self.errors['fails'][build.key()] = []
     521        return self.errors['fails'][build.key()]
     522
     523    def _count(self, label):
     524        count = 0
     525        for build in self.errors[label]:
     526            count += len(self.errors[label][build])
     527        return count
     528
     529    def _max_col(self, label):
     530        max_col = 0
     531        for build in self.errors[label]:
     532            arch, bsp, build_config = arch_bsp_build_parse(build)
     533            arch_bsp = self._arch_bsp(arch, bsp)
     534            if len(arch_bsp) > max_col:
     535                max_col = len(arch_bsp)
     536        return max_col
     537
     538    def get_warning_count(self):
     539        return self.warnings_errors.get_warning_count()
     540
     541    def get_error_count(self):
     542        return self.warnings_errors.get_error_count()
     543
     544    def get_warning_messages(self, build):
     545        return self.warnings_errors.get_warning_messages(build)
     546
     547    def get_error_messages(self, build):
     548        return self.warnings_errors.get_error_messages(build)
     549
     550    def status(self):
     551        self.lock.acquire()
     552        try:
     553            s = 'Pass: %4d  Fail: %4d (configure:%d build:%d)' % \
     554                (self.errors['pass'],
     555                 self.errors['configure'] + self.errors['build'],
     556                 self.errors['configure'], self.errors['build'])
     557        except:
     558            raise
     559        finally:
     560            self.lock.release()
     561        return s;
     562
     563    def add_fail(self, phase, build, configure, warnings, error_messages):
     564        fails = self._arch_bsp_fails(build)
     565        self.lock.acquire()
     566        try:
     567            self.errors[phase] += 1
     568            fails += [(phase, build.build_config, configure, error_messages)]
     569        finally:
     570            self.lock.release()
     571
     572    def add_pass(self, build, configure, warnings):
     573        passes = self._arch_bsp_passes(build)
     574        self.lock.acquire()
     575        try:
     576            self.errors['pass'] += 1
     577            passes += [(build.build_config, configure, warnings, None)]
     578        finally:
     579            self.lock.release()
     580
     581    def pass_count(self):
     582        return self._count('passes')
     583
     584    def fail_count(self):
     585        return self._count('fails')
     586
     587    def _failures_report(self, build, count):
     588        if type(build) is str:
     589            build_key = build
    391590        else:
    392             self.fails += [(arch, bsp, configure, warnings, error_messages)]
     591            build_key = build.key()
     592        if build_key not in self.errors['fails'] or \
     593           len(self.errors['fails'][build_key]) == 0:
     594            return count, 0, ' No failure(s)'
     595        absize = 0
     596        bsize = 0
     597        ssize = 0
     598        arch, bsp, build_set = arch_bsp_build_parse(build_key)
     599        arch_bsp = self._arch_bsp(arch, bsp)
     600        fails = self.errors['fails'][build_key]
     601        for f in fails:
     602            if len(f[0]) > ssize:
     603                ssize = len(f[0])
     604        s = ''
     605        for f in fails:
     606            count += 1
     607            fcl = ' %3d' % (count)
     608            state = f[0]
     609            s += '%s %s %s %-*s:%s' % \
     610                 (fcl, build_set, arch_bsp, ssize, state, os.linesep)
     611            s1 = ' ' * 6
     612            s += wrap((s1, 'configure: ' + f[2]), lineend = '\\', width = 75)
     613            s1 = ' ' * 5
     614            for e in self.warnings_errors.get_error_messages(build):
     615                s += wrap([s1 + 'error: ', e])
     616        return count, len(fails), s
     617
     618    def failures_report(self, build = None):
     619        s = ''
     620        count = 0
     621        if build is not None:
     622            count, build_fails, bs = self._failures_report(build, count)
     623            if build_fails > 0:
     624                s += bs + os.linesep
     625        else:
     626            self.lock.acquire()
     627            builds = sorted(self.errors['fails'].keys())
     628            self.lock.release()
     629            for build in builds:
     630                count, build_fails, bs = self._failures_report(build, count)
     631                if build_fails > 0:
     632                    s += bs + os.linesep
     633        if count == 0:
     634            s = ' No failure(s)'
     635        return s
     636
     637    def warnings_report(self):
     638        return self.warnings_errors.warnings_report()
    393639
    394640    def report(self):
    395         log.notice('* Passes: %d   Failures: %d' %
    396                    (len(self.passes), len(self.fails)))
     641        self.lock.acquire()
     642        log_lock.acquire()
     643        passes = self.pass_count()
     644        fails = self.fail_count()
     645        log.notice('Passes: %d   Failures: %d' % (passes, fails))
    397646        log.output()
    398647        log.output('Build Report')
    399         log.output('   Passes: %d   Failures: %d' %
    400                    (len(self.passes), len(self.fails)))
     648        log.output('   Passes: %d   Failures: %d' % (passes, fails))
    401649        log.output(' Failures:')
    402         if len(self.fails) == 0:
     650        if fails == 0:
    403651            log.output('  None')
    404652        else:
    405             max_col = 0
    406             for f in self.fails:
    407                 arch_bsp = self._arch_bsp(f[0], f[1])
    408                 if len(arch_bsp) > max_col:
    409                     max_col = len(arch_bsp)
    410             for f in self.fails:
    411                 config_cmd = f[2]
    412                 config_at = config_cmd.find('configure')
    413                 if config_at != -1:
    414                     config_cmd = config_cmd[config_at:]
    415                 log.output(' %*s:' % (max_col + 2, self._arch_bsp(f[0], f[1])))
    416                 s1 = ' ' * 6
    417                 log.output(wrap([s1, config_cmd], lineend = '\\', width = 75))
    418                 if f[4] is not None:
    419                     s1 = ' ' * len(s1)
    420                     for msg in f[4]:
    421                         log.output(wrap([s1, msg], lineend = '\\'))
     653            max_col = self._max_col('fails')
     654            for build in self.errors['fails']:
     655                arch, bsp, build_config = arch_bsp_build_parse(build)
     656                arch_bsp = self._arch_bsp(arch, bsp)
     657                for f in self.errors['fails'][build]:
     658                    config_cmd = f[2]
     659                    config_at = config_cmd.find('configure')
     660                    if config_at != -1:
     661                        config_cmd = config_cmd[config_at:]
     662                    log.output(' %*s:' % (max_col + 2, arch_bsp))
     663                    s1 = ' ' * 6
     664                    log.output(wrap([s1, config_cmd], lineend = '\\', width = 75))
     665                    if f[3] is not None:
     666                        s1 = ' ' * len(s1)
     667                        for msg in f[3]:
     668                            log.output(wrap([s1, msg], lineend = '\\'))
    422669        log.output(' Passes:')
    423         if len(self.passes) == 0:
     670        if passes == 0:
    424671            log.output('  None')
    425672        else:
    426             max_col = 0
    427             for f in self.passes:
    428                 arch_bsp = self._arch_bsp(f[0], f[1])
    429                 if len(arch_bsp) > max_col:
    430                     max_col = len(arch_bsp)
    431             for f in self.passes:
    432                 config_cmd = f[2]
    433                 config_at = config_cmd.find('configure')
    434                 if config_at != -1:
    435                     config_cmd = config_cmd[config_at:]
    436                 log.output(' %s (%5d):' % (self._arch_bsp(f[0], f[1]), f[3]))
    437                 log.output(wrap([' ' * 6, config_cmd], lineend = '\\', width = 75))
     673            max_col = self._max_col('passes')
     674            for build in self.errors['passes']:
     675                arch, bsp, build_config = arch_bsp_build_parse(build)
     676                arch_bsp = self._arch_bsp(arch, bsp)
     677                for f in self.errors['passes'][build]:
     678                    config_cmd = f[1]
     679                    config_at = config_cmd.find('configure')
     680                    if config_at != -1:
     681                        config_cmd = config_cmd[config_at:]
     682                    log.output(' %s (%5d):' % (arch_bsp, f[2]))
     683                    log.output(wrap([' ' * 6, config_cmd], lineend = '\\', width = 75))
     684        log_lock.release()
     685        self.lock.release()
     686
     687class arch_bsp_builder:
     688
     689    def __init__(self, results_, build, commands, build_dir, tag):
     690        self.lock = threading.Lock()
     691        self.state = 'ready'
     692        self.thread = None
     693        self.proc = None
     694        self.results = results_
     695        self.build = build
     696        self.commands = commands
     697        self.build_dir = build_dir
     698        self.tag = tag
     699        self.output = output_worker(results_.warnings_errors, build)
     700        self.counts = { 'h'        : 0,
     701                        'exes'     : 0,
     702                        'objs'     : 0,
     703                        'libs'     : 0 }
     704
     705    def _notice(self, text):
     706        global max_build_label
     707        arch, bsp, build_set = arch_bsp_build_parse(self.build.key())
     708        label = '%s/%s (%s)' % (arch, bsp, build_set)
     709        log.notice('[%s] %-*s %s' % (self.tag, max_build_label, label, text))
     710
     711    def _build_dir(self):
     712        return path.join(self.build_dir, self.build.key())
     713
     714    def _make_build_dir(self):
     715        if not path.exists(self._build_dir()):
     716            path.mkdir(self._build_dir())
     717
     718    def _count_files(self):
     719        for root, dirs, files in os.walk(self._build_dir()):
     720            for file in files:
     721                if file.endswith('.exe'):
     722                    self.counts['exes'] += 1
     723                elif file.endswith('.o'):
     724                    self.counts['objs'] += 1
     725                elif file.endswith('.a'):
     726                    self.counts['libs'] += 1
     727                elif file.endswith('.h'):
     728                    self.counts['h'] += 1
     729
     730    def _execute(self, phase):
     731        exit_code = 0
     732        cmd = self.commands[phase]
     733        try:
     734            # This should locked; not sure how to do that
     735            self.proc = execute.capture_execution(log = self.output)
     736            log.output(wrap(('run: %s: ', self.build.key(), cmd), lineend = '\\'))
     737            if not self.commands['dry-run']:
     738                exit_code, proc, output = self.proc.shell(cmd,
     739                                                          cwd = path.host(self._build_dir()))
     740        except:
     741            traceback.print_exc()
     742            self.lock.acquire()
     743            if self.proc is not None:
     744                self.proc.kill()
     745            self.lock.release()
     746            exit_code = 1
     747        self.lock.acquire()
     748        self.proc = None
     749        self.lock.release()
     750        return exit_code == 0
     751
     752    def _configure(self):
     753        return self._execute('configure')
     754
     755    def _make(self):
     756        return self._execute('build')
     757
     758    def _worker(self):
     759        self.lock.acquire()
     760        self.state = 'running'
     761        self.lock.release()
     762        self.build.start()
     763        warnings = self.results.get_warning_count()
     764        ok = False
     765        try:
     766            log_lock.acquire()
     767            try:
     768                self._notice('Start')
     769                self._notice('Creating: %s' % (self._build_dir()))
     770            except:
     771                raise
     772            finally:
     773                log_lock.release()
     774            self._make_build_dir()
     775            self._notice('Configuring')
     776            ok = self._configure()
     777            if not ok:
     778                warnings = self.results.get_warning_count() - warnings
     779                self.results.add_fail('configure',
     780                                      self.build,
     781                                      self.commands['configure'],
     782                                      warnings,
     783                                      self.results.get_error_messages(self.build))
     784            self.lock.acquire()
     785            if self.state == 'killing':
     786                ok = False
     787            self.lock.release()
     788            if ok:
     789                self._notice('Building')
     790                ok = self._make()
     791                if not ok:
     792                    warnings = self.results.get_warning_count() - warnings
     793                    self.results.add_fail('build',
     794                                          self.build,
     795                                          self.commands['configure'],
     796                                          warnings,
     797                                          self.results.get_error_messages(self.build))
     798            if ok:
     799                warnings = self.results.get_warning_count() - warnings
     800                self.results.add_pass(self.build,
     801                                      self.commands['configure'],
     802                                      warnings)
     803        except:
     804            ok = False
     805            self._notice('Build Exception')
     806            traceback.print_exc()
     807        self.build.stop()
     808        log_lock.acquire()
     809        try:
     810            self._count_files()
     811            if ok:
     812                self._notice('PASS')
     813            else:
     814                self._notice('FAIL')
     815            self._notice('Warnings:%d  exes:%d  objs:%d  libs:%d' % \
     816                         (warnings, self.counts['exes'],
     817                          self.counts['objs'], self.counts['libs']))
     818            log.output('  %s: Failure Report:' % (self.build.key()))
     819            log.output(self.results.failures_report(self.build))
     820            self._notice('Finished (duration:%s)' % (str(self.build.duration())))
     821            self._notice('Status: %s' % (self.results.status()))
     822        except:
     823            self._notice('Build Exception:')
     824            traceback.print_exc()
     825        finally:
     826            log_lock.release()
     827        self.lock.acquire()
     828        self.state = 'finished'
     829        self.lock.release()
     830
     831    def get_file_counts(self):
     832        return self.counts
     833
     834    def run(self):
     835        self.lock.acquire()
     836        try:
     837            if self.state != 'ready':
     838                raise error.general('builder already run')
     839            self.state = 'starting'
     840            self.thread = threading.Thread(target = self._worker)
     841            self.thread.start()
     842        except:
     843            raise
     844        finally:
     845            self.lock.release()
     846
     847    def kill(self):
     848        self.lock.acquire()
     849        if self.thread is not None:
     850            self.state = 'killing'
     851            if self.proc is not None:
     852                try:
     853                    self.proc.kill()
     854                except:
     855                    pass
     856            self.lock.release()
     857            self.thread.join(5)
     858            self.lock.acquire()
     859        self.state = 'finished'
     860        self.lock.release()
     861
     862    def current_state(self):
     863        self.lock.acquire()
     864        state = self.state
     865        self.lock.release()
     866        return state
     867
     868    def log_output(self):
     869        self.output.log_output(['-' * 79, '] %s: Build output:' % (self.build.key())])
     870
     871    def clean(self):
     872        if not self.commands['no-clean']:
     873            self._notice('Cleaning: %s' % (self._build_dir()))
     874            path.removeall(self._build_dir())
    438875
    439876class configuration_:
     
    446883
    447884    def __str__(self):
    448         import pprint
    449885        s = self.name + os.linesep
    450886        s += 'Archs:' + os.linesep + \
     
    5801016
    5811017    def bspopts(self, arch, bsp):
     1018        if arch not in self.archs:
     1019            raise error.general('invalid architecture: %s' % (arch))
     1020        if bsp not in self.archs[arch]:
     1021            raise error.general('invalid BSP: %s' % (bsp))
    5821022        return self.archs[arch][bsp]['bspopts']
    5831023
     
    5861026
    5871027    def profile_archs(self, profile):
     1028        if profile not in self.profiles:
     1029            raise error.general('invalid profile: %s' % (profile))
    5881030        return self.profiles[profile]['archs']
    5891031
    5901032    def profile_arch_bsps(self, profile, arch):
     1033        if profile not in self.profiles:
     1034            raise error.general('invalid profile: %s' % (profile))
     1035        if 'bsps_%s' % (arch) not in self.profiles[profile]:
     1036            raise error.general('invalid profile arch: %s' % (arch))
    5911037        return self.profiles[profile]['bsps_%s' % (arch)]
    5921038
     
    7301176        return s
    7311177
    732 class build:
     1178class build_jobs:
     1179
     1180    def __init__(self, config, arch, bsp):
     1181        self.arch = arch
     1182        self.bsp = bsp
     1183        self.builds = config.builds()
     1184        if self.builds is None:
     1185            raise error.general('build not found: %s' % (config.build()))
     1186        excludes = set(config.excludes(self.arch) +
     1187                       config.bsp_excludes(self.arch, self.bsp))
     1188        remove = []
     1189        for e in excludes:
     1190            remove += [b for b in self.builds if e in b]
     1191        for b in remove:
     1192            self.builds.remove(b)
     1193        self.build_set = { }
     1194        for build in self.builds:
     1195            self.build_set[build] = config.build_options(build)
     1196
     1197    def jobs(self):
     1198        return [arch_bsp_build(self.arch, self.bsp, b, self.build_set[b]) \
     1199                for b in sorted(self.build_set.keys())]
     1200
     1201class builder:
    7331202
    7341203    def __init__(self, config, version, prefix, tools, rtems, build_dir, options):
     
    7401209        self.rtems = rtems
    7411210        self.options = options
    742         self.errors = { 'configure': 0,
    743                         'build':     0,
    744                         'tests':     0,
    745                         'fails':     []}
    7461211        self.counts = { 'h'        : 0,
    7471212                        'exes'     : 0,
    7481213                        'objs'     : 0,
    7491214                        'libs'     : 0 }
    750         self.warnings_errors = warnings_errors(rtems)
    751         self.results = results()
     1215        self.results = results(rtems,
     1216                               { 'groups'  : ['Shared', 'BSP', 'Network', 'Tests',
     1217                                              'LibCPU', 'CPU Kit'],
     1218                                 'exclude' : '.*Makefile.*',
     1219                                 'CPU Kit' : '.*cpukit/.*',
     1220                                 'Network' : '.*libnetworking/.*|.*librpc/.*',
     1221                                 'Tests'   : '.*testsuites/.*',
     1222                                 'BSP'     : '.*libbsp/.*',
     1223                                 'LibCPU'  : '.*libcpu/.*',
     1224                                 'Shared'  : '.*shared/.*' })
    7521225        if not path.exists(path.join(rtems, 'configure')) or \
    7531226           not path.exists(path.join(rtems, 'Makefile.in')) or \
     
    7551228            raise error.general('RTEMS source path does not look like RTEMS')
    7561229
    757     def _error_str(self):
    758         return 'Status: configure:%d build:%d' % \
    759             (self.errors['configure'], self.errors['build'])
    760 
    761     def _path(self, arch, bsp):
    762         return path.join(self.build_dir, arch, bsp)
    763 
    764     def _archs(self, build_data):
    765         return sorted(build_data.keys())
    766 
    7671230    def _bsps(self, arch):
    7681231        return self.config.arch_bsps(arch)
    7691232
    770     def _build(self):
    771         return self.config.build()
    772 
    773     def _builds(self, arch, bsp):
    774         builds = self.config.builds()
    775         if builds is None:
    776             return None
    777         excludes = set(self.config.excludes(arch) +
    778                        self.config.bsp_excludes(arch, bsp))
    779         remove = []
    780         for e in excludes:
    781             remove += [b for b in builds if e in b]
    782         for b in remove:
    783             builds.remove(b)
    784         return builds
    785 
    786     def _arch_bsp_dir_make(self, arch, bsp):
    787         if not path.exists(self._path(arch, bsp)):
    788             path.mkdir(self._path(arch, bsp))
    789 
    790     def _arch_bsp_dir_clean(self, arch, bsp):
    791         if path.exists(self._path(arch, bsp)):
    792             path.removeall(self._path(arch, bsp))
    793 
    794     def _config_command(self, commands, arch, bsp):
    795         if type(commands) is not list:
    796             commands = [commands]
     1233    def _create_build_jobs(self, jobs, build_job_count):
     1234        max_job_size = len('%d' % len(jobs))
     1235        build_jobs = []
     1236        job_index = 1
     1237        for job in jobs:
     1238            tag = '%*d/%d' % (max_job_size, job_index, len(jobs))
     1239            build_jobs += [arch_bsp_builder(self.results,
     1240                                            job,
     1241                                            self._commands(job, build_job_count),
     1242                                            self.build_dir,
     1243                                            tag)]
     1244            job_index += 1
     1245        set_max_build_label(build_jobs)
     1246        return build_jobs
     1247
     1248    def _commands(self, build, build_jobs):
     1249        commands = { 'dry-run'  : self.options['dry-run'],
     1250                     'no-clean' : self.options['no-clean'],
     1251                     'configure': None,
     1252                     'build'    : None }
     1253        cmds = build.build_config.split()
     1254        cmds += self.config.bspopts(build.arch, build.bsp)
    7971255        cmd = [path.join(self.rtems, 'configure')]
    798         commands += self.config.bspopts(arch, bsp)
    799         for c in commands:
     1256        for c in cmds:
    8001257            c = c.replace('@PREFIX@', self.prefix)
    8011258            c = c.replace('@RTEMS_VERSION@', self.rtems_version)
    802             c = c.replace('@ARCH@', arch)
    803             c = c.replace('@BSP@', bsp)
     1259            c = c.replace('@ARCH@', build.arch)
     1260            c = c.replace('@BSP@', build.bsp)
    8041261            cmd += [c]
    805         return ' '.join(cmd)
    806 
    807     def _build_set(self, builds):
    808         build_set = { }
    809         for build in builds:
    810             build_set[build] = self.config.build_options(build)
    811         return build_set
    812 
    813     def _build_dir(self, arch, bsp, build):
    814         return path.join(self._path(arch, bsp), build)
    815 
    816     def _count_files(self, arch, bsp, build):
    817         counts = { 'h'    : 0,
    818                    'exes' : 0,
    819                    'objs' : 0,
    820                    'libs' : 0 }
    821         for root, dirs, files in os.walk(self._build_dir(arch, bsp, build)):
    822             for file in files:
    823                 if file.endswith('.exe'):
    824                     counts['exes'] += 1
    825                 elif file.endswith('.o'):
    826                     counts['objs'] += 1
    827                 elif file.endswith('.a'):
    828                     counts['libs'] += 1
    829                 elif file.endswith('.h'):
    830                     counts['h'] += 1
     1262        commands['configure'] = ' '.join(cmd)
     1263        cmd = 'make -j %s' % (build_jobs)
     1264        commands['build'] = cmd
     1265        return commands
     1266
     1267    def _update_file_counts(self, counts):
    8311268        for f in self.counts:
    8321269            if f in counts:
    8331270                self.counts[f] += counts[f]
    8341271        return counts
    835 
    836     def _have_failures(self, fails):
    837         return len(fails) != 0
    8381272
    8391273    def _warnings_report(self):
     
    8461280                f.write(os.linesep)
    8471281                f.write(command_line() + os.linesep)
    848                 f.write(self.warnings_errors.report())
     1282                f.write(self.results.warnings_errors.report())
    8491283
    8501284    def _finished(self):
    851         log.notice('+  warnings:%d  exes:%d  objs:%d  libs:%d' % \
    852                    (self.warnings_errors.get_warning_count(), self.counts['exes'],
     1285        log.notice('Total: Warnings:%d  exes:%d  objs:%d  libs:%d' % \
     1286                   (self.results.get_warning_count(), self.counts['exes'],
    8531287                    self.counts['objs'], self.counts['libs']))
    8541288        log.output()
    8551289        log.output('Warnings:')
    856         log.output(self.warnings_errors.report())
     1290        log.output(self.results.warnings_report())
    8571291        log.output()
    8581292        log.notice('Failures:')
    859         log.notice(self.failures_report(self.errors['fails']))
     1293        log.notice(self.results.failures_report())
    8601294        self._warnings_report()
    8611295
    862     def failures_report(self, fails):
    863         if not self._have_failures(fails):
    864             return ' No failure(s)'
    865         absize = 0
    866         bsize = 0
    867         ssize = 0
    868         for f in fails:
    869             arch_bsp = '%s/%s' % (f[1], f[2])
    870             if len(arch_bsp) > absize:
    871                 absize = len(arch_bsp)
    872             if len(f[3]) > bsize:
    873                 bsize = len(f[3])
    874             if len(f[0]) > ssize:
    875                 ssize = len(f[0])
    876         fc = 1
    877         s = ''
    878         for f in fails:
    879             fcl = ' %3d' % (fc)
    880             arch_bsp = '%s/%s' % (f[1], f[2])
    881             state = f[0]
    882             s += '%s %-*s %-*s %-*s:%s' % \
    883                  (fcl, bsize, f[3], absize, arch_bsp, ssize, state, os.linesep)
    884             s1 = ' ' * 6
    885             s += wrap((s1, 'configure: ' + f[4]), lineend = '\\', width = 75)
    886             for e in self.warnings_errors.get_error_messages(f[1], f[2], f[3]):
    887                 s += wrap([s1, 'error: ' + e])
    888             fc += 1
    889         return s
    890 
    891     def build_arch_bsp(self, arch, bsp):
    892         if not self.config.bsp_present(arch, bsp):
    893             raise error.general('BSP not found: %s/%s' % (arch, bsp))
    894         log.output('-' * 70)
    895         log.notice('] BSP: %s/%s' % (arch, bsp))
    896         log.notice('. Creating: %s' % (self._path(arch, bsp)))
    897         self._arch_bsp_dir_clean(arch, bsp)
    898         self._arch_bsp_dir_make(arch, bsp)
    899         builds = self._builds(arch, bsp)
    900         if builds is None:
    901             raise error.general('build not found: %s' % (self._build()))
    902         build_set = self._build_set(builds)
    903         bsp_start = datetime.datetime.now()
     1296    def run_jobs(self, jobs):
     1297        if path.exists(self.build_dir):
     1298            log.notice('Cleaning: %s' % (self.build_dir))
     1299            path.removeall(self.build_dir)
     1300        start = datetime.datetime.now()
    9041301        env_path = os.environ['PATH']
    9051302        os.environ['PATH'] = path.host(path.join(self.tools, 'bin')) + \
    9061303                             os.pathsep + os.environ['PATH']
    907         fails = []
    908         for bs in sorted(build_set.keys()):
    909             self.warnings_errors.set_build(arch, bsp, bs)
    910             start = datetime.datetime.now()
    911             log.output('- ' * 35)
    912             log.notice('. Configuring: %s' % (bs))
    913             try:
    914                 warnings = self.warnings_errors.get_warning_count()
    915                 result = '+ Pass'
    916                 bpath = self._build_dir(arch, bsp, bs)
    917                 good = True
    918                 error_messages = None
    919                 path.mkdir(bpath)
    920                 config_cmd = self._config_command(build_set[bs], arch, bsp)
    921                 cmd = config_cmd
    922                 e = execute.capture_execution(log = self.warnings_errors)
    923                 log.output(wrap(('run: ', cmd), lineend = '\\'))
    924                 if self.options['dry-run']:
    925                     exit_code = 0
    926                 else:
    927                     exit_code, proc, output = e.shell(cmd, cwd = path.host(bpath))
    928                 if exit_code != 0:
    929                     result = '- FAIL'
    930                     failure = ('configure', arch, bsp, bs, config_cmd)
    931                     fails += [failure]
    932                     self.errors['configure'] += 1
    933                     self.errors['fails'] += [failure]
    934                     log.notice('- Configure failed: %s' % (bs))
    935                     log.output('cmd failed: %s' % (cmd))
    936                     good = False
    937                 else:
    938                     log.notice('. Building: %s' % (bs))
    939                     cmd = 'make'
    940                     if 'jobs' in self.options:
    941                         cmd += ' -j %s' % (self.options['jobs'])
    942                     log.output('run: ' + cmd)
    943                     if self.options['dry-run']:
    944                         exit_code = 0
    945                     else:
    946                         exit_code, proc, output = e.shell(cmd, cwd = path.host(bpath))
    947                     if exit_code != 0:
    948                         error_messages = self.warnings_errors.get_error_messages()
    949                         result = '- FAIL'
    950                         failure = ('build', arch, bsp, bs, config_cmd, error_messages)
    951                         fails += [failure]
    952                         self.errors['build'] += 1
    953                         self.errors['fails'] += [failure]
    954                         log.notice('- FAIL: %s: %s' % (bs, self._error_str()))
    955                         log.output('cmd failed: %s' % (cmd))
    956                         good = False
    957                     files = self._count_files(arch, bsp, bs)
    958                     log.notice('%s: %s: warnings:%d  exes:%d  objs:%s  libs:%d' % \
    959                                (result, bs,
    960                                 self.warnings_errors.get_warning_count() - warnings,
    961                                 files['exes'], files['objs'], files['libs']))
    962                 log.notice('  %s' % (self._error_str()))
    963                 self.results.add(good, arch, bsp, config_cmd,
    964                                  self.warnings_errors.get_warning_count() - warnings,
    965                                  error_messages)
    966                 if not good and self.options['stop-on-error']:
    967                     raise error.general('Configuring %s failed' % (bs))
    968             finally:
    969                 end = datetime.datetime.now()
    970                 if not self.options['no-clean']:
    971                     log.notice('. Cleaning: %s' % (self._build_dir(arch, bsp, bs)))
    972                     path.removeall(self._build_dir(arch, bsp, bs))
    973             log.notice('^ Time %s' % (str(end - start)))
    974             self.warnings_errors.clear_build()
    975         bsp_end = datetime.datetime.now()
    976         log.notice('^ BSP Time %s' % (str(bsp_end - bsp_start)))
    977         log.output('Failure Report:')
    978         log.output(self.failures_report(fails))
     1304        job_count, build_job_count = jobs_option_parse(self.options['jobs'])
     1305        builds = self._create_build_jobs(jobs, build_job_count)
     1306        active_jobs = []
     1307        jobs_completed = 0
     1308        try:
     1309            while len(builds) > 0 or len(active_jobs) > 0:
     1310                new_jobs = job_count - len(active_jobs)
     1311                if new_jobs > 0:
     1312                    active_jobs += builds[:new_jobs]
     1313                    builds = builds[new_jobs:]
     1314                finished_jobs = []
     1315                for job in active_jobs:
     1316                    state = job.current_state()
     1317                    if state == 'ready':
     1318                        job.run()
     1319                    elif state != 'running':
     1320                        finished_jobs += [job]
     1321                for job in finished_jobs:
     1322                    self._update_file_counts(job.get_file_counts())
     1323                    job.log_output()
     1324                    job.clean()
     1325                    active_jobs.remove(job)
     1326                    jobs_completed += 1
     1327                time.sleep(0.250)
     1328        except:
     1329            for job in active_jobs:
     1330                try:
     1331                    job.kill()
     1332                except:
     1333                    pass
     1334            raise
     1335        end = datetime.datetime.now()
    9791336        os.environ['PATH'] = env_path
    980 
    981     def build_bsp(self, arch, bsp):
    982         self.build_arch_bsp(arch, bsp)
     1337        duration = end - start
     1338        if jobs_completed == 0:
     1339            jobs_completed = 1
    9831340        self._finished()
    984 
    985     def build_arch(self, arch):
    986         start = datetime.datetime.now()
    987         log.output('=' * 70)
    988         log.notice(']] Architecture: %s' % (arch))
    989         if not self.config.arch_present(arch):
    990             raise error.general('Architecture not found: %s' % (arch))
    991         for bsp in self._bsps(arch):
    992             self.build_arch_bsp(arch, bsp)
    993         end = datetime.datetime.now()
    994         log.notice('^ Architecture Time %s' % (str(end - start)))
    995         self._finished()
    996 
    997     def build(self):
    998         for arch in self.config.archs():
    999             self.build_arch(arch)
    1000         log.notice('^ Profile Time %s' % (str(end - start)))
    1001         self._finished()
    1002 
    1003     def build_profile(self, profile):
    1004         if not self.config.profile_present(profile):
    1005             raise error.general('Profile not found: %s' % (profile))
    1006         start = datetime.datetime.now()
    1007         log.notice(']] Profile: %s' % (profile))
    1008         for arch in self.config.profile_archs(profile):
    1009             for bsp in self.config.profile_arch_bsps(profile, arch):
    1010                 self.build_arch_bsp(arch, bsp)
    1011         end = datetime.datetime.now()
    1012         log.notice('^ Profile Time %s' % (str(end - start)))
    1013         self._finished()
     1341        log.notice('Average BSP Build Time: %s' % \
     1342                   (str(duration / jobs_completed)))
     1343        log.notice('Total Time %s' % (str(duration)))
     1344
     1345    def arch_bsp_jobs(self, arch, bsps):
     1346        jobs = []
     1347        for bsp in bsps:
     1348            jobs += build_jobs(self.config, arch, bsp).jobs()
     1349        return jobs
     1350
     1351    def bsp_jobs(self, bsps):
     1352        jobs = []
     1353        for bsp in bsps:
     1354            if bsp.count('/') != 1:
     1355                raise error.general('invalid bsp format (use: arch/bsp): %s' % (bsp))
     1356            arch, bsp = bsp.split('/')
     1357            jobs += build_jobs(self.config, arch, bsp).jobs()
     1358        return jobs
     1359
     1360    def arch_jobs(self, archs):
     1361        jobs = []
     1362        for arch in archs:
     1363            if not self.config.arch_present(arch):
     1364                raise error.general('Architecture not found: %s' % (arch))
     1365            jobs += self.arch_bsp_jobs(arch, self._bsps(arch))
     1366        return jobs
     1367
     1368    def profile_jobs(self, profiles):
     1369        jobs = []
     1370        for profile in profiles:
     1371            if not self.config.profile_present(profile):
     1372                raise error.general('Profile not found: %s' % (profile))
     1373            jobs += self.arch_jobs(self.config.profile_archs(profile))
     1374        return jobs
     1375
     1376    def build_bsps(self, bsps):
     1377        log.notice('BSPS(s): %s' % (', '.join(bsps)))
     1378        self.run_jobs(self.bsp_jobs(bsps))
     1379
     1380    def build_archs(self, archs):
     1381        log.notice('Architecture(s): %s' % (', '.join(archs)))
     1382        self.run_jobs(self.arch_jobs(archs))
     1383
     1384    def build_profiles(self, profiles):
     1385        log.notice('Profile(s): %s' % (', '.join(profiles)))
     1386        self.run_jobs(self.profile_jobs(profiles))
    10141387
    10151388def run_args(args):
     
    10461419        argsp.add_argument('--rtems', help = 'The RTEMS source tree.',
    10471420                           type = str)
     1421        argsp.add_argument('--build-path', help = 'Path to build in.',
     1422                           type = str)
     1423        argsp.add_argument('--log', help = 'Log file.', type = str)
    10481424        argsp.add_argument('--config-report', help = 'Report the configuration.',
    10491425                           action = 'store_true')
    10501426        argsp.add_argument('--warnings-report', help = 'Report the warnings to a file.',
    10511427                           type = str, default = None)
    1052         argsp.add_argument('--build-path', help = 'Path to build in.',
    1053                            type = str)
    1054         argsp.add_argument('--log', help = 'Log file.', type = str)
    10551428        argsp.add_argument('--stop-on-error', help = 'Stop on an error.',
    10561429                           action = 'store_true')
    10571430        argsp.add_argument('--no-clean', help = 'Do not clean the build output.',
    10581431                           action = 'store_true')
    1059         argsp.add_argument('--profiles', help = 'Build the listed profiles.',
     1432        argsp.add_argument('--profiles', help = 'Build the listed profiles (profile,profile,..).',
    10601433                           type = str, default = 'tier-1')
    1061         argsp.add_argument('--build', help = 'Build name to build.',
     1434        argsp.add_argument('--arch', help = 'Build the architectures (arch,arch,..).',
     1435                           type = str)
     1436        argsp.add_argument('--bsp', help = 'Build the BSPs (arch/bsp,arch/bsp,..).',
     1437                           type = str)
     1438        argsp.add_argument('--build', help = 'Build name to build (see --config-report).',
    10621439                           type = str, default='all')
    1063         argsp.add_argument('--arch', help = 'Build the specific architecture.',
    1064                            type = str)
    1065         argsp.add_argument('--bsp', help = 'Build the specific BSP.',
    1066                            type = str)
    10671440        argsp.add_argument('--jobs', help = 'Number of jobs to run.',
    1068                            type = int, default = host.cpus())
     1441                           type = str, default = '1/%d' % (host.cpus()))
    10691442        argsp.add_argument('--dry-run', help = 'Do not run the actual builds.',
    10701443                           action = 'store_true')
     
    10931466        if opts.build_path is not None:
    10941467            build_dir = path.shell(opts.build_path)
    1095         if opts.bsp is not None and opts.arch is None:
    1096             raise error.general('BSP provided but no architecture')
    10971468
    10981469        options = { 'stop-on-error'   : opts.stop_on_error,
     
    11021473                    'warnings-report' : opts.warnings_report }
    11031474
    1104         b = build(config, rtems_version(), prefix, tools,
    1105                   path.shell(opts.rtems), build_dir, options)
    1106         if opts.arch is not None:
    1107             if opts.bsp is not None:
    1108                 b.build_bsp(opts.arch, opts.bsp)
    1109             else:
    1110                 b.build_arch(opts.arch)
     1475        b = builder(config, rtems_version(), prefix, tools,
     1476                    path.shell(opts.rtems), build_dir, options)
     1477
     1478        profiles = comma_split(opts.profiles)
     1479        archs = comma_split(opts.arch)
     1480        bsps = comma_split(opts.bsp)
     1481
     1482        #
     1483        # The default is build a profile.
     1484        #
     1485        if bsps is not None:
     1486            if archs is not None:
     1487                raise error.general('--arch supplied with --bsp; use --bsp=arch/bsp,arch/bsp,..')
     1488            b.build_bsps(bsps)
     1489        elif archs is not None:
     1490            b.build_archs(archs)
    11111491        else:
    1112             for profile in opts.profiles.split(','):
    1113                 b.build_profile(profile.strip())
     1492            b.build_profiles(profiles)
    11141493
    11151494    except error.general as gerr:
Note: See TracChangeset for help on using the changeset viewer.