source: rtems-source-builder/source-builder/sb/setbuilder.py @ 8508944

4.104.114.95
Last change on this file since 8508944 was 8508944, checked in by Chris Johns <chrisj@…>, on 04/14/13 at 23:48:18

PR 2115 - Fixed the exception on no 'what'.

  • Property mode set to 100644
File size: 14.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
[bf13d27]27import distutils.dir_util
[71b8893]28import glob
[bf13d27]29import operator
30import os
[06834cf]31import sys
[bf13d27]32
[c18c4b6]33try:
34    import build
35    import check
36    import error
37    import log
[cb12e48]38    import options
[c18c4b6]39    import path
40    import reports
[affea81]41    import version
[c18c4b6]42except KeyboardInterrupt:
[06834cf]43    print 'abort: user terminated'
[c18c4b6]44    sys.exit(1)
45except:
[06834cf]46    print 'error: unknown application load error'
[c18c4b6]47    sys.exit(1)
[bf13d27]48
49def _trace(opts, text):
50    if opts.trace():
51        print text
52
53def _notice(opts, text):
54    if not opts.quiet() and not log.default.has_stdout():
55        print text
56    log.output(text)
57    log.flush()
58
[ab8319a]59class buildset:
60    """Build a set builds a set of packages."""
[bf13d27]61
[cb12e48]62    def __init__(self, bset, _configs, opts, macros = None):
[ab8319a]63        _trace(opts, '_bset:%s: init' % (bset))
[fba1136]64        self.configs = _configs
[984e4e6]65        self.opts = opts
[cb12e48]66        if macros is None:
67            self.macros = copy.copy(opts.defaults)
68        else:
69            self.macros = copy.copy(macros)
[ab8319a]70        self.bset = bset
[cb12e48]71        self.bset_pkg = '%s-%s-set' % (self.macros.expand('%{_target}'), self.bset)
[bf13d27]72
73    def _output(self, text):
74        if not self.opts.quiet():
75            log.output(text)
76
77    def copy(self, src, dst):
[cafbcc6]78        if not os.path.isdir(path.host(src)):
79            raise error.general('copying tree: no source directory: %s' % (path.host(src)))
80        if not self.opts.dry_run():
81            try:
82                files = distutils.dir_util.copy_tree(path.host(src),
83                                                     path.host(dst))
84                for f in files:
85                    self._output(f)
86            except IOError, err:
[8508944]87                raise error.general('copying tree: %s -> %s: %s' % (src, dst, str(err)))
[cafbcc6]88            except distutils.errors.DistutilsFileError, err:
89                raise error.general('copying tree: %s' % (str(err)))
[bf13d27]90
[864e8ff]91    def report(self, _config, _build):
[cb12e48]92        if not _build.opts.get_arg('--no-report'):
93            format = _build.opts.get_arg('--report-format')
[26595c7]94            if format is None:
95                format = 'html'
96                ext = '.html'
97            else:
98                if len(format) != 2:
99                    raise error.general('invalid report format option: %s' % ('='.join(format)))
100                if format[1] == 'text':
101                    format = 'text'
102                    ext = '.txt'
103                elif format[1] == 'asciidoc':
104                    format = 'asciidoc'
105                    ext = '.txt'
106                elif format[1] == 'html':
107                    format = 'html'
108                    ext = '.html'
109                else:
110                    raise error.general('invalid report format: %s' % (format[1]))
[864e8ff]111            buildroot = _build.config.abspath('%{buildroot}')
[cb12e48]112            prefix = _build.macros.expand('%{_prefix}')
[06834cf]113            name = _build.main_package().name() + ext
[1e1ea1e]114            outpath = path.host(path.join(buildroot, prefix, 'share', 'rtems-source-builder'))
[0759d98]115            outname = path.host(path.join(outpath, name))
[864e8ff]116            _notice(self.opts, 'reporting: %s -> %s' % (_config, name))
[cb12e48]117            if not _build.opts.dry_run():
[0759d98]118                _build.mkdir(outpath)
[cb12e48]119                r = reports.report(format, self.configs, _build.opts, _build.macros)
[26595c7]120                r.make(_config, outname)
121                del r
122
[4f26bdb]123    def root_copy(self, src, dst):
124        what = '%s -> %s' % \
125            (os.path.relpath(path.host(src)), os.path.relpath(path.host(dst)))
126        if self.opts.trace():
127            _notice(self.opts, 'collecting: %s' % (what))
128        if not self.opts.dry_run():
129            self.copy(src, dst)
[bf13d27]130
[4f26bdb]131    def install(self, name, buildroot, prefix):
132        dst = prefix
133        src = path.join(buildroot, prefix)
134        _notice(self.opts, 'installing: %s -> %s' % (name, path.host(dst)))
135        if not self.opts.dry_run():
136            self.copy(src, dst)
137
138    def canadian_cross(self, _build):
[cb12e48]139        # @fixme Switch to using a private macros map.
140        macros_to_save = ['%{_prefix}',
141                          '%{_tmproot}',
142                          '%{buildroot}',
143                          '%{_builddir}',
144                          '%{_host}']
145        macros_to_copy = [('%{_host}',     '%{_build}'),
146                          ('%{_tmproot}',  '%{_tmpcxcroot}'),
147                          ('%{buildroot}', '%{buildcxcroot}'),
148                          ('%{_builddir}', '%{_buildcxcdir}')]
149        orig_macros = {}
150        for m in macros_to_save:
151            orig_macros[m] = _build.config.macro(m)
152        for m in macros_to_copy:
153            _build.config.set_define(m[0], _build.config.macro(m[1]))
[4f26bdb]154        _build.make()
[e45a2e4]155        for m in macros_to_save:
[cb12e48]156            _build.config.set_define(m, orig_macros[m])
[0add2ea]157        self.root_copy(_build.config.expand('%{buildcxcroot}'),
[4f26bdb]158                       _build.config.expand('%{_tmpcxcroot}'))
159
160    def build_package(self, _config, _build):
161        if _build.canadian_cross():
162            self.canadian_cross(_build)
163        _build.make()
164        self.report(_config, _build)
[0add2ea]165        self.root_copy(_build.config.expand('%{buildroot}'),
[4f26bdb]166                       _build.config.expand('%{_tmproot}'))
167
168    def bset_tar(self, _build):
169        tardir = _build.config.expand('%{_tardir}')
[cafbcc6]170        if self.opts.get_arg('--bset-tar-file'):
[ee47d72]171            path.mkdir(tardir)
172            tar = path.join(tardir, _build.config.expand('%s.tar.bz2' % (self.bset_pkg)))
173            _notice(self.opts, 'tarball: %s' % (os.path.relpath(path.host(tar))))
[cafbcc6]174            if not self.opts.dry_run():
[4f26bdb]175                tmproot = _build.config.expand('%{_tmproot}')
[cafbcc6]176                cmd = _build.config.expand("'cd " + tmproot + \
177                                               " && %{__tar} -cf - . | %{__bzip2} > " + tar + "'")
178                _build.run(cmd, shell_opts = '-c', cwd = tmproot)
[bf13d27]179
[2f72d35]180    def parse(self, bset):
[bf13d27]181
182        def _clean(line):
183            line = line[0:-1]
184            b = line.find('#')
185            if b >= 0:
186                line = line[1:b]
187            return line.strip()
188
[e9af460]189        bsetname = bset
[bf13d27]190
[ab8319a]191        if not path.exists(bsetname):
[cb12e48]192            for cp in self.macros.expand('%{_configdir}').split(':'):
[ab8319a]193                configdir = path.abspath(cp)
[e9af460]194                bsetname = path.join(configdir, bset)
[ab8319a]195                if path.exists(bsetname):
[bf13d27]196                    break
[ab8319a]197                bsetname = None
198            if bsetname is None:
[e9af460]199                raise error.general('no build set file found: %s' % (bset))
[bf13d27]200        try:
201            if self.opts.trace():
[ab8319a]202                print '_bset:%s: open: %s' % (self.bset, bsetname)
203            bset = open(path.host(bsetname), 'r')
[bf13d27]204        except IOError, err:
[ab8319a]205            raise error.general('error opening bset file: %s' % (bsetname))
[bf13d27]206
207        configs = []
208
209        try:
210            lc = 0
[ab8319a]211            for l in bset:
[bf13d27]212                lc += 1
213                l = _clean(l)
214                if len(l) == 0:
215                    continue
216                if self.opts.trace():
217                    print '%03d: %s' % (lc, l)
[5cba075]218                ls = l.split()
219                if ls[0][-1] == ':' and ls[0][:-1] == 'package':
[cb12e48]220                    self.bset_pkg = self.macros.expand(ls[1].strip())
221                    self.macros['package'] = self.bset_pkg
[5cba075]222                elif ls[0][0] == '%':
223                    if ls[0] == '%define':
[6fad89b]224                        if len(ls) > 2:
[cb12e48]225                            self.macros.define(ls[1].strip(),
226                                               ' '.join([f.strip() for f in ls[2:]]))
[6fad89b]227                        else:
[cb12e48]228                            self.macros.define(ls[1].strip())
[fa653c2]229                    elif ls[0] == '%undefine':
230                        if len(ls) > 2:
231                            raise error.general('%undefine requires just the name')
[cb12e48]232                        self.macros.undefine(ls[1].strip())
[5cba075]233                    elif ls[0] == '%include':
[2f72d35]234                        configs += self.parse(ls[1].strip())
[bf13d27]235                    else:
[ab8319a]236                        raise error.general('invalid directive in build set files: %s' % (l))
[bf13d27]237                else:
[fba1136]238                    l = l.strip()
[0759d98]239                    c = build.find_config(l, self.configs)
[fba1136]240                    if c is None:
241                        raise error.general('cannot find file: %s' % (l))
242                    configs += [c]
[bf13d27]243        except:
[ab8319a]244            bset.close()
[bf13d27]245            raise
246
[ab8319a]247        bset.close()
[bf13d27]248
249        return configs
250
[2f72d35]251    def load(self):
252
[cb12e48]253        exbset = self.macros.expand(self.bset)
[2f72d35]254
[cb12e48]255        self.macros['_bset'] = exbset
[2f72d35]256
257        root, ext = path.splitext(exbset)
258
259        if exbset.endswith('.bset'):
260            bset = exbset
261        else:
262            bset = '%s.bset' % (exbset)
263
264        return self.parse(bset)
265
[79f80fd]266    def build(self, deps = None):
[bf13d27]267
[ab8319a]268        _trace(self.opts, '_bset:%s: make' % (self.bset))
[4882ff3]269        _notice(self.opts, 'Build Set: %s' % (self.bset))
[bf13d27]270
271        configs = self.load()
272
[ab8319a]273        _trace(self.opts, '_bset:%s: configs: %s'  % (self.bset, ','.join(configs)))
[bf13d27]274
275        current_path = os.environ['PATH']
[0add2ea]276
[251a42d]277        start = datetime.datetime.now()
278
[bf13d27]279        try:
280            builds = []
281            for s in range(0, len(configs)):
[729f0bb]282                try:
[984e4e6]283                    #
284                    # Each section of the build set gets a separate set of
[cb12e48]285                    # macros so we do not contaminate one configuration with
[984e4e6]286                    # another.
287                    #
[cb12e48]288                    opts = copy.copy(self.opts)
289                    macros = copy.copy(self.macros)
[729f0bb]290                    if configs[s].endswith('.bset'):
[cb12e48]291                        bs = buildset(configs[s], self.configs, opts, macros)
[79f80fd]292                        bs.build(deps)
[729f0bb]293                        del bs
294                    elif configs[s].endswith('.cfg'):
[cb12e48]295                        b = build.build(configs[s], self.opts.get_arg('--pkg-tar-files'),
296                                        opts, macros)
[79f80fd]297                        if deps is None:
[4f26bdb]298                            self.build_package(configs[s], b)
[79f80fd]299                            if s == len(configs) - 1:
[4f26bdb]300                                self.bset_tar(b)
[79f80fd]301                        else:
302                            deps += b.config.includes()
[729f0bb]303                        builds += [b]
304                    else:
305                        raise error.general('invalid config type: %s' % (configs[s]))
306                except error.general, gerr:
[0add2ea]307                    if self.opts.keep_going():
[729f0bb]308                        print gerr
[0add2ea]309                        if self.opts.always_clean():
310                            builds += [b]
[729f0bb]311                    else:
312                        raise
[4f26bdb]313            if deps is None and not self.opts.get_arg('--no-install'):
314                for b in builds:
315                    self.install(b.name(),
[0add2ea]316                                 b.config.expand('%{buildroot}'),
[4f26bdb]317                                 b.config.expand('%{_prefix}'))
318            if deps is None and \
[0add2ea]319                    (not self.opts.no_clean() or self.opts.always_clean()):
[bf13d27]320                for b in builds:
321                    _notice(self.opts, 'cleaning: %s' % (b.name()))
322                    b.cleanup()
323            for b in builds:
324                del b
325        except:
326            os.environ['PATH'] = current_path
327            raise
[251a42d]328
329        end = datetime.datetime.now()
330
[bf13d27]331        os.environ['PATH'] = current_path
332
[251a42d]333        _notice(self.opts, 'Build Set: Time %s' % (str(end - start)))
334
[d63f135]335def list_bset_cfg_files(opts, configs):
336    if opts.get_arg('--list-configs') or opts.get_arg('--list-bsets'):
337        if opts.get_arg('--list-configs'):
338            ext = '.cfg'
339        else:
340            ext = '.bset'
341        for p in configs['paths']:
342            print 'Examining: %s' % (os.path.relpath(p))
343        for c in configs['files']:
344            if c.endswith(ext):
345                print '    %s' % (c)
346        return True
347    return False
348
[bf13d27]349def run():
350    import sys
351    try:
[cafbcc6]352        optargs = { '--list-configs':  'List available configurations',
353                    '--list-bsets':    'List available build sets',
[79f80fd]354                    '--list-deps':     'List the dependent files.',
[cafbcc6]355                    '--no-install':    'Do not install the packages to the prefix.',
[26595c7]356                    '--no-report':     'Do not create a package report.',
357                    '--report-format': 'The report format (text, html, asciidoc).',
[cafbcc6]358                    '--bset-tar-file': 'Create a build set tar file',
359                    '--pkg-tar-files': 'Create package tar files' }
[cb12e48]360        opts = options.load(sys.argv, optargs)
[bf13d27]361        log.default = log.log(opts.logfiles())
[efb6688]362        _notice(opts, 'RTEMS Source Builder - Set Builder, v%s' % (version.str()))
[cb12e48]363        if not check.host_setup(opts):
[fba1136]364            raise error.general('host build environment is not set up correctly')
[cb12e48]365        configs = build.get_configs(opts)
[79f80fd]366        if opts.get_arg('--list-deps'):
367            deps = []
368        else:
369            deps = None
[d63f135]370        if not list_bset_cfg_files(opts, configs):
[71b8893]371            for bset in opts.params():
[cb12e48]372                b = buildset(bset, configs, opts)
[79f80fd]373                b.build(deps)
[fba1136]374                del b
[79f80fd]375        if deps is not None:
376            c = 0
377            for d in sorted(set(deps)):
378                c += 1
379                print 'dep[%d]: %s' % (c, d)
[bf13d27]380    except error.general, gerr:
381        print gerr
[aa4f8f6]382        print >> sys.stderr, 'Build FAILED'
[bf13d27]383        sys.exit(1)
384    except error.internal, ierr:
385        print ierr
386        sys.exit(1)
387    except error.exit, eerr:
388        pass
389    except KeyboardInterrupt:
[06834cf]390        _notice(opts, 'abort: user terminated')
[bf13d27]391        sys.exit(1)
392    sys.exit(0)
393
394if __name__ == "__main__":
395    run()
Note: See TracBrowser for help on using the repository browser.