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