## @file | |
# This file is used to generate DEPEX file for module's dependency expression | |
# | |
# Copyright (c) 2007 - 2018, 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 io import BytesIO | |
from struct import pack | |
from Common.BuildToolError import * | |
from Common.Misc import SaveFileOnChange | |
from Common.Misc import GuidStructureStringToGuidString | |
from Common.Misc import GuidStructureByteArrayToGuidString | |
from Common.Misc import GuidStringToGuidStructureString | |
from Common import EdkLogger as EdkLogger | |
from Common.BuildVersion import gBUILD_VERSION | |
from Common.DataType import * | |
## 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 = { | |
SUP_MODULE_BASE : None, | |
SUP_MODULE_SEC : "PEI", | |
SUP_MODULE_PEI_CORE : "PEI", | |
SUP_MODULE_PEIM : "PEI", | |
SUP_MODULE_DXE_CORE : "DXE", | |
SUP_MODULE_DXE_DRIVER : "DXE", | |
SUP_MODULE_DXE_SMM_DRIVER : "DXE", | |
SUP_MODULE_DXE_RUNTIME_DRIVER: "DXE", | |
SUP_MODULE_DXE_SAL_DRIVER : "DXE", | |
SUP_MODULE_UEFI_DRIVER : "DXE", | |
SUP_MODULE_UEFI_APPLICATION : "DXE", | |
SUP_MODULE_SMM_CORE : "DXE", | |
SUP_MODULE_MM_STANDALONE : "MM", | |
SUP_MODULE_MM_CORE_STANDALONE : "MM", | |
} | |
## 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 = { | |
'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 = { | |
DEPEX_OPCODE_AND : 1, | |
DEPEX_OPCODE_OR : 1, | |
DEPEX_OPCODE_NOT : 2, | |
} | |
Opcode = { | |
"PEI" : { | |
DEPEX_OPCODE_PUSH : 0x02, | |
DEPEX_OPCODE_AND : 0x03, | |
DEPEX_OPCODE_OR : 0x04, | |
DEPEX_OPCODE_NOT : 0x05, | |
DEPEX_OPCODE_TRUE : 0x06, | |
DEPEX_OPCODE_FALSE : 0x07, | |
DEPEX_OPCODE_END : 0x08 | |
}, | |
"DXE" : { | |
DEPEX_OPCODE_BEFORE: 0x00, | |
DEPEX_OPCODE_AFTER : 0x01, | |
DEPEX_OPCODE_PUSH : 0x02, | |
DEPEX_OPCODE_AND : 0x03, | |
DEPEX_OPCODE_OR : 0x04, | |
DEPEX_OPCODE_NOT : 0x05, | |
DEPEX_OPCODE_TRUE : 0x06, | |
DEPEX_OPCODE_FALSE : 0x07, | |
DEPEX_OPCODE_END : 0x08, | |
DEPEX_OPCODE_SOR : 0x09 | |
}, | |
"MM" : { | |
DEPEX_OPCODE_BEFORE: 0x00, | |
DEPEX_OPCODE_AFTER : 0x01, | |
DEPEX_OPCODE_PUSH : 0x02, | |
DEPEX_OPCODE_AND : 0x03, | |
DEPEX_OPCODE_OR : 0x04, | |
DEPEX_OPCODE_NOT : 0x05, | |
DEPEX_OPCODE_TRUE : 0x06, | |
DEPEX_OPCODE_FALSE : 0x07, | |
DEPEX_OPCODE_END : 0x08, | |
DEPEX_OPCODE_SOR : 0x09 | |
} | |
} | |
# all supported op codes and operands | |
SupportedOpcode = [DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER, DEPEX_OPCODE_PUSH, DEPEX_OPCODE_AND, DEPEX_OPCODE_OR, DEPEX_OPCODE_NOT, DEPEX_OPCODE_END, DEPEX_OPCODE_SOR] | |
SupportedOperand = [DEPEX_OPCODE_TRUE, DEPEX_OPCODE_FALSE] | |
OpcodeWithSingleOperand = [DEPEX_OPCODE_NOT, DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER] | |
OpcodeWithTwoOperand = [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR] | |
# op code that should not be the last one | |
NonEndingOpcode = [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR, DEPEX_OPCODE_NOT, DEPEX_OPCODE_SOR] | |
# op code must not present at the same time | |
ExclusiveOpcode = [DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER] | |
# op code that should be the first one if it presents | |
AboveAllOpcode = [DEPEX_OPCODE_SOR, DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_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 isinstance(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 == DEPEX_OPCODE_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(DEPEX_OPCODE_PUSH) | |
# check if OP is valid in this phase | |
elif Token in self.Opcode[self.Phase]: | |
if Token == DEPEX_OPCODE_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] != DEPEX_OPCODE_END: | |
self.PostfixNotation.append(DEPEX_OPCODE_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] != DEPEX_OPCODE_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] == DEPEX_OPCODE_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 DEPEX_OPCODE_END in self.TokenList and DEPEX_OPCODE_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): | |
OpcodeSet = set(self.OpcodeList) | |
# if there are isn't one in the set, return | |
if len(OpcodeSet) != 1: | |
return | |
Op = OpcodeSet.pop() | |
#if Op isn't either OR or AND, return | |
if Op not in [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR]: | |
return | |
NewOperand = [] | |
AllOperand = set() | |
for Token in self.PostfixNotation: | |
if Token in self.SupportedOpcode or Token in NewOperand: | |
continue | |
AllOperand.add(Token) | |
if Token == DEPEX_OPCODE_TRUE: | |
if Op == DEPEX_OPCODE_AND: | |
continue | |
else: | |
NewOperand.append(Token) | |
break | |
elif Token == DEPEX_OPCODE_FALSE: | |
if Op == DEPEX_OPCODE_OR: | |
continue | |
else: | |
NewOperand.append(Token) | |
break | |
NewOperand.append(Token) | |
# don't generate depex if only TRUE operand left | |
if self.ModuleType == SUP_MODULE_PEIM and len(NewOperand) == 1 and NewOperand[0] == DEPEX_OPCODE_TRUE: | |
self.PostfixNotation = [] | |
return | |
# don't generate depex if all operands are architecture protocols | |
if self.ModuleType in [SUP_MODULE_UEFI_DRIVER, SUP_MODULE_DXE_DRIVER, SUP_MODULE_DXE_RUNTIME_DRIVER, SUP_MODULE_DXE_SAL_DRIVER, SUP_MODULE_DXE_SMM_DRIVER, SUP_MODULE_MM_STANDALONE] and \ | |
Op == DEPEX_OPCODE_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 and len(GuidValueList) == 16: | |
GuidValueString = GuidStringToGuidStructureString(GuidStructureByteArrayToGuidString(Guid)) | |
GuidValueString = GuidValueString.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 = BytesIO() | |
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 is 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-2018, 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 is not None: | |
EdkLogger.SetLevel(Option.debug + 1) | |
else: | |
EdkLogger.SetLevel(EdkLogger.INFO) | |
try: | |
if Option.ModuleType is 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 is not 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 as X: | |
EdkLogger.quiet("") | |
if Option is not None and Option.debug is not None: | |
EdkLogger.quiet(traceback.format_exc()) | |
else: | |
EdkLogger.quiet(str(X)) | |
return 1 | |
return 0 | |
if __name__ == '__main__': | |
sys.exit(Main()) | |