source: rtems-source-builder/source-builder/sb/setbuilder.py @ 6444d58

4.104.95
Last change on this file since 6444d58 was 6444d58, checked in by Chris Johns <chrisj@…>, on 07/20/15 at 03:49:42

Canandian Cross Compiling and RTEMS 3rd party package building Fixes.

The change fixes installing for RTEMS 3rd Party packages where the
RSB considered them Canadian Cross Compiling (Cxc). Fixing the
Cxc issue broke real Cxc builds. The change corrects the issue of
macros being changed in the Cxc and the prep data not being udpated.
The configuration is loaded again after the updated macros. The
macros are also copied and restored to ensure a clean stable base.

The change also introduces --rtems-tools and --rtems-bsp to align
the command line with the waf configure process or RTEMS application.

  • Property mode set to 100644
File size: 20.4 KB
RevLine 
[bf13d27]1#
2# RTEMS Tools Project (http://www.rtems.org/)
[2f72d35]3# Copyright 2010-2013 Chris Johns (chrisj@rtems.org)
[bf13d27]4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-tools'.
7#
8# Permission to use, copy, modify, and/or distribute this software for any
9# purpose with or without fee is hereby granted, provided that the above
10# copyright notice and this permission notice appear in all copies.
11#
12# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#
[2f72d35]21# This code builds a package compiler tool suite given a tool set. A tool
[bf13d27]22# set lists the various tools. These are specific tool configurations.
23#
24
[984e4e6]25import copy
[251a42d]26import datetime
[71b8893]27import glob
[bf13d27]28import operator
29import os
[06834cf]30import sys
[bf13d27]31
[c18c4b6]32try:
33    import build
34    import check
35    import error
36    import log
[97a685f]37    import mailer
[cb12e48]38    import options
[c18c4b6]39    import path
40    import reports
[9a15c40]41    import sources
[affea81]42    import version
[c18c4b6]43except KeyboardInterrupt:
[06834cf]44    print 'abort: user terminated'
[c18c4b6]45    sys.exit(1)
46except:
[06834cf]47    print 'error: unknown application load error'
[c18c4b6]48    sys.exit(1)
[bf13d27]49
[ab8319a]50class buildset:
51    """Build a set builds a set of packages."""
[bf13d27]52
[cb12e48]53    def __init__(self, bset, _configs, opts, macros = None):
[5142bec]54        log.trace('_bset: %s: init' % (bset))
[fba1136]55        self.configs = _configs
[984e4e6]56        self.opts = opts
[cb12e48]57        if macros is None:
58            self.macros = copy.copy(opts.defaults)
59        else:
60            self.macros = copy.copy(macros)
[ab8319a]61        self.bset = bset
[ce0f7a1]62        _target = self.macros.expand('%{_target}')
63        if len(_target):
64            pkg_prefix = _target
65        else:
66            pkg_prefix = self.macros.expand('%{_host}')
67        self.bset_pkg = '%s-%s-set' % (pkg_prefix, self.bset)
[c914e1d]68        self.mail_header = ''
[97a685f]69        self.mail_report = ''
[df56f7e]70        self.build_failure = None
[c914e1d]71
72    def write_mail_header(self, text, prepend = False):
73        if len(text) == 0 or text[-1] != '\n' or text[-1] != '\r':
74            text += os.linesep
75        if prepend:
76            self.mail_header = text + self.mail_header
77        else:
78            self.mail_header += text
[adf0946]79
[97a685f]80    def write_mail_report(self, text, prepend = False):
[adf0946]81        if len(text) == 0 or text[-1] != '\n' or text[-1] != '\r':
82            text += os.linesep
83        if prepend:
[97a685f]84            self.mail_report = text + self.mail_report
[adf0946]85        else:
[97a685f]86            self.mail_report += text
[bf13d27]87
88    def copy(self, src, dst):
[01b2815]89        log.output('copy: %s => %s' % (path.host(src), path.host(dst)))
90        if not self.opts.dry_run():
[869b8a6]91            path.copy_tree(src, dst)
[bf13d27]92
[4bd058e]93    def report(self, _config, _build, opts, macros, format = None):
[ce60578]94        if len(_build.main_package().name()) > 0 \
[ebf8a1f]95           and not _build.macros.get('%{_disable_reporting}') \
[ce60578]96           and (not _build.opts.get_arg('--no-report') \
97                or _build.opts.get_arg('--mail')):
[4bd058e]98            if format is None:
99                format = _build.opts.get_arg('--report-format')
100                if format is not None:
101                    if len(format) != 2:
[6444d58]102                        raise error.general('invalid report format option: %s' % \
103                                            ('='.join(format)))
[4bd058e]104                    format = format[1]
[26595c7]105            if format is None:
[ce60578]106                format = 'text'
[4bd058e]107            if format == 'text':
108                ext = '.txt'
109            elif format == 'asciidoc':
[ce60578]110                ext = '.txt'
[4bd058e]111            elif format == 'html':
112                ext = '.html'
113            elif format == 'xml':
114                ext = '.xml'
115            elif format == 'ini':
116                ext = '.ini'
[26595c7]117            else:
[4bd058e]118                raise error.general('invalid report format: %s' % (format))
[864e8ff]119            buildroot = _build.config.abspath('%{buildroot}')
[cb12e48]120            prefix = _build.macros.expand('%{_prefix}')
[06834cf]121            name = _build.main_package().name() + ext
[5142bec]122            log.notice('reporting: %s -> %s' % (_config, name))
[adf0946]123            if not _build.opts.get_arg('--no-report'):
[ce60578]124                outpath = path.host(path.join(buildroot, prefix, 'share', 'rtems', 'rsb'))
125                if not _build.opts.dry_run():
126                    outname = path.host(path.join(outpath, name))
127                else:
128                    outname = None
129                r = reports.report(format, self.configs,
130                                   copy.copy(opts), copy.copy(macros))
[adf0946]131                r.introduction(_build.config.file_name())
[ce60578]132                r.generate(_build.config.file_name())
133                r.epilogue(_build.config.file_name())
[adf0946]134                if not _build.opts.dry_run():
135                    _build.mkdir(outpath)
136                    r.write(outname)
137                del r
[ce60578]138            if _build.opts.get_arg('--mail'):
139                r = reports.report('text', self.configs,
140                                   copy.copy(opts), copy.copy(macros))
[adf0946]141                r.introduction(_build.config.file_name())
[ce60578]142                r.generate(_build.config.file_name())
143                r.epilogue(_build.config.file_name())
[c914e1d]144                self.write_mail_report(r.out)
[26595c7]145                del r
146
[4f26bdb]147    def root_copy(self, src, dst):
148        what = '%s -> %s' % \
149            (os.path.relpath(path.host(src)), os.path.relpath(path.host(dst)))
[5142bec]150        log.trace('_bset: %s: collecting: %s' % (self.bset, what))
[04f447f]151        self.copy(src, dst)
[bf13d27]152
[4f26bdb]153    def install(self, name, buildroot, prefix):
154        dst = prefix
155        src = path.join(buildroot, prefix)
[5142bec]156        log.notice('installing: %s -> %s' % (name, path.host(dst)))
[04f447f]157        self.copy(src, dst)
[4f26bdb]158
159    def canadian_cross(self, _build):
[6444d58]160        log.trace('_bset: Cxc for build machine: _build => _host')
161        macros_to_copy = [('%{_host}',        '%{_build}'),
162                          ('%{_host_alias}',  '%{_build_alias}'),
163                          ('%{_host_arch}',   '%{_build_arch}'),
164                          ('%{_host_cpu}',    '%{_build_cpu}'),
165                          ('%{_host_os}',     '%{_build_os}'),
166                          ('%{_host_vendor}', '%{_build_vendor}'),
167                          ('%{_tmproot}',     '%{_tmpcxcroot}'),
168                          ('%{buildroot}',    '%{buildcxcroot}'),
169                          ('%{_builddir}',    '%{_buildcxcdir}')]
170        cxc_macros = _build.copy_init_macros()
[cb12e48]171        for m in macros_to_copy:
[6444d58]172            log.trace('_bset: Cxc: %s <= %s' % (m[0], cxc_macros[m[1]]))
173            cxc_macros[m[0]] = cxc_macros[m[1]]
174        _build.set_macros(cxc_macros)
175        _build.reload()
[4f26bdb]176        _build.make()
[ebf8a1f]177        if not _build.macros.get('%{_disable_collecting}'):
[6444d58]178            self.root_copy(_build.config.expand('%{buildroot}'),
179                           _build.config.expand('%{_tmproot}'))
180        _build.set_macros(_build.copy_init_macros())
181        _build.reload()
[4f26bdb]182
183    def build_package(self, _config, _build):
[ebf8a1f]184        if not _build.disabled():
185            if _build.canadian_cross():
186                self.canadian_cross(_build)
187            _build.make()
188            if not _build.macros.get('%{_disable_collecting}'):
189                self.root_copy(_build.config.expand('%{buildroot}'),
190                               _build.config.expand('%{_tmproot}'))
[4f26bdb]191
192    def bset_tar(self, _build):
193        tardir = _build.config.expand('%{_tardir}')
[6444d58]194        if (self.opts.get_arg('--bset-tar-file') or self.opts.canadian_cross()) \
[ebf8a1f]195           and not _build.macros.get('%{_disable_packaging}'):
[ee47d72]196            path.mkdir(tardir)
197            tar = path.join(tardir, _build.config.expand('%s.tar.bz2' % (self.bset_pkg)))
[5142bec]198            log.notice('tarball: %s' % (os.path.relpath(path.host(tar))))
[cafbcc6]199            if not self.opts.dry_run():
[4f26bdb]200                tmproot = _build.config.expand('%{_tmproot}')
[cafbcc6]201                cmd = _build.config.expand("'cd " + tmproot + \
202                                               " && %{__tar} -cf - . | %{__bzip2} > " + tar + "'")
203                _build.run(cmd, shell_opts = '-c', cwd = tmproot)
[bf13d27]204
[2f72d35]205    def parse(self, bset):
[bf13d27]206
207        def _clean(line):
208            line = line[0:-1]
209            b = line.find('#')
210            if b >= 0:
211                line = line[1:b]
212            return line.strip()
213
[e9af460]214        bsetname = bset
[bf13d27]215
[ab8319a]216        if not path.exists(bsetname):
[cb12e48]217            for cp in self.macros.expand('%{_configdir}').split(':'):
[ab8319a]218                configdir = path.abspath(cp)
[e9af460]219                bsetname = path.join(configdir, bset)
[ab8319a]220                if path.exists(bsetname):
[bf13d27]221                    break
[ab8319a]222                bsetname = None
223            if bsetname is None:
[e9af460]224                raise error.general('no build set file found: %s' % (bset))
[bf13d27]225        try:
[5142bec]226            log.trace('_bset: %s: open: %s' % (self.bset, bsetname))
[ab8319a]227            bset = open(path.host(bsetname), 'r')
[bf13d27]228        except IOError, err:
[ab8319a]229            raise error.general('error opening bset file: %s' % (bsetname))
[bf13d27]230
231        configs = []
232
233        try:
234            lc = 0
[ab8319a]235            for l in bset:
[bf13d27]236                lc += 1
237                l = _clean(l)
238                if len(l) == 0:
239                    continue
[5142bec]240                log.trace('_bset: %s: %03d: %s' % (self.bset, lc, l))
[5cba075]241                ls = l.split()
242                if ls[0][-1] == ':' and ls[0][:-1] == 'package':
[cb12e48]243                    self.bset_pkg = self.macros.expand(ls[1].strip())
244                    self.macros['package'] = self.bset_pkg
[5cba075]245                elif ls[0][0] == '%':
[a083b52]246                    def err(msg):
247                        raise error.general('%s:%d: %s' % (self.bset, lc, msg))
[5cba075]248                    if ls[0] == '%define':
[6fad89b]249                        if len(ls) > 2:
[cb12e48]250                            self.macros.define(ls[1].strip(),
251                                               ' '.join([f.strip() for f in ls[2:]]))
[6fad89b]252                        else:
[cb12e48]253                            self.macros.define(ls[1].strip())
[fa653c2]254                    elif ls[0] == '%undefine':
255                        if len(ls) > 2:
[8a1e7a0]256                            raise error.general('%s:%d: %undefine requires just the name' % \
257                                                    (self.bset, lc))
[cb12e48]258                        self.macros.undefine(ls[1].strip())
[5cba075]259                    elif ls[0] == '%include':
[2f72d35]260                        configs += self.parse(ls[1].strip())
[a083b52]261                    elif ls[0] in ['%patch', '%source']:
[9a15c40]262                        sources.process(ls[0][1:], ls[1:], self.macros, err)
[a083b52]263                    elif ls[0] == '%hash':
264                        sources.hash(ls[1:], self.macros, err)
[bf13d27]265                else:
[fba1136]266                    l = l.strip()
[0759d98]267                    c = build.find_config(l, self.configs)
[fba1136]268                    if c is None:
[8a1e7a0]269                        raise error.general('%s:%d: cannot find file: %s' % (self.bset, lc, l))
[fba1136]270                    configs += [c]
[bf13d27]271        except:
[ab8319a]272            bset.close()
[bf13d27]273            raise
274
[ab8319a]275        bset.close()
[bf13d27]276
277        return configs
278
[2f72d35]279    def load(self):
280
[adf0946]281        #
282        # If the build set file ends with .cfg the user has passed to the
283        # buildset builder a configuration so we just return it.
284        #
285        if self.bset.endswith('.cfg'):
286            configs = [self.bset]
[2f72d35]287        else:
[adf0946]288            exbset = self.macros.expand(self.bset)
289            self.macros['_bset'] = exbset
290            root, ext = path.splitext(exbset)
291            if exbset.endswith('.bset'):
292                bset = exbset
293            else:
294                bset = '%s.bset' % (exbset)
295            configs = self.parse(bset)
296        return configs
[2f72d35]297
[72f89c5]298    def build(self, deps = None, nesting_count = 0):
299
[120e101]300        build_error = False
301
[72f89c5]302        nesting_count += 1
[bf13d27]303
[5142bec]304        log.trace('_bset: %s: make' % (self.bset))
305        log.notice('Build Set: %s' % (self.bset))
[bf13d27]306
[97a685f]307        if self.opts.get_arg('--mail'):
[df56f7e]308            mail_report_subject = '%s %s' % (self.bset, self.macros.expand('%{_host}'))
[adf0946]309
[bf13d27]310        current_path = os.environ['PATH']
[0add2ea]311
[251a42d]312        start = datetime.datetime.now()
313
[df56f7e]314        mail_report = False
[2802080]315        have_errors = False
[df56f7e]316
[bf13d27]317        try:
[120e101]318            configs = self.load()
319
320            log.trace('_bset: %s: configs: %s'  % (self.bset, ','.join(configs)))
321
[bf13d27]322            builds = []
323            for s in range(0, len(configs)):
[ebf8a1f]324                b = None
[729f0bb]325                try:
[984e4e6]326                    #
327                    # Each section of the build set gets a separate set of
[cb12e48]328                    # macros so we do not contaminate one configuration with
[984e4e6]329                    # another.
330                    #
[cb12e48]331                    opts = copy.copy(self.opts)
332                    macros = copy.copy(self.macros)
[729f0bb]333                    if configs[s].endswith('.bset'):
[72f89c5]334                        log.trace('_bset: == %2d %s' % (nesting_count + 1, '=' * 75))
[cb12e48]335                        bs = buildset(configs[s], self.configs, opts, macros)
[72f89c5]336                        bs.build(deps, nesting_count)
[729f0bb]337                        del bs
338                    elif configs[s].endswith('.cfg'):
[df56f7e]339                        mail_report = self.opts.get_arg('--mail')
[72f89c5]340                        log.trace('_bset: -- %2d %s' % (nesting_count + 1, '-' * 75))
[120e101]341                        try:
342                            b = build.build(configs[s], self.opts.get_arg('--pkg-tar-files'),
343                                            opts, macros)
344                        except:
345                            build_error = True
346                            raise
[01b2815]347                        if b.macros.get('%{_disable_reporting}'):
348                            mail_report = False
[79f80fd]349                        if deps is None:
[4f26bdb]350                            self.build_package(configs[s], b)
[ce60578]351                            self.report(configs[s], b,
352                                        copy.copy(self.opts),
353                                        copy.copy(self.macros))
[4bd058e]354                            # Always product an XML report.
355                            self.report(configs[s], b,
356                                        copy.copy(self.opts),
357                                        copy.copy(self.macros),
358                                        format = 'xml')
[2802080]359                            if s == len(configs) - 1 and not have_errors:
[4f26bdb]360                                self.bset_tar(b)
[79f80fd]361                        else:
362                            deps += b.config.includes()
[729f0bb]363                        builds += [b]
364                    else:
365                        raise error.general('invalid config type: %s' % (configs[s]))
366                except error.general, gerr:
[2802080]367                    have_errors = True
[ebf8a1f]368                    if b is not None:
369                        if self.build_failure is None:
370                            self.build_failure = b.name()
371                        self.write_mail_header('')
372                        self.write_mail_header('= ' * 40)
373                        self.write_mail_header('Build FAILED: %s' % (b.name()))
374                        self.write_mail_header('- ' * 40)
375                        self.write_mail_header(str(log.default))
376                        self.write_mail_header('- ' * 40)
377                        if self.opts.keep_going():
[01b2815]378                            log.notice(str(gerr))
[ebf8a1f]379                            if self.opts.always_clean():
380                                builds += [b]
381                        else:
382                            raise
[729f0bb]383                    else:
384                        raise
[6444d58]385            #
386            # Installing ...
387            #
388            log.trace('_bset: installing: deps:%r no-install:%r' % \
389                      (deps is None, self.opts.no_install()))
[b843e62]390            if deps is None \
391               and not self.opts.no_install() \
392               and not have_errors:
[4f26bdb]393                for b in builds:
[6444d58]394                    log.trace('_bset: installing: %r' % b.installable())
395                    if b.installable():
[ebf8a1f]396                        self.install(b.name(),
397                                     b.config.expand('%{buildroot}'),
398                                     b.config.expand('%{_prefix}'))
[6444d58]399
[4f26bdb]400            if deps is None and \
[0add2ea]401                    (not self.opts.no_clean() or self.opts.always_clean()):
[bf13d27]402                for b in builds:
[ebf8a1f]403                    if not b.disabled():
404                        log.notice('cleaning: %s' % (b.name()))
405                        b.cleanup()
[bf13d27]406            for b in builds:
407                del b
[df56f7e]408        except error.general, gerr:
[120e101]409            if not build_error:
410                log.stderr(str(gerr))
[df56f7e]411            raise
412        except KeyboardInterrupt:
413            mail_report = False
414            raise
[bf13d27]415        except:
[df56f7e]416            self.build_failure = 'RSB general failure'
[bf13d27]417            raise
[c914e1d]418        finally:
419            end = datetime.datetime.now()
420            os.environ['PATH'] = current_path
421            build_time = str(end - start)
[df56f7e]422            if mail_report:
[c914e1d]423                to_addr = self.opts.get_arg('--mail-to')
424                if to_addr is not None:
425                    to_addr = to_addr[1]
426                else:
427                    to_addr = self.macros.expand('%{_mail_tools_to}')
428                log.notice('Mailing report: %s' % (to_addr))
429                self.write_mail_header('Build Time %s' % (build_time), True)
430                self.write_mail_header('')
431                m = mailer.mail(self.opts)
[df56f7e]432                if self.build_failure is not None:
433                    mail_report_subject = 'Build: FAILED %s (%s)' %\
434                        (mail_report_subject, self.build_failure)
435                    pass_fail = 'FAILED'
[c914e1d]436                else:
[df56f7e]437                    mail_report_subject = 'Build: PASSED %s' % (mail_report_subject)
[c914e1d]438                if not self.opts.dry_run():
[df56f7e]439                    m.send(to_addr, mail_report_subject,
440                           self.mail_header + self.mail_report)
[c914e1d]441            log.notice('Build Set: Time %s' % (build_time))
[adf0946]442
[d63f135]443def list_bset_cfg_files(opts, configs):
444    if opts.get_arg('--list-configs') or opts.get_arg('--list-bsets'):
445        if opts.get_arg('--list-configs'):
446            ext = '.cfg'
447        else:
448            ext = '.bset'
449        for p in configs['paths']:
450            print 'Examining: %s' % (os.path.relpath(p))
451        for c in configs['files']:
452            if c.endswith(ext):
453                print '    %s' % (c)
454        return True
455    return False
456
[bf13d27]457def run():
458    import sys
[74da24c]459    ec = 0
[120e101]460    setbuilder_error = False
[bf13d27]461    try:
[cafbcc6]462        optargs = { '--list-configs':  'List available configurations',
463                    '--list-bsets':    'List available build sets',
[79f80fd]464                    '--list-deps':     'List the dependent files.',
[adf0946]465                    '--bset-tar-file': 'Create a build set tar file',
466                    '--pkg-tar-files': 'Create package tar files',
[26595c7]467                    '--no-report':     'Do not create a package report.',
[97a685f]468                    '--report-format': 'The report format (text, html, asciidoc).' }
469        mailer.append_options(optargs)
[cb12e48]470        opts = options.load(sys.argv, optargs)
[5142bec]471        log.notice('RTEMS Source Builder - Set Builder, v%s' % (version.str()))
[72f89c5]472        opts.log_info()
[cb12e48]473        if not check.host_setup(opts):
[fba1136]474            raise error.general('host build environment is not set up correctly')
[cb12e48]475        configs = build.get_configs(opts)
[79f80fd]476        if opts.get_arg('--list-deps'):
477            deps = []
478        else:
479            deps = None
[d63f135]480        if not list_bset_cfg_files(opts, configs):
[2cc7a97]481            prefix = opts.defaults.expand('%{_prefix}')
[6444d58]482            if opts.canadian_cross():
483                opts.disable_install()
484
[3963ac4]485            if not opts.dry_run() and \
[eeded98]486               not opts.canadian_cross() and \
[3963ac4]487               not opts.no_install() and \
488               not path.ispathwritable(prefix):
[2cc7a97]489                raise error.general('prefix is not writable: %s' % (path.host(prefix)))
[71b8893]490            for bset in opts.params():
[120e101]491                setbuilder_error = True
[cb12e48]492                b = buildset(bset, configs, opts)
[79f80fd]493                b.build(deps)
[74da24c]494                b = None
[120e101]495                setbuilder_error = False
[79f80fd]496        if deps is not None:
497            c = 0
498            for d in sorted(set(deps)):
499                c += 1
500                print 'dep[%d]: %s' % (c, d)
[bf13d27]501    except error.general, gerr:
[120e101]502        if not setbuilder_error:
503            log.stderr(str(gerr))
[74da24c]504        log.stderr('Build FAILED')
505        ec = 1
[bf13d27]506    except error.internal, ierr:
[120e101]507        if not setbuilder_error:
508            log.stderr(str(ierr))
[74da24c]509        log.stderr('Internal Build FAILED')
510        ec = 1
[bf13d27]511    except error.exit, eerr:
512        pass
513    except KeyboardInterrupt:
[5142bec]514        log.notice('abort: user terminated')
[74da24c]515        ec = 1
516    sys.exit(ec)
[bf13d27]517
518if __name__ == "__main__":
519    run()
Note: See TracBrowser for help on using the repository browser.