diff options
author | Chris Johns <chrisj@rtems.org> | 2014-08-12 12:22:30 +1000 |
---|---|---|
committer | Chris Johns <chrisj@rtems.org> | 2014-08-12 12:22:30 +1000 |
commit | 8db521266516df0b371c87b2f8db4ab58e458668 (patch) | |
tree | 85a72dc9fe115ca3e6ae890a1c32ddedf8a3bb72 | |
parent | sb: Add INI format output to the reports. (diff) | |
download | rtems-source-builder-8db521266516df0b371c87b2f8db4ab58e458668.tar.bz2 |
sb: Complete support for INI file reporting.
The INI file format provides a parsable configuration file format.
-rw-r--r-- | rtems/.gitignore | 1 | ||||
-rw-r--r-- | source-builder/sb/reports.py | 258 | ||||
-rw-r--r-- | source-builder/sb/sources.py | 39 |
3 files changed, 210 insertions, 88 deletions
diff --git a/rtems/.gitignore b/rtems/.gitignore index b31949d..20951cb 100644 --- a/rtems/.gitignore +++ b/rtems/.gitignore @@ -5,6 +5,7 @@ tar build *.txt *.html +*.ini #*# .* log_* diff --git a/source-builder/sb/reports.py b/source-builder/sb/reports.py index f5256f3..ae0cd5d 100644 --- a/source-builder/sb/reports.py +++ b/source-builder/sb/reports.py @@ -27,6 +27,9 @@ import datetime import os import sys +import pprint +pp = pprint.PrettyPrinter(indent = 2) + try: import build import check @@ -46,6 +49,17 @@ except: print 'error: unknown application load error' sys.exit(1) +def _tree_name(path_): + return path.splitext(path.basename(path_))[0] + +def _merge(_dict, new): + new = copy.deepcopy(new) + for i in new: + if i not in _dict: + _dict[i] = new[i] + else: + _dict[i] += new[i] + class report: """Report the build details about a package given a config file.""" @@ -67,9 +81,8 @@ class report: self.cini = ';' else: self.cini = '' + self.tree = {} self.files = { 'buildsets':[], 'configs':[] } - self.sources = {} - self.patches = {} def _sbpath(self, *args): p = self.macros.expand('%{_sbdir}') @@ -177,6 +190,7 @@ class report: def introduction(self, name, intro_text = None): now = datetime.datetime.now().ctime() + title = 'RTEMS Tools Project <users@rtems.org>' if self.is_asciidoc(): h = 'RTEMS Source Builder Report' self.output(h) @@ -188,7 +202,7 @@ class report: self.output(':numbered:') self.output(':data-uri:') self.output('') - self.output('RTEMS Tools Project <rtems-users@rtems.org>') + self.output(title) self.output(datetime.datetime.now().ctime()) self.output('') image = self._sbpath(options.basepath, 'images', 'rtemswhitebg.jpg') @@ -198,14 +212,14 @@ class report: self.output('%s' % ('\n'.join(intro_text))) elif self.is_ini(): self.output(';') - self.output('; RTEMS Tools Project <rtems-users@rtems.org> %s' % now) + self.output('; %s %s' % (title, now)) if intro_text: self.output(';') self.output('; %s' % ('\n; '.join(intro_text))) self.output(';') else: self.output('=' * self.line_len) - self.output('RTEMS Tools Project <rtems-users@rtems.org> %s' % now) + self.output('%s %s' % (title, now)) if intro_text: self.output('') self.output('%s' % ('\n'.join(intro_text))) @@ -245,9 +259,15 @@ class report: def source(self, macros): def err(msg): raise error.general('%s' % (msg)) + _srcs = {} + for p in sources.get_source_names(macros, err): + if 'setup' in sources.get_source_keys(p, macros, err): + _srcs[p] = \ + [s for s in sources.get_sources(p, macros, err) if not s.startswith('%setup')] + _srcs[p] = [macros.expand(s) for s in _srcs[p]] srcs = {} - for n in sources.get_source_names(macros, err): - srcs[n] = sources.get_sources(n, macros, err) + for p in _srcs: + srcs[p] = [(s, sources.get_hash(path.basename(s).lower(), macros)) for s in _srcs[p]] return srcs def patch(self, macros): @@ -255,18 +275,15 @@ class report: raise error.general('%s' % (msg)) _patches = {} for n in sources.get_patch_names(macros, err): - _patches[n] = sources.get_patches(n, macros, err) + if 'setup' in sources.get_patch_keys(n, macros, err): + _patches[n] = \ + [p for p in sources.get_patches(n, macros, err) if not p.startswith('%setup')] + _patches[n] = [macros.expand(p.split()[-1]) for p in _patches[n]] patches = {} for n in _patches: - pl = [] - for p in _patches[n]: - pl += [p.split()[-1]] - patches[n] = pl + patches[n] = [(p, sources.get_hash(path.basename(p).lower(), macros)) for p in _patches[n]] return patches - def hash(self, name, macros): - return sources.get_hash(name, macros) - def output_info(self, name, info, separated = False): if info is not None: end = '' @@ -301,25 +318,41 @@ class report: if self.is_asciidoc(): self.output('--------------------------------------------') - def get_sources_patches(self, macros): - def _merge(src, dst): - for name in src: - if name not in dst: - dst[name] = [] - for s in src[name]: - dst[name] += [s] - dst[name] = dst[name] - sources = self.source(macros) - _merge(sources, self.sources) - patches = self.patch(macros) - _merge(patches, self.patches) - return sources, patches - - def config(self, _config, opts, macros): + def tree_sources(self, name, tree, sources = []): + if 'cfg' in tree: + packages = {} + if 'sources' in tree['cfg']: + _merge(packages, tree['cfg']['sources']) + if 'patches' in tree['cfg']: + _merge(packages, tree['cfg']['patches']) + for package in packages: + for source in packages[package]: + if not source[0].startswith('git') and not source[0].startswith('cvs'): + sources += [(path.basename(source[0]), source[0], source[1])] + if 'bset' in tree: + for node in sorted(tree['bset'].keys()): + self.tree_sources(_tree_name(node), tree['bset'][node], sources) + return sources + + def config(self, _config, tree, opts, macros): packages = _config.packages() package = packages['main'] name = package.name() - sources, patches = self.get_sources_patches(macros) + if len(name) == 0: + return + tree['file'] += [_config.file_name()] + sources = self.source(macros) + patches = self.patch(macros) + if len(sources): + if 'sources' in tree: + tree['sources'] = dict(tree['sources'].items() + sources.items()) + else: + tree['sources'] = sources + if len(patches): + if 'patches' in tree: + tree['patches'] = dict(tree['patches'].items() + patches.items()) + else: + tree['patches'] = patches self.config_start(name, _config) if self.is_ini(): return @@ -349,17 +382,16 @@ class report: for s in sources[name]: c += 1 if self.is_asciidoc(): - self.output('. %s' % (s)) + self.output('. %s' % (s[0])) else: - self.output(' %2d: %s' % (c, s)) - hash = self.hash(path.basename(s).lower(), macros) - if hash is None: + self.output(' %2d: %s' % (c, s[0])) + if s[1] is None: h = 'No checksum' else: - hash = hash.split() + hash = s[1].split() h = '%s: %s' % (hash[0], hash[1]) if self.is_asciidoc(): - self.output(' %s' % (h)) + self.output('+\n%s\n' % (h)) else: self.output(' %s' % (h)) if self.is_asciidoc(): @@ -370,20 +402,21 @@ class report: else: self.output(' Patches: %s' % (len(patches))) c = 0 - for p in patches: - c += 1 - if self.is_asciidoc(): - self.output('. %s' % (patches[p][0])) - else: - self.output(' %2d: %s' % (c, patches[p][0])) - hash = self.hash(path.basename(s).lower(), macros) + for name in patches: + for p in patches[name]: + c += 1 + if self.is_asciidoc(): + self.output('. %s' % (p[0])) + else: + self.output(' %2d: %s' % (c, p[0])) + hash = p[1] if hash is None: h = 'No checksum' else: hash = hash.split() h = '%s: %s' % (hash[0], hash[1]) if self.is_asciidoc(): - self.output(' %s' % (h)) + self.output('+\n(%s)\n' % (h)) else: self.output(' %s' % (h)) self.output_directive('Preparation', package.prep()) @@ -392,30 +425,94 @@ class report: self.output_directive('Clean', package.clean()) self.config_end(name, _config) - def generate_ini(self, sysname): - self.output(';') - self.output('; Buildset File(s):') - for bf in sorted(self.files['buildsets']): - self.output('; %s' % (bf)) - self.output(';') - self.output('; Configuration File(s):') - for cf in sorted(self.files['configs']): - self.output('; %s' % (cf)) - names = sorted(set(self.sources.keys() + self.patches.keys())) - self.output(';') - self.output('') - self.output('[%s]' % (sysname)) - for name in names: - self.output('%s = rtems-%s' % (name, name)) - for name in names: + def generate_ini_tree(self, name, tree, prefix_char, prefix = ''): + if prefix_char == '|': + c = '|' + else: + c = '+' + self.output('; %s %s- %s' % (prefix, c, name)) + prefix += ' %s ' % (prefix_char) + if 'cfg' in tree: + files = sorted(tree['cfg']['file']) + if len(files): + for f in range(0, len(files) - 1): + self.output('; %s |- %s' % (prefix, files[f])) + if 'bset' in tree and len(tree['bset'].keys()): + c = '|' + else: + c = '+' + self.output('; %s %s- %s' % (prefix, c, files[f + 1])) + if 'bset' in tree: + nodes = sorted(tree['bset'].keys()) + for node in range(0, len(nodes)): + if node == len(nodes) - 1: + prefix_char = ' ' + else: + prefix_char = '|' + self.generate_ini_tree(nodes[node], + tree['bset'][nodes[node]], + prefix_char, + prefix) + + def generate_ini_node(self, name, tree, sections = []): + if name not in sections: + sections += [name] self.output('') - self.output('[%s-%s]' % (sysname, name)) - if name in self.sources: - self.output('sources = %s' % (', '.join(set(self.sources[name])))) - if name in self.patches: - self.output('patches = %s' % (', '.join(set(self.patches[name])))) + self.output('[%s]' % (name)) + if 'bset' in tree and len(tree['bset']): + self.output(' packages = %s' % \ + (', '.join([_tree_name(n) for n in sorted(tree['bset'])]))) + if 'cfg' in tree: + packages = {} + if 'sources' in tree['cfg']: + _merge(packages, tree['cfg']['sources']) + if 'patches' in tree['cfg']: + _merge(packages, tree['cfg']['patches']) + for package in packages: + self.output(' %s = %s' % (package, ', '.join([s[0] for s in packages[package]]))) + if 'bset' in tree: + for node in sorted(tree['bset'].keys()): + self.generate_ini_node(_tree_name(node), tree['bset'][node], sections) + + def generate_ini_source(self, sources): + self.output('') + self.output('[source]') + for source in sources: + self.output(' %s = %s' % (source[0], source[1])) + + def generate_ini_hash(self, sources): + self.output('') + self.output('[hash]') + for source in sources: + if source[2] is None: + hash = '' + else: + hash = source[2].split() + hash = '%s:%s' % (hash[0], hash[1]) + self.output(' %s = %s' % (source[0], hash)) - def write(self, sysname, name): + def generate_ini(self): + #self.output(pp.pformat(self.tree)) + nodes = sorted([node for node in self.tree.keys() if node != 'bset']) + self.output(';') + self.output('; Configuration Tree:') + for node in range(0, len(nodes)): + if node == len(nodes) - 1: + prefix_char = ' ' + else: + prefix_char = '|' + self.generate_ini_tree(nodes[node], self.tree[nodes[node]], prefix_char) + self.output(';') + sources = [] + for node in nodes: + sources += self.tree_sources(_tree_name(node), self.tree[node]) + sources = sorted(set(sources)) + self.generate_ini_source(sources) + self.generate_ini_hash(sources) + for node in nodes: + self.generate_ini_node(_tree_name(node), self.tree[node]) + + def write(self, name): if self.is_html(): if self.asciidoc is None: raise error.general('asciidoc not initialised') @@ -427,7 +524,7 @@ class report: infile.close() outfile.close() elif self.is_ini(): - self.generate_ini(sysname) + self.generate_ini() if name is not None: try: o = open(path.host(name), "w") @@ -437,34 +534,37 @@ class report: except IOError, err: raise error.general('writing output file: %s: %s' % (name, err)) - def generate(self, name, opts = None, macros = None): + def generate(self, name, tree = None, opts = None, defaults = None): self.bset_nesting += 1 self.buildset_start(name) + if tree is None: + tree = self.tree if opts is None: opts = self.opts - if macros is None: - macros = self.macros - bset = setbuilder.buildset(name, self.configs, opts, macros) + bset = setbuilder.buildset(name, self.configs, opts, defaults) + if name in tree: + raise error.general('duplicate build set in tree: %s' % (name)) + tree[name] = { 'bset': { }, 'cfg': { 'file': [] } } for c in bset.load(): + macros = copy.copy(bset.macros) if c.endswith('.bset'): - self.generate(c, bset.opts, bset.macros) + self.generate(c, tree[name]['bset'], bset.opts, macros) elif c.endswith('.cfg'): - self.config(config.file(c, bset.opts, bset.macros), - bset.opts, bset.macros) + self.config(config.file(c, bset.opts, macros), + tree[name]['cfg'], bset.opts, macros) else: raise error.general('invalid config type: %s' % (c)) self.buildset_end(name) self.bset_nesting -= 1 - def create(self, sysname, inname, outname = None, intro_text = None): + def create(self, inname, outname = None, intro_text = None): self.setup() self.introduction(inname, intro_text) self.generate(inname) - self.write(sysname, outname) + self.write(outname) def run(args): try: - sysname = 'rtems' optargs = { '--list-bsets': 'List available build sets', '--list-configs': 'List available configurations', '--format': 'Output format (text, html, asciidoc, ini)', @@ -510,7 +610,7 @@ def run(args): config = build.find_config(_config, configs) if config is None: raise error.general('config file not found: %s' % (inname)) - r.create(sysname, config, outname) + r.create(config, outname) del r else: raise error.general('invalid config type: %s' % (config)) diff --git a/source-builder/sb/sources.py b/source-builder/sb/sources.py index f4989db..1a40a73 100644 --- a/source-builder/sb/sources.py +++ b/source-builder/sb/sources.py @@ -32,9 +32,14 @@ def add(label, args, macros, error): error('%%%s requires at least 2 arguments' % (label)) _map = '%s-%s' % (label, args[0]) macros.create_map(_map) + index = 0 + while True: + key = '%s%d' % (label, index) + if key not in macros.map_keys(_map): + break + index += 1 macros.set_write_map(_map) - index = macros.map_num_keys(_map) - macros.define('%s%d' % (label, index), ' '.join(args[1:])) + macros.define(key, ' '.join(args[1:])) macros.unset_write_map() return None @@ -55,8 +60,14 @@ def setup(label, args, macros, error): args = _args(args) if len(args) < 2: error('%%%s requires at least 2 arguments: %s' % (label, ' '.join(args))) + ss = '%%setup %s %s' % (label, ' '.join(args)) _map = '%s-%s' % (label, args[0]) - return ['%%setup %s %s' % (label, ' '.join(args))] + if 'setup' in macros.map_keys(_map): + error('%%%s already setup source: %s' % (label, ' '.join(args))) + macros.set_write_map(_map) + macros.define('setup', ss) + macros.unset_write_map() + return [ss] def process(label, args, macros, error): if label != 'source' and label != 'patch': @@ -87,11 +98,11 @@ def hash(args, macros, error): macros.unset_write_map() return None -def get(type_, name, macros, error): - _map = '%s-%s' % (type_, name) +def get(label, name, macros, error): + _map = '%s-%s' % (label, name) keys = macros.map_keys(_map) if len(keys) == 0: - error('no %s set: %s (%s)' % (type_, name, _map)) + error('no %s set: %s (%s)' % (label, name, _map)) srcs = [] for s in keys: sm = macros.get(s, globals = False, maps = _map) @@ -106,11 +117,21 @@ def get_sources(name, macros, error): def get_patches(name, macros, error): return get('patch', name, macros, error) -def get_names(type_, macros, error): +def get_keys(label, name, macros, error): + _map = '%s-%s' % (label, name) + return macros.map_keys(_map) + +def get_source_keys(name, macros, error): + return get_keys('source', name, macros, error) + +def get_patch_keys(name, macros, error): + return get_keys('patch', name, macros, error) + +def get_names(label, macros, error): names = [] for m in macros.maps(): - if m.startswith('%s-' % (type_)): - names += [m[len('%s-' % (type_)):]] + if m.startswith('%s-' % (label)): + names += [m[len('%s-' % (label)):]] return names def get_source_names(macros, error): |