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