Added CI job to check for likely mame.lst errors.

This commit is contained in:
Vas Crabb 2022-07-02 04:57:25 +10:00
parent 4a5196149b
commit 6176c84768
2 changed files with 143 additions and 0 deletions

View file

@ -55,6 +55,8 @@ jobs:
run: make -j2 run: make -j2
- name: Validate - name: Validate
run: ./mame -validate run: ./mame -validate
- name: Reconcile driver list
run: ./mame -listxml | python scripts/build/makedep.py reconcilelist -l src/mame/mame.lst -
- name: ORM check - name: ORM check
run: python scripts/minimaws/minimaws.py load --executable ./mame --softwarepath hash run: python scripts/minimaws/minimaws.py load --executable ./mame --softwarepath hash
- uses: actions/upload-artifact@master - uses: actions/upload-artifact@master

View file

@ -8,6 +8,7 @@ import glob
import io import io
import os.path import os.path
import sys import sys
import xml.sax
class ParserBase: class ParserBase:
@ -581,6 +582,128 @@ class DriverCollector(DriverFilter):
self.sources = sorted(sources) self.sources = sorted(sources)
class DriverReconciler(DriverFilter):
class InfoHandler:
def __init__(self, drivers, **kwargs):
super().__init__(**kwargs)
self.drivers = drivers
self.bad = False
self.locator = None
self.ignored_depth = 0
self.in_document = False
self.in_mame = False
def startElement(self, name, attrs):
if not self.in_document:
raise xml.sax.SAXParseException('Unexpected start of element "%s"' % (name, ), None, self.locator)
elif self.ignored_depth > 0:
self.ignored_depth += 1
elif not self.in_mame:
if name != 'mame':
raise xml.sax.SAXParseException('Unexpected start of element "%s"' % (name, ), None, self.locator)
self.in_mame = True
elif name != 'machine':
raise xml.sax.SAXParseException('Unexpected start of element "%s"' % (name, ), None, self.locator)
else:
runnable = attrs.get('runnable', 'yes')
if runnable == 'yes':
shortname = attrs['name']
source = attrs['sourcefile'].replace('\\', '/')
declared = self.drivers.get(shortname)
if declared is None:
sys.stderr.write('Driver "%s" not declared in list file\n' % (shortname, ))
self.bad = True
elif declared != source:
sys.stderr.write('Driver "%s" found for source file "%s" but defined in source file "%s"\n' % (shortname, declared, source))
self.bad = True
self.ignored_depth = 1
def endElement(self, name):
if self.ignored_depth > 0:
self.ignored_depth -= 1
elif self.in_mame:
if name != 'mame':
raise xml.sax.SAXParseException('Unexpected end of element "%s"' % (name, ), None, self.locator)
self.in_mame = False
else:
raise xml.sax.SAXParseException('Unexpected end of element "%s"' % (name, ), None, self.locator)
def startDocument(self):
if self.in_document:
raise xml.sax.SAXParseException('Unexpected start of document', None, self.locator)
self.in_document = True
def endDocument(self):
if not self.in_document:
raise xml.sax.SAXParseException('Unexpected end of document', None, self.locator)
self.in_document = False
def setDocumentLocator(self, locator):
self.locator = locator
def startPrefixMapping(self, prefix, uri):
pass
def endPrefixMapping(self, prefix):
pass
def characters(self, content):
pass
def ignorableWhitespace(self, whitespace):
pass
def processingInstruction(self, target, data):
pass
def __init__(self, options, **kwargs):
super().__init__(**kwargs)
def sourcefile(filename):
if (state['prevsource'] is not None) and (not state['prevdrivers']):
sys.stderr.write('No drivers for source file "%s"\n' % (state['prevsource'], ))
self.bad = True
if filename in self.sources:
sys.stderr.write('Duplicate source file "%s"\n' % (filename, ))
state['prevdrivers'] = self.sources[filename]
self.bad = True
else:
drivers = set()
state['prevdrivers'] = drivers
self.sources[filename] = drivers
state['prevsource'] = filename
def driver(shortname):
if shortname in self.drivers:
sys.stderr.write('Duplicate driver "%s" for source file "%s" (previously seen for source file "%s")\n' % (shortname, state['prevsource'], self.drivers[shortname]))
self.bad = True
else:
self.drivers[shortname] = state['prevsource']
drivers = state['prevdrivers']
if drivers is None:
sys.stderr.write('Driver "%s" found outside source file section\n' % (shortname, ))
self.bad = True
else:
drivers.add(shortname)
state = { 'prevsource': None, 'prevdrivers': None }
self.bad = False
self.sources = { }
self.drivers = { }
self.parse_list(options.list, sourcefile, driver)
def reconcile_xml(self, xmlfile):
handler = self.InfoHandler(self.drivers)
try:
xml.sax.parse(xmlfile, handler=handler)
if handler.bad:
self.bad = True
except xml.sax.SAXException as err:
sys.stderr.write('Error parsing system information file: %s\n' % (err, ))
self.bad = True
def split_path(path): def split_path(path):
path = os.path.normpath(path) path = os.path.normpath(path)
result = [ ] result = [ ]
@ -619,6 +742,10 @@ def parse_command_line():
subparser.add_argument('-f', '--filter', metavar='<fltfile>', help='input filter file') subparser.add_argument('-f', '--filter', metavar='<fltfile>', help='input filter file')
subparser.add_argument('list', metavar='<lstfile>', help='input list file') subparser.add_argument('list', metavar='<lstfile>', help='input list file')
subparser = subparsers.add_parser('reconcilelist', help='reconcile driver list')
subparser.add_argument('-l', '--list', metavar='<lstfile>', required=True, help='master driver list file')
subparser.add_argument('infoxml', metavar='<xmlfile>', nargs='?', help='XML system information file')
return parser.parse_args() return parser.parse_args()
@ -905,3 +1032,17 @@ if __name__ == '__main__':
write_filter(options, sys.stdout) write_filter(options, sys.stdout)
elif options.command == 'driverlist': elif options.command == 'driverlist':
DriverLister(options).write_source(sys.stdout) DriverLister(options).write_source(sys.stdout)
elif options.command == 'reconcilelist':
reconciler = DriverReconciler(options)
if options.infoxml == '-':
reconciler.reconcile_xml(sys.stdin)
elif options.infoxml is not None:
try:
xmlfile = io.open(options.infoxml, 'rb')
with xmlfile:
reconciler.reconcile_xml(xmlfile)
except IOError:
sys.stderr.write('Unable to open system information file "%s"\n' % (options.infoxml, ))
sys.exit(1)
if reconciler.bad:
sys.exit(1)