mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-23 16:17:40 +01:00
497 lines
16 KiB
Python
497 lines
16 KiB
Python
|
## @file
|
||
|
# This file is for converting package information data file to xml file.
|
||
|
#
|
||
|
# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
|
||
|
#
|
||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||
|
#
|
||
|
|
||
|
'''
|
||
|
IniToXml
|
||
|
'''
|
||
|
|
||
|
import os.path
|
||
|
import re
|
||
|
from time import strftime
|
||
|
from time import localtime
|
||
|
|
||
|
import Logger.Log as Logger
|
||
|
from Logger.ToolError import UPT_INI_PARSE_ERROR
|
||
|
from Logger.ToolError import FILE_NOT_FOUND
|
||
|
from Library.Xml.XmlRoutines import CreateXmlElement
|
||
|
from Library.DataType import TAB_VALUE_SPLIT
|
||
|
from Library.DataType import TAB_EQUAL_SPLIT
|
||
|
from Library.DataType import TAB_SECTION_START
|
||
|
from Library.DataType import TAB_SECTION_END
|
||
|
from Logger import StringTable as ST
|
||
|
from Library.StringUtils import ConvertSpecialChar
|
||
|
from Library.ParserValidate import IsValidPath
|
||
|
from Library import GlobalData
|
||
|
|
||
|
## log error:
|
||
|
#
|
||
|
# @param error: error
|
||
|
# @param File: File
|
||
|
# @param Line: Line
|
||
|
#
|
||
|
def IniParseError(Error, File, Line):
|
||
|
Logger.Error("UPT", UPT_INI_PARSE_ERROR, File=File,
|
||
|
Line=Line, ExtraData=Error)
|
||
|
|
||
|
## __ValidatePath
|
||
|
#
|
||
|
# @param Path: Path to be checked
|
||
|
#
|
||
|
def __ValidatePath(Path, Root):
|
||
|
Path = Path.strip()
|
||
|
if os.path.isabs(Path) or not IsValidPath(Path, Root):
|
||
|
return False, ST.ERR_FILELIST_LOCATION % (Root, Path)
|
||
|
return True, ''
|
||
|
|
||
|
## ValidateMiscFile
|
||
|
#
|
||
|
# @param Filename: File to be checked
|
||
|
#
|
||
|
def ValidateMiscFile(Filename):
|
||
|
Root = GlobalData.gWORKSPACE
|
||
|
return __ValidatePath(Filename, Root)
|
||
|
|
||
|
## ValidateToolsFile
|
||
|
#
|
||
|
# @param Filename: File to be checked
|
||
|
#
|
||
|
def ValidateToolsFile(Filename):
|
||
|
Valid, Cause = False, ''
|
||
|
if not Valid and 'EDK_TOOLS_PATH' in os.environ:
|
||
|
Valid, Cause = __ValidatePath(Filename, os.environ['EDK_TOOLS_PATH'])
|
||
|
if not Valid:
|
||
|
Valid, Cause = __ValidatePath(Filename, GlobalData.gWORKSPACE)
|
||
|
return Valid, Cause
|
||
|
|
||
|
## ParseFileList
|
||
|
#
|
||
|
# @param Line: Line
|
||
|
# @param Map: Map
|
||
|
# @param CurrentKey: CurrentKey
|
||
|
# @param PathFunc: Path validate function
|
||
|
#
|
||
|
def ParseFileList(Line, Map, CurrentKey, PathFunc):
|
||
|
FileList = ["", {}]
|
||
|
TokenList = Line.split(TAB_VALUE_SPLIT)
|
||
|
if len(TokenList) > 0:
|
||
|
Path = TokenList[0].strip().replace('\\', '/')
|
||
|
if not Path:
|
||
|
return False, ST.ERR_WRONG_FILELIST_FORMAT
|
||
|
Valid, Cause = PathFunc(Path)
|
||
|
if not Valid:
|
||
|
return Valid, Cause
|
||
|
FileList[0] = TokenList[0].strip()
|
||
|
for Token in TokenList[1:]:
|
||
|
Attr = Token.split(TAB_EQUAL_SPLIT)
|
||
|
if len(Attr) != 2 or not Attr[0].strip() or not Attr[1].strip():
|
||
|
return False, ST.ERR_WRONG_FILELIST_FORMAT
|
||
|
|
||
|
Key = Attr[0].strip()
|
||
|
Val = Attr[1].strip()
|
||
|
if Key not in ['OS', 'Executable']:
|
||
|
return False, ST.ERR_UNKNOWN_FILELIST_ATTR % Key
|
||
|
|
||
|
if Key == 'OS' and Val not in ["Win32", "Win64", "Linux32",
|
||
|
"Linux64", "OS/X32", "OS/X64",
|
||
|
"GenericWin", "GenericNix"]:
|
||
|
return False, ST.ERR_FILELIST_ATTR % 'OS'
|
||
|
elif Key == 'Executable' and Val not in ['true', 'false']:
|
||
|
return False, ST.ERR_FILELIST_ATTR % 'Executable'
|
||
|
FileList[1][Key] = Val
|
||
|
|
||
|
Map[CurrentKey].append(FileList)
|
||
|
return True, ''
|
||
|
|
||
|
## Create header XML file
|
||
|
#
|
||
|
# @param DistMap: DistMap
|
||
|
# @param Root: Root
|
||
|
#
|
||
|
def CreateHeaderXml(DistMap, Root):
|
||
|
Element1 = CreateXmlElement('Name', DistMap['Name'],
|
||
|
[], [['BaseName', DistMap['BaseName']]])
|
||
|
Element2 = CreateXmlElement('GUID', DistMap['GUID'],
|
||
|
[], [['Version', DistMap['Version']]])
|
||
|
AttributeList = [['ReadOnly', DistMap['ReadOnly']],
|
||
|
['RePackage', DistMap['RePackage']]]
|
||
|
NodeList = [Element1,
|
||
|
Element2,
|
||
|
['Vendor', DistMap['Vendor']],
|
||
|
['Date', DistMap['Date']],
|
||
|
['Copyright', DistMap['Copyright']],
|
||
|
['License', DistMap['License']],
|
||
|
['Abstract', DistMap['Abstract']],
|
||
|
['Description', DistMap['Description']],
|
||
|
['Signature', DistMap['Signature']],
|
||
|
['XmlSpecification', DistMap['XmlSpecification']],
|
||
|
]
|
||
|
Root.appendChild(CreateXmlElement('DistributionHeader', '',
|
||
|
NodeList, AttributeList))
|
||
|
|
||
|
## Create tools XML file
|
||
|
#
|
||
|
# @param Map: Map
|
||
|
# @param Root: Root
|
||
|
# @param Tag: Tag
|
||
|
#
|
||
|
def CreateToolsXml(Map, Root, Tag):
|
||
|
#
|
||
|
# Check if all elements in this section are empty
|
||
|
#
|
||
|
for Key in Map:
|
||
|
if len(Map[Key]) > 0:
|
||
|
break
|
||
|
else:
|
||
|
return
|
||
|
|
||
|
NodeList = [['Name', Map['Name']],
|
||
|
['Copyright', Map['Copyright']],
|
||
|
['License', Map['License']],
|
||
|
['Abstract', Map['Abstract']],
|
||
|
['Description', Map['Description']],
|
||
|
]
|
||
|
HeaderNode = CreateXmlElement('Header', '', NodeList, [])
|
||
|
NodeList = [HeaderNode]
|
||
|
|
||
|
for File in Map['FileList']:
|
||
|
AttrList = []
|
||
|
for Key in File[1]:
|
||
|
AttrList.append([Key, File[1][Key]])
|
||
|
NodeList.append(CreateXmlElement('Filename', File[0], [], AttrList))
|
||
|
Root.appendChild(CreateXmlElement(Tag, '', NodeList, []))
|
||
|
|
||
|
## ValidateValues
|
||
|
#
|
||
|
# @param Key: Key
|
||
|
# @param Value: Value
|
||
|
# @param SectionName: SectionName
|
||
|
#
|
||
|
def ValidateValues(Key, Value, SectionName):
|
||
|
if SectionName == 'DistributionHeader':
|
||
|
Valid, Cause = ValidateRegValues(Key, Value)
|
||
|
if not Valid:
|
||
|
return Valid, Cause
|
||
|
Valid = __ValidateDistHeader(Key, Value)
|
||
|
if not Valid:
|
||
|
return Valid, ST.ERR_VALUE_INVALID % (Key, SectionName)
|
||
|
else:
|
||
|
Valid = __ValidateOtherHeader(Key, Value)
|
||
|
if not Valid:
|
||
|
return Valid, ST.ERR_VALUE_INVALID % (Key, SectionName)
|
||
|
return True, ''
|
||
|
|
||
|
## ValidateRegValues
|
||
|
#
|
||
|
# @param Key: Key
|
||
|
# @param Value: Value
|
||
|
#
|
||
|
def ValidateRegValues(Key, Value):
|
||
|
ValidateMap = {
|
||
|
'ReadOnly' :
|
||
|
('true|false', ST.ERR_BOOLEAN_VALUE % (Key, Value)),
|
||
|
'RePackage' :
|
||
|
('true|false', ST.ERR_BOOLEAN_VALUE % (Key, Value)),
|
||
|
'GUID' :
|
||
|
('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}'
|
||
|
'-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}',
|
||
|
ST.ERR_GUID_VALUE % Value),
|
||
|
'Version' : ('[0-9]+(\.[0-9]+)?', ST.ERR_VERSION_VALUE % \
|
||
|
(Key, Value)),
|
||
|
'XmlSpecification' : ('1\.1', ST.ERR_VERSION_XMLSPEC % Value)
|
||
|
}
|
||
|
if Key not in ValidateMap:
|
||
|
return True, ''
|
||
|
Elem = ValidateMap[Key]
|
||
|
Match = re.compile(Elem[0]).match(Value)
|
||
|
if Match and Match.start() == 0 and Match.end() == len(Value):
|
||
|
return True, ''
|
||
|
return False, Elem[1]
|
||
|
|
||
|
## __ValidateDistHeaderName
|
||
|
#
|
||
|
# @param Name: Name
|
||
|
#
|
||
|
def __ValidateDistHeaderName(Name):
|
||
|
if len(Name) < 1:
|
||
|
return False
|
||
|
|
||
|
for Char in Name:
|
||
|
if ord(Char) < 0x20 or ord(Char) >= 0x7f:
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
## __ValidateDistHeaderBaseName
|
||
|
#
|
||
|
# @param BaseName: BaseName
|
||
|
#
|
||
|
def __ValidateDistHeaderBaseName(BaseName):
|
||
|
if not BaseName:
|
||
|
return False
|
||
|
# if CheckLen and len(BaseName) < 2:
|
||
|
# return False
|
||
|
if not BaseName[0].isalnum() and BaseName[0] != '_':
|
||
|
return False
|
||
|
for Char in BaseName[1:]:
|
||
|
if not Char.isalnum() and Char not in '-_':
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
## __ValidateDistHeaderAbstract
|
||
|
#
|
||
|
# @param Abstract: Abstract
|
||
|
#
|
||
|
def __ValidateDistHeaderAbstract(Abstract):
|
||
|
return '\t' not in Abstract and len(Abstract.splitlines()) == 1
|
||
|
|
||
|
## __ValidateOtherHeaderAbstract
|
||
|
#
|
||
|
# @param Abstract: Abstract
|
||
|
#
|
||
|
def __ValidateOtherHeaderAbstract(Abstract):
|
||
|
return __ValidateDistHeaderAbstract(Abstract)
|
||
|
|
||
|
## __ValidateDistHeader
|
||
|
#
|
||
|
# @param Key: Key
|
||
|
# @param Value: Value
|
||
|
#
|
||
|
def __ValidateDistHeader(Key, Value):
|
||
|
ValidateMap = {
|
||
|
'Name' : __ValidateDistHeaderName,
|
||
|
'BaseName' : __ValidateDistHeaderBaseName,
|
||
|
'Abstract' : __ValidateDistHeaderAbstract,
|
||
|
'Vendor' : __ValidateDistHeaderAbstract
|
||
|
}
|
||
|
return not (Value and Key in ValidateMap and not ValidateMap[Key](Value))
|
||
|
|
||
|
## __ValidateOtherHeader
|
||
|
#
|
||
|
# @param Key: Key
|
||
|
# @param Value: Value
|
||
|
#
|
||
|
def __ValidateOtherHeader(Key, Value):
|
||
|
ValidateMap = {
|
||
|
'Name' : __ValidateDistHeaderName,
|
||
|
'Abstract' : __ValidateOtherHeaderAbstract
|
||
|
}
|
||
|
return not (Value and Key in ValidateMap and not ValidateMap[Key](Value))
|
||
|
|
||
|
## Convert ini file to xml file
|
||
|
#
|
||
|
# @param IniFile
|
||
|
#
|
||
|
def IniToXml(IniFile):
|
||
|
if not os.path.exists(IniFile):
|
||
|
Logger.Error("UPT", FILE_NOT_FOUND, ST.ERR_TEMPLATE_NOTFOUND % IniFile)
|
||
|
|
||
|
DistMap = {'ReadOnly' : '', 'RePackage' : '', 'Name' : '',
|
||
|
'BaseName' : '', 'GUID' : '', 'Version' : '', 'Vendor' : '',
|
||
|
'Date' : '', 'Copyright' : '', 'License' : '', 'Abstract' : '',
|
||
|
'Description' : '', 'Signature' : '', 'XmlSpecification' : ''
|
||
|
}
|
||
|
|
||
|
ToolsMap = {'Name' : '', 'Copyright' : '', 'License' : '',
|
||
|
'Abstract' : '', 'Description' : '', 'FileList' : []}
|
||
|
#
|
||
|
# Only FileList is a list: [['file1', {}], ['file2', {}], ...]
|
||
|
#
|
||
|
MiscMap = {'Name' : '', 'Copyright' : '', 'License' : '',
|
||
|
'Abstract' : '', 'Description' : '', 'FileList' : []}
|
||
|
|
||
|
SectionMap = {
|
||
|
'DistributionHeader' : DistMap,
|
||
|
'ToolsHeader' : ToolsMap,
|
||
|
'MiscellaneousFilesHeader' : MiscMap
|
||
|
}
|
||
|
|
||
|
PathValidator = {
|
||
|
'ToolsHeader' : ValidateToolsFile,
|
||
|
'MiscellaneousFilesHeader' : ValidateMiscFile
|
||
|
}
|
||
|
|
||
|
ParsedSection = []
|
||
|
|
||
|
SectionName = ''
|
||
|
CurrentKey = ''
|
||
|
PreMap = None
|
||
|
Map = None
|
||
|
FileContent = ConvertSpecialChar(open(IniFile, 'r').readlines())
|
||
|
LastIndex = 0
|
||
|
for Index in range(0, len(FileContent)):
|
||
|
LastIndex = Index
|
||
|
Line = FileContent[Index].strip()
|
||
|
if Line == '' or Line.startswith(';'):
|
||
|
continue
|
||
|
if Line[0] == TAB_SECTION_START and Line[-1] == TAB_SECTION_END:
|
||
|
CurrentKey = ''
|
||
|
SectionName = Line[1:-1].strip()
|
||
|
if SectionName not in SectionMap:
|
||
|
IniParseError(ST.ERR_SECTION_NAME_INVALID % SectionName,
|
||
|
IniFile, Index+1)
|
||
|
|
||
|
if SectionName in ParsedSection:
|
||
|
IniParseError(ST.ERR_SECTION_REDEFINE % SectionName,
|
||
|
IniFile, Index+1)
|
||
|
else:
|
||
|
ParsedSection.append(SectionName)
|
||
|
|
||
|
Map = SectionMap[SectionName]
|
||
|
continue
|
||
|
if not Map:
|
||
|
IniParseError(ST.ERR_SECTION_NAME_NONE, IniFile, Index+1)
|
||
|
TokenList = Line.split(TAB_EQUAL_SPLIT, 1)
|
||
|
TempKey = TokenList[0].strip()
|
||
|
#
|
||
|
# Value spanned multiple or same keyword appears more than one time
|
||
|
#
|
||
|
if len(TokenList) < 2 or TempKey not in Map:
|
||
|
if CurrentKey == '':
|
||
|
IniParseError(ST.ERR_KEYWORD_INVALID % TempKey,
|
||
|
IniFile, Index+1)
|
||
|
elif CurrentKey == 'FileList':
|
||
|
#
|
||
|
# Special for FileList
|
||
|
#
|
||
|
Valid, Cause = ParseFileList(Line, Map, CurrentKey,
|
||
|
PathValidator[SectionName])
|
||
|
if not Valid:
|
||
|
IniParseError(Cause, IniFile, Index+1)
|
||
|
|
||
|
else:
|
||
|
#
|
||
|
# Multiple lines for one key such as license
|
||
|
# Or if string on the left side of '=' is not a keyword
|
||
|
#
|
||
|
Map[CurrentKey] = ''.join([Map[CurrentKey], '\n', Line])
|
||
|
Valid, Cause = ValidateValues(CurrentKey,
|
||
|
Map[CurrentKey], SectionName)
|
||
|
if not Valid:
|
||
|
IniParseError(Cause, IniFile, Index+1)
|
||
|
continue
|
||
|
|
||
|
if (TokenList[1].strip() == ''):
|
||
|
IniParseError(ST.ERR_EMPTY_VALUE, IniFile, Index+1)
|
||
|
|
||
|
#
|
||
|
# A keyword found
|
||
|
#
|
||
|
CurrentKey = TempKey
|
||
|
if Map[CurrentKey]:
|
||
|
IniParseError(ST.ERR_KEYWORD_REDEFINE % CurrentKey,
|
||
|
IniFile, Index+1)
|
||
|
|
||
|
if id(Map) != id(PreMap) and Map['Copyright']:
|
||
|
PreMap = Map
|
||
|
Copyright = Map['Copyright'].lower()
|
||
|
Pos = Copyright.find('copyright')
|
||
|
if Pos == -1:
|
||
|
IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, Index)
|
||
|
if not Copyright[Pos + len('copyright'):].lstrip(' ').startswith('('):
|
||
|
IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, Index)
|
||
|
|
||
|
if CurrentKey == 'FileList':
|
||
|
Valid, Cause = ParseFileList(TokenList[1], Map, CurrentKey,
|
||
|
PathValidator[SectionName])
|
||
|
if not Valid:
|
||
|
IniParseError(Cause, IniFile, Index+1)
|
||
|
else:
|
||
|
Map[CurrentKey] = TokenList[1].strip()
|
||
|
Valid, Cause = ValidateValues(CurrentKey,
|
||
|
Map[CurrentKey], SectionName)
|
||
|
if not Valid:
|
||
|
IniParseError(Cause, IniFile, Index+1)
|
||
|
|
||
|
if id(Map) != id(PreMap) and Map['Copyright'] and 'copyright' not in Map['Copyright'].lower():
|
||
|
IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, LastIndex)
|
||
|
|
||
|
#
|
||
|
# Check mandatory keys
|
||
|
#
|
||
|
CheckMdtKeys(DistMap, IniFile, LastIndex,
|
||
|
(('ToolsHeader', ToolsMap), ('MiscellaneousFilesHeader', MiscMap))
|
||
|
)
|
||
|
|
||
|
return CreateXml(DistMap, ToolsMap, MiscMap, IniFile)
|
||
|
|
||
|
|
||
|
## CheckMdtKeys
|
||
|
#
|
||
|
# @param MdtDistKeys: All mandatory keys
|
||
|
# @param DistMap: Dist content
|
||
|
# @param IniFile: Ini file
|
||
|
# @param LastIndex: Last index of Ini file
|
||
|
# @param Maps: Tools and Misc section name and map. (('section_name', map),*)
|
||
|
#
|
||
|
def CheckMdtKeys(DistMap, IniFile, LastIndex, Maps):
|
||
|
MdtDistKeys = ['Name', 'GUID', 'Version', 'Vendor', 'Copyright', 'License', 'Abstract', 'XmlSpecification']
|
||
|
for Key in MdtDistKeys:
|
||
|
if Key not in DistMap or DistMap[Key] == '':
|
||
|
IniParseError(ST.ERR_KEYWORD_MANDATORY % Key, IniFile, LastIndex+1)
|
||
|
|
||
|
if '.' not in DistMap['Version']:
|
||
|
DistMap['Version'] = DistMap['Version'] + '.0'
|
||
|
|
||
|
DistMap['Date'] = str(strftime("%Y-%m-%dT%H:%M:%S", localtime()))
|
||
|
|
||
|
#
|
||
|
# Check Tools Surface Area according to UPT Spec
|
||
|
# <Tools> {0,}
|
||
|
# <Header> ... </Header> {0,1}
|
||
|
# <Filename> ... </Filename> {1,}
|
||
|
# </Tools>
|
||
|
# <Header>
|
||
|
# <Name> xs:normalizedString </Name> {1}
|
||
|
# <Copyright> xs:string </Copyright> {0,1}
|
||
|
# <License> xs:string </License> {0,1}
|
||
|
# <Abstract> xs:normalizedString </Abstract> {0,1}
|
||
|
# <Description> xs:string </Description> {0,1}
|
||
|
# </Header>
|
||
|
#
|
||
|
for Item in Maps:
|
||
|
Map = Item[1]
|
||
|
NonEmptyKey = 0
|
||
|
for Key in Map:
|
||
|
if Map[Key]:
|
||
|
NonEmptyKey += 1
|
||
|
|
||
|
if NonEmptyKey > 0 and not Map['FileList']:
|
||
|
IniParseError(ST.ERR_KEYWORD_MANDATORY % (Item[0] + '.FileList'), IniFile, LastIndex+1)
|
||
|
|
||
|
if NonEmptyKey > 0 and not Map['Name']:
|
||
|
IniParseError(ST.ERR_KEYWORD_MANDATORY % (Item[0] + '.Name'), IniFile, LastIndex+1)
|
||
|
|
||
|
## CreateXml
|
||
|
#
|
||
|
# @param DistMap: Dist Content
|
||
|
# @param ToolsMap: Tools Content
|
||
|
# @param MiscMap: Misc Content
|
||
|
# @param IniFile: Ini File
|
||
|
#
|
||
|
def CreateXml(DistMap, ToolsMap, MiscMap, IniFile):
|
||
|
Attrs = [['xmlns', 'http://www.uefi.org/2011/1.1'],
|
||
|
['xmlns:xsi', 'http:/www.w3.org/2001/XMLSchema-instance'],
|
||
|
]
|
||
|
Root = CreateXmlElement('DistributionPackage', '', [], Attrs)
|
||
|
CreateHeaderXml(DistMap, Root)
|
||
|
CreateToolsXml(ToolsMap, Root, 'Tools')
|
||
|
CreateToolsXml(MiscMap, Root, 'MiscellaneousFiles')
|
||
|
|
||
|
FileAndExt = IniFile.rsplit('.', 1)
|
||
|
if len(FileAndExt) > 1:
|
||
|
FileName = FileAndExt[0] + '.xml'
|
||
|
else:
|
||
|
FileName = IniFile + '.xml'
|
||
|
File = open(FileName, 'w')
|
||
|
|
||
|
try:
|
||
|
File.write(Root.toprettyxml(indent = ' '))
|
||
|
finally:
|
||
|
File.close()
|
||
|
return FileName
|
||
|
|