| # |
| # Copyright 2017 Advanced Micro Devices, Inc. |
| # |
| # Permission is hereby granted, free of charge, to any person obtaining a |
| # copy of this software and associated documentation files (the "Software"), |
| # to deal in the Software without restriction, including without limitation |
| # on the rights to use, copy, modify, merge, publish, distribute, sub |
| # license, and/or sell copies of the Software, and to permit persons to whom |
| # the Software is furnished to do so, subject to the following conditions: |
| # |
| # The above copyright notice and this permission notice (including the next |
| # paragraph) shall be included in all copies or substantial portions of the |
| # Software. |
| # |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
| # THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, |
| # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
| # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
| # USE OR OTHER DEALINGS IN THE SOFTWARE. |
| |
| """ |
| usage: merge_driinfo.py <list of input files> |
| |
| Generates a source file which contains the DRI_CONF_xxx macros for generating |
| the driinfo XML that describes the available DriConf options for a driver and |
| its supported gallium frontends, based on the merged information from the input |
| files. |
| """ |
| |
| from __future__ import print_function |
| |
| import mako.template |
| import re |
| import sys |
| |
| |
| # Some regexps used during input parsing |
| RE_section_begin = re.compile(r'DRI_CONF_SECTION_(.*)') |
| RE_option = re.compile(r'DRI_CONF_(.*)\((.*)\)') |
| |
| |
| class Option(object): |
| """ |
| Represent a config option as: |
| * name: the xxx part of the DRI_CONF_xxx macro |
| * defaults: the defaults parameters that are passed into the macro |
| """ |
| def __init__(self, name, defaults): |
| self.name = name |
| self.defaults = defaults |
| |
| |
| class Verbatim(object): |
| """ |
| Represent a chunk of code that is copied into the result file verbatim. |
| """ |
| def __init__(self): |
| self.string = '' |
| |
| |
| class Section(object): |
| """ |
| Represent a config section description as: |
| * name: the xxx part of the DRI_CONF_SECTION_xxx macro |
| * options: list of options |
| """ |
| def __init__(self, name): |
| self.name = name |
| self.options = [] |
| |
| |
| def parse_inputs(input_filenames): |
| success = True |
| sections_lists = [] |
| |
| for input_filename in input_filenames: |
| with open(input_filename, 'r') as infile: |
| sections = [] |
| sections_lists.append(sections) |
| |
| section = None |
| |
| linenum = 0 |
| verbatim = None |
| for line in infile: |
| linenum += 1 |
| |
| if line.startswith('//= BEGIN VERBATIM'): |
| if verbatim is not None: |
| print('{}:{}: nested verbatim' |
| .format(input_filename, linenum)) |
| success = False |
| continue |
| verbatim = Verbatim() |
| |
| if verbatim is not None: |
| verbatim.string += line |
| |
| if line.startswith('//= END VERBATIM'): |
| if section is None: |
| sections.append(verbatim) |
| else: |
| section.options.append(verbatim) |
| verbatim = None |
| continue |
| |
| line = line.strip() |
| if not line: |
| continue |
| |
| if line.startswith('//'): |
| continue |
| |
| if line == 'DRI_CONF_SECTION_END': |
| if section is None: |
| print('{}:{}: no open section' |
| .format(input_filename, linenum)) |
| success = False |
| continue |
| section = None |
| continue |
| |
| m = RE_section_begin.match(line) |
| if m: |
| if section is not None: |
| print('{}:{}: nested sections are not supported' |
| .format(input_filename, linenum)) |
| success = False |
| continue |
| if sections is None: |
| print('{}:{}: missing DRIINFO line' |
| .format(input_filename, linenum)) |
| success = False |
| break # parsing the rest really makes no sense |
| section = Section(m.group(1)) |
| sections.append(section) |
| continue |
| |
| m = RE_option.match(line) |
| if m: |
| if section is None: |
| print('{}:{}: no open section' |
| .format(input_filename, linenum)) |
| success = False |
| break |
| section.options.append(Option(m.group(1), m.group(2))) |
| continue |
| |
| print('{}:{}: do not understand this line' |
| .format(input_filename, linenum)) |
| success = False |
| |
| if section is not None: |
| print('{}:end-of-file: missing end of section' |
| .format(input_filename)) |
| success = False |
| |
| if success: |
| return sections_lists |
| return None |
| |
| |
| def merge_sections(section_list): |
| """ |
| section_list: list of Section objects to be merged, all of the same name |
| Return a merged Section object (everything is deeply copied) |
| """ |
| merged_section = Section(section_list[0].name) |
| |
| for section in section_list: |
| assert section.name == merged_section.name |
| |
| for orig_option in section.options: |
| if isinstance(orig_option, Option): |
| for merged_option in merged_section.options: |
| if not isinstance(merged_option, Option): |
| continue |
| if orig_option.name == merged_option.name: |
| merged_option.defaults = orig_option.defaults |
| break |
| else: |
| merged_section.options.append(Option(orig_option.name, orig_option.defaults)) |
| else: |
| merged_section.options.append(orig_option) |
| |
| return merged_section |
| |
| |
| def merge_sections_lists(sections_lists): |
| """ |
| sections_lists: list of lists of Section objects to be merged |
| Return a merged list of merged Section objects; everything is deeply copied. |
| Default values for options in later lists override earlier default values. |
| """ |
| merged_sections = [] |
| |
| for idx,sections in enumerate(sections_lists): |
| for base_section in sections: |
| if not isinstance(base_section, Section): |
| merged_sections.append(base_section) |
| continue |
| |
| original_sections = [base_section] |
| for next_sections in sections_lists[idx+1:]: |
| for j,section in enumerate(next_sections): |
| if section.name == base_section.name: |
| original_sections.append(section) |
| del next_sections[j] |
| break |
| |
| merged_section = merge_sections(original_sections) |
| |
| merged_sections.append(merged_section) |
| |
| return merged_sections |
| |
| |
| def main(input_filenames): |
| sections_lists = parse_inputs(input_filenames) |
| if sections_lists is None: |
| return False |
| |
| merged_sections_list = merge_sections_lists(sections_lists) |
| |
| driinfo_h_template = mako.template.Template("""\ |
| // DO NOT EDIT - this file is automatically generated by merge_driinfo.py |
| |
| /* |
| Use as: |
| |
| #include "driconf.h" |
| |
| static const char driinfo_xml[] = |
| #include "this_file" |
| ; |
| */ |
| |
| DRI_CONF_BEGIN |
| % for section in sections: |
| % if isinstance(section, Section): |
| DRI_CONF_SECTION_${section.name} |
| % for option in section.options: |
| % if isinstance(option, Option): |
| DRI_CONF_${option.name}(${option.defaults}) |
| % else: |
| ${option.string} |
| % endif |
| % endfor |
| DRI_CONF_SECTION_END |
| % else: |
| ${section.string} |
| % endif |
| % endfor |
| DRI_CONF_END""") |
| |
| print(driinfo_h_template.render(sections=merged_sections_list, Section=Section, Option=Option)) |
| return True |
| |
| |
| if __name__ == '__main__': |
| if len(sys.argv) <= 1: |
| print('Missing arguments') |
| sys.exit(1) |
| |
| if not main(sys.argv[1:]): |
| sys.exit(1) |