## @file | |
# This file is used to generate DEPEX file for module's dependency expression | |
# | |
# Copyright (c) 2007 - 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. | |
## 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()) | |