2 # This file is for converting package information data file to xml file.
4 # Copyright (c) 2011, 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
42 def IniParseError(Error
, File
, Line
):
43 Logger
.Error("UPT", UPT_INI_PARSE_ERROR
, File
=File
,
44 Line
=Line
, ExtraData
=Error
)
48 # @param Path: Path to be checked
50 def __ValidatePath(Path
, Root
):
52 if os
.path
.isabs(Path
) or not IsValidPath(Path
, Root
):
53 return False, ST
.ERR_FILELIST_LOCATION
% (Root
, Path
)
58 # @param Filename: File to be checked
60 def ValidateMiscFile(Filename
):
62 if 'WORKSPACE' in os
.environ
:
63 Root
= os
.environ
['WORKSPACE']
64 return __ValidatePath(Filename
, Root
)
68 # @param Filename: File to be checked
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'])
82 # @param CurrentKey: CurrentKey
83 # @param PathFunc: Path validate function
85 def ParseFileList(Line
, Map
, CurrentKey
, PathFunc
):
87 TokenList
= Line
.split(TAB_VALUE_SPLIT
)
88 if len(TokenList
) > 0:
89 Path
= TokenList
[0].strip().replace('\\', '/')
91 return False, ST
.ERR_WRONG_FILELIST_FORMAT
92 Valid
, Cause
= PathFunc(Path
)
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
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
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
114 Map
[CurrentKey
].append(FileList
)
117 ## Create header XML file
119 # @param DistMap: DistMap
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
,
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']],
140 Root
.appendChild(CreateXmlElement('DistributionHeader', '',
141 NodeList
, AttributeList
))
143 ## Create tools XML file
149 def CreateToolsXml(Map
, Root
, Tag
):
151 # Check if all elements in this section are empty
154 if len(Map
[Key
]) > 0:
159 NodeList
= [['Name', Map
['Name']],
160 ['Copyright', Map
['Copyright']],
161 ['License', Map
['License']],
162 ['Abstract', Map
['Abstract']],
163 ['Description', Map
['Description']],
165 HeaderNode
= CreateXmlElement('Header', '', NodeList
, [])
166 NodeList
= [HeaderNode
]
168 for File
in Map
['FileList']:
171 AttrList
.append([Key
, File
[1][Key
]])
172 NodeList
.append(CreateXmlElement('Filename', File
[0], [], AttrList
))
173 Root
.appendChild(CreateXmlElement(Tag
, '', NodeList
, []))
178 # @param Value: Value
179 # @param SectionName: SectionName
181 def ValidateValues(Key
, Value
, SectionName
):
182 if SectionName
== 'DistributionHeader':
183 Valid
, Cause
= ValidateRegValues(Key
, Value
)
186 Valid
= __ValidateDistHeader(Key
, Value
)
188 return Valid
, ST
.ERR_VALUE_INVALID
% (Key
, SectionName
)
190 Valid
= __ValidateOtherHeader(Key
, Value
)
192 return Valid
, ST
.ERR_VALUE_INVALID
% (Key
, SectionName
)
198 # @param Value: Value
200 def ValidateRegValues(Key
, Value
):
203 ('true|false', ST
.ERR_BOOLEAN_VALUE
% (Key
, Value
)),
205 ('true|false', ST
.ERR_BOOLEAN_VALUE
% (Key
, Value
)),
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
% \
212 'XmlSpecification' : ('1\.1', ST
.ERR_VERSION_XMLSPEC
% Value
)
214 if Key
not in ValidateMap
:
216 Elem
= ValidateMap
[Key
]
217 Match
= re
.compile(Elem
[0]).match(Value
)
218 if Match
and Match
.start() == 0 and Match
.end() == len(Value
):
220 return False, Elem
[1]
222 ## __ValidateDistHeaderName
226 def __ValidateDistHeaderName(Name
):
231 if ord(Char
) < 0x20 or ord(Char
) >= 0x7f:
235 ## __ValidateDistHeaderBaseName
237 # @param BaseName: BaseName
239 def __ValidateDistHeaderBaseName(BaseName
):
242 # if CheckLen and len(BaseName) < 2:
244 if not BaseName
[0].isalnum() and BaseName
[0] != '_':
246 for Char
in BaseName
[1:]:
247 if not Char
.isalnum() and Char
not in '-_':
251 ## __ValidateDistHeaderAbstract
253 # @param Abstract: Abstract
255 def __ValidateDistHeaderAbstract(Abstract
):
256 return '\t' not in Abstract
and len(Abstract
.splitlines()) == 1
258 ## __ValidateOtherHeaderAbstract
260 # @param Abstract: Abstract
262 def __ValidateOtherHeaderAbstract(Abstract
):
263 return __ValidateDistHeaderAbstract(Abstract
)
265 ## __ValidateDistHeader
268 # @param Value: Value
270 def __ValidateDistHeader(Key
, Value
):
272 'Name' : __ValidateDistHeaderName
,
273 'BaseName' : __ValidateDistHeaderBaseName
,
274 'Abstract' : __ValidateDistHeaderAbstract
,
275 'Vendor' : __ValidateDistHeaderAbstract
277 return not (Value
and Key
in ValidateMap
and not ValidateMap
[Key
](Value
))
279 ## __ValidateOtherHeader
282 # @param Value: Value
284 def __ValidateOtherHeader(Key
, Value
):
286 'Name' : __ValidateDistHeaderName
,
287 'Abstract' : __ValidateOtherHeaderAbstract
289 return not (Value
and Key
in ValidateMap
and not ValidateMap
[Key
](Value
))
291 ## Convert ini file to xml file
295 def IniToXml(IniFile
):
296 if not os
.path
.exists(IniFile
):
297 Logger
.Error("UPT", FILE_NOT_FOUND
, ST
.ERR_TEMPLATE_NOTFOUND
% IniFile
)
299 DistMap
= {'ReadOnly' : '', 'RePackage' : '', 'Name' : '',
300 'BaseName' : '', 'GUID' : '', 'Version' : '', 'Vendor' : '',
301 'Date' : '', 'Copyright' : '', 'License' : '', 'Abstract' : '',
302 'Description' : '', 'Signature' : '', 'XmlSpecification' : ''
305 ToolsMap
= {'Name' : '', 'Copyright' : '', 'License' : '',
306 'Abstract' : '', 'Description' : '', 'FileList' : []}
308 # Only FileList is a list: [['file1', {}], ['file2', {}], ...]
310 MiscMap
= {'Name' : '', 'Copyright' : '', 'License' : '',
311 'Abstract' : '', 'Description' : '', 'FileList' : []}
314 'DistributionHeader' : DistMap
,
315 'ToolsHeader' : ToolsMap
,
316 'MiscellaneousFilesHeader' : MiscMap
320 'ToolsHeader' : ValidateToolsFile
,
321 'MiscellaneousFilesHeader' : ValidateMiscFile
330 FileContent
= ConvertSpecialChar(open(IniFile
, 'rb').readlines())
332 for Index
in range(0, len(FileContent
)):
334 Line
= FileContent
[Index
].strip()
337 if Line
[0] == TAB_SECTION_START
and Line
[-1] == TAB_SECTION_END
:
339 SectionName
= Line
[1:-1].strip()
340 if SectionName
not in SectionMap
:
341 IniParseError(ST
.ERR_SECTION_NAME_INVALID
% SectionName
,
344 if SectionName
in ParsedSection
:
345 IniParseError(ST
.ERR_SECTION_REDEFINE
% SectionName
,
348 ParsedSection
.append(SectionName
)
350 Map
= SectionMap
[SectionName
]
353 IniParseError(ST
.ERR_SECTION_NAME_NONE
, IniFile
, Index
+1)
354 TokenList
= Line
.split(TAB_EQUAL_SPLIT
, 1)
355 TempKey
= TokenList
[0].strip()
357 # Value spanned multiple or same keyword appears more than one time
359 if len(TokenList
) < 2 or TempKey
not in Map
:
361 IniParseError(ST
.ERR_KEYWORD_INVALID
% TempKey
,
363 elif CurrentKey
== 'FileList':
365 # Special for FileList
367 Valid
, Cause
= ParseFileList(Line
, Map
, CurrentKey
,
368 PathValidator
[SectionName
])
370 IniParseError(Cause
, IniFile
, Index
+1)
374 # Multiple lines for one key such as license
375 # Or if string on the left side of '=' is not a keyword
377 Map
[CurrentKey
] = ''.join([Map
[CurrentKey
], '\n', Line
])
378 Valid
, Cause
= ValidateValues(CurrentKey
,
379 Map
[CurrentKey
], SectionName
)
381 IniParseError(Cause
, IniFile
, Index
+1)
384 if (TokenList
[1].strip() == ''):
385 IniParseError(ST
.ERR_EMPTY_VALUE
, IniFile
, Index
+1)
392 IniParseError(ST
.ERR_KEYWORD_REDEFINE
% CurrentKey
,
395 if id(Map
) != id(PreMap
) and Map
['Copyright']:
397 Copyright
= Map
['Copyright'].lower()
398 Pos
= Copyright
.find('copyright')
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
)
404 if CurrentKey
== 'FileList':
405 Valid
, Cause
= ParseFileList(TokenList
[1], Map
, CurrentKey
,
406 PathValidator
[SectionName
])
408 IniParseError(Cause
, IniFile
, Index
+1)
410 Map
[CurrentKey
] = TokenList
[1].strip()
411 Valid
, Cause
= ValidateValues(CurrentKey
,
412 Map
[CurrentKey
], SectionName
)
414 IniParseError(Cause
, IniFile
, Index
+1)
416 if id(Map
) != id(PreMap
) and Map
['Copyright'] and 'copyright' not in Map
['Copyright'].lower():
417 IniParseError(ST
.ERR_COPYRIGHT_CONTENT
, IniFile
, LastIndex
)
420 # Check mandatory keys
422 CheckMdtKeys(DistMap
, IniFile
, LastIndex
,
423 (('ToolsHeader', ToolsMap
), ('MiscellaneousFilesHeader', MiscMap
))
426 return CreateXml(DistMap
, ToolsMap
, MiscMap
, IniFile
)
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),*)
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)
443 if '.' not in DistMap
['Version']:
444 DistMap
['Version'] = DistMap
['Version'] + '.0'
446 DistMap
['Date'] = str(strftime("%Y-%m-%dT%H:%M:%S", localtime()))
449 # Check Tools Surface Area according to UPT Spec
451 # <Header> ... </Header> {0,1}
452 # <Filename> ... </Filename> {1,}
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}
469 if NonEmptyKey
> 0 and not Map
['FileList']:
470 IniParseError(ST
.ERR_KEYWORD_MANDATORY
% (Item
[0] + '.FileList'), IniFile
, LastIndex
+1)
472 if NonEmptyKey
> 0 and not Map
['Name']:
473 IniParseError(ST
.ERR_KEYWORD_MANDATORY
% (Item
[0] + '.Name'), IniFile
, LastIndex
+1)
477 # @param DistMap: Dist Content
478 # @param ToolsMap: Tools Content
479 # @param MiscMap: Misc Content
480 # @param IniFile: Ini File
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'],
486 Root
= CreateXmlElement('DistributionPackage', '', [], Attrs
)
487 CreateHeaderXml(DistMap
, Root
)
488 CreateToolsXml(ToolsMap
, Root
, 'Tools')
489 CreateToolsXml(MiscMap
, Root
, 'MiscellaneousFiles')
491 FileAndExt
= IniFile
.rsplit('.', 1)
492 if len(FileAndExt
) > 1:
493 FileName
= FileAndExt
[0] + '.xml'
495 FileName
= IniFile
+ '.xml'
496 File
= open(FileName
, 'w')
499 File
.write(Root
.toprettyxml(indent
= ' '))