2 # This file is for converting package information data file to xml file.
4 # Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR>
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
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.
21 from time
import strftime
22 from time
import localtime
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
43 def IniParseError(Error
, File
, Line
):
44 Logger
.Error("UPT", UPT_INI_PARSE_ERROR
, File
=File
,
45 Line
=Line
, ExtraData
=Error
)
49 # @param Path: Path to be checked
51 def __ValidatePath(Path
, Root
):
53 if os
.path
.isabs(Path
) or not IsValidPath(Path
, Root
):
54 return False, ST
.ERR_FILELIST_LOCATION
% (Root
, Path
)
59 # @param Filename: File to be checked
61 def ValidateMiscFile(Filename
):
62 Root
= GlobalData
.gWORKSPACE
63 return __ValidatePath(Filename
, Root
)
67 # @param Filename: File to be checked
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'])
74 Valid
, Cause
= __ValidatePath(Filename
, GlobalData
.gWORKSPACE
)
81 # @param CurrentKey: CurrentKey
82 # @param PathFunc: Path validate function
84 def ParseFileList(Line
, Map
, CurrentKey
, PathFunc
):
86 TokenList
= Line
.split(TAB_VALUE_SPLIT
)
87 if len(TokenList
) > 0:
88 Path
= TokenList
[0].strip().replace('\\', '/')
90 return False, ST
.ERR_WRONG_FILELIST_FORMAT
91 Valid
, Cause
= PathFunc(Path
)
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
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
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
113 Map
[CurrentKey
].append(FileList
)
116 ## Create header XML file
118 # @param DistMap: DistMap
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
,
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']],
139 Root
.appendChild(CreateXmlElement('DistributionHeader', '',
140 NodeList
, AttributeList
))
142 ## Create tools XML file
148 def CreateToolsXml(Map
, Root
, Tag
):
150 # Check if all elements in this section are empty
153 if len(Map
[Key
]) > 0:
158 NodeList
= [['Name', Map
['Name']],
159 ['Copyright', Map
['Copyright']],
160 ['License', Map
['License']],
161 ['Abstract', Map
['Abstract']],
162 ['Description', Map
['Description']],
164 HeaderNode
= CreateXmlElement('Header', '', NodeList
, [])
165 NodeList
= [HeaderNode
]
167 for File
in Map
['FileList']:
170 AttrList
.append([Key
, File
[1][Key
]])
171 NodeList
.append(CreateXmlElement('Filename', File
[0], [], AttrList
))
172 Root
.appendChild(CreateXmlElement(Tag
, '', NodeList
, []))
177 # @param Value: Value
178 # @param SectionName: SectionName
180 def ValidateValues(Key
, Value
, SectionName
):
181 if SectionName
== 'DistributionHeader':
182 Valid
, Cause
= ValidateRegValues(Key
, Value
)
185 Valid
= __ValidateDistHeader(Key
, Value
)
187 return Valid
, ST
.ERR_VALUE_INVALID
% (Key
, SectionName
)
189 Valid
= __ValidateOtherHeader(Key
, Value
)
191 return Valid
, ST
.ERR_VALUE_INVALID
% (Key
, SectionName
)
197 # @param Value: Value
199 def ValidateRegValues(Key
, Value
):
202 ('true|false', ST
.ERR_BOOLEAN_VALUE
% (Key
, Value
)),
204 ('true|false', ST
.ERR_BOOLEAN_VALUE
% (Key
, Value
)),
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
% \
211 'XmlSpecification' : ('1\.1', ST
.ERR_VERSION_XMLSPEC
% Value
)
213 if Key
not in ValidateMap
:
215 Elem
= ValidateMap
[Key
]
216 Match
= re
.compile(Elem
[0]).match(Value
)
217 if Match
and Match
.start() == 0 and Match
.end() == len(Value
):
219 return False, Elem
[1]
221 ## __ValidateDistHeaderName
225 def __ValidateDistHeaderName(Name
):
230 if ord(Char
) < 0x20 or ord(Char
) >= 0x7f:
234 ## __ValidateDistHeaderBaseName
236 # @param BaseName: BaseName
238 def __ValidateDistHeaderBaseName(BaseName
):
241 # if CheckLen and len(BaseName) < 2:
243 if not BaseName
[0].isalnum() and BaseName
[0] != '_':
245 for Char
in BaseName
[1:]:
246 if not Char
.isalnum() and Char
not in '-_':
250 ## __ValidateDistHeaderAbstract
252 # @param Abstract: Abstract
254 def __ValidateDistHeaderAbstract(Abstract
):
255 return '\t' not in Abstract
and len(Abstract
.splitlines()) == 1
257 ## __ValidateOtherHeaderAbstract
259 # @param Abstract: Abstract
261 def __ValidateOtherHeaderAbstract(Abstract
):
262 return __ValidateDistHeaderAbstract(Abstract
)
264 ## __ValidateDistHeader
267 # @param Value: Value
269 def __ValidateDistHeader(Key
, Value
):
271 'Name' : __ValidateDistHeaderName
,
272 'BaseName' : __ValidateDistHeaderBaseName
,
273 'Abstract' : __ValidateDistHeaderAbstract
,
274 'Vendor' : __ValidateDistHeaderAbstract
276 return not (Value
and Key
in ValidateMap
and not ValidateMap
[Key
](Value
))
278 ## __ValidateOtherHeader
281 # @param Value: Value
283 def __ValidateOtherHeader(Key
, Value
):
285 'Name' : __ValidateDistHeaderName
,
286 'Abstract' : __ValidateOtherHeaderAbstract
288 return not (Value
and Key
in ValidateMap
and not ValidateMap
[Key
](Value
))
290 ## Convert ini file to xml file
294 def IniToXml(IniFile
):
295 if not os
.path
.exists(IniFile
):
296 Logger
.Error("UPT", FILE_NOT_FOUND
, ST
.ERR_TEMPLATE_NOTFOUND
% IniFile
)
298 DistMap
= {'ReadOnly' : '', 'RePackage' : '', 'Name' : '',
299 'BaseName' : '', 'GUID' : '', 'Version' : '', 'Vendor' : '',
300 'Date' : '', 'Copyright' : '', 'License' : '', 'Abstract' : '',
301 'Description' : '', 'Signature' : '', 'XmlSpecification' : ''
304 ToolsMap
= {'Name' : '', 'Copyright' : '', 'License' : '',
305 'Abstract' : '', 'Description' : '', 'FileList' : []}
307 # Only FileList is a list: [['file1', {}], ['file2', {}], ...]
309 MiscMap
= {'Name' : '', 'Copyright' : '', 'License' : '',
310 'Abstract' : '', 'Description' : '', 'FileList' : []}
313 'DistributionHeader' : DistMap
,
314 'ToolsHeader' : ToolsMap
,
315 'MiscellaneousFilesHeader' : MiscMap
319 'ToolsHeader' : ValidateToolsFile
,
320 'MiscellaneousFilesHeader' : ValidateMiscFile
329 FileContent
= ConvertSpecialChar(open(IniFile
, 'rb').readlines())
331 for Index
in range(0, len(FileContent
)):
333 Line
= FileContent
[Index
].strip()
334 if Line
== '' or Line
.startswith(';'):
336 if Line
[0] == TAB_SECTION_START
and Line
[-1] == TAB_SECTION_END
:
338 SectionName
= Line
[1:-1].strip()
339 if SectionName
not in SectionMap
:
340 IniParseError(ST
.ERR_SECTION_NAME_INVALID
% SectionName
,
343 if SectionName
in ParsedSection
:
344 IniParseError(ST
.ERR_SECTION_REDEFINE
% SectionName
,
347 ParsedSection
.append(SectionName
)
349 Map
= SectionMap
[SectionName
]
352 IniParseError(ST
.ERR_SECTION_NAME_NONE
, IniFile
, Index
+1)
353 TokenList
= Line
.split(TAB_EQUAL_SPLIT
, 1)
354 TempKey
= TokenList
[0].strip()
356 # Value spanned multiple or same keyword appears more than one time
358 if len(TokenList
) < 2 or TempKey
not in Map
:
360 IniParseError(ST
.ERR_KEYWORD_INVALID
% TempKey
,
362 elif CurrentKey
== 'FileList':
364 # Special for FileList
366 Valid
, Cause
= ParseFileList(Line
, Map
, CurrentKey
,
367 PathValidator
[SectionName
])
369 IniParseError(Cause
, IniFile
, Index
+1)
373 # Multiple lines for one key such as license
374 # Or if string on the left side of '=' is not a keyword
376 Map
[CurrentKey
] = ''.join([Map
[CurrentKey
], '\n', Line
])
377 Valid
, Cause
= ValidateValues(CurrentKey
,
378 Map
[CurrentKey
], SectionName
)
380 IniParseError(Cause
, IniFile
, Index
+1)
383 if (TokenList
[1].strip() == ''):
384 IniParseError(ST
.ERR_EMPTY_VALUE
, IniFile
, Index
+1)
391 IniParseError(ST
.ERR_KEYWORD_REDEFINE
% CurrentKey
,
394 if id(Map
) != id(PreMap
) and Map
['Copyright']:
396 Copyright
= Map
['Copyright'].lower()
397 Pos
= Copyright
.find('copyright')
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
)
403 if CurrentKey
== 'FileList':
404 Valid
, Cause
= ParseFileList(TokenList
[1], Map
, CurrentKey
,
405 PathValidator
[SectionName
])
407 IniParseError(Cause
, IniFile
, Index
+1)
409 Map
[CurrentKey
] = TokenList
[1].strip()
410 Valid
, Cause
= ValidateValues(CurrentKey
,
411 Map
[CurrentKey
], SectionName
)
413 IniParseError(Cause
, IniFile
, Index
+1)
415 if id(Map
) != id(PreMap
) and Map
['Copyright'] and 'copyright' not in Map
['Copyright'].lower():
416 IniParseError(ST
.ERR_COPYRIGHT_CONTENT
, IniFile
, LastIndex
)
419 # Check mandatory keys
421 CheckMdtKeys(DistMap
, IniFile
, LastIndex
,
422 (('ToolsHeader', ToolsMap
), ('MiscellaneousFilesHeader', MiscMap
))
425 return CreateXml(DistMap
, ToolsMap
, MiscMap
, IniFile
)
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),*)
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)
442 if '.' not in DistMap
['Version']:
443 DistMap
['Version'] = DistMap
['Version'] + '.0'
445 DistMap
['Date'] = str(strftime("%Y-%m-%dT%H:%M:%S", localtime()))
448 # Check Tools Surface Area according to UPT Spec
450 # <Header> ... </Header> {0,1}
451 # <Filename> ... </Filename> {1,}
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}
468 if NonEmptyKey
> 0 and not Map
['FileList']:
469 IniParseError(ST
.ERR_KEYWORD_MANDATORY
% (Item
[0] + '.FileList'), IniFile
, LastIndex
+1)
471 if NonEmptyKey
> 0 and not Map
['Name']:
472 IniParseError(ST
.ERR_KEYWORD_MANDATORY
% (Item
[0] + '.Name'), IniFile
, LastIndex
+1)
476 # @param DistMap: Dist Content
477 # @param ToolsMap: Tools Content
478 # @param MiscMap: Misc Content
479 # @param IniFile: Ini File
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'],
485 Root
= CreateXmlElement('DistributionPackage', '', [], Attrs
)
486 CreateHeaderXml(DistMap
, Root
)
487 CreateToolsXml(ToolsMap
, Root
, 'Tools')
488 CreateToolsXml(MiscMap
, Root
, 'MiscellaneousFiles')
490 FileAndExt
= IniFile
.rsplit('.', 1)
491 if len(FileAndExt
) > 1:
492 FileName
= FileAndExt
[0] + '.xml'
494 FileName
= IniFile
+ '.xml'
495 File
= open(FileName
, 'w')
498 File
.write(Root
.toprettyxml(indent
= ' '))