summaryrefslogblamecommitdiffstats
path: root/posix-compliance/posix_rst.py
blob: 17e4dd9842687fbbb5319049301cb3dc981411e4 (plain) (tree)



































































































                                                                              
                  




































































































































































                                                                                     
#! /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': ['',
               '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)