| ## @file | |
| # This file is for converting package information data file to xml file. | |
| # | |
| # Copyright (c) 2011 - 2014, 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.String 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 | |