mame/scripts/build/makedep.py
Vas Crabb ab6237da82 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
2022-06-27 18:06:20 +10:00

887 lines
35 KiB
Python
Executable File

#!/usr/bin/python
##
## license:BSD-3-Clause
## copyright-holders:Vas Crabb
import argparse
import io
import os.path
import sys
class ParserBase:
def process_lines(self, inputfile):
self.input_line = 1
for line in inputfile:
start = 0
if line.endswith('\n'):
line = line[:-1]
used = 0
while used is not None:
start += used
used = self.processors[self.parse_state](line[start:])
self.input_line += 1
class CppParser(ParserBase):
TOKEN_LEAD = frozenset(
[chr(x) for x in range(ord('A'), ord('Z') + 1)] +
[chr(x) for x in range(ord('a'), ord('z') + 1)] +
['_'])
TOKEN_CONTINUATION = frozenset(
[chr(x) for x in range(ord('0'), ord('9') + 1)] +
[chr(x) for x in range(ord('A'), ord('Z') + 1)] +
[chr(x) for x in range(ord('a'), ord('z') + 1)] +
['_'])
HEXADECIMAL_DIGIT = frozenset(
[chr(x) for x in range(ord('0'), ord('9') + 1)] +
[chr(x) for x in range(ord('A'), ord('F') + 1)] +
[chr(x) for x in range(ord('a'), ord('f') + 1)])
class Handler:
def line(self, text):
pass
def comment(self, text):
pass
def line_comment(self, text):
pass
class ParseState:
DEFAULT = 0
COMMENT = 1
LINE_COMMENT = 2
TOKEN = 3
STRING_CONSTANT = 4
CHARACTER_CONSTANT = 5
NUMERIC_CONSTANT = 6
def __init__(self, handler, **kwargs):
super().__init__(**kwargs)
self.handler = handler
self.processors = {
self.ParseState.DEFAULT: self.process_default,
self.ParseState.COMMENT: self.process_comment,
self.ParseState.LINE_COMMENT: self.process_line_comment,
self.ParseState.TOKEN: self.process_token,
self.ParseState.STRING_CONSTANT: self.process_text,
self.ParseState.CHARACTER_CONSTANT: self.process_text,
self.ParseState.NUMERIC_CONSTANT: self.process_numeric }
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(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:
raise Exception('unterminated character literal on line %d' % (self.input_line, ))
elif self.parse_state == self.ParseState.STRING_CONSTANT:
raise Exception('unterminated string literal on line %d' % (self.input_line, ))
def process_default(self, line):
escape = False
pos = 0
length = len(line)
while pos < length:
ch = line[pos]
if (ch == '"') or (ch == "'"):
self.parse_state = self.ParseState.STRING_CONSTANT if ch == '"' else self.ParseState.CHARACTER_CONSTANT
self.line_buffer += line[:pos + 1]
return pos + 1
elif ch == '*':
if escape:
self.parse_state = self.ParseState.COMMENT
self.comment_line = self.input_line
self.line_buffer += line[:pos - 1] + ' '
return pos + 1
elif ch == '/':
if escape:
self.parse_state = self.ParseState.LINE_COMMENT
self.handler.line(self.line_buffer + line[:pos - 1] + ' ')
self.line_buffer = ''
return pos + 1
elif ch in self.TOKEN_LEAD:
self.parse_state = self.ParseState.TOKEN
self.line_buffer += line[:pos]
return pos
elif (ch >= '0') and (ch <= '9'):
self.parse_state = self.ParseState.NUMERIC_CONSTANT
self.line_buffer += line[:pos]
return pos
escape = ch == '/'
pos += 1
if line.endswith('\\'):
self.line_buffer += line[:-1]
else:
self.handler.line(self.line_buffer + line)
self.line_buffer = ''
def process_comment(self, line):
escape = False
pos = 0
length = len(line)
while pos < length:
ch = line[pos]
if escape and (ch == '/'):
self.parse_state = self.ParseState.DEFAULT
self.comment_line = None
self.handler.comment(self.comment_buffer + line[:pos - 1])
self.comment_buffer = ''
return pos + 1
escape = ch == '*'
pos += 1
if line.endswith('\\'):
self.comment_buffer += line[:-1]
else:
self.comment_buffer += line + '\n'
def process_line_comment(self, line):
self.parse_state = self.ParseState.DEFAULT
self.handler.line_comment(self.comment_buffer + line)
self.comment_buffer = ''
def process_token(self, line):
pos = 0
length = len(line)
while pos < length:
ch = line[pos]
if ch not in self.TOKEN_CONTINUATION:
self.parse_state = self.ParseState.DEFAULT
self.line_buffer += line[:pos]
return pos
pos += 1
self.parse_state = self.ParseState.DEFAULT
self.handler.line(self.line_buffer + line)
self.line_buffer = ''
def process_text(self, line):
quote = '"' if self.parse_state == self.ParseState.STRING_CONSTANT else "'"
escape = False
pos = 0
length = len(line)
while pos < length:
ch = line[pos]
if (ch == quote) and not escape:
self.parse_state = self.ParseState.DEFAULT
self.line_buffer += line[:pos + 1]
return pos + 1
escape = (ch == '\\') and not escape
pos += 1
if line.endswith('\\'):
self.line_buffer += line[:-1]
else:
t = 'string' if self.ParseState == self.ParseState.STRING_CONSTANT else 'character'
raise Exception('unterminated %s literal on line %d' % (t, self.input_line))
def process_numeric(self, line):
escape = False
pos = 0
length = len(line)
while pos < length:
ch = line[pos]
if self.lead_digit is None:
self.lead_digit = ch
if ch != '0':
self.radix = 10
elif self.radix is None:
if ch == "'":
if escape:
raise Exception('adjacent digit separators on line %d' % (self.input_line, ))
else:
escape = True
elif (ch == 'B') or (ch == 'b'):
self.radix = 2
elif (ch == 'X') or (ch == 'x'):
self.radix = 16
elif (ch >= '0') and (ch <= '7'):
self.radix = 8
else:
self.parse_state = self.ParseState.DEFAULT # probably an argument to a token-pasting or stringifying macro
else:
if ch == "'":
if escape:
raise Exception('adjacent digit separators on line %d' % (self.input_line, ))
else:
escape = True
else:
escape = False
if self.radix == 2:
if (ch < '0') or (ch > '1'):
self.parse_state = self.ParseState.DEFAULT
elif self.radix == 8:
if (ch < '0') or (ch > '7'):
self.parse_state = self.ParseState.DEFAULT
elif self.radix == 10:
if (ch < '0') or (ch > '9'):
self.parse_state = self.ParseState.DEFAULT
elif self.radix == 16:
if ch not in self.HEXADECIMAL_DIGIT:
self.parse_state = self.ParseState.DEFAULT
if self.parse_state == self.ParseState.DEFAULT:
self.lead_digit = None
self.radix = None
self.line_buffer += line[:pos]
return pos
pos += 1
self.parse_state = self.ParseState.DEFAULT
self.lead_digit = None
self.radix = None
self.handler.line(self.line_buffer + line)
self.line_buffer = ''
class LuaParser(ParserBase):
class Handler:
def short_comment(self, text):
pass
def long_comment_start(self, level):
pass
def long_comment_line(self, text):
pass
def long_comment_end(self):
pass
class ParseState:
DEFAULT = 0
SHORT_COMMENT = 1
LONG_COMMENT = 2
STRING_CONSTANT = 3
LONG_STRING_CONSTANT = 4
def __init__(self, handler, **kwargs):
super().__init__(**kwargs)
self.handler = handler
self.processors = {
self.ParseState.DEFAULT: self.process_default,
self.ParseState.SHORT_COMMENT: self.process_short_comment,
self.ParseState.LONG_COMMENT: self.process_long_comment,
self.ParseState.STRING_CONSTANT: self.process_string_constant,
self.ParseState.LONG_STRING_CONSTANT: self.process_long_string_constant }
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(inputfile)
if self.parse_state == self.ParseState.LONG_COMMENT:
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
length = len(line)
while pos < length:
ch = line[pos]
if (ch == '"') or (ch == "'"):
self.string_quote = ch
self.parse_state = self.ParseState.STRING_CONSTANT
self.long_bracket_level = None
self.escape = False
return pos + 1
elif (ch == '-') and self.escape:
self.parse_state = self.ParseState.SHORT_COMMENT
self.long_bracket_level = None
self.escape = False
return pos + 1
elif self.long_bracket_level is not None:
if ch == '=':
self.long_bracket_level += 1
elif ch == '[':
self.block_line = self.input_line
self.block_level = self.long_bracket_level
self.parse_state = self.ParseState.LONG_STRING_CONSTANT
self.long_bracket_level = None
self.escape = False
return pos + 1
else:
self.long_bracket_level = None
elif ch == '[':
self.long_bracket_level = 0
self.escape = ch == '-'
pos += 1
self.escape = False
def process_short_comment(self, line):
pos = 0
length = len(line)
while pos < length:
ch = line[pos]
if self.long_bracket_level is not None:
if ch == '=':
self.long_bracket_level += 1
elif ch == '[':
self.block_line = self.input_line
self.block_level = self.long_bracket_level
self.parse_state = self.ParseState.LONG_COMMENT
self.long_bracket_level = None
self.handler.long_comment_start(self.block_level)
return pos + 1
else:
self.long_bracket_level = None
elif ch == '[':
self.long_bracket_level = 0
if self.long_bracket_level is None:
self.handler.short_comment(line[pos:])
self.parse_state = self.ParseState.DEFAULT
return None
pos += 1
self.handler.short_comment(line)
self.parse_state = self.ParseState.DEFAULT
def process_long_comment(self, line):
pos = 0
length = len(line)
while pos < length:
ch = line[pos]
if self.long_bracket_level is not None:
if ch == '=':
self.long_bracket_level += 1
elif ch == ']':
if self.long_bracket_level == self.block_level:
if self.parse_state == self.ParseState.LONG_COMMENT:
self.handler.long_comment_line(line[:endpos])
self.handler.long_comment_end()
self.parse_state = self.ParseState.DEFAULT
return pos + 1
else:
self.long_bracket_level = 0
else:
self.long_bracket_level = None
elif ch == ']':
endpos = pos
self.long_bracket_level = 0
pos += 1
self.long_bracket_level = None
self.handler.long_comment_line(line)
def process_string_constant(self, line):
pos = 0
length = len(line)
while pos < length:
ch = line[pos]
if (ch == self.string_quote) and not self.escape:
self.parse_state = self.ParseState.DEFAULT
return pos + 1
self.escape = (ch == '\\') and not self.escape
pos += 1
if not self.escape:
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:
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, 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)
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(
'#include "emu.h"\n' \
'\n' \
'#include "drivenum.h"\n' \
'\n')
for driver in self.drivers:
f.write('GAME_EXTERN(%s);\n' % driver)
f.write(
'\n' \
'game_driver const *const driver_list::s_drivers_sorted[%d] =\n' \
'{\n' % (len(self.drivers), ))
for driver in self.drivers:
f.write('\t&GAME_NAME(%s),\n' % driver)
f.write(
'};\n' \
'\n' \
'std::size_t const driver_list::s_driver_count = %d;\n' % (len(self.drivers), ))
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()
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):
path = os.path.normpath(path)
result = [ ]
while True:
dirname, basename = os.path.split(path)
if dirname == path:
result.insert(0, dirname)
return result
elif basename == path:
result.insert(0, basename)
return result
else:
result.insert(0, basename)
path = dirname
def parse_command_line():
parser = argparse.ArgumentParser()
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('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')
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')
return parser.parse_args()
def collect_lua_directives(options):
def short_comment_hook(text):
if text.startswith('@'):
name, action = text[1:].rstrip().rsplit(',', 1)
if name not in result:
result[name] = [ ]
result[name].append(action)
base = os.path.join(options.root, 'scripts', 'src')
result = { }
handler = LuaParser.Handler()
handler.short_comment = short_comment_hook
parser = LuaParser(handler)
for name in ('bus', 'cpu', 'machine', 'sound', 'video', 'formats'):
path = os.path.join(base, name + '.lua')
try:
f = io.open(path, 'r', encoding='utf-8')
except IOError:
sys.stderr.write('Unable to open source file "%s"\n' % (path, ))
sys.exit(1)
try:
with f:
parser.parse(f)
except IOError:
sys.stderr.write('Error reading source file "%s"\n' % (path, ))
sys.exit(1)
except Exception as e:
sys.stderr.write('Error parsing source file "%s": %s\n' % (path, e))
sys.exit(1)
return result
def scan_source_dependencies(root, sources):
def locate_include(path):
split = [ ]
forward = 0
reverse = 0
for part in path.split('/'):
if part and (part != '.'):
if part != '..':
forward += 1
split.append(part)
elif forward:
split.pop()
forward -= 1
else:
split.append(part)
reverse += 1
split = tuple(split)
for incdir, depth in roots:
if (not depth) or (not reverse):
components = incdir + split
depth = depth + forward - 1
elif depth >= reverse:
components = incdir[:-reverse] + split[reverse:]
depth = depth + forward - reverse - 1
else:
components = incdir[:-depth] + split[depth:]
depth = forward - 1
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(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)):
remaining.append((path, depth))
seen.add(path)
def line_hook(text):
text = text.lstrip()
if text.startswith('#'):
text = text[1:].lstrip()
if text.startswith('include'):
text = text[7:]
if text[:1].isspace():
text = text.strip()
if (len(text) > 2) and (text[0] == '"') and (text[-1] == '"'):
components, depth = locate_include(text[1:-1])
if components:
path = '/'.join(components)
if path not in seen:
remaining.append((path, depth))
seen.add(path)
base, ext = os.path.splitext(components[-1])
if ext.lower().startswith('.h'):
components = components[:-1]
test_siblings(components, 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 sources)
remaining = list([(x, 0) for x in seen])
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(root, *components), 'r', encoding='utf-8')
except IOError:
sys.stderr.write('Unable to open source file "%s"\n' % (source, ))
sys.exit(1)
try:
with f:
parser.parse(f)
except IOError:
sys.stderr.write('Error reading source file "%s"\n' % (source, ))
sys.exit(1)
except Exception as e:
sys.stderr.write('Error parsing source file "%s": %s\n' % (source, e))
sys.exit(1)
return seen
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:
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')
def write_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:
components = tuple(x for x in split_path(source) if x)
if (len(components) > 3) and (components[:2] == ('src', 'mame')):
ext = os.path.splitext(components[-1])[1].lower()
if ext.startswith('.c'):
if '/'.join(components[2:]) in sources:
drivers.add('/'.join(components[2:]))
for driver in sorted(drivers):
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.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':
DriverLister(options).write_source(sys.stdout)