3 # Copyright (c) 2017-2019 Tony Su
4 # Copyright (c) 2023 Red Hat, Inc.
6 # SPDX-License-Identifier: MIT
8 # Adapted from https://github.com/peitaosu/XML-Preprocessor
10 """This is a XML Preprocessor which can be used to process your XML file before
11 you use it, to process conditional statements, variables, iteration
12 statements, error/warning, execute command, etc.
18 <?include path/to/file ?>
23 $(env.EnvironmentVariable)
30 ### Conditional Statements
45 ### Iteration Statements
47 <?foreach VARNAME in 1;2;3?>
52 ### Errors and Warnings
54 <?error "This is error message!" ?>
56 <?warning "This is warning message!" ?>
61 <? cmd "echo hello world" ?>
70 from typing
import Optional
71 from xml
.dom
import minidom
75 """This class holds the XML preprocessing state"""
79 "ARCH": platform
.architecture()[0],
80 "SOURCE": os
.path
.abspath(__file__
),
81 "CURRENT": os
.getcwd(),
85 def _pp_include(self
, xml_str
: str) -> str:
86 include_regex
= r
"(<\?include([\w\s\\/.:_-]+)\s*\?>)"
87 matches
= re
.findall(include_regex
, xml_str
)
88 for group_inc
, group_xml
in matches
:
89 inc_file_path
= group_xml
.strip()
90 with
open(inc_file_path
, "r", encoding
="utf-8") as inc_file
:
91 inc_file_content
= inc_file
.read()
92 xml_str
= xml_str
.replace(group_inc
, inc_file_content
)
95 def _pp_env_var(self
, xml_str
: str) -> str:
96 envvar_regex
= r
"(\$\(env\.(\w+)\))"
97 matches
= re
.findall(envvar_regex
, xml_str
)
98 for group_env
, group_var
in matches
:
99 xml_str
= xml_str
.replace(group_env
, os
.environ
[group_var
])
102 def _pp_sys_var(self
, xml_str
: str) -> str:
103 sysvar_regex
= r
"(\$\(sys\.(\w+)\))"
104 matches
= re
.findall(sysvar_regex
, xml_str
)
105 for group_sys
, group_var
in matches
:
106 xml_str
= xml_str
.replace(group_sys
, self
.sys_vars
[group_var
])
109 def _pp_cus_var(self
, xml_str
: str) -> str:
110 define_regex
= r
"(<\?define\s*(\w+)\s*=\s*([\w\s\"]+)\s
*\?>)"
111 matches = re.findall(define_regex, xml_str)
112 for group_def, group_name, group_var in matches:
113 group_name = group_name.strip()
114 group_var = group_var.strip().strip("\"")
115 self.cus_vars[group_name] = group_var
116 xml_str = xml_str.replace(group_def, "")
117 cusvar_regex = r"(\$\
(var\
.(\w
+)\
))"
118 matches = re.findall(cusvar_regex, xml_str)
119 for group_cus, group_var in matches:
120 xml_str = xml_str.replace(
122 self.cus_vars.get(group_var, "")
126 def _pp_foreach(self, xml_str: str) -> str:
127 foreach_regex = r"(<\?foreach\s
+(\w
+)\s
+in\s
+([\w
;]+)\s
*\?>(.*)<\?endforeach
\?>)"
128 matches = re.findall(foreach_regex, xml_str)
129 for group_for, group_name, group_vars, group_text in matches:
131 for var in group_vars.split(";"):
132 self.cus_vars[group_name] = var
133 group_texts += self._pp_cus_var(group_text)
134 xml_str = xml_str.replace(group_for, group_texts)
137 def _pp_error_warning(self, xml_str: str) -> str:
138 error_regex = r"<\?error\s
*\"([^
\"]+)\"\s
*\?>"
139 matches = re.findall(error_regex, xml_str)
140 for group_var in matches:
141 raise RuntimeError("[Error
]: " + group_var)
142 warning_regex = r"(<\?warning\s
*\"([^
\"]+)\"\s
*\?>)"
143 matches = re.findall(warning_regex, xml_str)
144 for group_wrn, group_var in matches:
145 print("[Warning]: " + group_var)
146 xml_str = xml_str.replace(group_wrn, "")
149 def _pp_if_eval(self, xml_str: str) -> str:
151 r"(<\?(if|elseif
)\s
*([^
\"\s
=<>!]+)\s
*([!=<>]+)\s
*\"*([^
\"=<>!]+)\"*\s
*\?>)"
153 matches = re.findall(ifelif_regex, xml_str)
154 for ifelif, tag, left, operator, right in matches:
155 if "<" in operator or ">" in operator:
156 result = eval(f"{left} {operator} {right}
")
158 result = eval(f'"{left}
" {operator} "{right}
"')
159 xml_str = xml_str.replace(ifelif, f"<?{tag} {result}?
>")
162 def _pp_ifdef_ifndef(self, xml_str: str) -> str:
163 ifndef_regex = r"(<\?(ifdef|ifndef
)\s
*([\w
]+)\s
*\?>)"
164 matches = re.findall(ifndef_regex, xml_str)
165 for group_ifndef, group_tag, group_var in matches:
166 if group_tag == "ifdef
":
167 result = group_var in self.cus_vars
169 result = group_var not in self.cus_vars
170 xml_str = xml_str.replace(group_ifndef, f"<?
if {result}?
>")
173 def _pp_if_elseif(self, xml_str: str) -> str:
174 if_elif_else_regex = (
175 r"(<\?if\
s(True|
False)\?>"
177 r"<\?elseif\
s(True|
False)\?>"
184 r"(<\?if\
s(True|
False)\?>"
190 if_regex = r"(<\?if\
s(True|
False)\?>(.*?
)<\?endif
\?>)"
191 matches = re.findall(if_elif_else_regex, xml_str, re.DOTALL)
192 for (group_full, group_if, group_if_elif, group_elif,
193 group_elif_else, group_else) in matches:
195 if group_if == "True":
196 result = group_if_elif
197 elif group_elif == "True":
198 result = group_elif_else
201 xml_str = xml_str.replace(group_full, result)
202 matches = re.findall(if_else_regex, xml_str, re.DOTALL)
203 for group_full, group_if, group_if_else, group_else in matches:
205 if group_if == "True":
206 result = group_if_else
209 xml_str = xml_str.replace(group_full, result)
210 matches = re.findall(if_regex, xml_str, re.DOTALL)
211 for group_full, group_if, group_text in matches:
213 if group_if == "True":
215 xml_str = xml_str.replace(group_full, result)
218 def _pp_command(self, xml_str: str) -> str:
219 cmd_regex = r"(<\?cmd\s
*\"([^
\"]+)\"\s
*\?>)"
220 matches = re.findall(cmd_regex, xml_str)
221 for group_cmd, group_exec in matches:
222 output = subprocess.check_output(
223 group_exec, shell=True,
224 text=True, stderr=subprocess.STDOUT
226 xml_str = xml_str.replace(group_cmd, output)
229 def _pp_blanks(self, xml_str: str) -> str:
230 right_blank_regex = r">[\n\s
\t\r]*"
231 left_blank_regex = r"[\n\s
\t\r]*<"
232 xml_str = re.sub(right_blank_regex, ">", xml_str)
233 xml_str = re.sub(left_blank_regex, "<", xml_str)
236 def preprocess(self, xml_str: str) -> str:
245 self._pp_ifdef_ifndef,
248 self._pp_error_warning,
254 out_xml = func(xml_str)
255 if not changed and out_xml != xml_str:
264 def preprocess_xml(path: str) -> str:
265 with open(path, "r
", encoding="utf
-8") as original_file:
266 input_xml = original_file.read()
268 proc = Preprocessor()
269 return proc.preprocess(input_xml)
272 def save_xml(xml_str: str, path: Optional[str]):
273 xml = minidom.parseString(xml_str)
274 with open(path, "w
", encoding="utf
-8") if path else sys.stdout as output_file:
275 output_file.write(xml.toprettyxml())
279 if len(sys.argv) < 2:
280 print("Usage
: xml
-preprocessor
input.xml
[output
.xml
]")
284 if len(sys.argv) == 3:
285 output_file = sys.argv[2]
287 input_file = sys.argv[1]
288 output_xml = preprocess_xml(input_file)
289 save_xml(output_xml, output_file)
292 if __name__ == "__main__
":