source: rtems-source-builder/source-builder/sb/reports.py @ 514ad16

4.104.114.95
Last change on this file since 514ad16 was 514ad16, checked in by Chris Johns <chrisj@…>, on 03/04/13 at 07:08:03

Add more report detail.

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