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

4.104.114.9
Last change on this file since 5142bec was 5142bec, checked in by Chris Johns <chrisj@…>, on Apr 21, 2013 at 8:37:02 AM

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 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 copy
26import datetime
27import os
28import sys
29
30try:
31    import build
32    import check
33    import config
34    import error
35    import git
36    import log
37    import options
38    import path
39    import setbuilder
40    import version
41except KeyboardInterrupt:
42    print 'user terminated'
43    sys.exit(1)
44except:
45    print 'error: unknown application load error'
46    sys.exit(1)
47
48class report:
49    """Report the build details about a package given a config file."""
50
51    line_len = 78
52
53    def __init__(self, format, _configs, opts, macros = None):
54        self.format = format
55        self.configs = _configs
56        self.opts = opts
57        if macros is None:
58            self.macros = opts.defaults
59        else:
60            self.macros = macros
61        self.bset_nesting = 0
62        self.configs_active = False
63        self.out = ''
64        self.asciidoc = None
65
66    def _sbpath(self, *args):
67        p = self.macros.expand('%{_sbdir}')
68        for arg in args:
69            p = path.join(p, arg)
70        return os.path.abspath(path.host(p))
71
72    def output(self, text):
73        self.out += '%s\n' % (text)
74
75    def is_text(self):
76        return self.format == 'text'
77
78    def is_asciidoc(self):
79        return self.format == 'asciidoc' or self.format == 'html'
80
81    def setup(self):
82        if self.format == 'html':
83            try:
84                import asciidocapi
85            except:
86                raise error.general('installation error: no asciidocapi found')
87            asciidoc_py = self._sbpath(options.basepath, 'asciidoc', 'asciidoc.py')
88            try:
89                self.asciidoc = asciidocapi.AsciiDocAPI(asciidoc_py)
90            except:
91                raise error.general('application error: asciidocapi failed')
92
93    def header(self):
94        pass
95
96    def footer(self):
97        pass
98
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))
109        repo = git.repo('.', self.opts, self.macros)
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):
158        if self.is_asciidoc():
159            h = 'RTEMS Source Builder Report'
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('')
172            image = self._sbpath(options.basepath, 'images', 'rtemswhitebg.jpg')
173            self.output('image:%s["RTEMS",width="20%%"]' % (image))
174            self.output('')
175            if intro_text:
176                self.output('%s' % ('\n'.join(intro_text)))
177        else:
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()
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():
193            self.output('')
194            self.output("'''")
195            self.output('')
196
197    def buildset_start(self, name):
198        if self.is_asciidoc():
199            h = '%s' % (name)
200            self.output('=%s %s' % ('=' * self.bset_nesting, h))
201        else:
202            self.output('=-' * (self.line_len / 2))
203            self.output('Build Set: %s' % (name))
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
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
248    def config(self, configname, opts, macros):
249
250        _config = config.file(configname, opts, macros)
251        packages = _config.packages()
252        package = packages['main']
253        name = package.name()
254        self.config_start(name)
255        if self.is_asciidoc():
256            self.output('*Package*: _%s_ +' % (name))
257            self.output('*Config*: %s' % (configname))
258            self.output('')
259        else:
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('')
270        sources = package.sources()
271        if self.is_asciidoc():
272            self.output('*Sources:*::')
273            if len(sources) == 0:
274                self.output('No sources')
275        else:
276            self.output('  Sources: %d' % (len(sources)))
277        c = 0
278        for s in sources:
279            c += 1
280            if self.is_asciidoc():
281                self.output('. %s' % (sources[s][0]))
282            else:
283                self.output('   %2d: %s' % (c, sources[s][0]))
284        patches = package.patches()
285        if self.is_asciidoc():
286            self.output('')
287            self.output('*Patches:*::')
288            if len(patches) == 0:
289                self.output('No patches')
290        else:
291            self.output('  Patches: %s' % (len(patches)))
292        c = 0
293        for p in patches:
294            c += 1
295            if self.is_asciidoc():
296                self.output('. %s' % (patches[p][0]))
297            else:
298                self.output('   %2d: %s' % (c, patches[p][0]))
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())
303        self.config_end(name)
304
305    def buildset(self, name):
306        self.bset_nesting += 1
307        self.buildset_start(name)
308        opts = copy.copy(self.opts)
309        macros = copy.copy(self.macros)
310        bset = setbuilder.buildset(name, self.configs, opts, macros)
311        for c in bset.load():
312            if c.endswith('.bset'):
313                self.buildset(c)
314            elif c.endswith('.cfg'):
315                self.config(c, opts, macros)
316            else:
317                raise error.general('invalid config type: %s' % (c))
318        self.buildset_end(name)
319        self.bset_nesting -= 1
320
321    def generate(self, name):
322        if self.format == 'html':
323            if self.asciidoc is None:
324                raise error.general('asciidoc not initialised')
325            import StringIO
326            infile = StringIO.StringIO(self.out)
327            outfile = StringIO.StringIO()
328            self.asciidoc.execute(infile, outfile)
329            self.out = outfile.getvalue()
330            infile.close()
331            outfile.close()
332        try:
333            o = open(name, "w")
334            o.write(self.out)
335            o.close()
336            del o
337        except IOError, err:
338            raise error.general('writing output file: %s: %s' % (name, err))
339
340    def make(self, inname, outname, intro_text = None):
341        self.setup()
342        self.introduction(inname, intro_text)
343        config = build.find_config(inname, self.configs)
344        if config is None:
345            raise error.general('config file not found: %s' % (inname))
346        if config.endswith('.bset'):
347            self.buildset(config)
348        elif config.endswith('.cfg'):
349            self.config(config, self.opts, self.macros)
350        else:
351            raise error.general('invalid config type: %s' % (config))
352        self.generate(outname)
353
354def run(args):
355    try:
356        optargs = { '--list-bsets':   'List available build sets',
357                    '--list-configs': 'List available configurations',
358                    '--format':       'Output format (text, html, asciidoc)',
359                    '--output':       'File name to output the report' }
360        opts = options.load(args, optargs)
361        if opts.get_arg('--output') and len(opts.params()) > 1:
362            raise error.general('--output can only be used with a single config')
363        print 'RTEMS Source Builder, Reporter v%s' % (version.str())
364        if not check.host_setup(opts):
365            log.warning('forcing build with known host setup problems')
366        configs = build.get_configs(opts)
367        if not setbuilder.list_bset_cfg_files(opts, configs):
368            output = opts.get_arg('--output')
369            if output is not None:
370                output = output[1]
371            format = 'text'
372            ext = '.txt'
373            format_opt = opts.get_arg('--format')
374            if format_opt:
375                if len(format_opt) != 2:
376                    raise error.general('invalid format option: %s' % ('='.join(format_opt)))
377                if format_opt[1] == 'text':
378                    pass
379                elif format_opt[1] == 'asciidoc':
380                    format = 'asciidoc'
381                    ext = '.txt'
382                elif format_opt[1] == 'html':
383                    format = 'html'
384                    ext = '.html'
385                else:
386                    raise error.general('invalid format: %s' % (format_opt[1]))
387            r = report(format, configs, opts)
388            for _config in opts.params():
389                if output is None:
390                    outname = path.splitext(_config)[0] + ext
391                    outname = outname.replace('/', '-')
392                else:
393                    outname = output
394                r.make(_config, outname)
395            del r
396    except error.general, gerr:
397        print gerr
398        sys.exit(1)
399    except error.internal, ierr:
400        print ierr
401        sys.exit(1)
402    except error.exit, eerr:
403        pass
404    except KeyboardInterrupt:
405        log.notice('abort: user terminated')
406        sys.exit(1)
407    sys.exit(0)
408
409if __name__ == "__main__":
410    run(sys.argv)
Note: See TracBrowser for help on using the repository browser.