diff options
author | 2022-06-27 18:06:20 +1000 | |
---|---|---|
committer | 2022-06-27 18:06:20 +1000 | |
commit | ab6237da824a95898209ecbb568095981c4dbe69 (patch) | |
tree | 221d85add6951883cfac0ef3bbc41930794e5b93 /scripts/build/makedep.py | |
parent | cf11b3330261aea407a36911048f3835b7a48f31 (diff) |
Eliminated cross dependencies between driver projects.
Driver projects now use globs to search for files. There's less effort
editing the Lua files when things are moved around. Remember it won't
automatically pick up a change, so if you add/remove/change files, you
should touch makefile to get it to find the change.
Driver projects no longer get the top-level MAME directory as an include
path. This means you need to think about how you structure things and
not introduce nasty circular dependencies.
Subtarget projects can now be generated entirely from .flt files without
the need for separate Lua scripts and .lst files. This has been done
for the arcade, mess and virtual targets. It effectively works like a
SOURCES= build on a large scale. This means you need to organise things
so the dependency genrators can find them.
There's an issue with the mess subtarget right now. For some reason,
decmate2.cpp isn't picking up the dependency on the PDP8 CPU core for
the HD6120 device. I'll debug it later
Diffstat (limited to 'scripts/build/makedep.py')
-rwxr-xr-x | scripts/build/makedep.py | 514 |
1 files changed, 292 insertions, 222 deletions
diff --git a/scripts/build/makedep.py b/scripts/build/makedep.py index d9f92b08ac6..33e61ccc171 100755 --- a/scripts/build/makedep.py +++ b/scripts/build/makedep.py @@ -392,10 +392,136 @@ class DriverFilter: [chr(x) for x in range(ord('a'), ord('z') + 1)] + ['_']) + def parse_filter(self, 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: + sourcefile(text) + + 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: + 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().__init__(**kwargs) - self.parse_filter(options.filter) - self.parse_list(options.list) + + 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.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( @@ -416,124 +542,34 @@ class DriverFilter: '\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)) - 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)) - 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: - filterfile = io.open(n, 'r', encoding='utf-8') - except IOError: - sys.stderr.write('Unable to open filter file "%s"\n' % (p, )) - sys.exit(1) - with filterfile: - 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' % (p, )) - sys.exit(1) - except Exception as e: - sys.stderr.write('Error parsing filter file "%s": %s\n' % (p, e)) - sys.exit(1) - 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) +class DriverCollector(DriverFilter): + def __init__(self, options, **kwargs): + super().__init__(**kwargs) - 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: - listfile = io.open(n, 'r', encoding='utf-8') - except IOError: - sys.stderr.write('Unable to open list file "%s"\n' % (p, )) - 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' % (p, )) - sys.exit(1) - except Exception as e: - sys.stderr.write('Error parsing list file "%s": %s\n' % (p, e)) - sys.exit(1) + def includesource(filename): + sources.add(filename) - 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) + 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() + state = { 'prevsource': None } + self.parse_filter(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 split_path(path): @@ -561,6 +597,12 @@ def parse_command_line(): subparser.add_argument('-t', '--target', metavar='<target>', required=True, help='generated emulator target name') 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('-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('-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') @@ -604,7 +646,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 +673,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)): @@ -664,25 +706,21 @@ def scan_source_dependencies(options): components = components[:-1] test_siblings(components, base, depth) if components[:2] == ('src', 'mame'): - if components[2:] == ('includes', ): - for aspect in ('audio', 'drivers', 'video', 'machine'): - test_siblings(('src', 'mame', aspect), base, depth) - else: - for aspect in ('_a', '_v', '_m'): - test_siblings(components, base + aspect, depth) + 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', 'mame', 'messshared'), 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) @@ -698,97 +736,126 @@ def scan_source_dependencies(options): return seen -def write_project(options, projectfile, mappings, sources): - 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",\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)) - - -def write_filter(options, filterfile): - 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) - else: - sources.add(parts[1]) +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/mame/messshared",\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: - sys.stderr.write('%s:%s: Unsupported directive "%s"\n' % (p, parser.input_line, text)) - sys.exit(1) + 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/mame/messshared",\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') and (lib != 'messshared'): + projectfile.write(' "%s",\n' % (lib, )) + if 'shared' in libraries: + projectfile.write(' "shared",\n') + if 'messshared' in libraries: + projectfile.write(' "messshared",\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') - n = os.path.normpath(p) - if n not in lists: - lists.add(n) - try: - listfile = io.open(n, 'r', encoding='utf-8') - except IOError: - sys.stderr.write('Unable to open list file "%s"\n' % (p, )) - 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' % (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() +def write_filter(options, filterfile): sources = set() - do_parse(options.list) + DriverFilter().parse_list(options.list, lambda n: sources.add(n), lambda n: None) drivers = set() for source in options.sources: @@ -796,9 +863,7 @@ def write_filter(options, filterfile): if (len(components) > 3) and (components[:2] == ('src', 'mame')): ext = os.path.splitext(components[-1])[1].lower() if ext.startswith('.c'): - if components[2] == 'drivers': - drivers.add('/'.join(components[3:])) - elif '/'.join(components[2:]) in sources: + if '/'.join(components[2:]) in sources: drivers.add('/'.join(components[2:])) for driver in sorted(drivers): filterfile.write(driver + '\n') @@ -808,9 +873,14 @@ 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) + source_dependencies = scan_source_dependencies(options.root, options.sources) + write_project(options, sys.stdout, header_to_optional, source_dependencies, True) + elif options.command == 'filterproject': + 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, sys.stdout, header_to_optional, source_dependencies, False) elif options.command == 'sourcesfilter': write_filter(options, sys.stdout) elif options.command == 'driverlist': - DriverFilter(options).write_source(sys.stdout) + DriverLister(options).write_source(sys.stdout) |