1#!/usr/bin/python 2## 3## license:BSD-3-Clause 4## copyright-holders:Vas Crabb 5 6import argparse 7import io 8import os.path 9import sys 10 11 12class ParserBase(object): 13 def process_lines(self, f): 14 self.input_line = 1 15 for line in f: 16 pos = 0 17 start = 0 18 if line.endswith('\n'): 19 line = line[:-1] 20 used = 0 21 while used is not None: 22 start += used 23 used = self.processors[self.parse_state](line[start:]) 24 self.input_line += 1 25 26 27class CppParser(ParserBase): 28 TOKEN_LEAD = frozenset( 29 [chr(x) for x in range(ord('A'), ord('Z') + 1)] + 30 [chr(x) for x in range(ord('a'), ord('z') + 1)] + 31 ['_']) 32 TOKEN_CONTINUATION = frozenset( 33 [chr(x) for x in range(ord('0'), ord('9') + 1)] + 34 [chr(x) for x in range(ord('A'), ord('Z') + 1)] + 35 [chr(x) for x in range(ord('a'), ord('z') + 1)] + 36 ['_']) 37 HEXADECIMAL_DIGIT = frozenset( 38 [chr(x) for x in range(ord('0'), ord('9') + 1)] + 39 [chr(x) for x in range(ord('A'), ord('F') + 1)] + 40 [chr(x) for x in range(ord('a'), ord('f') + 1)]) 41 42 class Handler(object): 43 def line(self, text): 44 pass 45 46 def comment(self, text): 47 pass 48 49 def line_comment(self, text): 50 pass 51 52 class ParseState(object): 53 DEFAULT = 0 54 COMMENT = 1 55 LINE_COMMENT = 2 56 TOKEN = 3 57 STRING_CONSTANT = 4 58 CHARACTER_CONSTANT = 5 59 NUMERIC_CONSTANT = 6 60 61 def __init__(self, handler, **kwargs): 62 super(CppParser, self).__init__(**kwargs) 63 self.handler = handler 64 self.processors = { 65 self.ParseState.DEFAULT: self.process_default, 66 self.ParseState.COMMENT: self.process_comment, 67 self.ParseState.LINE_COMMENT: self.process_line_comment, 68 self.ParseState.TOKEN: self.process_token, 69 self.ParseState.STRING_CONSTANT: self.process_text, 70 self.ParseState.CHARACTER_CONSTANT: self.process_text, 71 self.ParseState.NUMERIC_CONSTANT: self.process_numeric } 72 73 def parse(self, f): 74 self.parse_state = self.ParseState.DEFAULT 75 self.comment_line = None 76 self.lead_digit = None 77 self.radix = None 78 self.line_buffer = '' 79 self.comment_buffer = '' 80 self.process_lines(f) 81 if self.parse_state == self.ParseState.COMMENT: 82 raise Exception('unterminated multi-line comment beginning on line %d' % (self.comment_line, )) 83 elif self.parse_state == self.ParseState.CHARACTER_CONSTANT: 84 raise Exception('unterminated character literal on line %d' % (self.input_line, )) 85 elif self.parse_state == self.ParseState.STRING_CONSTANT: 86 raise Exception('unterminated string literal on line %d' % (self.input_line, )) 87 88 def process_default(self, line): 89 escape = False 90 pos = 0 91 length = len(line) 92 while pos < length: 93 ch = line[pos] 94 if (ch == '"') or (ch == "'"): 95 self.parse_state = self.ParseState.STRING_CONSTANT if ch == '"' else self.ParseState.CHARACTER_CONSTANT 96 self.line_buffer += line[:pos + 1] 97 return pos + 1 98 elif ch == '*': 99 if escape: 100 self.parse_state = self.ParseState.COMMENT 101 self.comment_line = self.input_line 102 self.line_buffer += line[:pos - 1] + ' ' 103 return pos + 1 104 elif ch == '/': 105 if escape: 106 self.parse_state = self.ParseState.LINE_COMMENT 107 self.handler.line(self.line_buffer + line[:pos - 1] + ' ') 108 self.line_buffer = '' 109 return pos + 1 110 elif ch in self.TOKEN_LEAD: 111 self.parse_state = self.ParseState.TOKEN 112 self.line_buffer += line[:pos] 113 return pos 114 elif (ch >= '0') and (ch <= '9'): 115 self.parse_state = self.ParseState.NUMERIC_CONSTANT 116 self.line_buffer += line[:pos] 117 return pos 118 escape = ch == '/' 119 pos += 1 120 if line.endswith('\\'): 121 self.line_buffer += line[:-1] 122 else: 123 self.handler.line(self.line_buffer + line) 124 self.line_buffer = '' 125 126 def process_comment(self, line): 127 escape = False 128 pos = 0 129 length = len(line) 130 while pos < length: 131 ch = line[pos] 132 if escape and (ch == '/'): 133 self.parse_state = self.ParseState.DEFAULT 134 self.comment_line = None 135 self.handler.comment(self.comment_buffer + line[:pos - 1]) 136 self.comment_buffer = '' 137 return pos + 1 138 escape = ch == '*' 139 pos += 1 140 if line.endswith('\\'): 141 self.comment_buffer += line[:-1] 142 else: 143 self.comment_buffer += line + '\n' 144 145 def process_line_comment(self, line): 146 self.parse_state = self.ParseState.DEFAULT 147 self.handler.line_comment(self.comment_buffer + line) 148 self.comment_buffer = '' 149 150 def process_token(self, line): 151 pos = 0 152 length = len(line) 153 while pos < length: 154 ch = line[pos] 155 if ch not in self.TOKEN_CONTINUATION: 156 self.parse_state = self.ParseState.DEFAULT 157 self.line_buffer += line[:pos] 158 return pos 159 pos += 1 160 self.parse_state = self.ParseState.DEFAULT 161 self.handler.line(self.line_buffer + line) 162 self.line_buffer = '' 163 164 def process_text(self, line): 165 quote = '"' if self.parse_state == self.ParseState.STRING_CONSTANT else "'" 166 escape = False 167 pos = 0 168 length = len(line) 169 while pos < length: 170 ch = line[pos] 171 if (ch == quote) and not escape: 172 self.parse_state = self.ParseState.DEFAULT 173 self.line_buffer += line[:pos + 1] 174 return pos + 1 175 escape = (ch == '\\') and not escape 176 pos += 1 177 if line.endswith('\\'): 178 self.line_buffer += line[:-1] 179 else: 180 t = 'string' if self.ParseState == self.ParseState.STRING_CONSTANT else 'character' 181 raise Exception('unterminated %s literal on line %d' % (t, self.input_line)) 182 183 def process_numeric(self, line): 184 escape = False 185 pos = 0 186 length = len(line) 187 while pos < length: 188 ch = line[pos] 189 if self.lead_digit is None: 190 self.lead_digit = ch 191 if ch != '0': 192 self.radix = 10 193 elif self.radix is None: 194 if ch == "'": 195 if escape: 196 raise Exception('adjacent digit separators on line %d' % (self.input_line, )) 197 else: 198 escape = True 199 elif (ch == 'B') or (ch == 'b'): 200 self.radix = 2 201 elif (ch == 'X') or (ch == 'x'): 202 self.radix = 16 203 elif (ch >= '0') and (ch <= '7'): 204 self.radix = 8 205 else: 206 self.parse_state = self.ParseState.DEFAULT # probably an argument to a token-pasting or stringifying macro 207 else: 208 if ch == "'": 209 if escape: 210 raise Exception('adjacent digit separators on line %d' % (self.input_line, )) 211 else: 212 escape = True 213 else: 214 escape = False 215 if self.radix == 2: 216 if (ch < '0') or (ch > '1'): 217 self.parse_state = self.ParseState.DEFAULT 218 elif self.radix == 8: 219 if (ch < '0') or (ch > '7'): 220 self.parse_state = self.ParseState.DEFAULT 221 elif self.radix == 10: 222 if (ch < '0') or (ch > '9'): 223 self.parse_state = self.ParseState.DEFAULT 224 elif self.radix == 16: 225 if ch not in self.HEXADECIMAL_DIGIT: 226 self.parse_state = self.ParseState.DEFAULT 227 if self.parse_state == self.ParseState.DEFAULT: 228 self.lead_digit = None 229 self.radix = None 230 self.line_buffer += line[:pos] 231 return pos 232 pos += 1 233 self.parse_state = self.ParseState.DEFAULT 234 self.lead_digit = None 235 self.radix = None 236 self.handler.line(self.line_buffer + line) 237 self.line_buffer = '' 238 239 240class LuaParser(ParserBase): 241 class Handler(object): 242 def short_comment(self, text): 243 pass 244 245 def long_comment_start(self, level): 246 pass 247 248 def long_comment_line(self, text): 249 pass 250 251 def long_comment_end(self): 252 pass 253 254 class ParseState(object): 255 DEFAULT = 0 256 SHORT_COMMENT = 1 257 LONG_COMMENT = 2 258 STRING_CONSTANT = 3 259 LONG_STRING_CONSTANT = 4 260 261 def __init__(self, handler, **kwargs): 262 super(LuaParser, self).__init__(**kwargs) 263 self.handler = handler 264 self.processors = { 265 self.ParseState.DEFAULT: self.process_default, 266 self.ParseState.SHORT_COMMENT: self.process_short_comment, 267 self.ParseState.LONG_COMMENT: self.process_long_comment, 268 self.ParseState.STRING_CONSTANT: self.process_string_constant, 269 self.ParseState.LONG_STRING_CONSTANT: self.process_long_string_constant } 270 271 def parse(self, f): 272 self.parse_state = self.ParseState.DEFAULT 273 self.long_bracket_level = None 274 self.escape = False 275 self.block_line = None 276 self.block_level = None 277 self.string_quote = None 278 self.process_lines(f) 279 if self.parse_state == self.ParseState.LONG_COMMENT: 280 raise Exception('unterminated long comment beginning on line %d' % (self.block_line, )); 281 elif self.parse_state == self.ParseState.STRING_CONSTANT: 282 raise Exception('unterminated string literal on line %d' % (self.input_line, )); 283 elif self.parse_state == self.ParseState.LONG_STRING_CONSTANT: 284 raise Exception('unterminated long string literal beginning on line %d' % (self.block_line, )); 285 286 def process_default(self, line): 287 pos = 0 288 length = len(line) 289 while pos < length: 290 ch = line[pos] 291 if (ch == '"') or (ch == "'"): 292 self.string_quote = ch 293 self.parse_state = self.ParseState.STRING_CONSTANT 294 self.long_bracket_level = None 295 self.escape = False 296 return pos + 1; 297 elif (ch == '-') and self.escape: 298 self.parse_state = self.ParseState.SHORT_COMMENT 299 self.long_bracket_level = None 300 self.escape = False 301 return pos + 1 302 elif self.long_bracket_level is not None: 303 if ch == '=': 304 self.long_bracket_level += 1 305 elif ch == '[': 306 self.block_line = self.input_line 307 self.block_level = self.long_bracket_level 308 self.parse_state = self.ParseState.LONG_STRING_CONSTANT 309 self.long_bracket_level = None 310 self.escape = False 311 return pos + 1 312 else: 313 self.long_bracket_level = None 314 elif ch == '[': 315 self.long_bracket_level = 0 316 self.escape = ch == '-' 317 pos += 1 318 self.escape = False 319 320 def process_short_comment(self, line): 321 pos = 0 322 length = len(line) 323 while pos < length: 324 ch = line[pos] 325 if self.long_bracket_level is not None: 326 if ch == '=': 327 self.long_bracket_level += 1 328 elif ch == '[': 329 self.block_line = self.input_line 330 self.block_level = self.long_bracket_level 331 self.parse_state = self.ParseState.LONG_COMMENT 332 self.long_bracket_level = None 333 self.handler.long_comment_start(self.block_level) 334 return pos + 1 335 else: 336 self.long_bracket_level = None 337 elif ch == '[': 338 self.long_bracket_level = 0 339 if self.long_bracket_level is None: 340 self.handler.short_comment(line[pos:]) 341 self.parse_state = self.ParseState.DEFAULT 342 return None 343 pos += 1 344 self.handler.short_comment(line) 345 self.parse_state = self.ParseState.DEFAULT 346 347 def process_long_comment(self, line): 348 pos = 0 349 length = len(line) 350 while pos < length: 351 ch = line[pos] 352 if self.long_bracket_level is not None: 353 if ch == '=': 354 self.long_bracket_level += 1 355 elif ch == ']': 356 if self.long_bracket_level == self.block_level: 357 if self.parse_state == self.ParseState.LONG_COMMENT: 358 self.handler.long_comment_line(line[:endpos]) 359 self.handler.long_comment_end() 360 self.parse_state = self.ParseState.DEFAULT 361 return pos + 1 362 else: 363 self.long_bracket_level = 0 364 else: 365 self.long_bracket_level = None 366 elif ch == ']': 367 endpos = pos 368 self.long_bracket_level = 0 369 pos += 1 370 self.long_bracket_level = None 371 self.handler.long_comment_line(line) 372 373 def process_string_constant(self, line): 374 pos = 0 375 length = len(line) 376 while pos < length: 377 ch = line[pos] 378 if (ch == self.string_quote) and not self.escape: 379 self.parse_state = self.ParseState.DEFAULT 380 return pos + 1 381 self.escape = (ch == '\\') and not self.escape 382 pos += 1 383 if not self.escape: 384 raise Exception('unterminated string literal on line %d' % (self.input_line, )); 385 386 def process_long_string_constant(self, line): 387 self.process_long_comment(line) # this works because they're both closed by a matching long bracket 388 389 390class DriverFilter(object): 391 DRIVER_CHARS = frozenset( 392 [chr(x) for x in range(ord('0'), ord('9') + 1)] + 393 [chr(x) for x in range(ord('a'), ord('z') + 1)] + 394 ['_']) 395 396 def __init__(self, options, **kwargs): 397 super(DriverFilter, self).__init__(**kwargs) 398 self.parse_filter(options.filter) 399 self.parse_list(options.list) 400 401 def write_source(self, f): 402 f.write( 403 '#include "emu.h"\n' \ 404 '\n' \ 405 '#include "drivenum.h"\n' \ 406 '\n') 407 for driver in self.drivers: 408 f.write('GAME_EXTERN(%s);\n' % driver) 409 f.write( 410 '\n' \ 411 'game_driver const *const driver_list::s_drivers_sorted[%d] =\n' \ 412 '{\n' % (len(self.drivers), )) 413 for driver in self.drivers: 414 f.write('\t&GAME_NAME(%s),\n' % driver) 415 f.write( 416 '};\n' \ 417 '\n' \ 418 'std::size_t const driver_list::s_driver_count = %d;\n' % (len(self.drivers), )) 419 420 def parse_filter(self, path): 421 def do_parse(p): 422 def line_hook(text): 423 text = text.strip() 424 if text.startswith('#'): 425 do_parse(os.path.join(os.path.dirname(n), text[1:].lstrip())) 426 elif text.startswith('+'): 427 text = text[1:].lstrip() 428 if not text: 429 sys.stderr.write('%s:%s: Empty driver name\n' % (p, parser.input_line, text)) 430 sys.exit(1) 431 elif not all(x in self.DRIVER_CHARS for x in text): 432 sys.stderr.write('%s:%s: Invalid character in driver name "%s"\n' % (p, parser.input_line, text)) 433 sys.exit(1) 434 includes.add(text) 435 excludes.discard(text) 436 elif text.startswith('-'): 437 text = text[1:].lstrip() 438 if not text: 439 sys.stderr.write('%s:%s: Empty driver name\n' % (p, parser.input_line, text)) 440 sys.exit(1) 441 elif not all(x in self.DRIVER_CHARS for x in text): 442 sys.stderr.write('%s:%s: Invalid character in driver name "%s"\n' % (p, parser.input_line, text)) 443 sys.exit(1) 444 includes.discard(text) 445 excludes.add(text) 446 elif text: 447 sources.add(text) 448 449 n = os.path.normpath(p) 450 if n not in filters: 451 filters.add(n) 452 try: 453 f = io.open(n, 'r', encoding='utf-8') 454 except IOError: 455 sys.stderr.write('Unable to open filter file "%s"\n' % (p, )) 456 sys.exit(1) 457 with f: 458 handler = CppParser.Handler() 459 handler.line = line_hook 460 parser = CppParser(handler) 461 try: 462 parser.parse(f) 463 except IOError: 464 sys.stderr.write('Error reading filter file "%s"\n' % (p, )) 465 sys.exit(1) 466 except Exception as e: 467 sys.stderr.write('Error parsing filter file "%s": %s\n' % (p, e)) 468 sys.exit(1) 469 470 sources = set() 471 includes = set() 472 excludes = set() 473 filters = set() 474 if path is not None: 475 do_parse(path) 476 sys.stderr.write('%d source file(s) found\n' % (len(sources), )) 477 self.sources = frozenset(sources) 478 self.includes = frozenset(includes) 479 self.excludes = frozenset(excludes) 480 481 def parse_list(self, path): 482 def do_parse(p): 483 def line_hook(text): 484 text = text.strip() 485 if text.startswith('#'): 486 do_parse(os.path.join(os.path.dirname(n), text[1:].lstrip())) 487 elif text.startswith('@'): 488 parts = text[1:].lstrip().split(':', 1) 489 parts[0] = parts[0].strip() 490 if (parts[0] == 'source') and (len(parts) == 2): 491 parts[1] = parts[1].strip() 492 if not parts[1]: 493 sys.stderr.write('%s:%s: Empty source file name "%s"\n' % (p, parser.input_line, text)) 494 sys.exit(1) 495 elif self.sources: 496 state['includesrc'] = parts[1] in self.sources 497 else: 498 sys.stderr.write('%s:%s: Unsupported directive "%s"\n' % (p, parser.input_line, text)) 499 sys.exit(1) 500 elif text: 501 if not all(x in self.DRIVER_CHARS for x in text): 502 sys.stderr.write('%s:%s: Invalid character in driver name "%s"\n' % (p, parser.input_line, text)) 503 sys.exit(1) 504 elif state['includesrc'] and (text not in self.excludes): 505 drivers.add(text) 506 507 n = os.path.normpath(p) 508 if n not in lists: 509 lists.add(n) 510 try: 511 f = io.open(n, 'r', encoding='utf-8') 512 except IOError: 513 sys.stderr.write('Unable to open list file "%s"\n' % (p, )) 514 sys.exit(1) 515 with f: 516 handler = CppParser.Handler() 517 handler.line = line_hook 518 parser = CppParser(handler) 519 try: 520 parser.parse(f) 521 except IOError: 522 sys.stderr.write('Error reading list file "%s"\n' % (p, )) 523 sys.exit(1) 524 except Exception as e: 525 sys.stderr.write('Error parsing list file "%s": %s\n' % (p, e)) 526 sys.exit(1) 527 528 lists = set() 529 drivers = set() 530 state = object() 531 state = { 'includesrc': True } 532 do_parse(path) 533 for driver in self.includes: 534 drivers.add(driver) 535 sys.stderr.write('%d driver(s) found\n' % (len(drivers), )) 536 drivers.add('___empty') 537 self.drivers = sorted(drivers) 538 539 540def split_path(path): 541 path = os.path.normpath(path) 542 result = [ ] 543 while True: 544 dirname, basename = os.path.split(path) 545 if dirname == path: 546 result.insert(0, dirname) 547 return result 548 elif basename == path: 549 result.insert(0, basename) 550 return result 551 else: 552 result.insert(0, basename) 553 path = dirname 554 555 556def parse_command_line(): 557 parser = argparse.ArgumentParser() 558 subparsers = parser.add_subparsers(title='commands', dest='command', metavar='<command>') 559 560 subparser = subparsers.add_parser('sourcesproject', help='generate project directives for source files') 561 subparser.add_argument('-r', '--root', metavar='<srcroot>', default='.', help='path to emulator source root (defaults to working directory)') 562 subparser.add_argument('-t', '--target', metavar='<target>', required=True, help='generated emulator target name') 563 subparser.add_argument('sources', metavar='<srcfile>', nargs='+', help='source files to include') 564 565 subparser = subparsers.add_parser('sourcesfilter', help='generate driver filter for source files') 566 subparser.add_argument('sources', metavar='<srcfile>', nargs='+', help='source files to include') 567 568 subparser = subparsers.add_parser('driverlist', help='generate driver list source') 569 subparser.add_argument('-f', '--filter', metavar='<fltfile>', help='input filter file') 570 subparser.add_argument('list', metavar='<lstfile>', help='input list file') 571 572 return parser.parse_args() 573 574 575def collect_lua_directives(options): 576 def short_comment_hook(text): 577 if text.startswith('@'): 578 name, action = text[1:].rstrip().rsplit(',', 1) 579 if name not in result: 580 result[name] = [ ] 581 result[name].append(action) 582 583 base = os.path.join(options.root, 'scripts', 'src') 584 result = { } 585 handler = LuaParser.Handler() 586 handler.short_comment = short_comment_hook 587 parser = LuaParser(handler) 588 for name in ('bus', 'cpu', 'machine', 'sound', 'video', 'formats'): 589 path = os.path.join(base, name + '.lua') 590 try: 591 f = io.open(path, 'r', encoding='utf-8') 592 except IOError: 593 sys.stderr.write('Unable to open source file "%s"\n' % (path, )) 594 sys.exit(1) 595 try: 596 with f: 597 parser.parse(f) 598 except IOError: 599 sys.stderr.write('Error reading source file "%s"\n' % (path, )) 600 sys.exit(1) 601 except Exception as e: 602 sys.stderr.write('Error parsing source file "%s": %s\n' % (path, e)) 603 sys.exit(1) 604 return result 605 606 607def scan_source_dependencies(options): 608 def locate_include(path): 609 split = [ ] 610 forward = 0 611 reverse = 0 612 for part in path.split('/'): 613 if part and (part != '.'): 614 if part != '..': 615 forward += 1 616 split.append(part) 617 elif forward: 618 split.pop() 619 forward -= 1 620 else: 621 split.append(part) 622 reverse += 1 623 split = tuple(split) 624 for incdir, depth in roots: 625 if (not depth) or (not reverse): 626 components = incdir + split 627 depth = depth + forward - 1 628 elif depth >= reverse: 629 components = incdir[:-reverse] + split[reverse:] 630 depth = depth + forward - reverse - 1 631 else: 632 components = incdir[:-depth] + split[depth:] 633 depth = forward - 1 634 if os.path.isfile(os.path.join(options.root, *components)): 635 return components, depth 636 return None, 0 637 638 def test_siblings(relative, basename, depth): 639 pathbase = '/'.join(relative) + '/' 640 dirname = os.path.join(options.root, *relative) 641 for ext in ('.cpp', '.ipp', '.hxx'): 642 path = pathbase + basename + ext 643 if (path not in seen) and os.path.isfile(os.path.join(dirname, basename + ext)): 644 remaining.append((path, depth)) 645 seen.add(path) 646 647 def line_hook(text): 648 text = text.lstrip() 649 if text.startswith('#'): 650 text = text[1:].lstrip() 651 if text.startswith('include'): 652 text = text[7:] 653 if text[:1].isspace(): 654 text = text.strip() 655 if (len(text) > 2) and (text[0] == '"') and (text[-1] == '"'): 656 components, depth = locate_include(text[1:-1]) 657 if components: 658 path = '/'.join(components) 659 if path not in seen: 660 remaining.append((path, depth)) 661 seen.add(path) 662 base, ext = os.path.splitext(components[-1]) 663 if ext.lower().startswith('.h'): 664 components = components[:-1] 665 test_siblings(components, base, depth) 666 if components == ('src', 'mame', 'includes'): 667 for aspect in ('audio', 'drivers', 'video', 'machine'): 668 test_siblings(('src', 'mame', aspect), base, depth) 669 670 handler = CppParser.Handler() 671 handler.line = line_hook 672 parser = CppParser(handler) 673 seen = set('/'.join(x for x in split_path(source) if x) for source in options.sources) 674 remaining = list([(x, 0) for x in seen]) 675 default_roots = ((('src', 'devices'), 0), (('src', 'mame'), 0), (('src', 'lib'), 0)) 676 while remaining: 677 source, depth = remaining.pop() 678 components = tuple(source.split('/')) 679 roots = ((components[:-1], depth), ) + default_roots 680 try: 681 f = io.open(os.path.join(options.root, *components), 'r', encoding='utf-8') 682 except IOError: 683 sys.stderr.write('Unable to open source file "%s"\n' % (source, )) 684 sys.exit(1) 685 try: 686 with f: 687 parser.parse(f) 688 except IOError: 689 sys.stderr.write('Error reading source file "%s"\n' % (source, )) 690 sys.exit(1) 691 except Exception as e: 692 sys.stderr.write('Error parsing source file "%s": %s\n' % (source, e)) 693 sys.exit(1) 694 return seen 695 696 697def write_project(options, f, mappings, sources): 698 targetsrc = '' 699 for source in sorted(sources): 700 action = mappings.get(source) 701 if action: 702 for line in action: 703 f.write(line + '\n') 704 if source.startswith('src/mame/'): 705 targetsrc += ' MAME_DIR .. "%s",\n' % (source, ) 706 f.write( 707 '\n' \ 708 'function createProjects_mame_%s(_target, _subtarget)\n' \ 709 ' project ("mame_%s")\n' \ 710 ' targetsubdir(_target .."_" .. _subtarget)\n' \ 711 ' kind (LIBTYPE)\n' \ 712 ' uuid (os.uuid("drv-mame-%s"))\n' \ 713 ' addprojectflags()\n' \ 714 ' \n' \ 715 ' includedirs {\n' \ 716 ' MAME_DIR .. "src/osd",\n' \ 717 ' MAME_DIR .. "src/emu",\n' \ 718 ' MAME_DIR .. "src/devices",\n' \ 719 ' MAME_DIR .. "src/mame",\n' \ 720 ' MAME_DIR .. "src/lib",\n' \ 721 ' MAME_DIR .. "src/lib/util",\n' \ 722 ' MAME_DIR .. "src/lib/netlist",\n' \ 723 ' MAME_DIR .. "3rdparty",\n' \ 724 ' GEN_DIR .. "mame/layout",\n' \ 725 ' ext_includedir("flac"),\n' \ 726 ' ext_includedir("glm"),\n' \ 727 ' ext_includedir("jpeg"),\n' \ 728 ' ext_includedir("rapidjson"),\n' \ 729 ' ext_includedir("zlib"),\n' \ 730 ' }\n' \ 731 '\n' \ 732 ' files{\n%s' \ 733 ' }\n' \ 734 'end\n' \ 735 '\n' \ 736 'function linkProjects_mame_%s(_target, _subtarget)\n' \ 737 ' links {\n' \ 738 ' "mame_%s",\n' \ 739 ' }\n' \ 740 'end\n' % (options.target, options.target, options.target, targetsrc, options.target, options.target)) 741 742 743def write_filter(options, f): 744 drivers = set() 745 for source in options.sources: 746 components = tuple(x for x in split_path(source) if x) 747 if (len(components) > 3) and (components[:3] == ('src', 'mame', 'drivers')): 748 ext = os.path.splitext(components[-1])[1].lower() 749 if ext.startswith('.c'): 750 drivers.add('/'.join(components[3:])) 751 for driver in sorted(drivers): 752 f.write(driver + '\n') 753 754 755if __name__ == '__main__': 756 options = parse_command_line() 757 if options.command == 'sourcesproject': 758 header_to_optional = collect_lua_directives(options) 759 source_dependencies = scan_source_dependencies(options) 760 write_project(options, sys.stdout, header_to_optional, source_dependencies) 761 elif options.command == 'sourcesfilter': 762 write_filter(options, sys.stdout) 763 elif options.command == 'driverlist': 764 DriverFilter(options).write_source(sys.stdout) 765