| 1 | ## @file\r |
| 2 | # Trim files preprocessed by compiler\r |
| 3 | #\r |
| 4 | # Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r |
| 5 | # SPDX-License-Identifier: BSD-2-Clause-Patent\r |
| 6 | #\r |
| 7 | \r |
| 8 | ##\r |
| 9 | # Import Modules\r |
| 10 | #\r |
| 11 | import Common.LongFilePathOs as os\r |
| 12 | import sys\r |
| 13 | import re\r |
| 14 | from io import BytesIO\r |
| 15 | import codecs\r |
| 16 | from optparse import OptionParser\r |
| 17 | from optparse import make_option\r |
| 18 | from Common.BuildToolError import *\r |
| 19 | from Common.Misc import *\r |
| 20 | from Common.DataType import *\r |
| 21 | from Common.BuildVersion import gBUILD_VERSION\r |
| 22 | import Common.EdkLogger as EdkLogger\r |
| 23 | from Common.LongFilePathSupport import OpenLongFilePath as open\r |
| 24 | \r |
| 25 | # Version and Copyright\r |
| 26 | __version_number__ = ("0.10" + " " + gBUILD_VERSION)\r |
| 27 | __version__ = "%prog Version " + __version_number__\r |
| 28 | __copyright__ = "Copyright (c) 2007-2018, Intel Corporation. All rights reserved."\r |
| 29 | \r |
| 30 | ## Regular expression for matching Line Control directive like "#line xxx"\r |
| 31 | gLineControlDirective = re.compile('^\s*#(?:line)?\s+([0-9]+)\s+"*([^"]*)"')\r |
| 32 | ## Regular expression for matching "typedef struct"\r |
| 33 | gTypedefPattern = re.compile("^\s*typedef\s+struct(\s+\w+)?\s*[{]*$", re.MULTILINE)\r |
| 34 | ## Regular expression for matching "#pragma pack"\r |
| 35 | gPragmaPattern = re.compile("^\s*#pragma\s+pack", re.MULTILINE)\r |
| 36 | ## Regular expression for matching "typedef"\r |
| 37 | gTypedef_SinglePattern = re.compile("^\s*typedef", re.MULTILINE)\r |
| 38 | ## Regular expression for matching "typedef struct, typedef union, struct, union"\r |
| 39 | gTypedef_MulPattern = re.compile("^\s*(typedef)?\s+(struct|union)(\s+\w+)?\s*[{]*$", re.MULTILINE)\r |
| 40 | \r |
| 41 | #\r |
| 42 | # The following number pattern match will only match if following criteria is met:\r |
| 43 | # There is leading non-(alphanumeric or _) character, and no following alphanumeric or _\r |
| 44 | # as the pattern is greedily match, so it is ok for the gDecNumberPattern or gHexNumberPattern to grab the maximum match\r |
| 45 | #\r |
| 46 | ## Regular expression for matching HEX number\r |
| 47 | gHexNumberPattern = re.compile("(?<=[^a-zA-Z0-9_])(0[xX])([0-9a-fA-F]+)(U(?=$|[^a-zA-Z0-9_]))?")\r |
| 48 | ## Regular expression for matching decimal number with 'U' postfix\r |
| 49 | gDecNumberPattern = re.compile("(?<=[^a-zA-Z0-9_])([0-9]+)U(?=$|[^a-zA-Z0-9_])")\r |
| 50 | ## Regular expression for matching constant with 'ULL' 'LL' postfix\r |
| 51 | gLongNumberPattern = re.compile("(?<=[^a-zA-Z0-9_])(0[xX][0-9a-fA-F]+|[0-9]+)U?LL(?=$|[^a-zA-Z0-9_])")\r |
| 52 | \r |
| 53 | ## Regular expression for matching "Include ()" in asl file\r |
| 54 | gAslIncludePattern = re.compile("^(\s*)[iI]nclude\s*\(\"?([^\"\(\)]+)\"\)", re.MULTILINE)\r |
| 55 | ## Regular expression for matching C style #include "XXX.asl" in asl file\r |
| 56 | gAslCIncludePattern = re.compile(r'^(\s*)#include\s*[<"]\s*([-\\/\w.]+)\s*([>"])', re.MULTILINE)\r |
| 57 | ## Patterns used to convert EDK conventions to EDK2 ECP conventions\r |
| 58 | \r |
| 59 | ## Regular expression for finding header file inclusions\r |
| 60 | gIncludePattern = re.compile(r"^[ \t]*[%]?[ \t]*include(?:[ \t]*(?:\\(?:\r\n|\r|\n))*[ \t]*)*(?:\(?[\"<]?[ \t]*)([-\w.\\/() \t]+)(?:[ \t]*[\">]?\)?)", re.MULTILINE | re.UNICODE | re.IGNORECASE)\r |
| 61 | \r |
| 62 | \r |
| 63 | ## file cache to avoid circular include in ASL file\r |
| 64 | gIncludedAslFile = []\r |
| 65 | \r |
| 66 | ## Trim preprocessed source code\r |
| 67 | #\r |
| 68 | # Remove extra content made by preprocessor. The preprocessor must enable the\r |
| 69 | # line number generation option when preprocessing.\r |
| 70 | #\r |
| 71 | # @param Source File to be trimmed\r |
| 72 | # @param Target File to store the trimmed content\r |
| 73 | # @param Convert If True, convert standard HEX format to MASM format\r |
| 74 | #\r |
| 75 | def TrimPreprocessedFile(Source, Target, ConvertHex, TrimLong):\r |
| 76 | CreateDirectory(os.path.dirname(Target))\r |
| 77 | try:\r |
| 78 | with open(Source, "r") as File:\r |
| 79 | Lines = File.readlines()\r |
| 80 | except IOError:\r |
| 81 | EdkLogger.error("Trim", FILE_OPEN_FAILURE, ExtraData=Source)\r |
| 82 | except:\r |
| 83 | EdkLogger.error("Trim", AUTOGEN_ERROR, "TrimPreprocessedFile: Error while processing file", File=Source)\r |
| 84 | \r |
| 85 | PreprocessedFile = ""\r |
| 86 | InjectedFile = ""\r |
| 87 | LineIndexOfOriginalFile = None\r |
| 88 | NewLines = []\r |
| 89 | LineControlDirectiveFound = False\r |
| 90 | for Index in range(len(Lines)):\r |
| 91 | Line = Lines[Index]\r |
| 92 | #\r |
| 93 | # Find out the name of files injected by preprocessor from the lines\r |
| 94 | # with Line Control directive\r |
| 95 | #\r |
| 96 | MatchList = gLineControlDirective.findall(Line)\r |
| 97 | if MatchList != []:\r |
| 98 | MatchList = MatchList[0]\r |
| 99 | if len(MatchList) == 2:\r |
| 100 | LineNumber = int(MatchList[0], 0)\r |
| 101 | InjectedFile = MatchList[1]\r |
| 102 | InjectedFile = os.path.normpath(InjectedFile)\r |
| 103 | InjectedFile = os.path.normcase(InjectedFile)\r |
| 104 | # The first injected file must be the preprocessed file itself\r |
| 105 | if PreprocessedFile == "":\r |
| 106 | PreprocessedFile = InjectedFile\r |
| 107 | LineControlDirectiveFound = True\r |
| 108 | continue\r |
| 109 | elif PreprocessedFile == "" or InjectedFile != PreprocessedFile:\r |
| 110 | continue\r |
| 111 | \r |
| 112 | if LineIndexOfOriginalFile is None:\r |
| 113 | #\r |
| 114 | # Any non-empty lines must be from original preprocessed file.\r |
| 115 | # And this must be the first one.\r |
| 116 | #\r |
| 117 | LineIndexOfOriginalFile = Index\r |
| 118 | EdkLogger.verbose("Found original file content starting from line %d"\r |
| 119 | % (LineIndexOfOriginalFile + 1))\r |
| 120 | \r |
| 121 | if TrimLong:\r |
| 122 | Line = gLongNumberPattern.sub(r"\1", Line)\r |
| 123 | # convert HEX number format if indicated\r |
| 124 | if ConvertHex:\r |
| 125 | Line = gHexNumberPattern.sub(r"0\2h", Line)\r |
| 126 | else:\r |
| 127 | Line = gHexNumberPattern.sub(r"\1\2", Line)\r |
| 128 | \r |
| 129 | # convert Decimal number format\r |
| 130 | Line = gDecNumberPattern.sub(r"\1", Line)\r |
| 131 | \r |
| 132 | if LineNumber is not None:\r |
| 133 | EdkLogger.verbose("Got line directive: line=%d" % LineNumber)\r |
| 134 | # in case preprocessor removed some lines, like blank or comment lines\r |
| 135 | if LineNumber <= len(NewLines):\r |
| 136 | # possible?\r |
| 137 | NewLines[LineNumber - 1] = Line\r |
| 138 | else:\r |
| 139 | if LineNumber > (len(NewLines) + 1):\r |
| 140 | for LineIndex in range(len(NewLines), LineNumber-1):\r |
| 141 | NewLines.append(TAB_LINE_BREAK)\r |
| 142 | NewLines.append(Line)\r |
| 143 | LineNumber = None\r |
| 144 | EdkLogger.verbose("Now we have lines: %d" % len(NewLines))\r |
| 145 | else:\r |
| 146 | NewLines.append(Line)\r |
| 147 | \r |
| 148 | # in case there's no line directive or linemarker found\r |
| 149 | if (not LineControlDirectiveFound) and NewLines == []:\r |
| 150 | MulPatternFlag = False\r |
| 151 | SinglePatternFlag = False\r |
| 152 | Brace = 0\r |
| 153 | for Index in range(len(Lines)):\r |
| 154 | Line = Lines[Index]\r |
| 155 | if MulPatternFlag == False and gTypedef_MulPattern.search(Line) is None:\r |
| 156 | if SinglePatternFlag == False and gTypedef_SinglePattern.search(Line) is None:\r |
| 157 | # remove "#pragram pack" directive\r |
| 158 | if gPragmaPattern.search(Line) is None:\r |
| 159 | NewLines.append(Line)\r |
| 160 | continue\r |
| 161 | elif SinglePatternFlag == False:\r |
| 162 | SinglePatternFlag = True\r |
| 163 | if Line.find(";") >= 0:\r |
| 164 | SinglePatternFlag = False\r |
| 165 | elif MulPatternFlag == False:\r |
| 166 | # found "typedef struct, typedef union, union, struct", keep its position and set a flag\r |
| 167 | MulPatternFlag = True\r |
| 168 | \r |
| 169 | # match { and } to find the end of typedef definition\r |
| 170 | if Line.find("{") >= 0:\r |
| 171 | Brace += 1\r |
| 172 | elif Line.find("}") >= 0:\r |
| 173 | Brace -= 1\r |
| 174 | \r |
| 175 | # "typedef struct, typedef union, union, struct" must end with a ";"\r |
| 176 | if Brace == 0 and Line.find(";") >= 0:\r |
| 177 | MulPatternFlag = False\r |
| 178 | \r |
| 179 | # save to file\r |
| 180 | try:\r |
| 181 | with open(Target, 'w') as File:\r |
| 182 | File.writelines(NewLines)\r |
| 183 | except:\r |
| 184 | EdkLogger.error("Trim", FILE_OPEN_FAILURE, ExtraData=Target)\r |
| 185 | \r |
| 186 | ## Trim preprocessed VFR file\r |
| 187 | #\r |
| 188 | # Remove extra content made by preprocessor. The preprocessor doesn't need to\r |
| 189 | # enable line number generation option when preprocessing.\r |
| 190 | #\r |
| 191 | # @param Source File to be trimmed\r |
| 192 | # @param Target File to store the trimmed content\r |
| 193 | #\r |
| 194 | def TrimPreprocessedVfr(Source, Target):\r |
| 195 | CreateDirectory(os.path.dirname(Target))\r |
| 196 | \r |
| 197 | try:\r |
| 198 | with open(Source, "r") as File:\r |
| 199 | Lines = File.readlines()\r |
| 200 | except:\r |
| 201 | EdkLogger.error("Trim", FILE_OPEN_FAILURE, ExtraData=Source)\r |
| 202 | # read whole file\r |
| 203 | \r |
| 204 | FoundTypedef = False\r |
| 205 | Brace = 0\r |
| 206 | TypedefStart = 0\r |
| 207 | TypedefEnd = 0\r |
| 208 | for Index in range(len(Lines)):\r |
| 209 | Line = Lines[Index]\r |
| 210 | # don't trim the lines from "formset" definition to the end of file\r |
| 211 | if Line.strip() == 'formset':\r |
| 212 | break\r |
| 213 | \r |
| 214 | if FoundTypedef == False and (Line.find('#line') == 0 or Line.find('# ') == 0):\r |
| 215 | # empty the line number directive if it's not aomong "typedef struct"\r |
| 216 | Lines[Index] = "\n"\r |
| 217 | continue\r |
| 218 | \r |
| 219 | if FoundTypedef == False and gTypedefPattern.search(Line) is None:\r |
| 220 | # keep "#pragram pack" directive\r |
| 221 | if gPragmaPattern.search(Line) is None:\r |
| 222 | Lines[Index] = "\n"\r |
| 223 | continue\r |
| 224 | elif FoundTypedef == False:\r |
| 225 | # found "typedef struct", keept its position and set a flag\r |
| 226 | FoundTypedef = True\r |
| 227 | TypedefStart = Index\r |
| 228 | \r |
| 229 | # match { and } to find the end of typedef definition\r |
| 230 | if Line.find("{") >= 0:\r |
| 231 | Brace += 1\r |
| 232 | elif Line.find("}") >= 0:\r |
| 233 | Brace -= 1\r |
| 234 | \r |
| 235 | # "typedef struct" must end with a ";"\r |
| 236 | if Brace == 0 and Line.find(";") >= 0:\r |
| 237 | FoundTypedef = False\r |
| 238 | TypedefEnd = Index\r |
| 239 | # keep all "typedef struct" except to GUID, EFI_PLABEL and PAL_CALL_RETURN\r |
| 240 | if Line.strip("} ;\r\n") in [TAB_GUID, "EFI_PLABEL", "PAL_CALL_RETURN"]:\r |
| 241 | for i in range(TypedefStart, TypedefEnd+1):\r |
| 242 | Lines[i] = "\n"\r |
| 243 | \r |
| 244 | # save all lines trimmed\r |
| 245 | try:\r |
| 246 | with open(Target, 'w') as File:\r |
| 247 | File.writelines(Lines)\r |
| 248 | except:\r |
| 249 | EdkLogger.error("Trim", FILE_OPEN_FAILURE, ExtraData=Target)\r |
| 250 | \r |
| 251 | ## Read the content ASL file, including ASL included, recursively\r |
| 252 | #\r |
| 253 | # @param Source File to be read\r |
| 254 | # @param Indent Spaces before the Include() statement\r |
| 255 | # @param IncludePathList The list of external include file\r |
| 256 | # @param LocalSearchPath If LocalSearchPath is specified, this path will be searched\r |
| 257 | # first for the included file; otherwise, only the path specified\r |
| 258 | # in the IncludePathList will be searched.\r |
| 259 | #\r |
| 260 | def DoInclude(Source, Indent='', IncludePathList=[], LocalSearchPath=None, IncludeFileList = None, filetype=None):\r |
| 261 | NewFileContent = []\r |
| 262 | if IncludeFileList is None:\r |
| 263 | IncludeFileList = []\r |
| 264 | try:\r |
| 265 | #\r |
| 266 | # Search LocalSearchPath first if it is specified.\r |
| 267 | #\r |
| 268 | if LocalSearchPath:\r |
| 269 | SearchPathList = [LocalSearchPath] + IncludePathList\r |
| 270 | else:\r |
| 271 | SearchPathList = IncludePathList\r |
| 272 | \r |
| 273 | for IncludePath in SearchPathList:\r |
| 274 | IncludeFile = os.path.join(IncludePath, Source)\r |
| 275 | if os.path.isfile(IncludeFile):\r |
| 276 | try:\r |
| 277 | with open(IncludeFile, "r") as File:\r |
| 278 | F = File.readlines()\r |
| 279 | except:\r |
| 280 | with codecs.open(IncludeFile, "r", encoding='utf-8') as File:\r |
| 281 | F = File.readlines()\r |
| 282 | break\r |
| 283 | else:\r |
| 284 | EdkLogger.warn("Trim", "Failed to find include file %s" % Source)\r |
| 285 | return []\r |
| 286 | except:\r |
| 287 | EdkLogger.warn("Trim", FILE_OPEN_FAILURE, ExtraData=Source)\r |
| 288 | return []\r |
| 289 | \r |
| 290 | \r |
| 291 | # avoid A "include" B and B "include" A\r |
| 292 | IncludeFile = os.path.abspath(os.path.normpath(IncludeFile))\r |
| 293 | if IncludeFile in gIncludedAslFile:\r |
| 294 | EdkLogger.warn("Trim", "Circular include",\r |
| 295 | ExtraData= "%s -> %s" % (" -> ".join(gIncludedAslFile), IncludeFile))\r |
| 296 | return []\r |
| 297 | gIncludedAslFile.append(IncludeFile)\r |
| 298 | IncludeFileList.append(IncludeFile.strip())\r |
| 299 | for Line in F:\r |
| 300 | LocalSearchPath = None\r |
| 301 | if filetype == "ASL":\r |
| 302 | Result = gAslIncludePattern.findall(Line)\r |
| 303 | if len(Result) == 0:\r |
| 304 | Result = gAslCIncludePattern.findall(Line)\r |
| 305 | if len(Result) == 0 or os.path.splitext(Result[0][1])[1].lower() not in [".asl", ".asi"]:\r |
| 306 | NewFileContent.append("%s%s" % (Indent, Line))\r |
| 307 | continue\r |
| 308 | #\r |
| 309 | # We should first search the local directory if current file are using pattern #include "XXX"\r |
| 310 | #\r |
| 311 | if Result[0][2] == '"':\r |
| 312 | LocalSearchPath = os.path.dirname(IncludeFile)\r |
| 313 | CurrentIndent = Indent + Result[0][0]\r |
| 314 | IncludedFile = Result[0][1]\r |
| 315 | NewFileContent.extend(DoInclude(IncludedFile, CurrentIndent, IncludePathList, LocalSearchPath,IncludeFileList,filetype))\r |
| 316 | NewFileContent.append("\n")\r |
| 317 | elif filetype == "ASM":\r |
| 318 | Result = gIncludePattern.findall(Line)\r |
| 319 | if len(Result) == 0:\r |
| 320 | NewFileContent.append("%s%s" % (Indent, Line))\r |
| 321 | continue\r |
| 322 | \r |
| 323 | IncludedFile = Result[0]\r |
| 324 | \r |
| 325 | IncludedFile = IncludedFile.strip()\r |
| 326 | IncludedFile = os.path.normpath(IncludedFile)\r |
| 327 | NewFileContent.extend(DoInclude(IncludedFile, '', IncludePathList, LocalSearchPath,IncludeFileList,filetype))\r |
| 328 | NewFileContent.append("\n")\r |
| 329 | \r |
| 330 | gIncludedAslFile.pop()\r |
| 331 | \r |
| 332 | return NewFileContent\r |
| 333 | \r |
| 334 | \r |
| 335 | ## Trim ASL file\r |
| 336 | #\r |
| 337 | # Replace ASL include statement with the content the included file\r |
| 338 | #\r |
| 339 | # @param Source File to be trimmed\r |
| 340 | # @param Target File to store the trimmed content\r |
| 341 | # @param IncludePathFile The file to log the external include path\r |
| 342 | #\r |
| 343 | def TrimAslFile(Source, Target, IncludePathFile,AslDeps = False):\r |
| 344 | CreateDirectory(os.path.dirname(Target))\r |
| 345 | \r |
| 346 | SourceDir = os.path.dirname(Source)\r |
| 347 | if SourceDir == '':\r |
| 348 | SourceDir = '.'\r |
| 349 | \r |
| 350 | #\r |
| 351 | # Add source directory as the first search directory\r |
| 352 | #\r |
| 353 | IncludePathList = [SourceDir]\r |
| 354 | \r |
| 355 | #\r |
| 356 | # If additional include path file is specified, append them all\r |
| 357 | # to the search directory list.\r |
| 358 | #\r |
| 359 | if IncludePathFile:\r |
| 360 | try:\r |
| 361 | LineNum = 0\r |
| 362 | with open(IncludePathFile, 'r') as File:\r |
| 363 | FileLines = File.readlines()\r |
| 364 | for Line in FileLines:\r |
| 365 | LineNum += 1\r |
| 366 | if Line.startswith("/I") or Line.startswith ("-I"):\r |
| 367 | IncludePathList.append(Line[2:].strip())\r |
| 368 | else:\r |
| 369 | EdkLogger.warn("Trim", "Invalid include line in include list file.", IncludePathFile, LineNum)\r |
| 370 | except:\r |
| 371 | EdkLogger.error("Trim", FILE_OPEN_FAILURE, ExtraData=IncludePathFile)\r |
| 372 | AslIncludes = []\r |
| 373 | Lines = DoInclude(Source, '', IncludePathList,IncludeFileList=AslIncludes,filetype='ASL')\r |
| 374 | AslIncludes = [item for item in AslIncludes if item !=Source]\r |
| 375 | SaveFileOnChange(os.path.join(os.path.dirname(Target),os.path.basename(Source))+".trim.deps", " \\\n".join([Source+":"] +AslIncludes),False)\r |
| 376 | \r |
| 377 | #\r |
| 378 | # Undef MIN and MAX to avoid collision in ASL source code\r |
| 379 | #\r |
| 380 | Lines.insert(0, "#undef MIN\n#undef MAX\n")\r |
| 381 | \r |
| 382 | # save all lines trimmed\r |
| 383 | try:\r |
| 384 | with open(Target, 'w') as File:\r |
| 385 | File.writelines(Lines)\r |
| 386 | except:\r |
| 387 | EdkLogger.error("Trim", FILE_OPEN_FAILURE, ExtraData=Target)\r |
| 388 | \r |
| 389 | ## Trim ASM file\r |
| 390 | #\r |
| 391 | # Output ASM include statement with the content the included file\r |
| 392 | #\r |
| 393 | # @param Source File to be trimmed\r |
| 394 | # @param Target File to store the trimmed content\r |
| 395 | # @param IncludePathFile The file to log the external include path\r |
| 396 | #\r |
| 397 | def TrimAsmFile(Source, Target, IncludePathFile):\r |
| 398 | CreateDirectory(os.path.dirname(Target))\r |
| 399 | \r |
| 400 | SourceDir = os.path.dirname(Source)\r |
| 401 | if SourceDir == '':\r |
| 402 | SourceDir = '.'\r |
| 403 | \r |
| 404 | #\r |
| 405 | # Add source directory as the first search directory\r |
| 406 | #\r |
| 407 | IncludePathList = [SourceDir]\r |
| 408 | #\r |
| 409 | # If additional include path file is specified, append them all\r |
| 410 | # to the search directory list.\r |
| 411 | #\r |
| 412 | if IncludePathFile:\r |
| 413 | try:\r |
| 414 | LineNum = 0\r |
| 415 | with open(IncludePathFile, 'r') as File:\r |
| 416 | FileLines = File.readlines()\r |
| 417 | for Line in FileLines:\r |
| 418 | LineNum += 1\r |
| 419 | if Line.startswith("/I") or Line.startswith ("-I"):\r |
| 420 | IncludePathList.append(Line[2:].strip())\r |
| 421 | else:\r |
| 422 | EdkLogger.warn("Trim", "Invalid include line in include list file.", IncludePathFile, LineNum)\r |
| 423 | except:\r |
| 424 | EdkLogger.error("Trim", FILE_OPEN_FAILURE, ExtraData=IncludePathFile)\r |
| 425 | AsmIncludes = []\r |
| 426 | Lines = DoInclude(Source, '', IncludePathList,IncludeFileList=AsmIncludes,filetype='ASM')\r |
| 427 | AsmIncludes = [item for item in AsmIncludes if item != Source]\r |
| 428 | if AsmIncludes:\r |
| 429 | SaveFileOnChange(os.path.join(os.path.dirname(Target),os.path.basename(Source))+".trim.deps", " \\\n".join([Source+":"] +AsmIncludes),False)\r |
| 430 | # save all lines trimmed\r |
| 431 | try:\r |
| 432 | with open(Target, 'w') as File:\r |
| 433 | File.writelines(Lines)\r |
| 434 | except:\r |
| 435 | EdkLogger.error("Trim", FILE_OPEN_FAILURE, ExtraData=Target)\r |
| 436 | \r |
| 437 | def GenerateVfrBinSec(ModuleName, DebugDir, OutputFile):\r |
| 438 | VfrNameList = []\r |
| 439 | if os.path.isdir(DebugDir):\r |
| 440 | for CurrentDir, Dirs, Files in os.walk(DebugDir):\r |
| 441 | for FileName in Files:\r |
| 442 | Name, Ext = os.path.splitext(FileName)\r |
| 443 | if Ext == '.c' and Name != 'AutoGen':\r |
| 444 | VfrNameList.append (Name + 'Bin')\r |
| 445 | \r |
| 446 | VfrNameList.append (ModuleName + 'Strings')\r |
| 447 | \r |
| 448 | EfiFileName = os.path.join(DebugDir, ModuleName + '.efi')\r |
| 449 | MapFileName = os.path.join(DebugDir, ModuleName + '.map')\r |
| 450 | VfrUniOffsetList = GetVariableOffset(MapFileName, EfiFileName, VfrNameList)\r |
| 451 | \r |
| 452 | if not VfrUniOffsetList:\r |
| 453 | return\r |
| 454 | \r |
| 455 | try:\r |
| 456 | fInputfile = open(OutputFile, "wb+")\r |
| 457 | except:\r |
| 458 | EdkLogger.error("Trim", FILE_OPEN_FAILURE, "File open failed for %s" %OutputFile, None)\r |
| 459 | \r |
| 460 | # Use a instance of BytesIO to cache data\r |
| 461 | fStringIO = BytesIO()\r |
| 462 | \r |
| 463 | for Item in VfrUniOffsetList:\r |
| 464 | if (Item[0].find("Strings") != -1):\r |
| 465 | #\r |
| 466 | # UNI offset in image.\r |
| 467 | # GUID + Offset\r |
| 468 | # { 0x8913c5e0, 0x33f6, 0x4d86, { 0x9b, 0xf1, 0x43, 0xef, 0x89, 0xfc, 0x6, 0x66 } }\r |
| 469 | #\r |
| 470 | UniGuid = b'\xe0\xc5\x13\x89\xf63\x86M\x9b\xf1C\xef\x89\xfc\x06f'\r |
| 471 | fStringIO.write(UniGuid)\r |
| 472 | UniValue = pack ('Q', int (Item[1], 16))\r |
| 473 | fStringIO.write (UniValue)\r |
| 474 | else:\r |
| 475 | #\r |
| 476 | # VFR binary offset in image.\r |
| 477 | # GUID + Offset\r |
| 478 | # { 0xd0bc7cb4, 0x6a47, 0x495f, { 0xaa, 0x11, 0x71, 0x7, 0x46, 0xda, 0x6, 0xa2 } };\r |
| 479 | #\r |
| 480 | VfrGuid = b'\xb4|\xbc\xd0Gj_I\xaa\x11q\x07F\xda\x06\xa2'\r |
| 481 | fStringIO.write(VfrGuid)\r |
| 482 | type (Item[1])\r |
| 483 | VfrValue = pack ('Q', int (Item[1], 16))\r |
| 484 | fStringIO.write (VfrValue)\r |
| 485 | \r |
| 486 | #\r |
| 487 | # write data into file.\r |
| 488 | #\r |
| 489 | try :\r |
| 490 | fInputfile.write (fStringIO.getvalue())\r |
| 491 | except:\r |
| 492 | EdkLogger.error("Trim", FILE_WRITE_FAILURE, "Write data to file %s failed, please check whether the file been locked or using by other applications." %OutputFile, None)\r |
| 493 | \r |
| 494 | fStringIO.close ()\r |
| 495 | fInputfile.close ()\r |
| 496 | \r |
| 497 | \r |
| 498 | ## Parse command line options\r |
| 499 | #\r |
| 500 | # Using standard Python module optparse to parse command line option of this tool.\r |
| 501 | #\r |
| 502 | # @retval Options A optparse.Values object containing the parsed options\r |
| 503 | # @retval InputFile Path of file to be trimmed\r |
| 504 | #\r |
| 505 | def Options():\r |
| 506 | OptionList = [\r |
| 507 | make_option("-s", "--source-code", dest="FileType", const="SourceCode", action="store_const",\r |
| 508 | help="The input file is preprocessed source code, including C or assembly code"),\r |
| 509 | make_option("-r", "--vfr-file", dest="FileType", const="Vfr", action="store_const",\r |
| 510 | help="The input file is preprocessed VFR file"),\r |
| 511 | make_option("--Vfr-Uni-Offset", dest="FileType", const="VfrOffsetBin", action="store_const",\r |
| 512 | help="The input file is EFI image"),\r |
| 513 | make_option("--asl-deps", dest="AslDeps", const="True", action="store_const",\r |
| 514 | help="Generate Asl dependent files."),\r |
| 515 | make_option("-a", "--asl-file", dest="FileType", const="Asl", action="store_const",\r |
| 516 | help="The input file is ASL file"),\r |
| 517 | make_option( "--asm-file", dest="FileType", const="Asm", action="store_const",\r |
| 518 | help="The input file is asm file"),\r |
| 519 | make_option("-c", "--convert-hex", dest="ConvertHex", action="store_true",\r |
| 520 | help="Convert standard hex format (0xabcd) to MASM format (abcdh)"),\r |
| 521 | \r |
| 522 | make_option("-l", "--trim-long", dest="TrimLong", action="store_true",\r |
| 523 | help="Remove postfix of long number"),\r |
| 524 | make_option("-i", "--include-path-file", dest="IncludePathFile",\r |
| 525 | help="The input file is include path list to search for ASL include file"),\r |
| 526 | make_option("-o", "--output", dest="OutputFile",\r |
| 527 | help="File to store the trimmed content"),\r |
| 528 | make_option("--ModuleName", dest="ModuleName", help="The module's BASE_NAME"),\r |
| 529 | make_option("--DebugDir", dest="DebugDir",\r |
| 530 | help="Debug Output directory to store the output files"),\r |
| 531 | make_option("-v", "--verbose", dest="LogLevel", action="store_const", const=EdkLogger.VERBOSE,\r |
| 532 | help="Run verbosely"),\r |
| 533 | make_option("-d", "--debug", dest="LogLevel", type="int",\r |
| 534 | help="Run with debug information"),\r |
| 535 | make_option("-q", "--quiet", dest="LogLevel", action="store_const", const=EdkLogger.QUIET,\r |
| 536 | help="Run quietly"),\r |
| 537 | make_option("-?", action="help", help="show this help message and exit"),\r |
| 538 | ]\r |
| 539 | \r |
| 540 | # use clearer usage to override default usage message\r |
| 541 | UsageString = "%prog [-s|-r|-a|--Vfr-Uni-Offset] [-c] [-v|-d <debug_level>|-q] [-i <include_path_file>] [-o <output_file>] [--ModuleName <ModuleName>] [--DebugDir <DebugDir>] [<input_file>]"\r |
| 542 | \r |
| 543 | Parser = OptionParser(description=__copyright__, version=__version__, option_list=OptionList, usage=UsageString)\r |
| 544 | Parser.set_defaults(FileType="Vfr")\r |
| 545 | Parser.set_defaults(ConvertHex=False)\r |
| 546 | Parser.set_defaults(LogLevel=EdkLogger.INFO)\r |
| 547 | \r |
| 548 | Options, Args = Parser.parse_args()\r |
| 549 | \r |
| 550 | # error check\r |
| 551 | if Options.FileType == 'VfrOffsetBin':\r |
| 552 | if len(Args) == 0:\r |
| 553 | return Options, ''\r |
| 554 | elif len(Args) > 1:\r |
| 555 | EdkLogger.error("Trim", OPTION_NOT_SUPPORTED, ExtraData=Parser.get_usage())\r |
| 556 | if len(Args) == 0:\r |
| 557 | EdkLogger.error("Trim", OPTION_MISSING, ExtraData=Parser.get_usage())\r |
| 558 | if len(Args) > 1:\r |
| 559 | EdkLogger.error("Trim", OPTION_NOT_SUPPORTED, ExtraData=Parser.get_usage())\r |
| 560 | \r |
| 561 | InputFile = Args[0]\r |
| 562 | return Options, InputFile\r |
| 563 | \r |
| 564 | ## Entrance method\r |
| 565 | #\r |
| 566 | # This method mainly dispatch specific methods per the command line options.\r |
| 567 | # If no error found, return zero value so the caller of this tool can know\r |
| 568 | # if it's executed successfully or not.\r |
| 569 | #\r |
| 570 | # @retval 0 Tool was successful\r |
| 571 | # @retval 1 Tool failed\r |
| 572 | #\r |
| 573 | def Main():\r |
| 574 | try:\r |
| 575 | EdkLogger.Initialize()\r |
| 576 | CommandOptions, InputFile = Options()\r |
| 577 | if CommandOptions.LogLevel < EdkLogger.DEBUG_9:\r |
| 578 | EdkLogger.SetLevel(CommandOptions.LogLevel + 1)\r |
| 579 | else:\r |
| 580 | EdkLogger.SetLevel(CommandOptions.LogLevel)\r |
| 581 | except FatalError as X:\r |
| 582 | return 1\r |
| 583 | \r |
| 584 | try:\r |
| 585 | if CommandOptions.FileType == "Vfr":\r |
| 586 | if CommandOptions.OutputFile is None:\r |
| 587 | CommandOptions.OutputFile = os.path.splitext(InputFile)[0] + '.iii'\r |
| 588 | TrimPreprocessedVfr(InputFile, CommandOptions.OutputFile)\r |
| 589 | elif CommandOptions.FileType == "Asl":\r |
| 590 | if CommandOptions.OutputFile is None:\r |
| 591 | CommandOptions.OutputFile = os.path.splitext(InputFile)[0] + '.iii'\r |
| 592 | TrimAslFile(InputFile, CommandOptions.OutputFile, CommandOptions.IncludePathFile,CommandOptions.AslDeps)\r |
| 593 | elif CommandOptions.FileType == "VfrOffsetBin":\r |
| 594 | GenerateVfrBinSec(CommandOptions.ModuleName, CommandOptions.DebugDir, CommandOptions.OutputFile)\r |
| 595 | elif CommandOptions.FileType == "Asm":\r |
| 596 | TrimAsmFile(InputFile, CommandOptions.OutputFile, CommandOptions.IncludePathFile)\r |
| 597 | else :\r |
| 598 | if CommandOptions.OutputFile is None:\r |
| 599 | CommandOptions.OutputFile = os.path.splitext(InputFile)[0] + '.iii'\r |
| 600 | TrimPreprocessedFile(InputFile, CommandOptions.OutputFile, CommandOptions.ConvertHex, CommandOptions.TrimLong)\r |
| 601 | except FatalError as X:\r |
| 602 | import platform\r |
| 603 | import traceback\r |
| 604 | if CommandOptions is not None and CommandOptions.LogLevel <= EdkLogger.DEBUG_9:\r |
| 605 | EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r |
| 606 | return 1\r |
| 607 | except:\r |
| 608 | import traceback\r |
| 609 | import platform\r |
| 610 | EdkLogger.error(\r |
| 611 | "\nTrim",\r |
| 612 | CODE_ERROR,\r |
| 613 | "Unknown fatal error when trimming [%s]" % InputFile,\r |
| 614 | ExtraData="\n(Please send email to %s for help, attaching following call stack trace!)\n" % MSG_EDKII_MAIL_ADDR,\r |
| 615 | RaiseError=False\r |
| 616 | )\r |
| 617 | EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r |
| 618 | return 1\r |
| 619 | \r |
| 620 | return 0\r |
| 621 | \r |
| 622 | if __name__ == '__main__':\r |
| 623 | r = Main()\r |
| 624 | ## 0-127 is a safe return range, and 1 is a standard default error\r |
| 625 | if r < 0 or r > 127: r = 1\r |
| 626 | sys.exit(r)\r |
| 627 | \r |