1## @file 2# This file is for converting package information data file to xml file. 3# 4# Copyright (c) 2011 - 2014, Intel Corporation. All rights reserved.<BR> 5# 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 10# 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. 13# 14 15''' 16IniToXml 17''' 18 19import os.path 20import re 21from time import strftime 22from time import localtime 23 24import Logger.Log as Logger 25from Logger.ToolError import UPT_INI_PARSE_ERROR 26from Logger.ToolError import FILE_NOT_FOUND 27from Library.Xml.XmlRoutines import CreateXmlElement 28from Library.DataType import TAB_VALUE_SPLIT 29from Library.DataType import TAB_EQUAL_SPLIT 30from Library.DataType import TAB_SECTION_START 31from Library.DataType import TAB_SECTION_END 32from Logger import StringTable as ST 33from Library.String import ConvertSpecialChar 34from Library.ParserValidate import IsValidPath 35from Library import GlobalData 36 37## log error: 38# 39# @param error: error 40# @param File: File 41# @param Line: Line 42# 43def IniParseError(Error, File, Line): 44 Logger.Error("UPT", UPT_INI_PARSE_ERROR, File=File, 45 Line=Line, ExtraData=Error) 46 47## __ValidatePath 48# 49# @param Path: Path to be checked 50# 51def __ValidatePath(Path, Root): 52 Path = Path.strip() 53 if os.path.isabs(Path) or not IsValidPath(Path, Root): 54 return False, ST.ERR_FILELIST_LOCATION % (Root, Path) 55 return True, '' 56 57## ValidateMiscFile 58# 59# @param Filename: File to be checked 60# 61def ValidateMiscFile(Filename): 62 Root = GlobalData.gWORKSPACE 63 return __ValidatePath(Filename, Root) 64 65## ValidateToolsFile 66# 67# @param Filename: File to be checked 68# 69def ValidateToolsFile(Filename): 70 Valid, Cause = False, '' 71 if not Valid and 'EDK_TOOLS_PATH' in os.environ: 72 Valid, Cause = __ValidatePath(Filename, os.environ['EDK_TOOLS_PATH']) 73 if not Valid: 74 Valid, Cause = __ValidatePath(Filename, GlobalData.gWORKSPACE) 75 return Valid, Cause 76 77## ParseFileList 78# 79# @param Line: Line 80# @param Map: Map 81# @param CurrentKey: CurrentKey 82# @param PathFunc: Path validate function 83# 84def ParseFileList(Line, Map, CurrentKey, PathFunc): 85 FileList = ["", {}] 86 TokenList = Line.split(TAB_VALUE_SPLIT) 87 if len(TokenList) > 0: 88 Path = TokenList[0].strip().replace('\\', '/') 89 if not Path: 90 return False, ST.ERR_WRONG_FILELIST_FORMAT 91 Valid, Cause = PathFunc(Path) 92 if not Valid: 93 return Valid, Cause 94 FileList[0] = TokenList[0].strip() 95 for Token in TokenList[1:]: 96 Attr = Token.split(TAB_EQUAL_SPLIT) 97 if len(Attr) != 2 or not Attr[0].strip() or not Attr[1].strip(): 98 return False, ST.ERR_WRONG_FILELIST_FORMAT 99 100 Key = Attr[0].strip() 101 Val = Attr[1].strip() 102 if Key not in ['OS', 'Executable']: 103 return False, ST.ERR_UNKNOWN_FILELIST_ATTR % Key 104 105 if Key == 'OS' and Val not in ["Win32", "Win64", "Linux32", 106 "Linux64", "OS/X32", "OS/X64", 107 "GenericWin", "GenericNix"]: 108 return False, ST.ERR_FILELIST_ATTR % 'OS' 109 elif Key == 'Executable' and Val not in ['true', 'false']: 110 return False, ST.ERR_FILELIST_ATTR % 'Executable' 111 FileList[1][Key] = Val 112 113 Map[CurrentKey].append(FileList) 114 return True, '' 115 116## Create header XML file 117# 118# @param DistMap: DistMap 119# @param Root: Root 120# 121def CreateHeaderXml(DistMap, Root): 122 Element1 = CreateXmlElement('Name', DistMap['Name'], 123 [], [['BaseName', DistMap['BaseName']]]) 124 Element2 = CreateXmlElement('GUID', DistMap['GUID'], 125 [], [['Version', DistMap['Version']]]) 126 AttributeList = [['ReadOnly', DistMap['ReadOnly']], 127 ['RePackage', DistMap['RePackage']]] 128 NodeList = [Element1, 129 Element2, 130 ['Vendor', DistMap['Vendor']], 131 ['Date', DistMap['Date']], 132 ['Copyright', DistMap['Copyright']], 133 ['License', DistMap['License']], 134 ['Abstract', DistMap['Abstract']], 135 ['Description', DistMap['Description']], 136 ['Signature', DistMap['Signature']], 137 ['XmlSpecification', DistMap['XmlSpecification']], 138 ] 139 Root.appendChild(CreateXmlElement('DistributionHeader', '', 140 NodeList, AttributeList)) 141 142## Create tools XML file 143# 144# @param Map: Map 145# @param Root: Root 146# @param Tag: Tag 147# 148def CreateToolsXml(Map, Root, Tag): 149 # 150 # Check if all elements in this section are empty 151 # 152 for Key in Map: 153 if len(Map[Key]) > 0: 154 break 155 else: 156 return 157 158 NodeList = [['Name', Map['Name']], 159 ['Copyright', Map['Copyright']], 160 ['License', Map['License']], 161 ['Abstract', Map['Abstract']], 162 ['Description', Map['Description']], 163 ] 164 HeaderNode = CreateXmlElement('Header', '', NodeList, []) 165 NodeList = [HeaderNode] 166 167 for File in Map['FileList']: 168 AttrList = [] 169 for Key in File[1]: 170 AttrList.append([Key, File[1][Key]]) 171 NodeList.append(CreateXmlElement('Filename', File[0], [], AttrList)) 172 Root.appendChild(CreateXmlElement(Tag, '', NodeList, [])) 173 174## ValidateValues 175# 176# @param Key: Key 177# @param Value: Value 178# @param SectionName: SectionName 179# 180def ValidateValues(Key, Value, SectionName): 181 if SectionName == 'DistributionHeader': 182 Valid, Cause = ValidateRegValues(Key, Value) 183 if not Valid: 184 return Valid, Cause 185 Valid = __ValidateDistHeader(Key, Value) 186 if not Valid: 187 return Valid, ST.ERR_VALUE_INVALID % (Key, SectionName) 188 else: 189 Valid = __ValidateOtherHeader(Key, Value) 190 if not Valid: 191 return Valid, ST.ERR_VALUE_INVALID % (Key, SectionName) 192 return True, '' 193 194## ValidateRegValues 195# 196# @param Key: Key 197# @param Value: Value 198# 199def ValidateRegValues(Key, Value): 200 ValidateMap = { 201 'ReadOnly' : 202 ('true|false', ST.ERR_BOOLEAN_VALUE % (Key, Value)), 203 'RePackage' : 204 ('true|false', ST.ERR_BOOLEAN_VALUE % (Key, Value)), 205 'GUID' : 206 ('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}' 207 '-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}', 208 ST.ERR_GUID_VALUE % Value), 209 'Version' : ('[0-9]+(\.[0-9]+)?', ST.ERR_VERSION_VALUE % \ 210 (Key, Value)), 211 'XmlSpecification' : ('1\.1', ST.ERR_VERSION_XMLSPEC % Value) 212 } 213 if Key not in ValidateMap: 214 return True, '' 215 Elem = ValidateMap[Key] 216 Match = re.compile(Elem[0]).match(Value) 217 if Match and Match.start() == 0 and Match.end() == len(Value): 218 return True, '' 219 return False, Elem[1] 220 221## __ValidateDistHeaderName 222# 223# @param Name: Name 224# 225def __ValidateDistHeaderName(Name): 226 if len(Name) < 1: 227 return False 228 229 for Char in Name: 230 if ord(Char) < 0x20 or ord(Char) >= 0x7f: 231 return False 232 return True 233 234## __ValidateDistHeaderBaseName 235# 236# @param BaseName: BaseName 237# 238def __ValidateDistHeaderBaseName(BaseName): 239 if not BaseName: 240 return False 241# if CheckLen and len(BaseName) < 2: 242# return False 243 if not BaseName[0].isalnum() and BaseName[0] != '_': 244 return False 245 for Char in BaseName[1:]: 246 if not Char.isalnum() and Char not in '-_': 247 return False 248 return True 249 250## __ValidateDistHeaderAbstract 251# 252# @param Abstract: Abstract 253# 254def __ValidateDistHeaderAbstract(Abstract): 255 return '\t' not in Abstract and len(Abstract.splitlines()) == 1 256 257## __ValidateOtherHeaderAbstract 258# 259# @param Abstract: Abstract 260# 261def __ValidateOtherHeaderAbstract(Abstract): 262 return __ValidateDistHeaderAbstract(Abstract) 263 264## __ValidateDistHeader 265# 266# @param Key: Key 267# @param Value: Value 268# 269def __ValidateDistHeader(Key, Value): 270 ValidateMap = { 271 'Name' : __ValidateDistHeaderName, 272 'BaseName' : __ValidateDistHeaderBaseName, 273 'Abstract' : __ValidateDistHeaderAbstract, 274 'Vendor' : __ValidateDistHeaderAbstract 275 } 276 return not (Value and Key in ValidateMap and not ValidateMap[Key](Value)) 277 278## __ValidateOtherHeader 279# 280# @param Key: Key 281# @param Value: Value 282# 283def __ValidateOtherHeader(Key, Value): 284 ValidateMap = { 285 'Name' : __ValidateDistHeaderName, 286 'Abstract' : __ValidateOtherHeaderAbstract 287 } 288 return not (Value and Key in ValidateMap and not ValidateMap[Key](Value)) 289 290## Convert ini file to xml file 291# 292# @param IniFile 293# 294def IniToXml(IniFile): 295 if not os.path.exists(IniFile): 296 Logger.Error("UPT", FILE_NOT_FOUND, ST.ERR_TEMPLATE_NOTFOUND % IniFile) 297 298 DistMap = {'ReadOnly' : '', 'RePackage' : '', 'Name' : '', 299 'BaseName' : '', 'GUID' : '', 'Version' : '', 'Vendor' : '', 300 'Date' : '', 'Copyright' : '', 'License' : '', 'Abstract' : '', 301 'Description' : '', 'Signature' : '', 'XmlSpecification' : '' 302 } 303 304 ToolsMap = {'Name' : '', 'Copyright' : '', 'License' : '', 305 'Abstract' : '', 'Description' : '', 'FileList' : []} 306 # 307 # Only FileList is a list: [['file1', {}], ['file2', {}], ...] 308 # 309 MiscMap = {'Name' : '', 'Copyright' : '', 'License' : '', 310 'Abstract' : '', 'Description' : '', 'FileList' : []} 311 312 SectionMap = { 313 'DistributionHeader' : DistMap, 314 'ToolsHeader' : ToolsMap, 315 'MiscellaneousFilesHeader' : MiscMap 316 } 317 318 PathValidator = { 319 'ToolsHeader' : ValidateToolsFile, 320 'MiscellaneousFilesHeader' : ValidateMiscFile 321 } 322 323 ParsedSection = [] 324 325 SectionName = '' 326 CurrentKey = '' 327 PreMap = None 328 Map = None 329 FileContent = ConvertSpecialChar(open(IniFile, 'rb').readlines()) 330 LastIndex = 0 331 for Index in range(0, len(FileContent)): 332 LastIndex = Index 333 Line = FileContent[Index].strip() 334 if Line == '' or Line.startswith(';'): 335 continue 336 if Line[0] == TAB_SECTION_START and Line[-1] == TAB_SECTION_END: 337 CurrentKey = '' 338 SectionName = Line[1:-1].strip() 339 if SectionName not in SectionMap: 340 IniParseError(ST.ERR_SECTION_NAME_INVALID % SectionName, 341 IniFile, Index+1) 342 343 if SectionName in ParsedSection: 344 IniParseError(ST.ERR_SECTION_REDEFINE % SectionName, 345 IniFile, Index+1) 346 else: 347 ParsedSection.append(SectionName) 348 349 Map = SectionMap[SectionName] 350 continue 351 if not Map: 352 IniParseError(ST.ERR_SECTION_NAME_NONE, IniFile, Index+1) 353 TokenList = Line.split(TAB_EQUAL_SPLIT, 1) 354 TempKey = TokenList[0].strip() 355 # 356 # Value spanned multiple or same keyword appears more than one time 357 # 358 if len(TokenList) < 2 or TempKey not in Map: 359 if CurrentKey == '': 360 IniParseError(ST.ERR_KEYWORD_INVALID % TempKey, 361 IniFile, Index+1) 362 elif CurrentKey == 'FileList': 363 # 364 # Special for FileList 365 # 366 Valid, Cause = ParseFileList(Line, Map, CurrentKey, 367 PathValidator[SectionName]) 368 if not Valid: 369 IniParseError(Cause, IniFile, Index+1) 370 371 else: 372 # 373 # Multiple lines for one key such as license 374 # Or if string on the left side of '=' is not a keyword 375 # 376 Map[CurrentKey] = ''.join([Map[CurrentKey], '\n', Line]) 377 Valid, Cause = ValidateValues(CurrentKey, 378 Map[CurrentKey], SectionName) 379 if not Valid: 380 IniParseError(Cause, IniFile, Index+1) 381 continue 382 383 if (TokenList[1].strip() == ''): 384 IniParseError(ST.ERR_EMPTY_VALUE, IniFile, Index+1) 385 386 # 387 # A keyword found 388 # 389 CurrentKey = TempKey 390 if Map[CurrentKey]: 391 IniParseError(ST.ERR_KEYWORD_REDEFINE % CurrentKey, 392 IniFile, Index+1) 393 394 if id(Map) != id(PreMap) and Map['Copyright']: 395 PreMap = Map 396 Copyright = Map['Copyright'].lower() 397 Pos = Copyright.find('copyright') 398 if Pos == -1: 399 IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, Index) 400 if not Copyright[Pos + len('copyright'):].lstrip(' ').startswith('('): 401 IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, Index) 402 403 if CurrentKey == 'FileList': 404 Valid, Cause = ParseFileList(TokenList[1], Map, CurrentKey, 405 PathValidator[SectionName]) 406 if not Valid: 407 IniParseError(Cause, IniFile, Index+1) 408 else: 409 Map[CurrentKey] = TokenList[1].strip() 410 Valid, Cause = ValidateValues(CurrentKey, 411 Map[CurrentKey], SectionName) 412 if not Valid: 413 IniParseError(Cause, IniFile, Index+1) 414 415 if id(Map) != id(PreMap) and Map['Copyright'] and 'copyright' not in Map['Copyright'].lower(): 416 IniParseError(ST.ERR_COPYRIGHT_CONTENT, IniFile, LastIndex) 417 418 # 419 # Check mandatory keys 420 # 421 CheckMdtKeys(DistMap, IniFile, LastIndex, 422 (('ToolsHeader', ToolsMap), ('MiscellaneousFilesHeader', MiscMap)) 423 ) 424 425 return CreateXml(DistMap, ToolsMap, MiscMap, IniFile) 426 427 428## CheckMdtKeys 429# 430# @param MdtDistKeys: All mandatory keys 431# @param DistMap: Dist content 432# @param IniFile: Ini file 433# @param LastIndex: Last index of Ini file 434# @param Maps: Tools and Misc section name and map. (('section_name', map),*) 435# 436def CheckMdtKeys(DistMap, IniFile, LastIndex, Maps): 437 MdtDistKeys = ['Name', 'GUID', 'Version', 'Vendor', 'Copyright', 'License', 'Abstract', 'XmlSpecification'] 438 for Key in MdtDistKeys: 439 if Key not in DistMap or DistMap[Key] == '': 440 IniParseError(ST.ERR_KEYWORD_MANDATORY % Key, IniFile, LastIndex+1) 441 442 if '.' not in DistMap['Version']: 443 DistMap['Version'] = DistMap['Version'] + '.0' 444 445 DistMap['Date'] = str(strftime("%Y-%m-%dT%H:%M:%S", localtime())) 446 447 # 448 # Check Tools Surface Area according to UPT Spec 449 # <Tools> {0,} 450 # <Header> ... </Header> {0,1} 451 # <Filename> ... </Filename> {1,} 452 # </Tools> 453 # <Header> 454 # <Name> xs:normalizedString </Name> {1} 455 # <Copyright> xs:string </Copyright> {0,1} 456 # <License> xs:string </License> {0,1} 457 # <Abstract> xs:normalizedString </Abstract> {0,1} 458 # <Description> xs:string </Description> {0,1} 459 # </Header> 460 # 461 for Item in Maps: 462 Map = Item[1] 463 NonEmptyKey = 0 464 for Key in Map: 465 if Map[Key]: 466 NonEmptyKey += 1 467 468 if NonEmptyKey > 0 and not Map['FileList']: 469 IniParseError(ST.ERR_KEYWORD_MANDATORY % (Item[0] + '.FileList'), IniFile, LastIndex+1) 470 471 if NonEmptyKey > 0 and not Map['Name']: 472 IniParseError(ST.ERR_KEYWORD_MANDATORY % (Item[0] + '.Name'), IniFile, LastIndex+1) 473 474## CreateXml 475# 476# @param DistMap: Dist Content 477# @param ToolsMap: Tools Content 478# @param MiscMap: Misc Content 479# @param IniFile: Ini File 480# 481def CreateXml(DistMap, ToolsMap, MiscMap, IniFile): 482 Attrs = [['xmlns', 'http://www.uefi.org/2011/1.1'], 483 ['xmlns:xsi', 'http:/www.w3.org/2001/XMLSchema-instance'], 484 ] 485 Root = CreateXmlElement('DistributionPackage', '', [], Attrs) 486 CreateHeaderXml(DistMap, Root) 487 CreateToolsXml(ToolsMap, Root, 'Tools') 488 CreateToolsXml(MiscMap, Root, 'MiscellaneousFiles') 489 490 FileAndExt = IniFile.rsplit('.', 1) 491 if len(FileAndExt) > 1: 492 FileName = FileAndExt[0] + '.xml' 493 else: 494 FileName = IniFile + '.xml' 495 File = open(FileName, 'w') 496 497 try: 498 File.write(Root.toprettyxml(indent = ' ')) 499 finally: 500 File.close() 501 return FileName 502 503