diff options
Diffstat (limited to 'scripts/build/makedep.py')
-rwxr-xr-x | scripts/build/makedep.py | 727 |
1 files changed, 523 insertions, 204 deletions
diff --git a/scripts/build/makedep.py b/scripts/build/makedep.py index 6867a6cb34a..860c11a890f 100755 --- a/scripts/build/makedep.py +++ b/scripts/build/makedep.py @@ -4,16 +4,29 @@ ## copyright-holders:Vas Crabb import argparse +import glob import io import os.path import sys +import xml.sax -class ParserBase(object): - def process_lines(self, f): +def path_components(path): + result = [ ] + while True: + path, basename = os.path.split(path) + if basename: + result.append(basename) + else: + if path: + result.append(path) + return tuple(reversed(result)) + + +class ParserBase: + def process_lines(self, inputfile): self.input_line = 1 - for line in f: - pos = 0 + for line in inputfile: start = 0 if line.endswith('\n'): line = line[:-1] @@ -39,7 +52,7 @@ class CppParser(ParserBase): [chr(x) for x in range(ord('A'), ord('F') + 1)] + [chr(x) for x in range(ord('a'), ord('f') + 1)]) - class Handler(object): + class Handler: def line(self, text): pass @@ -49,7 +62,7 @@ class CppParser(ParserBase): def line_comment(self, text): pass - class ParseState(object): + class ParseState: DEFAULT = 0 COMMENT = 1 LINE_COMMENT = 2 @@ -59,7 +72,7 @@ class CppParser(ParserBase): NUMERIC_CONSTANT = 6 def __init__(self, handler, **kwargs): - super(CppParser, self).__init__(**kwargs) + super().__init__(**kwargs) self.handler = handler self.processors = { self.ParseState.DEFAULT: self.process_default, @@ -70,14 +83,14 @@ class CppParser(ParserBase): self.ParseState.CHARACTER_CONSTANT: self.process_text, self.ParseState.NUMERIC_CONSTANT: self.process_numeric } - def parse(self, f): + def parse(self, inputfile): self.parse_state = self.ParseState.DEFAULT self.comment_line = None self.lead_digit = None self.radix = None self.line_buffer = '' self.comment_buffer = '' - self.process_lines(f) + self.process_lines(inputfile) if self.parse_state == self.ParseState.COMMENT: raise Exception('unterminated multi-line comment beginning on line %d' % (self.comment_line, )) elif self.parse_state == self.ParseState.CHARACTER_CONSTANT: @@ -238,7 +251,7 @@ class CppParser(ParserBase): class LuaParser(ParserBase): - class Handler(object): + class Handler: def short_comment(self, text): pass @@ -251,7 +264,7 @@ class LuaParser(ParserBase): def long_comment_end(self): pass - class ParseState(object): + class ParseState: DEFAULT = 0 SHORT_COMMENT = 1 LONG_COMMENT = 2 @@ -259,7 +272,7 @@ class LuaParser(ParserBase): LONG_STRING_CONSTANT = 4 def __init__(self, handler, **kwargs): - super(LuaParser, self).__init__(**kwargs) + super().__init__(**kwargs) self.handler = handler self.processors = { self.ParseState.DEFAULT: self.process_default, @@ -268,20 +281,20 @@ class LuaParser(ParserBase): self.ParseState.STRING_CONSTANT: self.process_string_constant, self.ParseState.LONG_STRING_CONSTANT: self.process_long_string_constant } - def parse(self, f): + def parse(self, inputfile): self.parse_state = self.ParseState.DEFAULT self.long_bracket_level = None self.escape = False self.block_line = None self.block_level = None self.string_quote = None - self.process_lines(f) + self.process_lines(inputfile) if self.parse_state == self.ParseState.LONG_COMMENT: - raise Exception('unterminated long comment beginning on line %d' % (self.block_line, )); - elif self.parse_state == self.ParseState.STRING_CONSTANT: - raise Exception('unterminated string literal on line %d' % (self.input_line, )); - elif self.parse_state == self.ParseState.LONG_STRING_CONSTANT: - raise Exception('unterminated long string literal beginning on line %d' % (self.block_line, )); + raise Exception('unterminated long comment beginning on line %d' % (self.block_line, )) + if self.parse_state == self.ParseState.STRING_CONSTANT: + raise Exception('unterminated string literal on line %d' % (self.input_line, )) + if self.parse_state == self.ParseState.LONG_STRING_CONSTANT: + raise Exception('unterminated long string literal beginning on line %d' % (self.block_line, )) def process_default(self, line): pos = 0 @@ -293,7 +306,7 @@ class LuaParser(ParserBase): self.parse_state = self.ParseState.STRING_CONSTANT self.long_bracket_level = None self.escape = False - return pos + 1; + return pos + 1 elif (ch == '-') and self.escape: self.parse_state = self.ParseState.SHORT_COMMENT self.long_bracket_level = None @@ -381,22 +394,156 @@ class LuaParser(ParserBase): self.escape = (ch == '\\') and not self.escape pos += 1 if not self.escape: - raise Exception('unterminated string literal on line %d' % (self.input_line, )); + raise Exception('unterminated string literal on line %d' % (self.input_line, )) def process_long_string_constant(self, line): self.process_long_comment(line) # this works because they're both closed by a matching long bracket -class DriverFilter(object): +class DriverFilter: DRIVER_CHARS = frozenset( [chr(x) for x in range(ord('0'), ord('9') + 1)] + [chr(x) for x in range(ord('a'), ord('z') + 1)] + ['_']) + def parse_filter(self, root, path, sourcefile, inclusion, exclusion): + def line_hook(text): + text = text.strip() + if text.startswith('#'): + do_parse(os.path.join(os.path.dirname(n), text[1:].lstrip())) + elif text.startswith('+'): + text = text[1:].lstrip() + if not text: + sys.stderr.write('%s:%s: Empty driver name\n' % (path, parser.input_line)) + sys.exit(1) + elif not all(x in self.DRIVER_CHARS for x in text): + sys.stderr.write('%s:%s: Invalid character in driver name "%s"\n' % (path, parser.input_line, text)) + sys.exit(1) + inclusion(text) + elif text.startswith('-'): + text = text[1:].lstrip() + if not text: + sys.stderr.write('%s:%s: Empty driver name\n' % (path, parser.input_line)) + sys.exit(1) + elif not all(x in self.DRIVER_CHARS for x in text): + sys.stderr.write('%s:%s: Invalid character in driver name "%s"\n' % (path, parser.input_line, text)) + sys.exit(1) + exclusion(text) + elif text: + if (len(text) >= 2) and ((text[0] == '"') or (text[0] == "'")) and (text[0] == text[-1]): + text = text[1:-1] + paths = glob.glob(os.path.join(basepath, *text.split('/'))) + if not paths: + sys.stderr.write('%s:%s: Pattern "%s" did not match any source files\n' % (path, parser.input_line, text)) + sys.exit(1) + for source in paths: + sourcefile('/'.join(path_components(os.path.relpath(source, basepath)))) + + try: + filterfile = io.open(path, 'r', encoding='utf-8') + except IOError: + sys.stderr.write('Unable to open filter file "%s"\n' % (path, )) + sys.exit(1) + with filterfile: + basepath = os.path.join(root, 'src', 'mame') + handler = CppParser.Handler() + handler.line = line_hook + parser = CppParser(handler) + try: + parser.parse(filterfile) + except IOError: + sys.stderr.write('Error reading filter file "%s"\n' % (path, )) + sys.exit(1) + except Exception as e: + sys.stderr.write('Error parsing filter file "%s": %s\n' % (path, e)) + sys.exit(1) + + def parse_list(self, path, sourcefile, driver): + def line_hook(text): + text = text.strip() + if text.startswith('#'): + do_parse(os.path.join(os.path.dirname(n), text[1:].lstrip())) + elif text.startswith('@'): + parts = text[1:].lstrip().split(':', 1) + parts[0] = parts[0].strip() + if (parts[0] == 'source') and (len(parts) == 2): + parts[1] = parts[1].strip() + if not parts[1]: + sys.stderr.write('%s:%s: Empty source file name "%s"\n' % (path, parser.input_line, text)) + sys.exit(1) + else: + sourcefile(parts[1]) + else: + sys.stderr.write('%s:%s: Unsupported directive "%s"\n' % (path, parser.input_line, text)) + sys.exit(1) + elif text: + if not all(x in self.DRIVER_CHARS for x in text): + sys.stderr.write('%s:%s: Invalid character in driver name "%s"\n' % (path, parser.input_line, text)) + sys.exit(1) + else: + driver(text) + + try: + listfile = io.open(path, 'r', encoding='utf-8') + except IOError: + sys.stderr.write('Unable to open list file "%s"\n' % (path, )) + sys.exit(1) + with listfile: + handler = CppParser.Handler() + handler.line = line_hook + parser = CppParser(handler) + try: + parser.parse(listfile) + except IOError: + sys.stderr.write('Error reading list file "%s"\n' % (path, )) + sys.exit(1) + except Exception as e: + sys.stderr.write('Error parsing list file "%s": %s\n' % (path, e)) + sys.exit(1) + + +class DriverLister(DriverFilter): def __init__(self, options, **kwargs): - super(DriverFilter, self).__init__(**kwargs) - self.parse_filter(options.filter) - self.parse_list(options.list) + super().__init__(**kwargs) + + def includesource(filename): + sources.add(filename) + + def includedriver(shortname): + includes.add(shortname) + excludes.discard(shortname) + + def excludedriver(shortname): + includes.discard(shortname) + excludes.add(shortname) + + def sourcefile(filename): + if self.sources: + state['includesrc'] = filename in self.sources + + def driver(shortname): + if state['includesrc'] and (shortname not in self.excludes): + drivers.add(shortname) + + sources = set() + includes = set() + excludes = set() + if options.filter is not None: + self.parse_filter(options.root, options.filter, includesource, includedriver, excludedriver) + sys.stderr.write('%d source file(s) found\n' % (len(sources), )) + self.sources = frozenset(sources) + self.includes = frozenset(includes) + self.excludes = frozenset(excludes) + + drivers = set() + state = { 'includesrc': True } + self.parse_list(options.list, sourcefile, driver) + + for driver in self.includes: + drivers.add(driver) + sys.stderr.write('%d driver(s) found\n' % (len(drivers), )) + drivers.add('___empty') + self.drivers = sorted(drivers) def write_source(self, f): f.write( @@ -417,124 +564,156 @@ class DriverFilter(object): '\n' \ 'std::size_t const driver_list::s_driver_count = %d;\n' % (len(self.drivers), )) - def parse_filter(self, path): - def do_parse(p): - def line_hook(text): - text = text.strip() - if text.startswith('#'): - do_parse(os.path.join(os.path.dirname(n), text[1:].lstrip())) - elif text.startswith('+'): - text = text[1:].lstrip() - if not text: - sys.stderr.write('%s:%s: Empty driver name\n' % (p, parser.input_line, text)) - sys.exit(1) - elif not all(x in self.DRIVER_CHARS for x in text): - sys.stderr.write('%s:%s: Invalid character in driver name "%s"\n' % (p, parser.input_line, text)) - sys.exit(1) - includes.add(text) - excludes.discard(text) - elif text.startswith('-'): - text = text[1:].lstrip() - if not text: - sys.stderr.write('%s:%s: Empty driver name\n' % (p, parser.input_line, text)) - sys.exit(1) - elif not all(x in self.DRIVER_CHARS for x in text): - sys.stderr.write('%s:%s: Invalid character in driver name "%s"\n' % (p, parser.input_line, text)) - sys.exit(1) - includes.discard(text) - excludes.add(text) - elif text: - sources.add(text) - - n = os.path.normpath(p) - if n not in filters: - filters.add(n) - try: - f = io.open(n, 'r', encoding='utf-8') - except IOError: - sys.stderr.write('Unable to open filter file "%s"\n' % (p, )) - sys.exit(1) - with f: - handler = CppParser.Handler() - handler.line = line_hook - parser = CppParser(handler) - try: - parser.parse(f) - except IOError: - sys.stderr.write('Error reading filter file "%s"\n' % (p, )) - sys.exit(1) - except Exception as e: - sys.stderr.write('Error parsing filter file "%s": %s\n' % (p, e)) - sys.exit(1) + +class DriverCollector(DriverFilter): + def __init__(self, options, **kwargs): + super().__init__(**kwargs) + + def includesource(filename): + sources.add(filename) + + def includedriver(shortname): + includes.add(shortname) + + def excludedriver(shortname): + includes.discard(shortname) + + def sourcefile(filename): + state['prevsource'] = filename + + def driver(shortname): + if shortname in includes: + sources.add(state['prevsource']) sources = set() includes = set() - excludes = set() - filters = set() - if path is not None: - do_parse(path) - sys.stderr.write('%d source file(s) found\n' % (len(sources), )) - self.sources = frozenset(sources) - self.includes = frozenset(includes) - self.excludes = frozenset(excludes) + state = { 'prevsource': None } + self.parse_filter(options.root, options.filter, includesource, includedriver, excludedriver) + self.parse_list(options.list, sourcefile, driver) + sys.stderr.write('%d source file(s) found\n' % (len(sources), )) + self.sources = sorted(sources) - def parse_list(self, path): - def do_parse(p): - def line_hook(text): - text = text.strip() - if text.startswith('#'): - do_parse(os.path.join(os.path.dirname(n), text[1:].lstrip())) - elif text.startswith('@'): - parts = text[1:].lstrip().split(':', 1) - parts[0] = parts[0].strip() - if (parts[0] == 'source') and (len(parts) == 2): - parts[1] = parts[1].strip() - if not parts[1]: - sys.stderr.write('%s:%s: Empty source file name "%s"\n' % (p, parser.input_line, text)) - sys.exit(1) - elif self.sources: - state['includesrc'] = parts[1] in self.sources - else: - sys.stderr.write('%s:%s: Unsupported directive "%s"\n' % (p, parser.input_line, text)) - sys.exit(1) - elif text: - if not all(x in self.DRIVER_CHARS for x in text): - sys.stderr.write('%s:%s: Invalid character in driver name "%s"\n' % (p, parser.input_line, text)) - sys.exit(1) - elif state['includesrc'] and (text not in self.excludes): - drivers.add(text) - - n = os.path.normpath(p) - if n not in lists: - lists.add(n) - try: - f = io.open(n, 'r', encoding='utf-8') - except IOError: - sys.stderr.write('Unable to open list file "%s"\n' % (p, )) - sys.exit(1) - with f: - handler = CppParser.Handler() - handler.line = line_hook - parser = CppParser(handler) - try: - parser.parse(f) - except IOError: - sys.stderr.write('Error reading list file "%s"\n' % (p, )) - sys.exit(1) - except Exception as e: - sys.stderr.write('Error parsing list file "%s": %s\n' % (p, e)) - sys.exit(1) - lists = set() - drivers = set() - state = object() - state = { 'includesrc': True } - do_parse(path) - for driver in self.includes: - drivers.add(driver) - sys.stderr.write('%d driver(s) found\n' % (len(drivers), )) - drivers.add('___empty') - self.drivers = sorted(drivers) +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): @@ -555,20 +734,31 @@ def split_path(path): def parse_command_line(): parser = argparse.ArgumentParser() + parser.add_argument('-r', '--root', metavar='<srcroot>', default='.', help='path to emulator source root (defaults to working directory)') subparsers = parser.add_subparsers(title='commands', dest='command', metavar='<command>') subparser = subparsers.add_parser('sourcesproject', help='generate project directives for source files') - subparser.add_argument('-r', '--root', metavar='<srcroot>', default='.', help='path to emulator source root (defaults to working directory)') subparser.add_argument('-t', '--target', metavar='<target>', required=True, help='generated emulator target name') + subparser.add_argument('-l', '--list', metavar='<lstfile>', required=True, help='master driver list file') subparser.add_argument('sources', metavar='<srcfile>', nargs='+', help='source files to include') + subparser = subparsers.add_parser('filterproject', help='generate project directives using filter file') + subparser.add_argument('-t', '--target', metavar='<target>', required=True, help='generated emulator target name') + subparser.add_argument('-f', '--filter', metavar='<fltfile>', required=True, help='input filter file') + subparser.add_argument('list', metavar='<lstfile>', help='input list file') + subparser = subparsers.add_parser('sourcesfilter', help='generate driver filter for source files') + subparser.add_argument('-l', '--list', metavar='<lstfile>', required=True, help='master driver list file') subparser.add_argument('sources', metavar='<srcfile>', nargs='+', help='source files to include') subparser = subparsers.add_parser('driverlist', help='generate driver list source') subparser.add_argument('-f', '--filter', metavar='<fltfile>', help='input filter 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() @@ -604,7 +794,7 @@ def collect_lua_directives(options): return result -def scan_source_dependencies(options): +def scan_source_dependencies(root, sources): def locate_include(path): split = [ ] forward = 0 @@ -631,13 +821,13 @@ def scan_source_dependencies(options): else: components = incdir[:-depth] + split[depth:] depth = forward - 1 - if os.path.isfile(os.path.join(options.root, *components)): + if os.path.isfile(os.path.join(root, *components)): return components, depth return None, 0 def test_siblings(relative, basename, depth): pathbase = '/'.join(relative) + '/' - dirname = os.path.join(options.root, *relative) + dirname = os.path.join(root, *relative) for ext in ('.cpp', '.ipp', '.hxx'): path = pathbase + basename + ext if (path not in seen) and os.path.isfile(os.path.join(dirname, basename + ext)): @@ -663,22 +853,22 @@ def scan_source_dependencies(options): if ext.lower().startswith('.h'): components = components[:-1] test_siblings(components, base, depth) - if components == ('src', 'mame', 'includes'): - for aspect in ('audio', 'drivers', 'video', 'machine'): - test_siblings(('src', 'mame', aspect), base, depth) + if components[:2] == ('src', 'mame'): + for aspect in ('_a', '_v', '_m'): + test_siblings(components, base + aspect, depth) handler = CppParser.Handler() handler.line = line_hook parser = CppParser(handler) - seen = set('/'.join(x for x in split_path(source) if x) for source in options.sources) + seen = set('/'.join(x for x in split_path(source) if x) for source in sources) remaining = list([(x, 0) for x in seen]) - default_roots = ((('src', 'devices'), 0), (('src', 'mame'), 0), (('src', 'lib'), 0)) + default_roots = ((('src', 'devices'), 0), (('src', 'mame', 'shared'), 0), (('src', 'lib'), 0)) while remaining: source, depth = remaining.pop() components = tuple(source.split('/')) roots = ((components[:-1], depth), ) + default_roots try: - f = io.open(os.path.join(options.root, *components), 'r', encoding='utf-8') + f = io.open(os.path.join(root, *components), 'r', encoding='utf-8') except IOError: sys.stderr.write('Unable to open source file "%s"\n' % (source, )) sys.exit(1) @@ -694,71 +884,200 @@ def scan_source_dependencies(options): return seen -def write_project(options, f, mappings, sources): - targetsrc = '' - for source in sorted(sources): - action = mappings.get(source) - if action: - for line in action: - f.write(line + '\n') - if source.startswith('src/mame/'): - targetsrc += ' MAME_DIR .. "%s",\n' % (source, ) - f.write( - '\n' \ - 'function createProjects_mame_%s(_target, _subtarget)\n' \ - ' project ("mame_%s")\n' \ - ' targetsubdir(_target .."_" .. _subtarget)\n' \ - ' kind (LIBTYPE)\n' \ - ' uuid (os.uuid("drv-mame-%s"))\n' \ - ' addprojectflags()\n' \ - ' \n' \ - ' includedirs {\n' \ - ' MAME_DIR .. "src/osd",\n' \ - ' MAME_DIR .. "src/emu",\n' \ - ' MAME_DIR .. "src/devices",\n' \ - ' MAME_DIR .. "src/mame",\n' \ - ' MAME_DIR .. "src/lib",\n' \ - ' MAME_DIR .. "src/lib/util",\n' \ - ' MAME_DIR .. "src/lib/netlist",\n' \ - ' MAME_DIR .. "3rdparty",\n' \ - ' GEN_DIR .. "mame/layout",\n' \ - ' ext_includedir("flac"),\n' \ - ' ext_includedir("glm"),\n' \ - ' ext_includedir("jpeg"),\n' \ - ' ext_includedir("rapidjson"),\n' \ - ' ext_includedir("zlib"),\n' \ - ' }\n' \ - '\n' \ - ' files{\n%s' \ - ' }\n' \ - 'end\n' \ - '\n' \ - 'function linkProjects_mame_%s(_target, _subtarget)\n' \ - ' links {\n' \ - ' "mame_%s",\n' \ - ' }\n' \ - 'end\n' % (options.target, options.target, options.target, targetsrc, options.target, options.target)) - - -def write_filter(options, f): +def write_project(options, projectfile, mappings, sources, single): + if single: + targetsrc = '' + for source in sorted(sources): + action = mappings.get(source) + if action: + for line in action: + projectfile.write(line + '\n') + if source.startswith('src/mame/'): + targetsrc += ' MAME_DIR .. "%s",\n' % (source, ) + projectfile.write( + '\n' \ + 'function createProjects_mame_%s(_target, _subtarget)\n' \ + ' project ("mame_%s")\n' \ + ' targetsubdir(_target .."_" .. _subtarget)\n' \ + ' kind (LIBTYPE)\n' \ + ' uuid (os.uuid("drv-mame-%s"))\n' \ + ' addprojectflags()\n' \ + ' \n' \ + ' includedirs {\n' \ + ' MAME_DIR .. "src/osd",\n' \ + ' MAME_DIR .. "src/emu",\n' \ + ' MAME_DIR .. "src/devices",\n' \ + ' MAME_DIR .. "src/mame/shared",\n' \ + ' MAME_DIR .. "src/lib",\n' \ + ' MAME_DIR .. "src/lib/util",\n' \ + ' MAME_DIR .. "src/lib/netlist",\n' \ + ' MAME_DIR .. "3rdparty",\n' \ + ' GEN_DIR .. "mame/layout",\n' \ + ' ext_includedir("asio"),\n' \ + ' ext_includedir("flac"),\n' \ + ' ext_includedir("glm"),\n' \ + ' ext_includedir("jpeg"),\n' \ + ' ext_includedir("rapidjson"),\n' \ + ' ext_includedir("zlib"),\n' \ + ' }\n' \ + '\n' \ + ' files{\n%s' \ + ' }\n' \ + 'end\n' \ + '\n' \ + 'function linkProjects_mame_%s(_target, _subtarget)\n' \ + ' links {\n' \ + ' "mame_%s",\n' \ + ' }\n' \ + 'end\n' % (options.target, options.target, options.target, targetsrc, options.target, options.target)) + else: + libraries = { } + for source in sorted(sources): + components = source.split('/') + if (len(components) > 3) and (components[:2] == ['src', 'mame']): + line = ' MAME_DIR .. "%s",\n' % (source, ) + liblines = libraries.get(components[2]) + if liblines is not None: + liblines.append(line) + else: + libraries[components[2]] = [line] + action = mappings.get(source) + if action: + for line in action: + projectfile.write(line + '\n') + libnames = sorted(libraries.keys()) + projectfile.write( + '\n' \ + 'function createMAMEProjects(_target, _subtarget, _name)\n' \ + ' project (_name)\n' \ + ' targetsubdir(_target .."_" .. _subtarget)\n' \ + ' kind (LIBTYPE)\n' \ + ' uuid (os.uuid("drv-" .. _target .. "_" .. _subtarget .. "-" .. _name))\n' \ + ' addprojectflags()\n' \ + ' \n' \ + ' includedirs {\n' \ + ' MAME_DIR .. "src/osd",\n' \ + ' MAME_DIR .. "src/emu",\n' \ + ' MAME_DIR .. "src/devices",\n' \ + ' MAME_DIR .. "src/mame/shared",\n' \ + ' MAME_DIR .. "src/lib",\n' \ + ' MAME_DIR .. "src/lib/util",\n' \ + ' MAME_DIR .. "src/lib/netlist",\n' \ + ' MAME_DIR .. "3rdparty",\n' \ + ' GEN_DIR .. "mame/layout",\n' \ + ' ext_includedir("asio"),\n' \ + ' ext_includedir("flac"),\n' \ + ' ext_includedir("glm"),\n' \ + ' ext_includedir("jpeg"),\n' \ + ' ext_includedir("rapidjson"),\n' \ + ' ext_includedir("zlib"),\n' \ + ' }\n' \ + 'end\n' \ + '\n' \ + 'function linkProjects_mame_%s(_target, _subtarget)\n' \ + ' links {\n' % (options.target, )) + for lib in libnames: + if lib != 'shared': + projectfile.write(' "%s",\n' % (lib, )) + if 'shared' in libraries: + projectfile.write(' "shared",\n') + projectfile.write( + ' }\n' \ + 'end\n' \ + '\n' \ + 'function createProjects_mame_%s(_target, _subtarget)\n' \ + '\n' % (options.target, )) + for lib in libnames: + projectfile.write( + 'createMAMEProjects(_target, _subtarget, "%s")\n' \ + 'files {\n' % (lib, )) + for line in libraries[lib]: + projectfile.write(line) + projectfile.write('}\n\n') + projectfile.write('end\n') + + +def collect_sources(root, sources): + result = [ ] + for source in sources: + fullpath = os.path.join(root, source) + if os.path.isdir(fullpath): + for subdir, dirs, files in os.walk(fullpath): + for candidate in files: + if os.path.splitext(candidate)[1] == '.cpp': + if subdir != fullpath: + result.append(os.path.join(source, os.path.relpath(subdir, fullpath), candidate)) + else: + result.append(os.path.join(source, candidate)) + else: + result.append(source) + return result + + +def write_sources_project(options, projectfile): + def sourcefile(filename): + if tuple(filename.split('/')) in splitsources: + state['havedrivers'] = True + + def driver(shortname): + pass + + header_to_optional = collect_lua_directives(options) + sources = collect_sources(options.root, options.sources) + splitsources = frozenset(s[2:] for s in (path_components(s) for s in sources) if s[:2] == ('src', 'mame')) + state = { 'havedrivers': False } + DriverFilter().parse_list(options.list, sourcefile, driver) + if not state['havedrivers']: + sys.stderr.write('None of the specified source files contain system drivers\n') + sys.exit(1) + source_dependencies = scan_source_dependencies(options.root, sources) + write_project(options, projectfile, header_to_optional, source_dependencies, True) + + +def write_filter_project(options, projectfile): + header_to_optional = collect_lua_directives(options) + sources = DriverCollector(options).sources + source_dependencies = scan_source_dependencies(options.root, (os.path.join('src', 'mame', *n.split('/')) for n in sources)) + write_project(options, projectfile, header_to_optional, source_dependencies, False) + + +def write_sources_filter(options, filterfile): + sources = set() + DriverFilter().parse_list(options.list, lambda n: sources.add(n), lambda n: None) + drivers = set() - for source in options.sources: + for source in collect_sources(options.root, options.sources): components = tuple(x for x in split_path(source) if x) - if (len(components) > 3) and (components[:3] == ('src', 'mame', 'drivers')): + if (len(components) > 3) and (components[:2] == ('src', 'mame')): ext = os.path.splitext(components[-1])[1].lower() if ext.startswith('.c'): - drivers.add('/'.join(components[3:])) + if '/'.join(components[2:]) in sources: + drivers.add('/'.join(components[2:])) for driver in sorted(drivers): - f.write(driver + '\n') + filterfile.write(driver + '\n') if __name__ == '__main__': options = parse_command_line() if options.command == 'sourcesproject': - header_to_optional = collect_lua_directives(options) - source_dependencies = scan_source_dependencies(options) - write_project(options, sys.stdout, header_to_optional, source_dependencies) + write_sources_project(options, sys.stdout) + elif options.command == 'filterproject': + write_filter_project(options, sys.stdout) elif options.command == 'sourcesfilter': - write_filter(options, sys.stdout) + write_sources_filter(options, sys.stdout) elif options.command == 'driverlist': - DriverFilter(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) |