util: add merge_driinfo.py
authorNicolai Hähnle <nicolai.haehnle@amd.com>
Thu, 29 Jun 2017 15:34:37 +0000 (17:34 +0200)
committerNicolai Hähnle <nicolai.haehnle@amd.com>
Wed, 2 Aug 2017 07:50:57 +0000 (09:50 +0200)
This tool merges driinfo XML that is built using DRI_CONF_xxx macros.
The intention is to merge together state-tracker options with
driver-specific options.

Acked-by: Marek Olšák <marek.olsak@amd.com>
src/util/merge_driinfo.py [new file with mode: 0644]

diff --git a/src/util/merge_driinfo.py b/src/util/merge_driinfo.py
new file mode 100644 (file)
index 0000000..e6ccca5
--- /dev/null
@@ -0,0 +1,222 @@
+#
+# 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 state trackers, 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 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
+         for line in infile:
+            linenum += 1
+            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:
+         for merged_option in merged_section.options:
+            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))
+
+   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:
+         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 "xmlpool.h"
+
+static const char driinfo_xml[] =
+#include "this_file"
+;
+*/
+
+DRI_CONF_BEGIN
+% for section in sections:
+   DRI_CONF_SECTION_${section.name}
+% for option in section.options:
+      DRI_CONF_${option.name}(${option.defaults})
+% endfor
+   DRI_CONF_SECTION_END
+% endfor
+DRI_CONF_END""")
+
+   print(driinfo_h_template.render(sections=merged_sections_list))
+   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)