2 # This file is for converting package information data file to xml file.
4 # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
6 # SPDX-License-Identifier: BSD-2-Clause-Patent
15 from time
import strftime
16 from time
import localtime
18 import Logger
.Log
as Logger
19 from Logger
.ToolError
import UPT_INI_PARSE_ERROR
20 from Logger
.ToolError
import FILE_NOT_FOUND
21 from Library
.Xml
.XmlRoutines
import CreateXmlElement
22 from Library
.DataType
import TAB_VALUE_SPLIT
23 from Library
.DataType
import TAB_EQUAL_SPLIT
24 from Library
.DataType
import TAB_SECTION_START
25 from Library
.DataType
import TAB_SECTION_END
26 from Logger
import StringTable
as ST
27 from Library
.StringUtils
import ConvertSpecialChar
28 from Library
.ParserValidate
import IsValidPath
29 from Library
import GlobalData
37 def IniParseError(Error
, File
, Line
):
38 Logger
.Error("UPT", UPT_INI_PARSE_ERROR
, File
=File
,
39 Line
=Line
, ExtraData
=Error
)
43 # @param Path: Path to be checked
45 def __ValidatePath(Path
, Root
):
47 if os
.path
.isabs(Path
) or not IsValidPath(Path
, Root
):
48 return False, ST
.ERR_FILELIST_LOCATION
% (Root
, Path
)
53 # @param Filename: File to be checked
55 def ValidateMiscFile(Filename
):
56 Root
= GlobalData
.gWORKSPACE
57 return __ValidatePath(Filename
, Root
)
61 # @param Filename: File to be checked
63 def ValidateToolsFile(Filename
):
64 Valid
, Cause
= False, ''
65 if not Valid
and 'EDK_TOOLS_PATH' in os
.environ
:
66 Valid
, Cause
= __ValidatePath(Filename
, os
.environ
['EDK_TOOLS_PATH'])
68 Valid
, Cause
= __ValidatePath(Filename
, GlobalData
.gWORKSPACE
)
75 # @param CurrentKey: CurrentKey
76 # @param PathFunc: Path validate function
78 def ParseFileList(Line
, Map
, CurrentKey
, PathFunc
):
80 TokenList
= Line
.split(TAB_VALUE_SPLIT
)
81 if len(TokenList
) > 0:
82 Path
= TokenList
[0].strip().replace('\\', '/')
84 return False, ST
.ERR_WRONG_FILELIST_FORMAT
85 Valid
, Cause
= PathFunc(Path
)
88 FileList
[0] = TokenList
[0].strip()
89 for Token
in TokenList
[1:]:
90 Attr
= Token
.split(TAB_EQUAL_SPLIT
)
91 if len(Attr
) != 2 or not Attr
[0].strip() or not Attr
[1].strip():
92 return False, ST
.ERR_WRONG_FILELIST_FORMAT
96 if Key
not in ['OS', 'Executable']:
97 return False, ST
.ERR_UNKNOWN_FILELIST_ATTR
% Key
99 if Key
== 'OS' and Val
not in ["Win32", "Win64", "Linux32",
100 "Linux64", "OS/X32", "OS/X64",
101 "GenericWin", "GenericNix"]:
102 return False, ST
.ERR_FILELIST_ATTR
% 'OS'
103 elif Key
== 'Executable' and Val
not in ['true', 'false']:
104 return False, ST
.ERR_FILELIST_ATTR
% 'Executable'
105 FileList
[1][Key
] = Val
107 Map
[CurrentKey
].append(FileList
)
110 ## Create header XML file
112 # @param DistMap: DistMap
115 def CreateHeaderXml(DistMap
, Root
):
116 Element1
= CreateXmlElement('Name', DistMap
['Name'],
117 [], [['BaseName', DistMap
['BaseName']]])
118 Element2
= CreateXmlElement('GUID', DistMap
['GUID'],
119 [], [['Version', DistMap
['Version']]])
120 AttributeList
= [['ReadOnly', DistMap
['ReadOnly']],
121 ['RePackage', DistMap
['RePackage']]]
122 NodeList
= [Element1
,
124 ['Vendor', DistMap
['Vendor']],
125 ['Date', DistMap
['Date']],
126 ['Copyright', DistMap
['Copyright']],
127 ['License', DistMap
['License']],
128 ['Abstract', DistMap
['Abstract']],
129 ['Description', DistMap
['Description']],
130 ['Signature', DistMap
['Signature']],
131 ['XmlSpecification', DistMap
['XmlSpecification']],
133 Root
.appendChild(CreateXmlElement('DistributionHeader', '',
134 NodeList
, AttributeList
))
136 ## Create tools XML file
142 def CreateToolsXml(Map
, Root
, Tag
):
144 # Check if all elements in this section are empty
147 if len(Map
[Key
]) > 0:
152 NodeList
= [['Name', Map
['Name']],
153 ['Copyright', Map
['Copyright']],
154 ['License', Map
['License']],
155 ['Abstract', Map
['Abstract']],
156 ['Description', Map
['Description']],
158 HeaderNode
= CreateXmlElement('Header', '', NodeList
, [])
159 NodeList
= [HeaderNode
]
161 for File
in Map
['FileList']:
164 AttrList
.append([Key
, File
[1][Key
]])
165 NodeList
.append(CreateXmlElement('Filename', File
[0], [], AttrList
))
166 Root
.appendChild(CreateXmlElement(Tag
, '', NodeList
, []))
171 # @param Value: Value
172 # @param SectionName: SectionName
174 def ValidateValues(Key
, Value
, SectionName
):
175 if SectionName
== 'DistributionHeader':
176 Valid
, Cause
= ValidateRegValues(Key
, Value
)
179 Valid
= __ValidateDistHeader(Key
, Value
)
181 return Valid
, ST
.ERR_VALUE_INVALID
% (Key
, SectionName
)
183 Valid
= __ValidateOtherHeader(Key
, Value
)
185 return Valid
, ST
.ERR_VALUE_INVALID
% (Key
, SectionName
)
191 # @param Value: Value
193 def ValidateRegValues(Key
, Value
):
196 ('true|false', ST
.ERR_BOOLEAN_VALUE
% (Key
, Value
)),
198 ('true|false', ST
.ERR_BOOLEAN_VALUE
% (Key
, Value
)),
200 ('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}'
201 '-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}',
202 ST
.ERR_GUID_VALUE
% Value
),
203 'Version' : ('[0-9]+(\.[0-9]+)?', ST
.ERR_VERSION_VALUE
% \
205 'XmlSpecification' : ('1\.1', ST
.ERR_VERSION_XMLSPEC
% Value
)
207 if Key
not in ValidateMap
:
209 Elem
= ValidateMap
[Key
]
210 Match
= re
.compile(Elem
[0]).match(Value
)
211 if Match
and Match
.start() == 0 and Match
.end() == len(Value
):
213 return False, Elem
[1]
215 ## __ValidateDistHeaderName
219 def __ValidateDistHeaderName(Name
):
224 if ord(Char
) < 0x20 or ord(Char
) >= 0x7f:
228 ## __ValidateDistHeaderBaseName
230 # @param BaseName: BaseName
232 def __ValidateDistHeaderBaseName(BaseName
):
235 # if CheckLen and len(BaseName) < 2:
237 if not BaseName
[0].isalnum() and BaseName
[0] != '_':
239 for Char
in BaseName
[1:]:
240 if not Char
.isalnum() and Char
not in '-_':
244 ## __ValidateDistHeaderAbstract
246 # @param Abstract: Abstract
248 def __ValidateDistHeaderAbstract(Abstract
):
249 return '\t' not in Abstract
and len(Abstract
.splitlines()) == 1
251 ## __ValidateOtherHeaderAbstract
253 # @param Abstract: Abstract
255 def __ValidateOtherHeaderAbstract(Abstract
):
256 return __ValidateDistHeaderAbstract(Abstract
)
258 ## __ValidateDistHeader
261 # @param Value: Value
263 def __ValidateDistHeader(Key
, Value
):
265 'Name' : __ValidateDistHeaderName
,
266 'BaseName' : __ValidateDistHeaderBaseName
,
267 'Abstract' : __ValidateDistHeaderAbstract
,
268 'Vendor' : __ValidateDistHeaderAbstract
270 return not (Value
and Key
in ValidateMap
and not ValidateMap
[Key
](Value
))
272 ## __ValidateOtherHeader
275 # @param Value: Value
277 def __ValidateOtherHeader(Key
, Value
):
279 'Name' : __ValidateDistHeaderName
,
280 'Abstract' : __ValidateOtherHeaderAbstract
282 return not (Value
and Key
in ValidateMap
and not ValidateMap
[Key
](Value
))
284 ## Convert ini file to xml file
288 def IniToXml(IniFile
):
289 if not os
.path
.exists(IniFile
):
290 Logger
.Error("UPT", FILE_NOT_FOUND
, ST
.ERR_TEMPLATE_NOTFOUND
% IniFile
)
292 DistMap
= {'ReadOnly' : '', 'RePackage' : '', 'Name' : '',
293 'BaseName' : '', 'GUID' : '', 'Version' : '', 'Vendor' : '',
294 'Date' : '', 'Copyright' : '', 'License' : '', 'Abstract' : '',
295 'Description' : '', 'Signature' : '', 'XmlSpecification' : ''
298 ToolsMap
= {'Name' : '', 'Copyright' : '', 'License' : '',
299 'Abstract' : '', 'Description' : '', 'FileList' : []}
301 # Only FileList is a list: [['file1', {}], ['file2', {}], ...]
303 MiscMap
= {'Name' : '', 'Copyright' : '', 'License' : '',
304 'Abstract' : '', 'Description' : '', 'FileList' : []}
307 'DistributionHeader' : DistMap
,
308 'ToolsHeader' : ToolsMap
,
309 'MiscellaneousFilesHeader' : MiscMap
313 'ToolsHeader' : ValidateToolsFile
,
314 'MiscellaneousFilesHeader' : ValidateMiscFile
323 FileContent
= ConvertSpecialChar(open(IniFile
, 'r').readlines())
325 for Index
in range(0, len(FileContent
)):
327 Line
= FileContent
[Index
].strip()
328 if Line
== '' or Line
.startswith(';'):
330 if Line
[0] == TAB_SECTION_START
and Line
[-1] == TAB_SECTION_END
:
332 SectionName
= Line
[1:-1].strip()
333 if SectionName
not in SectionMap
:
334 IniParseError(ST
.ERR_SECTION_NAME_INVALID
% SectionName
,
337 if SectionName
in ParsedSection
:
338 IniParseError(ST
.ERR_SECTION_REDEFINE
% SectionName
,
341 ParsedSection
.append(SectionName
)
343 Map
= SectionMap
[SectionName
]
346 IniParseError(ST
.ERR_SECTION_NAME_NONE
, IniFile
, Index
+1)
347 TokenList
= Line
.split(TAB_EQUAL_SPLIT
, 1)
348 TempKey
= TokenList
[0].strip()
350 # Value spanned multiple or same keyword appears more than one time
352 if len(TokenList
) < 2 or TempKey
not in Map
:
354 IniParseError(ST
.ERR_KEYWORD_INVALID
% TempKey
,
356 elif CurrentKey
== 'FileList':
358 # Special for FileList
360 Valid
, Cause
= ParseFileList(Line
, Map
, CurrentKey
,
361 PathValidator
[SectionName
])
363 IniParseError(Cause
, IniFile
, Index
+1)
367 # Multiple lines for one key such as license
368 # Or if string on the left side of '=' is not a keyword
370 Map
[CurrentKey
] = ''.join([Map
[CurrentKey
], '\n', Line
])
371 Valid
, Cause
= ValidateValues(CurrentKey
,
372 Map
[CurrentKey
], SectionName
)
374 IniParseError(Cause
, IniFile
, Index
+1)
377 if (TokenList
[1].strip() == ''):
378 IniParseError(ST
.ERR_EMPTY_VALUE
, IniFile
, Index
+1)
385 IniParseError(ST
.ERR_KEYWORD_REDEFINE
% CurrentKey
,
388 if id(Map
) != id(PreMap
) and Map
['Copyright']:
390 Copyright
= Map
['Copyright'].lower()
391 Pos
= Copyright
.find('copyright')
393 IniParseError(ST
.ERR_COPYRIGHT_CONTENT
, IniFile
, Index
)
394 if not Copyright
[Pos
+ len('copyright'):].lstrip(' ').startswith('('):
395 IniParseError(ST
.ERR_COPYRIGHT_CONTENT
, IniFile
, Index
)
397 if CurrentKey
== 'FileList':
398 Valid
, Cause
= ParseFileList(TokenList
[1], Map
, CurrentKey
,
399 PathValidator
[SectionName
])
401 IniParseError(Cause
, IniFile
, Index
+1)
403 Map
[CurrentKey
] = TokenList
[1].strip()
404 Valid
, Cause
= ValidateValues(CurrentKey
,
405 Map
[CurrentKey
], SectionName
)
407 IniParseError(Cause
, IniFile
, Index
+1)
409 if id(Map
) != id(PreMap
) and Map
['Copyright'] and 'copyright' not in Map
['Copyright'].lower():
410 IniParseError(ST
.ERR_COPYRIGHT_CONTENT
, IniFile
, LastIndex
)
413 # Check mandatory keys
415 CheckMdtKeys(DistMap
, IniFile
, LastIndex
,
416 (('ToolsHeader', ToolsMap
), ('MiscellaneousFilesHeader', MiscMap
))
419 return CreateXml(DistMap
, ToolsMap
, MiscMap
, IniFile
)
424 # @param MdtDistKeys: All mandatory keys
425 # @param DistMap: Dist content
426 # @param IniFile: Ini file
427 # @param LastIndex: Last index of Ini file
428 # @param Maps: Tools and Misc section name and map. (('section_name', map),*)
430 def CheckMdtKeys(DistMap
, IniFile
, LastIndex
, Maps
):
431 MdtDistKeys
= ['Name', 'GUID', 'Version', 'Vendor', 'Copyright', 'License', 'Abstract', 'XmlSpecification']
432 for Key
in MdtDistKeys
:
433 if Key
not in DistMap
or DistMap
[Key
] == '':
434 IniParseError(ST
.ERR_KEYWORD_MANDATORY
% Key
, IniFile
, LastIndex
+1)
436 if '.' not in DistMap
['Version']:
437 DistMap
['Version'] = DistMap
['Version'] + '.0'
439 DistMap
['Date'] = str(strftime("%Y-%m-%dT%H:%M:%S", localtime()))
442 # Check Tools Surface Area according to UPT Spec
444 # <Header> ... </Header> {0,1}
445 # <Filename> ... </Filename> {1,}
448 # <Name> xs:normalizedString </Name> {1}
449 # <Copyright> xs:string </Copyright> {0,1}
450 # <License> xs:string </License> {0,1}
451 # <Abstract> xs:normalizedString </Abstract> {0,1}
452 # <Description> xs:string </Description> {0,1}
462 if NonEmptyKey
> 0 and not Map
['FileList']:
463 IniParseError(ST
.ERR_KEYWORD_MANDATORY
% (Item
[0] + '.FileList'), IniFile
, LastIndex
+1)
465 if NonEmptyKey
> 0 and not Map
['Name']:
466 IniParseError(ST
.ERR_KEYWORD_MANDATORY
% (Item
[0] + '.Name'), IniFile
, LastIndex
+1)
470 # @param DistMap: Dist Content
471 # @param ToolsMap: Tools Content
472 # @param MiscMap: Misc Content
473 # @param IniFile: Ini File
475 def CreateXml(DistMap
, ToolsMap
, MiscMap
, IniFile
):
476 Attrs
= [['xmlns', 'http://www.uefi.org/2011/1.1'],
477 ['xmlns:xsi', 'http:/www.w3.org/2001/XMLSchema-instance'],
479 Root
= CreateXmlElement('DistributionPackage', '', [], Attrs
)
480 CreateHeaderXml(DistMap
, Root
)
481 CreateToolsXml(ToolsMap
, Root
, 'Tools')
482 CreateToolsXml(MiscMap
, Root
, 'MiscellaneousFiles')
484 FileAndExt
= IniFile
.rsplit('.', 1)
485 if len(FileAndExt
) > 1:
486 FileName
= FileAndExt
[0] + '.xml'
488 FileName
= IniFile
+ '.xml'
489 File
= open(FileName
, 'w')
492 File
.write(Root
.toprettyxml(indent
= ' '))