[rtems-docs commit] posix-compliance: Add automatic generation of the ReST file from CSV data.

Joel Sherrill joel at rtems.org
Fri Oct 13 01:26:07 UTC 2017


Module:    rtems-docs
Branch:    master
Commit:    2804294c6c1858553cff6f7b355fe8a5ba9a7259
Changeset: http://git.rtems.org/rtems-docs/commit/?id=2804294c6c1858553cff6f7b355fe8a5ba9a7259

Author:    Chris Johns <chrisj at rtems.org>
Date:      Thu Oct 12 17:47:23 2017 -0700

posix-compliance: Add automatic generation of the ReST file from CSV data.

Closes #3177.

---

 posix-compliance/posix-compliance.rst |   1 +
 posix-compliance/posix_rst.py         | 267 ++++++++++++++++++++++++++++++++++
 posix-compliance/wscript              |  24 ++-
 3 files changed, 291 insertions(+), 1 deletion(-)

diff --git a/posix-compliance/posix-compliance.rst b/posix-compliance/posix-compliance.rst
new file mode 100644
index 0000000..6189c26
--- /dev/null
+++ b/posix-compliance/posix-compliance.rst
@@ -0,0 +1 @@
+.. include:: ../build/posix-compliance/generated-posix-compliance.rst
diff --git a/posix-compliance/posix_rst.py b/posix-compliance/posix_rst.py
new file mode 100755
index 0000000..b7ad80c
--- /dev/null
+++ b/posix-compliance/posix_rst.py
@@ -0,0 +1,267 @@
+#! /usr/bin/env python
+#
+# Convert the CSV compliance data to ReST Format.
+#
+
+from __future__ import print_function
+
+import copy
+import csv
+import os
+import sys
+
+standards = [
+    'RTEMS',
+    'POSIX-2008',
+    'PSE51',
+    'PSE52',
+    'PSE53',
+    'PSE54',
+    'C99',
+    'FACE 2.1 Security',
+    'FACE 2.1 Safety Base',
+    'FACE 2.1 Safety Extended',
+    'FACE 2.1 General Purpose'
+]
+
+standard_names = {
+    'RTEMS'                   : 'RTEMS Complete Profile',
+    'POSIX-2008'              : 'POSIX-2008',
+    'PSE51'                   : 'POSIX PSE51 - Minimal',
+    'PSE52'                   : 'POSIX PSE52 - Real-Time Controller',
+    'PSE53'                   : 'POSIX PSE53 - Dedicated',
+    'PSE54'                   : 'POSIX PSE54 - Multipurpose',
+    'C99'                     : 'C99 Standard Library',
+    'FACE 2.1 Security'       : 'FACE 2.1 Security',
+    'FACE 2.1 Safety Base'    : 'FACE 2.1 Safety Base',
+    'FACE 2.1 Safety Extended': 'FACE 2.1 Safety Extended',
+    'FACE 2.1 General Purpose': 'FACE 2.1 General Purpose'
+}
+
+col_names = {
+    'api'                      : 'Methods',
+    'header'                   : 'Header File',
+    'rtems-net'                : 'RTEMS w/ Networking',
+    'rtems-impl'               : 'RTEMS Impl Note',
+    'POSIX-2008'               : 'IEEE Std 1003.1-2008',
+    'PSE51'                    : 'PSE51',
+    'PSE52'                    : 'PSE52',
+    'PSE53'                    : 'PSE53',
+    'PSE54'                    : 'PSE54',
+    'C99'                      : 'C99',
+    'FACE 2.1 Security'        : 'FACE 2.1 Security',
+    'FACE 2.1 Safety Base'     : 'FACE 2.1 Safety Base',
+    'FACE 2.1 Safety Extended' : 'FACE 2.1 Safety Extended',
+    'FACE 2.1 General Purpose' : 'FACE 2.1 General Purpose'
+}
+
+#
+# The columns here contain the logic to determine the
+#
+categories = {
+    'order': ['supported', 'enosys', 'not-supported'],
+    'name' : {
+        'supported'    : 'Supported',
+        'enosys'       : 'ENOSYS',
+        'not-supported': 'Not supported'
+    },
+    'supported': ['The following methods and variables in ``<@HEADER@>``',
+                  'are supported:',
+                  ''],
+    'not-supported': ['The following methods and variables in ``<@HEADER@>``',
+                      'are not supported:',
+                      ''],
+    'enosys': ['The following methods in ``<@HEADER@>`` are implemented as',
+               'stubs returning ``-1`` and setting ``errno`` to ``ENOSYS``:',
+               '']
+}
+
+cat_columns = {
+    'order': ['rtems-net', 'rtems-impl'],
+    'rtems-net': {
+        'supported' : {
+            'CTS-YES' : ['invalid'],
+            'RT-YES'  : ['invalid'],
+            'HAND-YES': ['invalid']
+        },
+        'not-supported': {
+            'CTS-NO' : ['invalid'],
+            'RT-NO'  : ['invalid'],
+            'HAND-NO': ['invalid']
+        }
+    },
+    'rtems-impl': {
+        'enosys': {
+            'ENOSYS': ['supported']
+        }
+    }
+}
+
+rst_defaults = {
+    'header': ['.. comment SPDX-License-Identifier: CC-BY-SA-4.0',
+               '',
+               'This chapter has a subsection per header file to detail the methods',
+               'provided by RTEMS that are in that header file.',
+               '']
+}
+
+class error(Exception):
+    pass
+
+class compliance:
+    def __init__(self):
+        self.data = None
+
+    def load(self, name):
+        with open(name, 'rb') as f:
+            data = csv.reader(f, delimiter = ',', quotechar = '"')
+            hdr = None
+            rows = []
+            for row in data:
+                if hdr is None:
+                    hdr = row
+                else:
+                    rows += [row]
+        for col in col_names:
+            if col_names[col] not in hdr:
+                raise error('column not found: %s' % (col_names[col]))
+        cdata = { 'columns': hdr, 'headers': {}, 'apis': {} }
+        apic = hdr.index(col_names['api'])
+        hfc = hdr.index(col_names['header'])
+        for row in rows:
+            api = row[apic]
+            header = row[hfc]
+            if len(api) == 0 or len(header) == 0:
+                continue
+            if header not in cdata['headers']:
+                cdata['headers'][header] = [api]
+            else:
+                cdata['headers'][header] += [api]
+            if api in cdata['apis']:
+                raise error('duplicate api: %s' % (api))
+            cdata['apis'][api] = row
+        self.data = cdata
+
+    def summary(self, standard = 'RTEMS'):
+        results = { }
+        for header in self.data['headers']:
+            hr = self.process_header(header, standard)
+            if 'invalid' in hr:
+                error('header contains "invalid": %s' % (header))
+            for cat in hr:
+                if cat not in results:
+                    results[cat] = len(hr[cat])
+                else:
+                    results[cat] += len(hr[cat])
+        if standard == 'RTEMS':
+            std_line = 'The follow table summarizes RTEMS supported' \
+                       ' methods for all tracked standards:'
+        else:
+            std_line = 'The follow table summarizes alignment with ' \
+                       'the %s standard:' % (standard_names[standard])
+        s = ['Summary',
+             '=======',
+             '',
+             std_line,
+             '']
+        cols = [0, 1]
+        for cat in categories['order']:
+            if len(categories['name'][cat]) > cols[0]:
+                cols[0] = len(categories['name'][cat])
+            if cat in results:
+                num = '%d' % results[cat]
+                if len(num) > cols[1]:
+                    cols[1] = len(num)
+        table_def = ' %s  %s' % ('=' * cols[0], '=' * cols[1])
+        s += [table_def]
+        for cat in categories['order']:
+            if cat in results:
+                s += [' %-*s  %d' % (cols[0], categories['name'][cat], results[cat])]
+            else:
+                s += [' %-*s  %d' % (cols[0], categories['name'][cat], 0)]
+        s += [table_def, '']
+        return s
+
+    def output(self, standard = 'RTEMS'):
+        def _category_filter(text, patterns):
+            for l in range(0, len(text)):
+                for pat in patterns:
+                    if pat in text[l]:
+                        text[l] = text[l].replace(pat, patterns[pat])
+            return text
+
+        if standard not in standards:
+            error('invalid standard": %s' % (standard))
+        s = rst_defaults['header'] + self.summary(standard)
+        for header in sorted(self.data['headers'].keys()):
+            hr = self.process_header(header, standard)
+            if 'invalid' in hr:
+                error('header contains "invalid": %s' % (header))
+            print_heading = True
+            for cat in categories['order']:
+                if cat in hr:
+                    if print_heading:
+                        s += ['``<%s>``' % (header),
+                              '=' * (len(header) + 2),
+                              '']
+                        print_heading = False
+                    patterns = { '@HEADER@': header }
+                    cat_text = copy.copy(categories[cat])
+                    _category_filter(cat_text, patterns)
+                    s += cat_text
+                    for api in hr[cat]:
+                        s += ['* ``%s``' % (api)]
+                    s += ['']
+        return s
+
+    def process_header(self, header, standard = 'RTEMS'):
+        results = { }
+        if standard != 'RTEMS':
+            std_col = self.data['columns'].index(col_names[standard])
+        else:
+            std_col = -1
+        for api in sorted(self.data['headers'][header]):
+            api_row = self.data['apis'][api]
+            if std_col > 0:
+                if api_row[std_col] != 'INCL':
+                    continue
+            state = 'invalid'
+            for test in cat_columns['order']:
+                col = self.data['columns'].index(col_names[test])
+                value = api_row[col]
+                for test_state in cat_columns[test]:
+                    if value in cat_columns[test][test_state]:
+                        if state in cat_columns[test][test_state][value]:
+                            state = test_state
+            if state not in results:
+                results[state] = [api]
+            else:
+                results[state] += [api]
+        return results
+
+if __name__ == "__main__":
+    try:
+        import pprint
+        pp = pprint.PrettyPrinter(indent=2)
+        if len(sys.argv) != 2:
+            raise error('not enough arguments')
+        c = compliance()
+        c.load(sys.argv[1])
+        for h in sorted(c.data['headers']):
+            print('-- %s' % (h), '-' * 50)
+            hr = c.process_header(h)
+            if 'invalid' in hr:
+                error('header contains invalid: %s' % (h))
+            hr = c.process_header(h, 'PSE51')
+            if 'invalid' in hr:
+                error('header contains invalid: %s' % (h))
+            pp.pprint(hr)
+        print('=' * 80)
+        print(os.linesep.join(c.output('PSE51')))
+        print('=' * 80)
+        print(os.linesep.join(c.output()))
+        for s in standards:
+            print('=-' * 40)
+            print(os.linesep.join(c.summary(s)))
+    except error as e:
+        print('error: %s' % (e), file = sys.stderr)
diff --git a/posix-compliance/wscript b/posix-compliance/wscript
index 26ab0ca..080a7b1 100644
--- a/posix-compliance/wscript
+++ b/posix-compliance/wscript
@@ -3,9 +3,31 @@ from os.path import abspath
 path.append(abspath('../common/'))
 
 from waf import cmd_configure as configure
-from waf import cmd_build as build
+from waf import cmd_build as doc_build
 from waf import cmd_options as options
 from waf import spell
 from waf import cmd_spell
 from waf import linkcheck
 from waf import cmd_linkcheck
+
+import posix_rst
+
+def gen_posix_rst(task):
+    c = posix_rst.compliance()
+    c.load(task.inputs[1].abspath())
+    s = ['']
+    for standard in posix_rst.standards:
+        s += ['',
+              posix_rst.standard_names[standard],
+              '*' * len(posix_rst.standard_names[standard]),
+               ''] + c.output(standard)
+    with open(task.outputs[0].abspath(), 'w') as w:
+        from os import linesep
+        w.write(linesep.join(s))
+
+def build(ctx):
+    ctx(rule = gen_posix_rst,
+        source = ['posix_rst.py', 'RTEMS-Standards-Compliance-v1.csv'],
+        target = 'generated-posix-compliance.rst')
+    ctx.add_group()
+    doc_build(ctx, extra_source = ['generated-posix-compliance.rst'])



More information about the vc mailing list