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