audk/BaseTools/Source/Python/UPT/Xml/IniToXml.py

503 lines
17 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>
#
# This program and the accompanying materials are licensed and made available
# under the terms and conditions of the BSD License which accompanies this
# distribution. The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
'''
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, 'rb').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