183 lines
5.8 KiB
Python
183 lines
5.8 KiB
Python
|
#!/usr/bin/env python3
|
||
|
"""Update Options.td for the flags changes in https://reviews.llvm.org/Dxyz
|
||
|
|
||
|
This script translates Options.td from using Flags to control option visibility
|
||
|
to using Vis instead. It is meant to be idempotent and usable to help update
|
||
|
downstream forks if they have their own changes to Options.td.
|
||
|
|
||
|
Usage:
|
||
|
```sh
|
||
|
% update_options_td_flags.py path/to/Options.td > Options.td.new
|
||
|
% mv Options.td.new path/to/Options.td
|
||
|
```
|
||
|
|
||
|
This script will be removed after the next LLVM release.
|
||
|
"""
|
||
|
|
||
|
import argparse
|
||
|
import re
|
||
|
import shutil
|
||
|
import sys
|
||
|
import tempfile
|
||
|
|
||
|
def rewrite_option_flags(input_file, output_file):
|
||
|
for src_line in input_file:
|
||
|
for dst_line in process_line(src_line):
|
||
|
output_file.write(dst_line)
|
||
|
|
||
|
def process_line(line):
|
||
|
# We only deal with one thing per line. If multiple things can be
|
||
|
# on the same line (like NegFlag and PosFlag), please preprocess
|
||
|
# that first.
|
||
|
m = re.search(r'((NegFlag|PosFlag)<[A-Za-z]+, |BothFlags<)'
|
||
|
r'\[([A-Za-z0-9, ]+)\](, \[ClangOption\]|(?=>))', line)
|
||
|
if m:
|
||
|
return process_boolflags(m.group(3), line[:m.end(1)], line[m.end():])
|
||
|
m = re.search(r'\bFlags<\[([A-Za-z0-9, ]*)\]>', line)
|
||
|
if m:
|
||
|
return process_flags(m.group(1), line[:m.start()], line[m.end():])
|
||
|
m = re.search(r'let Flags = \[([A-Za-z0-9, ]*)\]', line)
|
||
|
if m:
|
||
|
return process_letflags(m.group(1), line[:m.start(1)], line[m.end():])
|
||
|
|
||
|
return [line]
|
||
|
|
||
|
def process_boolflags(flag_group, prefix, suffix):
|
||
|
flags = [f.strip() for f in flag_group.split(',')] if flag_group else []
|
||
|
if not flags:
|
||
|
return f'{prefix}[], [ClangOption]{suffix}'
|
||
|
|
||
|
flags_to_keep, vis_mods = translate_flags(flags)
|
||
|
flag_text = f'[{", ".join(flags_to_keep)}]'
|
||
|
vis_text = f'[{", ".join(vis_mods)}]'
|
||
|
new_text = ', '.join([flag_text, vis_text])
|
||
|
|
||
|
if prefix.startswith('Both'):
|
||
|
indent = ' ' * len(prefix)
|
||
|
else:
|
||
|
indent = ' ' * (len(prefix) - len(prefix.lstrip()) + len('XyzFlag<'))
|
||
|
|
||
|
return get_edited_lines(prefix, new_text, suffix, indent=indent)
|
||
|
|
||
|
def process_flags(flag_group, prefix, suffix):
|
||
|
flags = [f.strip() for f in flag_group.split(',')]
|
||
|
|
||
|
flags_to_keep, vis_mods = translate_flags(flags)
|
||
|
|
||
|
flag_text = ''
|
||
|
vis_text = ''
|
||
|
if flags_to_keep:
|
||
|
flag_text = f'Flags<[{", ".join(flags_to_keep)}]>'
|
||
|
if vis_mods:
|
||
|
flag_text += ', '
|
||
|
if vis_mods:
|
||
|
vis_text = f'Visibility<[{", ".join(vis_mods)}]>'
|
||
|
|
||
|
return get_edited_lines(prefix, flag_text, vis_text, suffix)
|
||
|
|
||
|
def process_letflags(flag_group, prefix, suffix):
|
||
|
is_end_comment = prefix.startswith('} //')
|
||
|
if not is_end_comment and not prefix.startswith('let'):
|
||
|
raise AssertionError(f'Unusual let block: {prefix}')
|
||
|
|
||
|
flags = [f.strip() for f in flag_group.split(',')]
|
||
|
|
||
|
flags_to_keep, vis_mods = translate_flags(flags)
|
||
|
|
||
|
lines = []
|
||
|
if flags_to_keep:
|
||
|
lines += [f'let Flags = [{", ".join(flags_to_keep)}]']
|
||
|
if vis_mods:
|
||
|
lines += [f'let Visibility = [{", ".join(vis_mods)}]']
|
||
|
|
||
|
if is_end_comment:
|
||
|
lines = list(reversed([f'}} // {l}\n' for l in lines]))
|
||
|
else:
|
||
|
lines = [f'{l} in {{\n' for l in lines]
|
||
|
return lines
|
||
|
|
||
|
def get_edited_lines(prefix, *fragments, indent=' '):
|
||
|
out_lines = []
|
||
|
current = prefix
|
||
|
for fragment in fragments:
|
||
|
if fragment and len(current) + len(fragment) > 80:
|
||
|
# Make a minimal attempt at reasonable line lengths
|
||
|
if fragment.startswith(',') or fragment.startswith(';'):
|
||
|
# Avoid wrapping the , or ; to the new line
|
||
|
current += fragment[0]
|
||
|
fragment = fragment[1:].lstrip()
|
||
|
out_lines += [current.rstrip() + '\n']
|
||
|
current = max(' ' * (len(current) - len(current.lstrip())), indent)
|
||
|
current += fragment
|
||
|
|
||
|
if current.strip():
|
||
|
out_lines += [current]
|
||
|
return out_lines
|
||
|
|
||
|
def translate_flags(flags):
|
||
|
driver_flags = [
|
||
|
'HelpHidden',
|
||
|
'RenderAsInput',
|
||
|
'RenderJoined',
|
||
|
'RenderSeparate',
|
||
|
]
|
||
|
custom_flags = [
|
||
|
'NoXarchOption',
|
||
|
'LinkerInput',
|
||
|
'NoArgumentUnused',
|
||
|
'Unsupported',
|
||
|
'LinkOption',
|
||
|
'Ignored',
|
||
|
'TargetSpecific',
|
||
|
]
|
||
|
flag_to_vis = {
|
||
|
'CoreOption': ['ClangOption', 'CLOption', 'DXCOption'],
|
||
|
'CLOption': ['CLOption'],
|
||
|
'CC1Option': ['ClangOption', 'CC1Option'],
|
||
|
'CC1AsOption': ['ClangOption', 'CC1AsOption'],
|
||
|
'FlangOption': ['ClangOption', 'FlangOption'],
|
||
|
'FC1Option': ['ClangOption', 'FC1Option'],
|
||
|
'DXCOption': ['DXCOption'],
|
||
|
'CLDXCOption': ['CLOption', 'DXCOption'],
|
||
|
}
|
||
|
new_flags = []
|
||
|
vis_mods = []
|
||
|
has_no_driver = False
|
||
|
has_flang_only = False
|
||
|
for flag in flags:
|
||
|
if flag in driver_flags or flag in custom_flags:
|
||
|
new_flags += [flag]
|
||
|
elif flag in flag_to_vis:
|
||
|
vis_mods += flag_to_vis[flag]
|
||
|
elif flag == 'NoDriverOption':
|
||
|
has_no_driver = True
|
||
|
elif flag == 'FlangOnlyOption':
|
||
|
has_flang_only = True
|
||
|
else:
|
||
|
raise AssertionError(f'Unknown flag: {flag}')
|
||
|
|
||
|
new_vis_mods = []
|
||
|
for vis in vis_mods:
|
||
|
if vis in new_vis_mods:
|
||
|
continue
|
||
|
if has_no_driver and vis == 'ClangOption':
|
||
|
continue
|
||
|
if has_flang_only and vis == 'ClangOption':
|
||
|
continue
|
||
|
new_vis_mods += [vis]
|
||
|
|
||
|
return new_flags, new_vis_mods
|
||
|
|
||
|
def main():
|
||
|
parser = argparse.ArgumentParser()
|
||
|
parser.add_argument('src', nargs='?', default='-',
|
||
|
type=argparse.FileType('r', encoding='UTF-8'))
|
||
|
parser.add_argument('-o', dest='dst', default='-',
|
||
|
type=argparse.FileType('w', encoding='UTF-8'))
|
||
|
|
||
|
args = parser.parse_args()
|
||
|
rewrite_option_flags(args.src, args.dst)
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|