source: rtems-source-builder/source-builder/sb/reports.py @ 055e490

4.104.114.95
Last change on this file since 055e490 was 055e490, checked in by Chris Johns <chrisj@…>, on 04/28/13 at 06:10:02

Fix the report command. Only output if an outname is given.

  • Property mode set to 100644
File size: 14.3 KB
RevLine 
[4754f1e]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 given a config file. It only builds to be
22# installed not to be package unless you run a packager around this.
23#
24
[fd4e4fb]25import copy
[d7e4900]26import datetime
[4754f1e]27import os
28import sys
29
[d7e4900]30try:
31    import build
32    import check
33    import config
34    import error
35    import git
36    import log
[cb12e48]37    import options
[d7e4900]38    import path
39    import setbuilder
[efb6688]40    import version
[d7e4900]41except KeyboardInterrupt:
42    print 'user terminated'
43    sys.exit(1)
44except:
[aabd20d]45    print 'error: unknown application load error'
[d7e4900]46    sys.exit(1)
[4754f1e]47
48class report:
49    """Report the build details about a package given a config file."""
50
51    line_len = 78
52
[cb12e48]53    def __init__(self, format, _configs, opts, macros = None):
[4754f1e]54        self.format = format
55        self.configs = _configs
56        self.opts = opts
[cb12e48]57        if macros is None:
58            self.macros = opts.defaults
59        else:
60            self.macros = macros
[4754f1e]61        self.bset_nesting = 0
62        self.configs_active = False
[d7e4900]63        self.out = ''
64        self.asciidoc = None
[4754f1e]65
[6ee25e5]66    def _sbpath(self, *args):
[cb12e48]67        p = self.macros.expand('%{_sbdir}')
[6ee25e5]68        for arg in args:
69            p = path.join(p, arg)
70        return os.path.abspath(path.host(p))
71
[d7e4900]72    def output(self, text):
73        self.out += '%s\n' % (text)
[4754f1e]74
75    def is_text(self):
76        return self.format == 'text'
77
78    def is_asciidoc(self):
[d6638aa]79        return self.format == 'asciidoc' or self.format == 'html'
[4754f1e]80
81    def setup(self):
[d6638aa]82        if self.format == 'html':
[d7e4900]83            try:
84                import asciidocapi
85            except:
86                raise error.general('installation error: no asciidocapi found')
[cb12e48]87            asciidoc_py = self._sbpath(options.basepath, 'asciidoc', 'asciidoc.py')
[d7e4900]88            try:
[6ee25e5]89                self.asciidoc = asciidocapi.AsciiDocAPI(asciidoc_py)
[d7e4900]90            except:
91                raise error.general('application error: asciidocapi failed')
[4754f1e]92
93    def header(self):
94        pass
95
96    def footer(self):
97        pass
98
[d7e4900]99    def git_status(self):
100        text = 'RTEMS Source Builder Repository Status'
101        if self.is_asciidoc():
102            self.output('')
103            self.output("'''")
104            self.output('')
105            self.output('.%s' % (text))
106        else:
107            self.output('-' * self.line_len)
108            self.output('%s' % (text))
[cb12e48]109        repo = git.repo('.', self.opts, self.macros)
[d7e4900]110        repo_valid = repo.valid()
111        if repo_valid:
112            if self.is_asciidoc():
113                self.output('*Remotes*:;;')
114            else:
115                self.output(' Remotes:')
116            repo_remotes = repo.remotes()
117            rc = 0
118            for r in repo_remotes:
119                rc += 1
120                if 'url' in repo_remotes[r]:
121                    text = repo_remotes[r]['url']
122                else:
123                    text = 'no URL found'
124                text = '%s: %s' % (r, text)
125                if self.is_asciidoc():
126                    self.output('. %s' % (text))
127                else:
128                    self.output('  %2d: %s' % (rc, text))
129            if self.is_asciidoc():
130                self.output('*Status*:;;')
131            else:
132                self.output(' Status:')
133            if repo.clean():
134                if self.is_asciidoc():
135                    self.output('Clean')
136                else:
137                    self.output('  Clean')
138            else:
139                if self.is_asciidoc():
140                    self.output('_Repository is dirty_')
141                else:
142                    self.output('  Repository is dirty')
143            repo_head = repo.head()
144            if self.is_asciidoc():
145                self.output('*Head*:;;')
146                self.output('Commit: %s' % (repo_head))
147            else:
148                self.output(' Head:')
149                self.output('  Commit: %s' % (repo_head))
150        else:
151            self.output('_Not a valid GIT repository_')
152        if self.is_asciidoc():
153            self.output('')
154            self.output("'''")
155            self.output('')
156
157    def introduction(self, name, intro_text):
[4754f1e]158        if self.is_asciidoc():
159            h = 'RTEMS Source Builder Report'
[d7e4900]160            self.output(h)
161            self.output('=' * len(h))
162            self.output(':doctype: book')
163            self.output(':toc2:')
164            self.output(':toclevels: 5')
165            self.output(':icons:')
166            self.output(':numbered:')
167            self.output(':data-uri:')
168            self.output('')
169            self.output('RTEMS Tools Project <rtems-users@rtems.org>')
170            self.output(datetime.datetime.now().ctime())
171            self.output('')
[cb12e48]172            image = self._sbpath(options.basepath, 'images', 'rtemswhitebg.jpg')
[d7e4900]173            self.output('image:%s["RTEMS",width="20%%"]' % (image))
174            self.output('')
175            if intro_text:
176                self.output('%s' % ('\n'.join(intro_text)))
[4754f1e]177        else:
[d7e4900]178            self.output('=' * self.line_len)
179            self.output('RTEMS Tools Project <rtems-users@rtems.org> %s' % datetime.datetime.now().ctime())
180            if intro_text:
181                self.output('')
182                self.output('%s' % ('\n'.join(intro_text)))
183            self.output('=' * self.line_len)
184            self.output('Report: %s' % (name))
185        self.git_status()
[4754f1e]186
187    def config_start(self, name):
188        first = not self.configs_active
189        self.configs_active = True
190
191    def config_end(self, name):
192        if self.is_asciidoc():
[d7e4900]193            self.output('')
194            self.output("'''")
195            self.output('')
[4754f1e]196
197    def buildset_start(self, name):
198        if self.is_asciidoc():
199            h = '%s' % (name)
[d7e4900]200            self.output('=%s %s' % ('=' * self.bset_nesting, h))
[4754f1e]201        else:
[d7e4900]202            self.output('=-' * (self.line_len / 2))
203            self.output('Build Set: %s' % (name))
[4754f1e]204
205    def buildset_end(self, name):
206        self.configs_active = False
207
208    def source(self, package, source_tag):
209        return package.sources()
210
211    def patch(self, package, args):
212        return package.patches()
213
[514ad16]214    def output_info(self, name, info, separated = False):
215        if info is not None:
216            end = ''
217            if self.is_asciidoc():
218                if separated:
219                    self.output('*%s:*::' % (name))
220                    self.output('')
221                else:
222                    self.output('*%s:* ' % (name))
223                    end = ' +'
224                spaces = ''
225            else:
226                self.output(' %s:' % (name))
227                spaces = '  '
228            for l in info:
229                self.output('%s%s%s' % (spaces, l, end))
230            if self.is_asciidoc() and separated:
231                self.output('')
232
233    def output_directive(self, name, directive):
234        if directive is not None:
235            if self.is_asciidoc():
236                self.output('')
237                self.output('*%s*:' % (name))
238                self.output('--------------------------------------------')
239                spaces = ''
240            else:
241                self.output(' %s:' % (name))
242                spaces = '  '
243            for l in directive:
244                self.output('%s%s' % (spaces, l))
245            if self.is_asciidoc():
246                self.output('--------------------------------------------')
247
[cb12e48]248    def config(self, configname, opts, macros):
[514ad16]249
[cb12e48]250        _config = config.file(configname, opts, macros)
[4754f1e]251        packages = _config.packages()
252        package = packages['main']
253        name = package.name()
[514ad16]254        self.config_start(name)
[4754f1e]255        if self.is_asciidoc():
[514ad16]256            self.output('*Package*: _%s_ +' % (name))
257            self.output('*Config*: %s' % (configname))
[d7e4900]258            self.output('')
[4754f1e]259        else:
[514ad16]260            self.output('-' * self.line_len)
261            self.output('Package: %s' % (name))
262            self.output(' Config: %s' % (configname))
263        self.output_info('Summary', package.get_info('summary'), True)
264        self.output_info('URL', package.get_info('url'))
265        self.output_info('Version', package.get_info('version'))
266        self.output_info('Release', package.get_info('release'))
267        self.output_info('Build Arch', package.get_info('buildarch'))
268        if self.is_asciidoc():
269            self.output('')
[4754f1e]270        sources = package.sources()
271        if self.is_asciidoc():
[514ad16]272            self.output('*Sources:*::')
[4754f1e]273            if len(sources) == 0:
[d7e4900]274                self.output('No sources')
[4754f1e]275        else:
[d7e4900]276            self.output('  Sources: %d' % (len(sources)))
[4754f1e]277        c = 0
278        for s in sources:
279            c += 1
280            if self.is_asciidoc():
[d7e4900]281                self.output('. %s' % (sources[s][0]))
[4754f1e]282            else:
[d7e4900]283                self.output('   %2d: %s' % (c, sources[s][0]))
[4754f1e]284        patches = package.patches()
285        if self.is_asciidoc():
[d7e4900]286            self.output('')
[514ad16]287            self.output('*Patches:*::')
[4754f1e]288            if len(patches) == 0:
[d7e4900]289                self.output('No patches')
[4754f1e]290        else:
[d7e4900]291            self.output('  Patches: %s' % (len(patches)))
[4754f1e]292        c = 0
293        for p in patches:
294            c += 1
295            if self.is_asciidoc():
[d7e4900]296                self.output('. %s' % (patches[p][0]))
[4754f1e]297            else:
[d7e4900]298                self.output('   %2d: %s' % (c, patches[p][0]))
[514ad16]299        self.output_directive('Preparation', package.prep())
300        self.output_directive('Build', package.build())
301        self.output_directive('Install', package.install())
302        self.output_directive('Clean', package.clean())
[4754f1e]303        self.config_end(name)
304
[055e490]305    def buildset(self, name, opts = None, macros = None):
[864e8ff]306        self.bset_nesting += 1
307        self.buildset_start(name)
[055e490]308        if opts is None:
309            opts = self.opts
310        if macros is None:
311            macros = self.macros
[cb12e48]312        bset = setbuilder.buildset(name, self.configs, opts, macros)
[864e8ff]313        for c in bset.load():
314            if c.endswith('.bset'):
[055e490]315                self.buildset(c, bset.opts, bset.macros)
[864e8ff]316            elif c.endswith('.cfg'):
[055e490]317                self.config(c, bset.opts, bset.macros)
[4754f1e]318            else:
[864e8ff]319                raise error.general('invalid config type: %s' % (c))
320        self.buildset_end(name)
321        self.bset_nesting -= 1
[4754f1e]322
[d7e4900]323    def generate(self, name):
[d6638aa]324        if self.format == 'html':
[d7e4900]325            if self.asciidoc is None:
326                raise error.general('asciidoc not initialised')
327            import StringIO
328            infile = StringIO.StringIO(self.out)
329            outfile = StringIO.StringIO()
330            self.asciidoc.execute(infile, outfile)
331            self.out = outfile.getvalue()
332            infile.close()
333            outfile.close()
[055e490]334        if name is not None:
335            try:
336                o = open(name, "w")
337                o.write(self.out)
338                o.close()
339                del o
340            except IOError, err:
341                raise error.general('writing output file: %s: %s' % (name, err))
[d7e4900]342
[055e490]343    def make(self, inname, outname = None, intro_text = None):
[d7e4900]344        self.setup()
345        self.introduction(inname, intro_text)
[0759d98]346        config = build.find_config(inname, self.configs)
[864e8ff]347        if config is None:
348            raise error.general('config file not found: %s' % (inname))
349        if config.endswith('.bset'):
350            self.buildset(config)
351        elif config.endswith('.cfg'):
[cb12e48]352            self.config(config, self.opts, self.macros)
[864e8ff]353        else:
354            raise error.general('invalid config type: %s' % (config))
[d7e4900]355        self.generate(outname)
[4754f1e]356
357def run(args):
358    try:
359        optargs = { '--list-bsets':   'List available build sets',
360                    '--list-configs': 'List available configurations',
[d6638aa]361                    '--format':       'Output format (text, html, asciidoc)',
[d7e4900]362                    '--output':       'File name to output the report' }
[cb12e48]363        opts = options.load(args, optargs)
[d7e4900]364        if opts.get_arg('--output') and len(opts.params()) > 1:
365            raise error.general('--output can only be used with a single config')
[efb6688]366        print 'RTEMS Source Builder, Reporter v%s' % (version.str())
[cb12e48]367        if not check.host_setup(opts):
[5142bec]368            log.warning('forcing build with known host setup problems')
[cb12e48]369        configs = build.get_configs(opts)
[4754f1e]370        if not setbuilder.list_bset_cfg_files(opts, configs):
[d7e4900]371            output = opts.get_arg('--output')
372            if output is not None:
373                output = output[1]
[4754f1e]374            format = 'text'
[d7e4900]375            ext = '.txt'
376            format_opt = opts.get_arg('--format')
377            if format_opt:
378                if len(format_opt) != 2:
379                    raise error.general('invalid format option: %s' % ('='.join(format_opt)))
380                if format_opt[1] == 'text':
381                    pass
382                elif format_opt[1] == 'asciidoc':
383                    format = 'asciidoc'
[d6638aa]384                    ext = '.txt'
385                elif format_opt[1] == 'html':
386                    format = 'html'
[d7e4900]387                    ext = '.html'
388                else:
389                    raise error.general('invalid format: %s' % (format_opt[1]))
[cb12e48]390            r = report(format, configs, opts)
[d7e4900]391            for _config in opts.params():
392                if output is None:
393                    outname = path.splitext(_config)[0] + ext
[cb12e48]394                    outname = outname.replace('/', '-')
[d7e4900]395                else:
396                    outname = output
397                r.make(_config, outname)
398            del r
[4754f1e]399    except error.general, gerr:
400        print gerr
401        sys.exit(1)
402    except error.internal, ierr:
403        print ierr
404        sys.exit(1)
405    except error.exit, eerr:
406        pass
407    except KeyboardInterrupt:
[5142bec]408        log.notice('abort: user terminated')
[4754f1e]409        sys.exit(1)
410    sys.exit(0)
411
412if __name__ == "__main__":
413    run(sys.argv)
Note: See TracBrowser for help on using the repository browser.