## @file # This file is used to generate DEPEX file for module's dependency expression # # Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.
# 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. ## Import Modules # import sys import Common.LongFilePathOs as os import re import traceback from Common.LongFilePathSupport import OpenLongFilePath as open from StringIO import StringIO from struct import pack from Common.BuildToolError import * from Common.Misc import SaveFileOnChange from Common.Misc import GuidStructureStringToGuidString from Common import EdkLogger as EdkLogger from Common.BuildVersion import gBUILD_VERSION ## Regular expression for matching "DEPENDENCY_START ... DEPENDENCY_END" gStartClosePattern = re.compile(".*DEPENDENCY_START(.+)DEPENDENCY_END.*", re.S) ## Mapping between module type and EFI phase gType2Phase = { "BASE" : None, "SEC" : "PEI", "PEI_CORE" : "PEI", "PEIM" : "PEI", "DXE_CORE" : "DXE", "DXE_DRIVER" : "DXE", "DXE_SMM_DRIVER" : "DXE", "DXE_RUNTIME_DRIVER": "DXE", "DXE_SAL_DRIVER" : "DXE", "UEFI_DRIVER" : "DXE", "UEFI_APPLICATION" : "DXE", "SMM_CORE" : "DXE", } ## Convert dependency expression string into EFI internal representation # # DependencyExpression class is used to parse dependency expression string and # convert it into its binary form. # class DependencyExpression: ArchProtocols = set([ '665e3ff6-46cc-11d4-9a38-0090273fc14d', # 'gEfiBdsArchProtocolGuid' '26baccb1-6f42-11d4-bce7-0080c73c8881', # 'gEfiCpuArchProtocolGuid' '26baccb2-6f42-11d4-bce7-0080c73c8881', # 'gEfiMetronomeArchProtocolGuid' '1da97072-bddc-4b30-99f1-72a0b56fff2a', # 'gEfiMonotonicCounterArchProtocolGuid' '27cfac87-46cc-11d4-9a38-0090273fc14d', # 'gEfiRealTimeClockArchProtocolGuid' '27cfac88-46cc-11d4-9a38-0090273fc14d', # 'gEfiResetArchProtocolGuid' 'b7dfb4e1-052f-449f-87be-9818fc91b733', # 'gEfiRuntimeArchProtocolGuid' 'a46423e3-4617-49f1-b9ff-d1bfa9115839', # 'gEfiSecurityArchProtocolGuid' '26baccb3-6f42-11d4-bce7-0080c73c8881', # 'gEfiTimerArchProtocolGuid' '6441f818-6362-4e44-b570-7dba31dd2453', # 'gEfiVariableWriteArchProtocolGuid' '1e5668e2-8481-11d4-bcf1-0080c73c8881', # 'gEfiVariableArchProtocolGuid' '665e3ff5-46cc-11d4-9a38-0090273fc14d' # 'gEfiWatchdogTimerArchProtocolGuid' ] ) OpcodePriority = { "AND" : 1, "OR" : 1, "NOT" : 2, # "SOR" : 9, # "BEFORE": 9, # "AFTER" : 9, } Opcode = { "PEI" : { "PUSH" : 0x02, "AND" : 0x03, "OR" : 0x04, "NOT" : 0x05, "TRUE" : 0x06, "FALSE" : 0x07, "END" : 0x08 }, "DXE" : { "BEFORE": 0x00, "AFTER" : 0x01, "PUSH" : 0x02, "AND" : 0x03, "OR" : 0x04, "NOT" : 0x05, "TRUE" : 0x06, "FALSE" : 0x07, "END" : 0x08, "SOR" : 0x09 } } # all supported op codes and operands SupportedOpcode = ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "END", "SOR"] SupportedOperand = ["TRUE", "FALSE"] OpcodeWithSingleOperand = ['NOT', 'BEFORE', 'AFTER'] OpcodeWithTwoOperand = ['AND', 'OR'] # op code that should not be the last one NonEndingOpcode = ["AND", "OR", "NOT", 'SOR'] # op code must not present at the same time ExclusiveOpcode = ["BEFORE", "AFTER"] # op code that should be the first one if it presents AboveAllOpcode = ["SOR", "BEFORE", "AFTER"] # # open and close brace must be taken as individual tokens # TokenPattern = re.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)") ## Constructor # # @param Expression The list or string of dependency expression # @param ModuleType The type of the module using the dependency expression # def __init__(self, Expression, ModuleType, Optimize=False): self.ModuleType = ModuleType self.Phase = gType2Phase[ModuleType] if type(Expression) == type([]): self.ExpressionString = " ".join(Expression) self.TokenList = Expression else: self.ExpressionString = Expression self.GetExpressionTokenList() self.PostfixNotation = [] self.OpcodeList = [] self.GetPostfixNotation() self.ValidateOpcode() EdkLogger.debug(EdkLogger.DEBUG_8, repr(self)) if Optimize: self.Optimize() EdkLogger.debug(EdkLogger.DEBUG_8, "\n Optimized: " + repr(self)) def __str__(self): return " ".join(self.TokenList) def __repr__(self): WellForm = '' for Token in self.PostfixNotation: if Token in self.SupportedOpcode: WellForm += "\n " + Token else: WellForm += ' ' + Token return WellForm ## Split the expression string into token list def GetExpressionTokenList(self): self.TokenList = self.TokenPattern.findall(self.ExpressionString) ## Convert token list into postfix notation def GetPostfixNotation(self): Stack = [] LastToken = '' for Token in self.TokenList: if Token == "(": if LastToken not in self.SupportedOpcode + ['(', '', None]: EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before open parentheses", ExtraData="Near %s" % LastToken) Stack.append(Token) elif Token == ")": if '(' not in Stack: EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses", ExtraData=str(self)) elif LastToken in self.SupportedOpcode + ['', None]: EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before close parentheses", ExtraData="Near %s" % LastToken) while len(Stack) > 0: if Stack[-1] == '(': Stack.pop() break self.PostfixNotation.append(Stack.pop()) elif Token in self.OpcodePriority: if Token == "NOT": if LastToken not in self.SupportedOpcode + ['(', '', None]: EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before NOT", ExtraData="Near %s" % LastToken) elif LastToken in self.SupportedOpcode + ['(', '', None]: EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before " + Token, ExtraData="Near %s" % LastToken) while len(Stack) > 0: if Stack[-1] == "(" or self.OpcodePriority[Token] >= self.OpcodePriority[Stack[-1]]: break self.PostfixNotation.append(Stack.pop()) Stack.append(Token) self.OpcodeList.append(Token) else: if Token not in self.SupportedOpcode: # not OP, take it as GUID if LastToken not in self.SupportedOpcode + ['(', '', None]: EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before %s" % Token, ExtraData="Near %s" % LastToken) if len(self.OpcodeList) == 0 or self.OpcodeList[-1] not in self.ExclusiveOpcode: if Token not in self.SupportedOperand: self.PostfixNotation.append("PUSH") # check if OP is valid in this phase elif Token in self.Opcode[self.Phase]: if Token == "END": break self.OpcodeList.append(Token) else: EdkLogger.error("GenDepex", PARSER_ERROR, "Opcode=%s doesn't supported in %s stage " % (Token, self.Phase), ExtraData=str(self)) self.PostfixNotation.append(Token) LastToken = Token # there should not be parentheses in Stack if '(' in Stack or ')' in Stack: EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses", ExtraData=str(self)) while len(Stack) > 0: self.PostfixNotation.append(Stack.pop()) if self.PostfixNotation[-1] != 'END': self.PostfixNotation.append("END") ## Validate the dependency expression def ValidateOpcode(self): for Op in self.AboveAllOpcode: if Op in self.PostfixNotation: if Op != self.PostfixNotation[0]: EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the first opcode in the expression" % Op, ExtraData=str(self)) if len(self.PostfixNotation) < 3: EdkLogger.error("GenDepex", PARSER_ERROR, "Missing operand for %s" % Op, ExtraData=str(self)) for Op in self.ExclusiveOpcode: if Op in self.OpcodeList: if len(self.OpcodeList) > 1: EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the only opcode in the expression" % Op, ExtraData=str(self)) if len(self.PostfixNotation) < 3: EdkLogger.error("GenDepex", PARSER_ERROR, "Missing operand for %s" % Op, ExtraData=str(self)) if self.TokenList[-1] != 'END' and self.TokenList[-1] in self.NonEndingOpcode: EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-1], ExtraData=str(self)) if self.TokenList[-1] == 'END' and self.TokenList[-2] in self.NonEndingOpcode: EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-2], ExtraData=str(self)) if "END" in self.TokenList and "END" != self.TokenList[-1]: EdkLogger.error("GenDepex", PARSER_ERROR, "Extra expressions after END", ExtraData=str(self)) ## Simply optimize the dependency expression by removing duplicated operands def Optimize(self): ValidOpcode = list(set(self.OpcodeList)) if len(ValidOpcode) != 1 or ValidOpcode[0] not in ['AND', 'OR']: return Op = ValidOpcode[0] NewOperand = [] AllOperand = set() for Token in self.PostfixNotation: if Token in self.SupportedOpcode or Token in NewOperand: continue AllOperand.add(Token) if Token == 'TRUE': if Op == 'AND': continue else: NewOperand.append(Token) break elif Token == 'FALSE': if Op == 'OR': continue else: NewOperand.append(Token) break NewOperand.append(Token) # don't generate depex if only TRUE operand left if self.ModuleType == 'PEIM' and len(NewOperand) == 1 and NewOperand[0] == 'TRUE': self.PostfixNotation = [] return # don't generate depex if all operands are architecture protocols if self.ModuleType in ['UEFI_DRIVER', 'DXE_DRIVER', 'DXE_RUNTIME_DRIVER', 'DXE_SAL_DRIVER', 'DXE_SMM_DRIVER'] and \ Op == 'AND' and \ self.ArchProtocols == set([GuidStructureStringToGuidString(Guid) for Guid in AllOperand]): self.PostfixNotation = [] return if len(NewOperand) == 0: self.TokenList = list(AllOperand) else: self.TokenList = [] while True: self.TokenList.append(NewOperand.pop(0)) if NewOperand == []: break self.TokenList.append(Op) self.PostfixNotation = [] self.GetPostfixNotation() ## Convert a GUID value in C structure format into its binary form # # @param Guid The GUID value in C structure format # # @retval array The byte array representing the GUID value # def GetGuidValue(self, Guid): GuidValueString = Guid.replace("{", "").replace("}", "").replace(" ", "") GuidValueList = GuidValueString.split(",") if len(GuidValueList) != 11: EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid GUID value string or opcode: %s" % Guid) return pack("1I2H8B", *(int(value, 16) for value in GuidValueList)) ## Save the binary form of dependency expression in file # # @param File The path of file. If None is given, put the data on console # # @retval True If the file doesn't exist or file is changed # @retval False If file exists and is not changed. # def Generate(self, File=None): Buffer = StringIO() if len(self.PostfixNotation) == 0: return False for Item in self.PostfixNotation: if Item in self.Opcode[self.Phase]: Buffer.write(pack("B", self.Opcode[self.Phase][Item])) elif Item in self.SupportedOpcode: EdkLogger.error("GenDepex", FORMAT_INVALID, "Opcode [%s] is not expected in %s phase" % (Item, self.Phase), ExtraData=self.ExpressionString) else: Buffer.write(self.GetGuidValue(Item)) FilePath = "" FileChangeFlag = True if File == None: sys.stdout.write(Buffer.getvalue()) FilePath = "STDOUT" else: FileChangeFlag = SaveFileOnChange(File, Buffer.getvalue(), True) Buffer.close() return FileChangeFlag versionNumber = ("0.04" + " " + gBUILD_VERSION) __version__ = "%prog Version " + versionNumber __copyright__ = "Copyright (c) 2007-2010, Intel Corporation All rights reserved." __usage__ = "%prog [options] [dependency_expression_file]" ## Parse command line options # # @retval OptionParser # def GetOptions(): from optparse import OptionParser Parser = OptionParser(description=__copyright__, version=__version__, usage=__usage__) Parser.add_option("-o", "--output", dest="OutputFile", default=None, metavar="FILE", help="Specify the name of depex file to be generated") Parser.add_option("-t", "--module-type", dest="ModuleType", default=None, help="The type of module for which the dependency expression serves") Parser.add_option("-e", "--dependency-expression", dest="Expression", default="", help="The string of dependency expression. If this option presents, the input file will be ignored.") Parser.add_option("-m", "--optimize", dest="Optimize", default=False, action="store_true", help="Do some simple optimization on the expression.") Parser.add_option("-v", "--verbose", dest="verbose", default=False, action="store_true", help="build with verbose information") Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.") Parser.add_option("-q", "--quiet", dest="quiet", default=False, action="store_true", help="build with little information") return Parser.parse_args() ## Entrance method # # @retval 0 Tool was successful # @retval 1 Tool failed # def Main(): EdkLogger.Initialize() Option, Input = GetOptions() # Set log level if Option.quiet: EdkLogger.SetLevel(EdkLogger.QUIET) elif Option.verbose: EdkLogger.SetLevel(EdkLogger.VERBOSE) elif Option.debug != None: EdkLogger.SetLevel(Option.debug + 1) else: EdkLogger.SetLevel(EdkLogger.INFO) try: if Option.ModuleType == None or Option.ModuleType not in gType2Phase: EdkLogger.error("GenDepex", OPTION_MISSING, "Module type is not specified or supported") DxsFile = '' if len(Input) > 0 and Option.Expression == "": DxsFile = Input[0] DxsString = open(DxsFile, 'r').read().replace("\n", " ").replace("\r", " ") DxsString = gStartClosePattern.sub("\\1", DxsString) elif Option.Expression != "": if Option.Expression[0] == '"': DxsString = Option.Expression[1:-1] else: DxsString = Option.Expression else: EdkLogger.error("GenDepex", OPTION_MISSING, "No expression string or file given") Dpx = DependencyExpression(DxsString, Option.ModuleType, Option.Optimize) if Option.OutputFile != None: FileChangeFlag = Dpx.Generate(Option.OutputFile) if not FileChangeFlag and DxsFile: # # Touch the output file if its time stamp is older than the original # DXS file to avoid re-invoke this tool for the dependency check in build rule. # if os.stat(DxsFile)[8] > os.stat(Option.OutputFile)[8]: os.utime(Option.OutputFile, None) else: Dpx.Generate() except BaseException, X: EdkLogger.quiet("") if Option != None and Option.debug != None: EdkLogger.quiet(traceback.format_exc()) else: EdkLogger.quiet(str(X)) return 1 return 0 if __name__ == '__main__': sys.exit(Main())