MWCC/opt_recomp.py

382 lines
17 KiB
Python

import json
class OptionCompiler:
def __init__(self):
self.last_id = 0
self.debug_indent = ''
self.root_basename = ''
self.current_basename = ''
self.current_forward_decls = []
self.current_output = []
self.conflict_groups = {}
def debug_push(self):
self.debug_indent += ' '
def debug_pop(self):
self.debug_indent = self.debug_indent[:-2]
def get_new_id(self):
self.last_id += 1
return self.last_id
def skip_id(self):
self.last_id += 1
def add_root(self, root):
self.assign_all_ids(root)
print('-'*80)
self.assign_all_names(root)
print('-'*80)
self.current_forward_decls = []
self.current_output = []
self.conflict_groups = {}
self.emit_root(root)
filename = f"Opts{root['name']}.opt"
with open(f'compiler_and_linker/CmdLine_Tools/MacOS_PPC/Tools_PPC/Lib/mac-ppc-cc/{filename}', 'w') as f:
for line in self.current_forward_decls:
f.write(line)
f.write('\n')
if self.current_forward_decls:
f.write('\n')
for line in self.current_output:
f.write(line)
f.write('\n')
# Phase 1
def assign_all_ids(self, root):
for option in root['options']:
self.assign_id_to_option(option)
root['_list_id'] = self.get_new_id()
def assign_id_to_option(self, option):
print(f"{self.debug_indent}option {option['names']}")
self.debug_push()
if 'sub_options' in option:
for sub_option in reversed(option['sub_options']):
self.assign_id_to_option(sub_option)
option['_sub_list_id'] = self.get_new_id()
print(f"{self.debug_indent}{option['_sub_list_id']}: option sublist")
for param in reversed(option['params']):
self.assign_id_to_param(param)
option['_id'] = self.get_new_id()
print(f"{self.debug_indent}{option['_id']}: option ({option['names']})")
self.debug_pop()
def assign_id_to_param(self, param):
if param['_type'] == 'IfArg':
for sub_param in reversed(param['if_no_arg']):
self.assign_id_to_param(sub_param)
for sub_param in reversed(param['if_arg']):
self.assign_id_to_param(sub_param)
if '_idskip' in param:
self.last_id += param['_idskip']
param['_id'] = self.get_new_id()
print(f"{self.debug_indent}{param['_id']}: param {param['_type']}")
# Phase 2
def assign_all_names(self, root):
self.root_basename = f"optlst{root['name']}"
self.current_basename = f"optlst{root['name']}"
root['_name'] = self.current_basename
root['_list_name'] = f"{self.current_basename}_{root['_list_id']:03d}_list"
for option in root['options']:
self.assign_name_to_option(option)
def assign_name_to_option(self, option):
if 'conflict_group' in option:
option['_name'] = f"{option['conflict_group']}_{option['_id']:03d}"
elif 'custom_name' in option:
option['_name'] = f"{self.current_basename}_{option['custom_name']}"
else:
option['_name'] = f"{self.current_basename}_{option['_id']:03d}"
print(f"{self.debug_indent}{option['_name']} = {option['names']}")
self.debug_push()
for param in option['params']:
self.assign_name_to_param(param)
if 'sub_options' in option:
save_basename = self.current_basename
option['_sub_list_name'] = f"{self.current_basename}_{option['_sub_list_id']:03d}"
if 'sub_options_exclusive' in option and option['sub_options_exclusive']:
self.current_basename = option['_sub_list_name']
for sub_option in option['sub_options']:
self.assign_name_to_option(sub_option)
self.current_basename = save_basename
self.debug_pop()
def assign_name_to_param(self, param):
if param['_type'] == 'IfArg':
self.debug_push()
for sub_param in param['if_no_arg']:
self.assign_name_to_param(sub_param)
for sub_param in param['if_arg']:
self.assign_name_to_param(sub_param)
self.debug_pop()
param['_name'] = f"{self.root_basename}{param['_id']:03d}"
print(f"{self.debug_indent}{param['_name']} = {param['_type']}")
# Phase 3
def emit_root(self, root):
self.current_output.append(f"/* {root['name']} */")
for option in root['options']:
self.emit_option(option)
self.current_output.append(f"Option *{root['_list_name']}[] = {{")
for option in root['options']:
self.current_output.append(f"\t&{option['_name']},")
self.current_output.append('\t0')
self.current_output.append('};')
list_flags = build_list_flags(root)
self.current_output.append(f"OptionList {root['_name']} = {{")
self.current_output.append(f"\t/* help = */ {json.dumps(root['help'])},")
self.current_output.append(f"\t/* flags = */ {list_flags},")
self.current_output.append(f"\t/* list = */ {root['_list_name']}")
self.current_output.append('};')
for (i, (group, option_names)) in enumerate(self.conflict_groups.items()):
self.current_forward_decls.append(f'/* forward declare */ extern OptionList {group}_conflicts;')
self.current_output.append(f'Option *{group}_{i:03d}_list[] = {{')
for option_name in option_names:
self.current_output.append(f'\t&{option_name},')
self.current_output.append('\t0')
self.current_output.append('};')
self.current_output.append(f'OptionList {group}_conflicts = {{')
self.current_output.append(f'\t/* help = */ 0,')
self.current_output.append(f'\t/* flags = */ 0,')
self.current_output.append(f'\t/* list = */ {group}_{i:03d}_list')
self.current_output.append('};')
def emit_option(self, option, is_conflicted=None):
option_flags = build_option_flags(option, is_conflicted)
sub_name = '0'
if 'sub_options' in option:
sub_name = '&' + option['_sub_list_name']
sub_conflicts = None
if ('sub_options_exclusive' in option) and option['sub_options_exclusive']:
sub_conflicts = f"&{option['_sub_list_name']}_conflicts"
self.current_output.append(f"/* forward declare */ extern OptionList {option['_sub_list_name']}_conflicts;")
for sub_option in option['sub_options']:
self.emit_option(sub_option, sub_conflicts)
self.current_output.append(f"Option *{option['_sub_list_name']}_list[] = {{")
for sub_option in option['sub_options']:
self.current_output.append(f"\t&{sub_option['_name']},")
self.current_output.append('\t0')
self.current_output.append('};')
list_flags = build_list_flags(option)
self.current_output.append(f"OptionList {option['_sub_list_name']} = {{")
self.current_output.append(f"\t/* help = */ 0,")
self.current_output.append(f"\t/* flags = */ {list_flags},")
self.current_output.append(f"\t/* list = */ {option['_sub_list_name']}_list")
self.current_output.append('};')
if ('sub_options_exclusive' in option) and option['sub_options_exclusive']:
self.current_output.append(f"OptionList {option['_sub_list_name']}_conflicts = {{")
self.current_output.append(f"\t/* help = */ 0,")
self.current_output.append(f"\t/* flags = */ {list_flags},")
self.current_output.append(f"\t/* list = */ {option['_sub_list_name']}_list")
self.current_output.append('};')
if is_conflicted:
conflicts_name = is_conflicted
elif 'conflict_group' in option:
conflict_group = option['conflict_group']
conflicts_name = f'&{conflict_group}_conflicts'
if conflict_group in self.conflict_groups:
self.conflict_groups[conflict_group].append(option['_name'])
else:
self.conflict_groups[conflict_group] = [option['_name']]
else:
conflicts_name = '0'
last_param = self.emit_params(option['params'])
help_str = json.dumps(option['help']) if ('help' in option and option['help'] is not None) else '0'
self.current_output.append(f"Option {option['_name']} = {{")
self.current_output.append(f"\t/* names = */ {json.dumps(option['names'])},")
self.current_output.append(f"\t/* avail = */ {option_flags},")
self.current_output.append(f"\t/* param = */ {last_param},")
self.current_output.append(f"\t/* sub = */ {sub_name},")
self.current_output.append(f"\t/* conflicts = */ {conflicts_name},")
self.current_output.append(f"\t/* help = */ {help_str}")
self.current_output.append('};')
def emit_params(self, params):
last_param = '0'
for param in reversed(params):
if param['_type'] == 'IfArg':
ia_last_parg = self.emit_params(param['if_arg'])
ia_last_pnone = self.emit_params(param['if_no_arg'])
myname_str = json.dumps(param['myname']) if 'myname' in param else '0'
self.current_output.append(f"{PARAM_TYPES[param['_type']]} {param['_name']} = {{")
self.current_output.append(f"\t/* which = */ PARAMWHICH_{param['_type']},")
self.current_output.append(f"\t/* flags = */ 0x{param['flags']:02X},")
self.current_output.append(f"\t/* myname = */ {myname_str},")
self.current_output.append(f"\t/* next = */ {last_param},")
if param['_type'] == 'FTypeCreator':
self.current_output.append(f"\t/* fc = */ &{param['target']},")
self.current_output.append(f"\t/* iscreator = */ {param['is_creator']}")
elif param['_type'] == 'FilePath':
self.current_output.append(f"\t/* fflags = */ {param['fflags']},")
df = json.dumps(param['default']) if param['default'] is not None else '0'
self.current_output.append(f"\t/* defaultstr = */ {df},")
self.current_output.append(f"\t/* filename = */ {param['target']},")
self.current_output.append(f"\t/* maxlen = */ {param['max_length']}")
elif param['_type'] == 'Number':
self.current_output.append(f"\t/* size = */ {param['byte_size']},")
self.current_output.append(f"\t/* fit = */ {param['fit']},")
self.current_output.append(f"\t/* lo = */ {param['minimum']},")
self.current_output.append(f"\t/* hi = */ {param['maximum']},")
self.current_output.append(f"\t/* num = */ &{param['target']}")
elif param['_type'] in ('String','Id','Sym'):
self.current_output.append(f"\t/* maxlen = */ {param['max_length']},")
self.current_output.append(f"\t/* pstring = */ {int(param['pascal'])},")
self.current_output.append(f"\t/* str = */ {param['target']}")
elif param['_type'] in ('OnOff','OffOn'):
self.current_output.append(f"\t/* var = */ &{param['target']}")
elif param['_type'] == 'Mask':
self.current_output.append(f"\t/* size = */ {param['byte_size']},")
self.current_output.append(f"\t/* ormask = */ 0x{param['or_mask']:X},")
self.current_output.append(f"\t/* andmask = */ 0x{param['and_mask']:X},")
self.current_output.append(f"\t/* num = */ &{param['target']}")
elif param['_type'] == 'Toggle':
self.current_output.append(f"\t/* size = */ {param['byte_size']},")
self.current_output.append(f"\t/* mask = */ 0x{param['mask']:X},")
self.current_output.append(f"\t/* num = */ &{param['target']}")
elif param['_type'] == 'Set':
self.current_output.append(f"\t/* size = */ {param['byte_size']},")
self.current_output.append(f"\t/* value = */ 0x{param['value']:X},")
if param['target']:
self.current_output.append(f"\t/* num = */ (char *) &{param['target']}")
else:
self.current_output.append(f"\t/* num = */ 0")
elif param['_type'] == 'SetString':
self.current_output.append(f"\t/* value = */ {json.dumps(param['value'])},")
self.current_output.append(f"\t/* pstring = */ {int(param['pascal'])},")
self.current_output.append(f"\t/* var = */ {param['target']}")
elif param['_type'] == 'Generic':
self.current_output.append(f"\t/* parse = */ &{param['function']},")
self.current_output.append(f"\t/* arg = */ (void *) {json.dumps(param['arg'])},")
help_str = json.dumps(param['help']) if ('help' in param and param['help'] is not None) else '0'
self.current_output.append(f"\t/* help = */ {help_str}")
elif param['_type'] == 'IfArg':
self.current_output.append(f"\t/* parg = */ {ia_last_parg},")
self.current_output.append(f"\t/* helpa = */ {json.dumps(param['help_a'])},")
self.current_output.append(f"\t/* pnone = */ {ia_last_pnone},")
self.current_output.append(f"\t/* helpn = */ {json.dumps(param['help_n'])}")
elif param['_type'] == 'Setting':
self.current_output.append(f"\t/* parse = */ &{param['function']},")
self.current_output.append(f"\t/* valuename = */ {json.dumps(param['value_name'])}")
self.current_output.append('};')
last_param = '(PARAM_T *) &' + param['_name']
return last_param
PARAM_TYPES = {
'FTypeCreator': 'FTYPE_T',
'FilePath': 'FILEPATH_T',
'Number': 'NUM_T',
'String': 'STRING_T',
'Id': 'STRING_T',
'Sym': 'STRING_T',
'OnOff': 'ONOFF_T',
'OffOn': 'OFFON_T',
'Mask': 'MASK_T',
'Toggle': 'TOGGLE_T',
'Set': 'SET_T',
'SetString': 'SETSTRING_T',
'Generic': 'GENERIC_T',
'IfArg': 'IFARG_T',
'Setting': 'SETTING_T'
}
def build_list_flags(what):
bits = []
if 'sub_options_exclusive' in what and what['sub_options_exclusive']:
bits.append('LISTFLAGS_EXCLUSIVE')
if 'sub_options_allow_unknowns' in what and what['sub_options_allow_unknowns']:
bits.append('LISTFLAGS_ALLOW_UNKNOWNS')
for tool in what['tools']:
if tool == 'compiler':
bits.append('LISTFLAGS_COMPILER')
elif tool == 'linker':
bits.append('LISTFLAGS_LINKER')
elif tool == 'disassembler':
bits.append('LISTFLAGS_DISASSEMBLER')
if bits:
return ' | '.join(bits)
else:
return '0'
def build_option_flags(option, has_conflicts=False):
bits = []
if 'global' in option and option['global']:
bits.append('OTF_GLOBAL')
if 'sticky' in option and option['sticky']:
bits.append('OTF_STICKY')
if 'cased' in option and option['cased']:
bits.append('OTF_CASED')
if 'obsolete' in option and option['obsolete']:
bits.append('OTF_OBSOLETE')
if 'substituted' in option and option['substituted']:
bits.append('OTF_SUBSTITUTED')
if 'deprecated' in option and option['deprecated']:
bits.append('OTF_DEPRECATED')
if 'ignored' in option and option['ignored']:
bits.append('OTF_IGNORED')
if 'secret' in option and option['secret']:
bits.append('OTF_SECRET')
if 'hide_default' in option and option['hide_default']:
bits.append('OTF_HIDE_DEFAULT')
if 'compatibility' in option and option['compatibility']:
bits.append('OTF_COMPATIBILITY')
if 'only_once' in option and option['only_once']:
bits.append('OTF_ONLY_ONCE')
if 'warning' in option and option['warning']:
bits.append('OTF_WARNING')
if 'can_be_negated' in option and option['can_be_negated']:
bits.append('OTF_SLFLAGS_8')
if 'can_be_negated_2' in option and option['can_be_negated_2']:
bits.append('OTF_SLFLAGS_10')
if 'can_be_negated_3' in option and option['can_be_negated_3']:
bits.append('OTF_SLFLAGS_20')
if 'meaningless' in option and option['meaningless']:
bits.append('OTF_MEANINGLESS')
for tool in option['tools']:
if tool == 'compiler':
bits.append('OTF_TOOL_COMPILER')
elif tool == 'linker':
bits.append('OTF_TOOL_LINKER')
elif tool == 'disassembler':
bits.append('OTF_TOOL_DISASSEMBLER')
if 'sub_options' in option:
bits.append('OTF_HAS_SUB_OPTIONS')
if 'sub_options_optional' in option and option['sub_options_optional']:
bits.append('OTF_SUB_OPTIONS_OPTIONAL')
if 'conflict_group' in option or has_conflicts:
bits.append('OTF_HAS_CONFLICTS')
if bits:
return ' | '.join(bits)
else:
return '0'
with open('../../nice_opts.json', 'r') as f:
roots = json.load(f)
oc = OptionCompiler()
for root in roots:
oc.add_root(root)