]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/UPT/Xml/IniToXml.py
BaseTools: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / BaseTools / Source / Python / UPT / Xml / IniToXml.py
CommitLineData
4234283c
LG
1## @file\r
2# This file is for converting package information data file to xml file.\r
3#\r
64285f15 4# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>\r
4234283c 5#\r
2e351cbe 6# SPDX-License-Identifier: BSD-2-Clause-Patent\r
4234283c
LG
7#\r
8\r
9'''\r
10IniToXml\r
11'''\r
12\r
13import os.path\r
14import re\r
15from time import strftime\r
16from time import localtime\r
17\r
18import Logger.Log as Logger\r
19from Logger.ToolError import UPT_INI_PARSE_ERROR\r
20from Logger.ToolError import FILE_NOT_FOUND\r
21from Library.Xml.XmlRoutines import CreateXmlElement\r
22from Library.DataType import TAB_VALUE_SPLIT\r
23from Library.DataType import TAB_EQUAL_SPLIT\r
24from Library.DataType import TAB_SECTION_START\r
25from Library.DataType import TAB_SECTION_END\r
26from Logger import StringTable as ST\r
64285f15 27from Library.StringUtils import ConvertSpecialChar\r
4234283c 28from Library.ParserValidate import IsValidPath\r
421ccda3 29from Library import GlobalData\r
4234283c
LG
30\r
31## log error:\r
32#\r
33# @param error: error\r
34# @param File: File\r
35# @param Line: Line\r
36#\r
37def IniParseError(Error, File, Line):\r
38 Logger.Error("UPT", UPT_INI_PARSE_ERROR, File=File,\r
39 Line=Line, ExtraData=Error)\r
40\r
41## __ValidatePath\r
42#\r
43# @param Path: Path to be checked\r
44#\r
45def __ValidatePath(Path, Root):\r
46 Path = Path.strip()\r
47 if os.path.isabs(Path) or not IsValidPath(Path, Root):\r
48 return False, ST.ERR_FILELIST_LOCATION % (Root, Path)\r
49 return True, ''\r
50\r
51## ValidateMiscFile\r
52#\r
53# @param Filename: File to be checked\r
54#\r
55def ValidateMiscFile(Filename):\r
421ccda3 56 Root = GlobalData.gWORKSPACE\r
4234283c
LG
57 return __ValidatePath(Filename, Root)\r
58\r
59## ValidateToolsFile\r
60#\r
61# @param Filename: File to be checked\r
62#\r
63def ValidateToolsFile(Filename):\r
64 Valid, Cause = False, ''\r
65 if not Valid and 'EDK_TOOLS_PATH' in os.environ:\r
66 Valid, Cause = __ValidatePath(Filename, os.environ['EDK_TOOLS_PATH'])\r
421ccda3
HC
67 if not Valid:\r
68 Valid, Cause = __ValidatePath(Filename, GlobalData.gWORKSPACE)\r
4234283c
LG
69 return Valid, Cause\r
70\r
71## ParseFileList\r
72#\r
73# @param Line: Line\r
74# @param Map: Map\r
75# @param CurrentKey: CurrentKey\r
76# @param PathFunc: Path validate function\r
77#\r
78def ParseFileList(Line, Map, CurrentKey, PathFunc):\r
79 FileList = ["", {}]\r
80 TokenList = Line.split(TAB_VALUE_SPLIT)\r
81 if len(TokenList) > 0:\r
82 Path = TokenList[0].strip().replace('\\', '/')\r
83 if not Path:\r
84 return False, ST.ERR_WRONG_FILELIST_FORMAT\r
85 Valid, Cause = PathFunc(Path)\r
86 if not Valid:\r
87 return Valid, Cause\r
88 FileList[0] = TokenList[0].strip()\r
89 for Token in TokenList[1:]:\r
90 Attr = Token.split(TAB_EQUAL_SPLIT)\r
91 if len(Attr) != 2 or not Attr[0].strip() or not Attr[1].strip():\r
92 return False, ST.ERR_WRONG_FILELIST_FORMAT\r
f7496d71 93\r
4234283c
LG
94 Key = Attr[0].strip()\r
95 Val = Attr[1].strip()\r
96 if Key not in ['OS', 'Executable']:\r
97 return False, ST.ERR_UNKNOWN_FILELIST_ATTR % Key\r
f7496d71
LG
98\r
99 if Key == 'OS' and Val not in ["Win32", "Win64", "Linux32",\r
100 "Linux64", "OS/X32", "OS/X64",\r
4234283c
LG
101 "GenericWin", "GenericNix"]:\r
102 return False, ST.ERR_FILELIST_ATTR % 'OS'\r
103 elif Key == 'Executable' and Val not in ['true', 'false']:\r
104 return False, ST.ERR_FILELIST_ATTR % 'Executable'\r
105 FileList[1][Key] = Val\r
f7496d71 106\r
4234283c
LG
107 Map[CurrentKey].append(FileList)\r
108 return True, ''\r
109\r
110## Create header XML file\r
111#\r
112# @param DistMap: DistMap\r
113# @param Root: Root\r
114#\r
115def CreateHeaderXml(DistMap, Root):\r
116 Element1 = CreateXmlElement('Name', DistMap['Name'],\r
117 [], [['BaseName', DistMap['BaseName']]])\r
118 Element2 = CreateXmlElement('GUID', DistMap['GUID'],\r
119 [], [['Version', DistMap['Version']]])\r
120 AttributeList = [['ReadOnly', DistMap['ReadOnly']],\r
121 ['RePackage', DistMap['RePackage']]]\r
122 NodeList = [Element1,\r
123 Element2,\r
124 ['Vendor', DistMap['Vendor']],\r
125 ['Date', DistMap['Date']],\r
126 ['Copyright', DistMap['Copyright']],\r
127 ['License', DistMap['License']],\r
128 ['Abstract', DistMap['Abstract']],\r
129 ['Description', DistMap['Description']],\r
130 ['Signature', DistMap['Signature']],\r
131 ['XmlSpecification', DistMap['XmlSpecification']],\r
132 ]\r
133 Root.appendChild(CreateXmlElement('DistributionHeader', '',\r
134 NodeList, AttributeList))\r
135\r
136## Create tools XML file\r
137#\r
138# @param Map: Map\r
139# @param Root: Root\r
f7496d71 140# @param Tag: Tag\r
4234283c
LG
141#\r
142def CreateToolsXml(Map, Root, Tag):\r
143 #\r
144 # Check if all elements in this section are empty\r
145 #\r
146 for Key in Map:\r
147 if len(Map[Key]) > 0:\r
148 break\r
149 else:\r
150 return\r
151\r
152 NodeList = [['Name', Map['Name']],\r
153 ['Copyright', Map['Copyright']],\r
154 ['License', Map['License']],\r
155 ['Abstract', Map['Abstract']],\r
156 ['Description', Map['Description']],\r
157 ]\r
158 HeaderNode = CreateXmlElement('Header', '', NodeList, [])\r
159 NodeList = [HeaderNode]\r
160\r
161 for File in Map['FileList']:\r
162 AttrList = []\r
163 for Key in File[1]:\r
164 AttrList.append([Key, File[1][Key]])\r
165 NodeList.append(CreateXmlElement('Filename', File[0], [], AttrList))\r
166 Root.appendChild(CreateXmlElement(Tag, '', NodeList, []))\r
167\r
168## ValidateValues\r
169#\r
170# @param Key: Key\r
171# @param Value: Value\r
172# @param SectionName: SectionName\r
173#\r
174def ValidateValues(Key, Value, SectionName):\r
175 if SectionName == 'DistributionHeader':\r
176 Valid, Cause = ValidateRegValues(Key, Value)\r
177 if not Valid:\r
178 return Valid, Cause\r
179 Valid = __ValidateDistHeader(Key, Value)\r
180 if not Valid:\r
181 return Valid, ST.ERR_VALUE_INVALID % (Key, SectionName)\r
182 else:\r
183 Valid = __ValidateOtherHeader(Key, Value)\r
184 if not Valid:\r
185 return Valid, ST.ERR_VALUE_INVALID % (Key, SectionName)\r
186 return True, ''\r
187\r
188## ValidateRegValues\r
189#\r
190# @param Key: Key\r
191# @param Value: Value\r
192#\r
193def ValidateRegValues(Key, Value):\r
194 ValidateMap = {\r
195 'ReadOnly' :\r
196 ('true|false', ST.ERR_BOOLEAN_VALUE % (Key, Value)),\r
197 'RePackage' :\r
198 ('true|false', ST.ERR_BOOLEAN_VALUE % (Key, Value)),\r
199 'GUID' :\r
200 ('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}'\r
201 '-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}',\r
202 ST.ERR_GUID_VALUE % Value),\r
203 'Version' : ('[0-9]+(\.[0-9]+)?', ST.ERR_VERSION_VALUE % \\r
204 (Key, Value)),\r
205 'XmlSpecification' : ('1\.1', ST.ERR_VERSION_XMLSPEC % Value)\r
206 }\r
207 if Key not in ValidateMap:\r
208 return True, ''\r
209 Elem = ValidateMap[Key]\r
210 Match = re.compile(Elem[0]).match(Value)\r
211 if Match and Match.start() == 0 and Match.end() == len(Value):\r
212 return True, ''\r
213 return False, Elem[1]\r
214\r
215## __ValidateDistHeaderName\r
216#\r
217# @param Name: Name\r
218#\r
219def __ValidateDistHeaderName(Name):\r
220 if len(Name) < 1:\r
221 return False\r
f7496d71 222\r
4234283c
LG
223 for Char in Name:\r
224 if ord(Char) < 0x20 or ord(Char) >= 0x7f:\r
225 return False\r
226 return True\r
227\r
228## __ValidateDistHeaderBaseName\r
229#\r
230# @param BaseName: BaseName\r
231#\r
232def __ValidateDistHeaderBaseName(BaseName):\r
233 if not BaseName:\r
234 return False\r
235# if CheckLen and len(BaseName) < 2:\r
236# return False\r
237 if not BaseName[0].isalnum() and BaseName[0] != '_':\r
238 return False\r
239 for Char in BaseName[1:]:\r
240 if not Char.isalnum() and Char not in '-_':\r
241 return False\r
242 return True\r
243\r
244## __ValidateDistHeaderAbstract\r
245#\r
246# @param Abstract: Abstract\r
247#\r
248def __ValidateDistHeaderAbstract(Abstract):\r
249 return '\t' not in Abstract and len(Abstract.splitlines()) == 1\r
250\r
251## __ValidateOtherHeaderAbstract\r
252#\r
253# @param Abstract: Abstract\r
254#\r
255def __ValidateOtherHeaderAbstract(Abstract):\r
256 return __ValidateDistHeaderAbstract(Abstract)\r
257\r
258## __ValidateDistHeader\r
259#\r
260# @param Key: Key\r
261# @param Value: Value\r
262#\r
263def __ValidateDistHeader(Key, Value):\r
264 ValidateMap = {\r
265 'Name' : __ValidateDistHeaderName,\r
266 'BaseName' : __ValidateDistHeaderBaseName,\r
267 'Abstract' : __ValidateDistHeaderAbstract,\r
268 'Vendor' : __ValidateDistHeaderAbstract\r
269 }\r
270 return not (Value and Key in ValidateMap and not ValidateMap[Key](Value))\r
271\r
272## __ValidateOtherHeader\r
273#\r
274# @param Key: Key\r
275# @param Value: Value\r
276#\r
277def __ValidateOtherHeader(Key, Value):\r
278 ValidateMap = {\r
279 'Name' : __ValidateDistHeaderName,\r
280 'Abstract' : __ValidateOtherHeaderAbstract\r
281 }\r
282 return not (Value and Key in ValidateMap and not ValidateMap[Key](Value))\r
283\r
284## Convert ini file to xml file\r
285#\r
286# @param IniFile\r
287#\r
288def IniToXml(IniFile):\r
289 if not os.path.exists(IniFile):\r
290 Logger.Error("UPT", FILE_NOT_FOUND, ST.ERR_TEMPLATE_NOTFOUND % IniFile)\r
291\r
292 DistMap = {'ReadOnly' : '', 'RePackage' : '', 'Name' : '',\r
293 'BaseName' : '', 'GUID' : '', 'Version' : '', 'Vendor' : '',\r
294 'Date' : '', 'Copyright' : '', 'License' : '', 'Abstract' : '',\r
295 'Description' : '', 'Signature' : '', 'XmlSpecification' : ''\r
296 }\r
297\r
298 ToolsMap = {'Name' : '', 'Copyright' : '', 'License' : '',\r
299 'Abstract' : '', 'Description' : '', 'FileList' : []}\r
300 #\r
301 # Only FileList is a list: [['file1', {}], ['file2', {}], ...]\r
302 #\r
303 MiscMap = {'Name' : '', 'Copyright' : '', 'License' : '',\r
304 'Abstract' : '', 'Description' : '', 'FileList' : []}\r
305\r
306 SectionMap = {\r
307 'DistributionHeader' : DistMap,\r
308 'ToolsHeader' : ToolsMap,\r
309 'MiscellaneousFilesHeader' : MiscMap\r
310 }\r
f7496d71 311\r
4234283c
LG
312 PathValidator = {\r
313 'ToolsHeader' : ValidateToolsFile,\r
314 'MiscellaneousFilesHeader' : ValidateMiscFile\r
315 }\r
f7496d71 316\r
4234283c
LG
317 ParsedSection = []\r
318\r
319 SectionName = ''\r
320 CurrentKey = ''\r
321 PreMap = None\r
322 Map = None\r
174a9d3c 323 FileContent = ConvertSpecialChar(open(IniFile, 'r').readlines())\r
4234283c
LG
324 LastIndex = 0\r
325 for Index in range(0, len(FileContent)):\r
326 LastIndex = Index\r
327 Line = FileContent[Index].strip()\r
421ccda3 328 if Line == '' or Line.startswith(';'):\r
4234283c
LG
329 continue\r
330 if Line[0] == TAB_SECTION_START and Line[-1] == TAB_SECTION_END:\r
331 CurrentKey = ''\r
332 SectionName = Line[1:-1].strip()\r
333 if SectionName not in SectionMap:\r
334 IniParseError(ST.ERR_SECTION_NAME_INVALID % SectionName,\r
335 IniFile, Index+1)\r
f7496d71 336\r
4234283c
LG
337 if SectionName in ParsedSection:\r
338 IniParseError(ST.ERR_SECTION_REDEFINE % SectionName,\r
339 IniFile, Index+1)\r
340 else:\r
341 ParsedSection.append(SectionName)\r
f7496d71 342\r
4234283c
LG
343 Map = SectionMap[SectionName]\r
344 continue\r
345 if not Map:\r
346 IniParseError(ST.ERR_SECTION_NAME_NONE, IniFile, Index+1)\r
347 TokenList = Line.split(TAB_EQUAL_SPLIT, 1)\r
348 TempKey = TokenList[0].strip()\r
349 #\r
350 # Value spanned multiple or same keyword appears more than one time\r
351 #\r
352 if len(TokenList) < 2 or TempKey not in Map:\r
353 if CurrentKey == '':\r
354 IniParseError(ST.ERR_KEYWORD_INVALID % TempKey,\r
355 IniFile, Index+1)\r
356 elif CurrentKey == 'FileList':\r
357 #\r
358 # Special for FileList\r
359 #\r
f7496d71 360 Valid, Cause = ParseFileList(Line, Map, CurrentKey,\r
4234283c
LG
361 PathValidator[SectionName])\r
362 if not Valid:\r
363 IniParseError(Cause, IniFile, Index+1)\r
364\r
365 else:\r
366 #\r
367 # Multiple lines for one key such as license\r
368 # Or if string on the left side of '=' is not a keyword\r
369 #\r
370 Map[CurrentKey] = ''.join([Map[CurrentKey], '\n', Line])\r
f7496d71 371 Valid, Cause = ValidateValues(CurrentKey,\r
4234283c
LG
372 Map[CurrentKey], SectionName)\r
373 if not Valid:\r
374 IniParseError(Cause, IniFile, Index+1)\r
375 continue\r
376\r
377 if (TokenList[1].strip() == ''):\r
378 IniParseError(ST.ERR_EMPTY_VALUE, IniFile, Index+1)\r
379\r
380 #\r
381 # A keyword found\r
382 #\r
383 CurrentKey = TempKey\r
384 if Map[CurrentKey]:\r
385 IniParseError(ST.ERR_KEYWORD_REDEFINE % CurrentKey,\r
386 IniFile, Index+1)\r
f7496d71 387\r
4234283c
LG
388 if id(Map) != id(PreMap) and Map['Copyright']:\r
389 PreMap = Map\r
390 Copyright = Map['Copyright'].lower()\r
391 Pos = Copyright.find('copyright')\r
392 if Pos == -1:\r
393 IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, Index)\r
394 if not Copyright[Pos + len('copyright'):].lstrip(' ').startswith('('):\r
395 IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, Index)\r
f7496d71 396\r
4234283c 397 if CurrentKey == 'FileList':\r
f7496d71 398 Valid, Cause = ParseFileList(TokenList[1], Map, CurrentKey,\r
4234283c
LG
399 PathValidator[SectionName])\r
400 if not Valid:\r
401 IniParseError(Cause, IniFile, Index+1)\r
402 else:\r
403 Map[CurrentKey] = TokenList[1].strip()\r
404 Valid, Cause = ValidateValues(CurrentKey,\r
405 Map[CurrentKey], SectionName)\r
406 if not Valid:\r
407 IniParseError(Cause, IniFile, Index+1)\r
f7496d71 408\r
4234283c
LG
409 if id(Map) != id(PreMap) and Map['Copyright'] and 'copyright' not in Map['Copyright'].lower():\r
410 IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, LastIndex)\r
411\r
412 #\r
413 # Check mandatory keys\r
f7496d71
LG
414 #\r
415 CheckMdtKeys(DistMap, IniFile, LastIndex,\r
4234283c
LG
416 (('ToolsHeader', ToolsMap), ('MiscellaneousFilesHeader', MiscMap))\r
417 )\r
f7496d71 418\r
4234283c
LG
419 return CreateXml(DistMap, ToolsMap, MiscMap, IniFile)\r
420\r
421\r
422## CheckMdtKeys\r
423#\r
424# @param MdtDistKeys: All mandatory keys\r
425# @param DistMap: Dist content\r
426# @param IniFile: Ini file\r
427# @param LastIndex: Last index of Ini file\r
428# @param Maps: Tools and Misc section name and map. (('section_name', map),*)\r
429#\r
f7496d71 430def CheckMdtKeys(DistMap, IniFile, LastIndex, Maps):\r
4234283c
LG
431 MdtDistKeys = ['Name', 'GUID', 'Version', 'Vendor', 'Copyright', 'License', 'Abstract', 'XmlSpecification']\r
432 for Key in MdtDistKeys:\r
433 if Key not in DistMap or DistMap[Key] == '':\r
434 IniParseError(ST.ERR_KEYWORD_MANDATORY % Key, IniFile, LastIndex+1)\r
f7496d71 435\r
4234283c
LG
436 if '.' not in DistMap['Version']:\r
437 DistMap['Version'] = DistMap['Version'] + '.0'\r
f7496d71 438\r
4234283c
LG
439 DistMap['Date'] = str(strftime("%Y-%m-%dT%H:%M:%S", localtime()))\r
440\r
441 #\r
442 # Check Tools Surface Area according to UPT Spec\r
443 # <Tools> {0,}\r
444 # <Header> ... </Header> {0,1}\r
445 # <Filename> ... </Filename> {1,}\r
446 # </Tools>\r
447 # <Header>\r
448 # <Name> xs:normalizedString </Name> {1}\r
449 # <Copyright> xs:string </Copyright> {0,1}\r
450 # <License> xs:string </License> {0,1}\r
451 # <Abstract> xs:normalizedString </Abstract> {0,1}\r
452 # <Description> xs:string </Description> {0,1}\r
453 # </Header>\r
454 #\r
455 for Item in Maps:\r
456 Map = Item[1]\r
457 NonEmptyKey = 0\r
458 for Key in Map:\r
459 if Map[Key]:\r
460 NonEmptyKey += 1\r
f7496d71 461\r
4234283c
LG
462 if NonEmptyKey > 0 and not Map['FileList']:\r
463 IniParseError(ST.ERR_KEYWORD_MANDATORY % (Item[0] + '.FileList'), IniFile, LastIndex+1)\r
f7496d71 464\r
4234283c
LG
465 if NonEmptyKey > 0 and not Map['Name']:\r
466 IniParseError(ST.ERR_KEYWORD_MANDATORY % (Item[0] + '.Name'), IniFile, LastIndex+1)\r
467\r
468## CreateXml\r
469#\r
470# @param DistMap: Dist Content\r
471# @param ToolsMap: Tools Content\r
472# @param MiscMap: Misc Content\r
473# @param IniFile: Ini File\r
474#\r
f7496d71 475def CreateXml(DistMap, ToolsMap, MiscMap, IniFile):\r
4234283c
LG
476 Attrs = [['xmlns', 'http://www.uefi.org/2011/1.1'],\r
477 ['xmlns:xsi', 'http:/www.w3.org/2001/XMLSchema-instance'],\r
478 ]\r
479 Root = CreateXmlElement('DistributionPackage', '', [], Attrs)\r
480 CreateHeaderXml(DistMap, Root)\r
481 CreateToolsXml(ToolsMap, Root, 'Tools')\r
482 CreateToolsXml(MiscMap, Root, 'MiscellaneousFiles')\r
483\r
484 FileAndExt = IniFile.rsplit('.', 1)\r
485 if len(FileAndExt) > 1:\r
486 FileName = FileAndExt[0] + '.xml'\r
487 else:\r
488 FileName = IniFile + '.xml'\r
489 File = open(FileName, 'w')\r
f7496d71 490\r
4234283c
LG
491 try:\r
492 File.write(Root.toprettyxml(indent = ' '))\r
493 finally:\r
494 File.close()\r
495 return FileName\r
496\r