source: rtems-source-builder/source-builder/sb/setbuilder.py @ 5142bec

4.104.114.95
Last change on this file since 5142bec was 5142bec, checked in by Chris Johns <chrisj@…>, on 04/21/13 at 08:37:02

Refactor the logging support.

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