mirror of
https://git.rtems.org/rtems-docs/
synced 2025-10-18 08:21:31 +08:00
posix-compliance: Add automatic generation of the ReST file from CSV data.
Closes #3177.
This commit is contained in:

committed by
Joel Sherrill

parent
8e8094abc8
commit
2804294c6c
1
posix-compliance/posix-compliance.rst
Normal file
1
posix-compliance/posix-compliance.rst
Normal file
@@ -0,0 +1 @@
|
||||
.. include:: ../build/posix-compliance/generated-posix-compliance.rst
|
267
posix-compliance/posix_rst.py
Executable file
267
posix-compliance/posix_rst.py
Executable file
@@ -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)
|
@@ -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'])
|
||||
|
Reference in New Issue
Block a user