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