1b8e80941Smrg#
2b8e80941Smrg# Copyright 2017 Advanced Micro Devices, Inc.
3b8e80941Smrg#
4b8e80941Smrg# Permission is hereby granted, free of charge, to any person obtaining a
5b8e80941Smrg# copy of this software and associated documentation files (the "Software"),
6b8e80941Smrg# to deal in the Software without restriction, including without limitation
7b8e80941Smrg# on the rights to use, copy, modify, merge, publish, distribute, sub
8b8e80941Smrg# license, and/or sell copies of the Software, and to permit persons to whom
9b8e80941Smrg# the Software is furnished to do so, subject to the following conditions:
10b8e80941Smrg#
11b8e80941Smrg# The above copyright notice and this permission notice (including the next
12b8e80941Smrg# paragraph) shall be included in all copies or substantial portions of the
13b8e80941Smrg# Software.
14b8e80941Smrg#
15b8e80941Smrg# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16b8e80941Smrg# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17b8e80941Smrg# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18b8e80941Smrg# THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19b8e80941Smrg# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20b8e80941Smrg# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21b8e80941Smrg# USE OR OTHER DEALINGS IN THE SOFTWARE.
22b8e80941Smrg
23b8e80941Smrg"""
24b8e80941Smrgusage: merge_driinfo.py <list of input files>
25b8e80941Smrg
26b8e80941SmrgGenerates a source file which contains the DRI_CONF_xxx macros for generating
27b8e80941Smrgthe driinfo XML that describes the available DriConf options for a driver and
28b8e80941Smrgits supported state trackers, based on the merged information from the input
29b8e80941Smrgfiles.
30b8e80941Smrg"""
31b8e80941Smrg
32b8e80941Smrgfrom __future__ import print_function
33b8e80941Smrg
34b8e80941Smrgimport mako.template
35b8e80941Smrgimport re
36b8e80941Smrgimport sys
37b8e80941Smrg
38b8e80941Smrg
39b8e80941Smrg# Some regexps used during input parsing
40b8e80941SmrgRE_section_begin = re.compile(r'DRI_CONF_SECTION_(.*)')
41b8e80941SmrgRE_option = re.compile(r'DRI_CONF_(.*)\((.*)\)')
42b8e80941Smrg
43b8e80941Smrg
44b8e80941Smrgclass Option(object):
45b8e80941Smrg   """
46b8e80941Smrg   Represent a config option as:
47b8e80941Smrg    * name: the xxx part of the DRI_CONF_xxx macro
48b8e80941Smrg    * defaults: the defaults parameters that are passed into the macro
49b8e80941Smrg   """
50b8e80941Smrg   def __init__(self, name, defaults):
51b8e80941Smrg      self.name = name
52b8e80941Smrg      self.defaults = defaults
53b8e80941Smrg
54b8e80941Smrg
55b8e80941Smrgclass Verbatim(object):
56b8e80941Smrg   """
57b8e80941Smrg   Represent a chunk of code that is copied into the result file verbatim.
58b8e80941Smrg   """
59b8e80941Smrg   def __init__(self):
60b8e80941Smrg      self.string = ''
61b8e80941Smrg
62b8e80941Smrg
63b8e80941Smrgclass Section(object):
64b8e80941Smrg   """
65b8e80941Smrg   Represent a config section description as:
66b8e80941Smrg    * name: the xxx part of the DRI_CONF_SECTION_xxx macro
67b8e80941Smrg    * options: list of options
68b8e80941Smrg   """
69b8e80941Smrg   def __init__(self, name):
70b8e80941Smrg      self.name = name
71b8e80941Smrg      self.options = []
72b8e80941Smrg
73b8e80941Smrg
74b8e80941Smrgdef parse_inputs(input_filenames):
75b8e80941Smrg   success = True
76b8e80941Smrg   sections_lists = []
77b8e80941Smrg
78b8e80941Smrg   for input_filename in input_filenames:
79b8e80941Smrg      with open(input_filename, 'r') as infile:
80b8e80941Smrg         sections = []
81b8e80941Smrg         sections_lists.append(sections)
82b8e80941Smrg
83b8e80941Smrg         section = None
84b8e80941Smrg
85b8e80941Smrg         linenum = 0
86b8e80941Smrg         verbatim = None
87b8e80941Smrg         for line in infile:
88b8e80941Smrg            linenum += 1
89b8e80941Smrg
90b8e80941Smrg            if line.startswith('//= BEGIN VERBATIM'):
91b8e80941Smrg               if verbatim is not None:
92b8e80941Smrg                  print('{}:{}: nested verbatim'
93b8e80941Smrg                        .format(input_filename, linenum))
94b8e80941Smrg                  success = False
95b8e80941Smrg                  continue
96b8e80941Smrg               verbatim = Verbatim()
97b8e80941Smrg
98b8e80941Smrg            if verbatim is not None:
99b8e80941Smrg               verbatim.string += line
100b8e80941Smrg
101b8e80941Smrg               if line.startswith('//= END VERBATIM'):
102b8e80941Smrg                  if section is None:
103b8e80941Smrg                     sections.append(verbatim)
104b8e80941Smrg                  else:
105b8e80941Smrg                     section.options.append(verbatim)
106b8e80941Smrg                  verbatim = None
107b8e80941Smrg               continue
108b8e80941Smrg
109b8e80941Smrg            line = line.strip()
110b8e80941Smrg            if not line:
111b8e80941Smrg               continue
112b8e80941Smrg
113b8e80941Smrg            if line.startswith('//'):
114b8e80941Smrg               continue
115b8e80941Smrg
116b8e80941Smrg            if line == 'DRI_CONF_SECTION_END':
117b8e80941Smrg               if section is None:
118b8e80941Smrg                  print('{}:{}: no open section'
119b8e80941Smrg                        .format(input_filename, linenum))
120b8e80941Smrg                  success = False
121b8e80941Smrg                  continue
122b8e80941Smrg               section = None
123b8e80941Smrg               continue
124b8e80941Smrg
125b8e80941Smrg            m = RE_section_begin.match(line)
126b8e80941Smrg            if m:
127b8e80941Smrg               if section is not None:
128b8e80941Smrg                  print('{}:{}: nested sections are not supported'
129b8e80941Smrg                        .format(input_filename, linenum))
130b8e80941Smrg                  success = False
131b8e80941Smrg                  continue
132b8e80941Smrg               if sections is None:
133b8e80941Smrg                  print('{}:{}: missing DRIINFO line'
134b8e80941Smrg                        .format(input_filename, linenum))
135b8e80941Smrg                  success = False
136b8e80941Smrg                  break # parsing the rest really makes no sense
137b8e80941Smrg               section = Section(m.group(1))
138b8e80941Smrg               sections.append(section)
139b8e80941Smrg               continue
140b8e80941Smrg
141b8e80941Smrg            m = RE_option.match(line)
142b8e80941Smrg            if m:
143b8e80941Smrg               if section is None:
144b8e80941Smrg                  print('{}:{}: no open section'
145b8e80941Smrg                        .format(input_filename, linenum))
146b8e80941Smrg                  success = False
147b8e80941Smrg                  break
148b8e80941Smrg               section.options.append(Option(m.group(1), m.group(2)))
149b8e80941Smrg               continue
150b8e80941Smrg
151b8e80941Smrg            print('{}:{}: do not understand this line'
152b8e80941Smrg                  .format(input_filename, linenum))
153b8e80941Smrg            success = False
154b8e80941Smrg
155b8e80941Smrg         if section is not None:
156b8e80941Smrg            print('{}:end-of-file: missing end of section'
157b8e80941Smrg                  .format(input_filename))
158b8e80941Smrg            success = False
159b8e80941Smrg
160b8e80941Smrg   if success:
161b8e80941Smrg      return sections_lists
162b8e80941Smrg   return None
163b8e80941Smrg
164b8e80941Smrg
165b8e80941Smrgdef merge_sections(section_list):
166b8e80941Smrg   """
167b8e80941Smrg   section_list: list of Section objects to be merged, all of the same name
168b8e80941Smrg   Return a merged Section object (everything is deeply copied)
169b8e80941Smrg   """
170b8e80941Smrg   merged_section = Section(section_list[0].name)
171b8e80941Smrg
172b8e80941Smrg   for section in section_list:
173b8e80941Smrg      assert section.name == merged_section.name
174b8e80941Smrg
175b8e80941Smrg      for orig_option in section.options:
176b8e80941Smrg         if isinstance(orig_option, Option):
177b8e80941Smrg            for merged_option in merged_section.options:
178b8e80941Smrg               if not isinstance(merged_option, Option):
179b8e80941Smrg                  continue
180b8e80941Smrg               if orig_option.name == merged_option.name:
181b8e80941Smrg                  merged_option.defaults = orig_option.defaults
182b8e80941Smrg                  break
183b8e80941Smrg            else:
184b8e80941Smrg               merged_section.options.append(Option(orig_option.name, orig_option.defaults))
185b8e80941Smrg         else:
186b8e80941Smrg            merged_section.options.append(orig_option)
187b8e80941Smrg
188b8e80941Smrg   return merged_section
189b8e80941Smrg
190b8e80941Smrg
191b8e80941Smrgdef merge_sections_lists(sections_lists):
192b8e80941Smrg   """
193b8e80941Smrg   sections_lists: list of lists of Section objects to be merged
194b8e80941Smrg   Return a merged list of merged Section objects; everything is deeply copied.
195b8e80941Smrg   Default values for options in later lists override earlier default values.
196b8e80941Smrg   """
197b8e80941Smrg   merged_sections = []
198b8e80941Smrg
199b8e80941Smrg   for idx,sections in enumerate(sections_lists):
200b8e80941Smrg      for base_section in sections:
201b8e80941Smrg         if not isinstance(base_section, Section):
202b8e80941Smrg            merged_sections.append(base_section)
203b8e80941Smrg            continue
204b8e80941Smrg
205b8e80941Smrg         original_sections = [base_section]
206b8e80941Smrg         for next_sections in sections_lists[idx+1:]:
207b8e80941Smrg            for j,section in enumerate(next_sections):
208b8e80941Smrg               if section.name == base_section.name:
209b8e80941Smrg                  original_sections.append(section)
210b8e80941Smrg                  del next_sections[j]
211b8e80941Smrg                  break
212b8e80941Smrg
213b8e80941Smrg         merged_section = merge_sections(original_sections)
214b8e80941Smrg
215b8e80941Smrg         merged_sections.append(merged_section)
216b8e80941Smrg
217b8e80941Smrg   return merged_sections
218b8e80941Smrg
219b8e80941Smrg
220b8e80941Smrgdef main(input_filenames):
221b8e80941Smrg   sections_lists = parse_inputs(input_filenames)
222b8e80941Smrg   if sections_lists is None:
223b8e80941Smrg      return False
224b8e80941Smrg
225b8e80941Smrg   merged_sections_list = merge_sections_lists(sections_lists)
226b8e80941Smrg
227b8e80941Smrg   driinfo_h_template = mako.template.Template("""\
228b8e80941Smrg// DO NOT EDIT - this file is automatically generated by merge_driinfo.py
229b8e80941Smrg
230b8e80941Smrg/*
231b8e80941SmrgUse as:
232b8e80941Smrg
233b8e80941Smrg#include "xmlpool.h"
234b8e80941Smrg
235b8e80941Smrgstatic const char driinfo_xml[] =
236b8e80941Smrg#include "this_file"
237b8e80941Smrg;
238b8e80941Smrg*/
239b8e80941Smrg
240b8e80941SmrgDRI_CONF_BEGIN
241b8e80941Smrg% for section in sections:
242b8e80941Smrg% if isinstance(section, Section):
243b8e80941Smrg   DRI_CONF_SECTION_${section.name}
244b8e80941Smrg% for option in section.options:
245b8e80941Smrg% if isinstance(option, Option):
246b8e80941Smrg      DRI_CONF_${option.name}(${option.defaults})
247b8e80941Smrg% else:
248b8e80941Smrg${option.string}
249b8e80941Smrg% endif
250b8e80941Smrg% endfor
251b8e80941Smrg   DRI_CONF_SECTION_END
252b8e80941Smrg% else:
253b8e80941Smrg${section.string}
254b8e80941Smrg% endif
255b8e80941Smrg% endfor
256b8e80941SmrgDRI_CONF_END""")
257b8e80941Smrg
258b8e80941Smrg   print(driinfo_h_template.render(sections=merged_sections_list, Section=Section, Option=Option))
259b8e80941Smrg   return True
260b8e80941Smrg
261b8e80941Smrg
262b8e80941Smrgif __name__ == '__main__':
263b8e80941Smrg   if len(sys.argv) <= 1:
264b8e80941Smrg      print('Missing arguments')
265b8e80941Smrg      sys.exit(1)
266b8e80941Smrg
267b8e80941Smrg   if not main(sys.argv[1:]):
268b8e80941Smrg      sys.exit(1)
269