1## @file
2# This file is used to generate DEPEX file for module's dependency expression
3#
4# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
5# This program and the accompanying materials
6# are licensed and made available under the terms and conditions of the BSD License
7# which accompanies this distribution.    The full text of the license may be found at
8# http://opensource.org/licenses/bsd-license.php
9#
10# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13## Import Modules
14#
15import sys
16import Common.LongFilePathOs as os
17import re
18import traceback
19from Common.LongFilePathSupport import OpenLongFilePath as open
20from StringIO import StringIO
21from struct import pack
22from Common.BuildToolError import *
23from Common.Misc import SaveFileOnChange
24from Common.Misc import GuidStructureStringToGuidString
25from Common import EdkLogger as EdkLogger
26from Common.BuildVersion import gBUILD_VERSION
27
28## Regular expression for matching "DEPENDENCY_START ... DEPENDENCY_END"
29gStartClosePattern = re.compile(".*DEPENDENCY_START(.+)DEPENDENCY_END.*", re.S)
30
31## Mapping between module type and EFI phase
32gType2Phase = {
33    "BASE"              :   None,
34    "SEC"               :   "PEI",
35    "PEI_CORE"          :   "PEI",
36    "PEIM"              :   "PEI",
37    "DXE_CORE"          :   "DXE",
38    "DXE_DRIVER"        :   "DXE",
39    "DXE_SMM_DRIVER"    :   "DXE",
40    "DXE_RUNTIME_DRIVER":   "DXE",
41    "DXE_SAL_DRIVER"    :   "DXE",
42    "UEFI_DRIVER"       :   "DXE",
43    "UEFI_APPLICATION"  :   "DXE",
44    "SMM_CORE"          :   "DXE",
45}
46
47## Convert dependency expression string into EFI internal representation
48#
49#   DependencyExpression class is used to parse dependency expression string and
50# convert it into its binary form.
51#
52class DependencyExpression:
53
54    ArchProtocols = set([
55                        '665e3ff6-46cc-11d4-9a38-0090273fc14d',     #   'gEfiBdsArchProtocolGuid'
56                        '26baccb1-6f42-11d4-bce7-0080c73c8881',     #   'gEfiCpuArchProtocolGuid'
57                        '26baccb2-6f42-11d4-bce7-0080c73c8881',     #   'gEfiMetronomeArchProtocolGuid'
58                        '1da97072-bddc-4b30-99f1-72a0b56fff2a',     #   'gEfiMonotonicCounterArchProtocolGuid'
59                        '27cfac87-46cc-11d4-9a38-0090273fc14d',     #   'gEfiRealTimeClockArchProtocolGuid'
60                        '27cfac88-46cc-11d4-9a38-0090273fc14d',     #   'gEfiResetArchProtocolGuid'
61                        'b7dfb4e1-052f-449f-87be-9818fc91b733',     #   'gEfiRuntimeArchProtocolGuid'
62                        'a46423e3-4617-49f1-b9ff-d1bfa9115839',     #   'gEfiSecurityArchProtocolGuid'
63                        '26baccb3-6f42-11d4-bce7-0080c73c8881',     #   'gEfiTimerArchProtocolGuid'
64                        '6441f818-6362-4e44-b570-7dba31dd2453',     #   'gEfiVariableWriteArchProtocolGuid'
65                        '1e5668e2-8481-11d4-bcf1-0080c73c8881',     #   'gEfiVariableArchProtocolGuid'
66                        '665e3ff5-46cc-11d4-9a38-0090273fc14d'      #   'gEfiWatchdogTimerArchProtocolGuid'
67                        ]
68                    )
69
70    OpcodePriority = {
71        "AND"   :   1,
72        "OR"    :   1,
73        "NOT"   :   2,
74        # "SOR"   :   9,
75        # "BEFORE":   9,
76        # "AFTER" :   9,
77    }
78
79    Opcode = {
80        "PEI"   : {
81            "PUSH"  :   0x02,
82            "AND"   :   0x03,
83            "OR"    :   0x04,
84            "NOT"   :   0x05,
85            "TRUE"  :   0x06,
86            "FALSE" :   0x07,
87            "END"   :   0x08
88        },
89
90        "DXE"   : {
91            "BEFORE":   0x00,
92            "AFTER" :   0x01,
93            "PUSH"  :   0x02,
94            "AND"   :   0x03,
95            "OR"    :   0x04,
96            "NOT"   :   0x05,
97            "TRUE"  :   0x06,
98            "FALSE" :   0x07,
99            "END"   :   0x08,
100            "SOR"   :   0x09
101        }
102    }
103
104    # all supported op codes and operands
105    SupportedOpcode = ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "END", "SOR"]
106    SupportedOperand = ["TRUE", "FALSE"]
107
108    OpcodeWithSingleOperand = ['NOT', 'BEFORE', 'AFTER']
109    OpcodeWithTwoOperand = ['AND', 'OR']
110
111    # op code that should not be the last one
112    NonEndingOpcode = ["AND", "OR", "NOT", 'SOR']
113    # op code must not present at the same time
114    ExclusiveOpcode = ["BEFORE", "AFTER"]
115    # op code that should be the first one if it presents
116    AboveAllOpcode = ["SOR", "BEFORE", "AFTER"]
117
118    #
119    # open and close brace must be taken as individual tokens
120    #
121    TokenPattern = re.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)")
122
123    ## Constructor
124    #
125    #   @param  Expression  The list or string of dependency expression
126    #   @param  ModuleType  The type of the module using the dependency expression
127    #
128    def __init__(self, Expression, ModuleType, Optimize=False):
129        self.ModuleType = ModuleType
130        self.Phase = gType2Phase[ModuleType]
131        if type(Expression) == type([]):
132            self.ExpressionString = " ".join(Expression)
133            self.TokenList = Expression
134        else:
135            self.ExpressionString = Expression
136            self.GetExpressionTokenList()
137
138        self.PostfixNotation = []
139        self.OpcodeList = []
140
141        self.GetPostfixNotation()
142        self.ValidateOpcode()
143
144        EdkLogger.debug(EdkLogger.DEBUG_8, repr(self))
145        if Optimize:
146            self.Optimize()
147            EdkLogger.debug(EdkLogger.DEBUG_8, "\n    Optimized: " + repr(self))
148
149    def __str__(self):
150        return " ".join(self.TokenList)
151
152    def __repr__(self):
153        WellForm = ''
154        for Token in self.PostfixNotation:
155            if Token in self.SupportedOpcode:
156                WellForm += "\n    " + Token
157            else:
158                WellForm += ' ' + Token
159        return WellForm
160
161    ## Split the expression string into token list
162    def GetExpressionTokenList(self):
163        self.TokenList = self.TokenPattern.findall(self.ExpressionString)
164
165    ## Convert token list into postfix notation
166    def GetPostfixNotation(self):
167        Stack = []
168        LastToken = ''
169        for Token in self.TokenList:
170            if Token == "(":
171                if LastToken not in self.SupportedOpcode + ['(', '', None]:
172                    EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before open parentheses",
173                                    ExtraData="Near %s" % LastToken)
174                Stack.append(Token)
175            elif Token == ")":
176                if '(' not in Stack:
177                    EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses",
178                                    ExtraData=str(self))
179                elif LastToken in self.SupportedOpcode + ['', None]:
180                    EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before close parentheses",
181                                    ExtraData="Near %s" % LastToken)
182                while len(Stack) > 0:
183                    if Stack[-1] == '(':
184                        Stack.pop()
185                        break
186                    self.PostfixNotation.append(Stack.pop())
187            elif Token in self.OpcodePriority:
188                if Token == "NOT":
189                    if LastToken not in self.SupportedOpcode + ['(', '', None]:
190                        EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before NOT",
191                                        ExtraData="Near %s" % LastToken)
192                elif LastToken in self.SupportedOpcode + ['(', '', None]:
193                        EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before " + Token,
194                                        ExtraData="Near %s" % LastToken)
195
196                while len(Stack) > 0:
197                    if Stack[-1] == "(" or self.OpcodePriority[Token] >= self.OpcodePriority[Stack[-1]]:
198                        break
199                    self.PostfixNotation.append(Stack.pop())
200                Stack.append(Token)
201                self.OpcodeList.append(Token)
202            else:
203                if Token not in self.SupportedOpcode:
204                    # not OP, take it as GUID
205                    if LastToken not in self.SupportedOpcode + ['(', '', None]:
206                        EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before %s" % Token,
207                                        ExtraData="Near %s" % LastToken)
208                    if len(self.OpcodeList) == 0 or self.OpcodeList[-1] not in self.ExclusiveOpcode:
209                        if Token not in self.SupportedOperand:
210                            self.PostfixNotation.append("PUSH")
211                # check if OP is valid in this phase
212                elif Token in self.Opcode[self.Phase]:
213                    if Token == "END":
214                        break
215                    self.OpcodeList.append(Token)
216                else:
217                    EdkLogger.error("GenDepex", PARSER_ERROR,
218                                    "Opcode=%s doesn't supported in %s stage " % (Token, self.Phase),
219                                    ExtraData=str(self))
220                self.PostfixNotation.append(Token)
221            LastToken = Token
222
223        # there should not be parentheses in Stack
224        if '(' in Stack or ')' in Stack:
225            EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses",
226                            ExtraData=str(self))
227        while len(Stack) > 0:
228            self.PostfixNotation.append(Stack.pop())
229        if self.PostfixNotation[-1] != 'END':
230            self.PostfixNotation.append("END")
231
232    ## Validate the dependency expression
233    def ValidateOpcode(self):
234        for Op in self.AboveAllOpcode:
235            if Op in self.PostfixNotation:
236                if Op != self.PostfixNotation[0]:
237                    EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the first opcode in the expression" % Op,
238                                    ExtraData=str(self))
239                if len(self.PostfixNotation) < 3:
240                    EdkLogger.error("GenDepex", PARSER_ERROR, "Missing operand for %s" % Op,
241                                    ExtraData=str(self))
242        for Op in self.ExclusiveOpcode:
243            if Op in self.OpcodeList:
244                if len(self.OpcodeList) > 1:
245                    EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the only opcode in the expression" % Op,
246                                    ExtraData=str(self))
247                if len(self.PostfixNotation) < 3:
248                    EdkLogger.error("GenDepex", PARSER_ERROR, "Missing operand for %s" % Op,
249                                    ExtraData=str(self))
250        if self.TokenList[-1] != 'END' and self.TokenList[-1] in self.NonEndingOpcode:
251            EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-1],
252                            ExtraData=str(self))
253        if self.TokenList[-1] == 'END' and self.TokenList[-2] in self.NonEndingOpcode:
254            EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-2],
255                            ExtraData=str(self))
256        if "END" in self.TokenList and "END" != self.TokenList[-1]:
257            EdkLogger.error("GenDepex", PARSER_ERROR, "Extra expressions after END",
258                            ExtraData=str(self))
259
260    ## Simply optimize the dependency expression by removing duplicated operands
261    def Optimize(self):
262        ValidOpcode = list(set(self.OpcodeList))
263        if len(ValidOpcode) != 1 or ValidOpcode[0] not in ['AND', 'OR']:
264            return
265        Op = ValidOpcode[0]
266        NewOperand = []
267        AllOperand = set()
268        for Token in self.PostfixNotation:
269            if Token in self.SupportedOpcode or Token in NewOperand:
270                continue
271            AllOperand.add(Token)
272            if Token == 'TRUE':
273                if Op == 'AND':
274                    continue
275                else:
276                    NewOperand.append(Token)
277                    break
278            elif Token == 'FALSE':
279                if Op == 'OR':
280                    continue
281                else:
282                    NewOperand.append(Token)
283                    break
284            NewOperand.append(Token)
285
286        # don't generate depex if only TRUE operand left
287        if self.ModuleType == 'PEIM' and len(NewOperand) == 1 and NewOperand[0] == 'TRUE':
288            self.PostfixNotation = []
289            return
290
291        # don't generate depex if all operands are architecture protocols
292        if self.ModuleType in ['UEFI_DRIVER', 'DXE_DRIVER', 'DXE_RUNTIME_DRIVER', 'DXE_SAL_DRIVER', 'DXE_SMM_DRIVER'] and \
293           Op == 'AND' and \
294           self.ArchProtocols == set([GuidStructureStringToGuidString(Guid) for Guid in AllOperand]):
295            self.PostfixNotation = []
296            return
297
298        if len(NewOperand) == 0:
299            self.TokenList = list(AllOperand)
300        else:
301            self.TokenList = []
302            while True:
303                self.TokenList.append(NewOperand.pop(0))
304                if NewOperand == []:
305                    break
306                self.TokenList.append(Op)
307        self.PostfixNotation = []
308        self.GetPostfixNotation()
309
310
311    ## Convert a GUID value in C structure format into its binary form
312    #
313    #   @param  Guid    The GUID value in C structure format
314    #
315    #   @retval array   The byte array representing the GUID value
316    #
317    def GetGuidValue(self, Guid):
318        GuidValueString = Guid.replace("{", "").replace("}", "").replace(" ", "")
319        GuidValueList = GuidValueString.split(",")
320        if len(GuidValueList) != 11:
321            EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid GUID value string or opcode: %s" % Guid)
322        return pack("1I2H8B", *(int(value, 16) for value in GuidValueList))
323
324    ## Save the binary form of dependency expression in file
325    #
326    #   @param  File    The path of file. If None is given, put the data on console
327    #
328    #   @retval True    If the file doesn't exist or file is changed
329    #   @retval False   If file exists and is not changed.
330    #
331    def Generate(self, File=None):
332        Buffer = StringIO()
333        if len(self.PostfixNotation) == 0:
334            return False
335
336        for Item in self.PostfixNotation:
337            if Item in self.Opcode[self.Phase]:
338                Buffer.write(pack("B", self.Opcode[self.Phase][Item]))
339            elif Item in self.SupportedOpcode:
340                EdkLogger.error("GenDepex", FORMAT_INVALID,
341                                "Opcode [%s] is not expected in %s phase" % (Item, self.Phase),
342                                ExtraData=self.ExpressionString)
343            else:
344                Buffer.write(self.GetGuidValue(Item))
345
346        FilePath = ""
347        FileChangeFlag = True
348        if File == None:
349            sys.stdout.write(Buffer.getvalue())
350            FilePath = "STDOUT"
351        else:
352            FileChangeFlag = SaveFileOnChange(File, Buffer.getvalue(), True)
353
354        Buffer.close()
355        return FileChangeFlag
356
357versionNumber = ("0.04" + " " + gBUILD_VERSION)
358__version__ = "%prog Version " + versionNumber
359__copyright__ = "Copyright (c) 2007-2010, Intel Corporation  All rights reserved."
360__usage__ = "%prog [options] [dependency_expression_file]"
361
362## Parse command line options
363#
364#   @retval OptionParser
365#
366def GetOptions():
367    from optparse import OptionParser
368
369    Parser = OptionParser(description=__copyright__, version=__version__, usage=__usage__)
370
371    Parser.add_option("-o", "--output", dest="OutputFile", default=None, metavar="FILE",
372                      help="Specify the name of depex file to be generated")
373    Parser.add_option("-t", "--module-type", dest="ModuleType", default=None,
374                      help="The type of module for which the dependency expression serves")
375    Parser.add_option("-e", "--dependency-expression", dest="Expression", default="",
376                      help="The string of dependency expression. If this option presents, the input file will be ignored.")
377    Parser.add_option("-m", "--optimize", dest="Optimize", default=False, action="store_true",
378                      help="Do some simple optimization on the expression.")
379    Parser.add_option("-v", "--verbose", dest="verbose", default=False, action="store_true",
380                      help="build with verbose information")
381    Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")
382    Parser.add_option("-q", "--quiet", dest="quiet", default=False, action="store_true",
383                      help="build with little information")
384
385    return Parser.parse_args()
386
387
388## Entrance method
389#
390# @retval 0     Tool was successful
391# @retval 1     Tool failed
392#
393def Main():
394    EdkLogger.Initialize()
395    Option, Input = GetOptions()
396
397    # Set log level
398    if Option.quiet:
399        EdkLogger.SetLevel(EdkLogger.QUIET)
400    elif Option.verbose:
401        EdkLogger.SetLevel(EdkLogger.VERBOSE)
402    elif Option.debug != None:
403        EdkLogger.SetLevel(Option.debug + 1)
404    else:
405        EdkLogger.SetLevel(EdkLogger.INFO)
406
407    try:
408        if Option.ModuleType == None or Option.ModuleType not in gType2Phase:
409            EdkLogger.error("GenDepex", OPTION_MISSING, "Module type is not specified or supported")
410
411        DxsFile = ''
412        if len(Input) > 0 and Option.Expression == "":
413            DxsFile = Input[0]
414            DxsString = open(DxsFile, 'r').read().replace("\n", " ").replace("\r", " ")
415            DxsString = gStartClosePattern.sub("\\1", DxsString)
416        elif Option.Expression != "":
417            if Option.Expression[0] == '"':
418                DxsString = Option.Expression[1:-1]
419            else:
420                DxsString = Option.Expression
421        else:
422            EdkLogger.error("GenDepex", OPTION_MISSING, "No expression string or file given")
423
424        Dpx = DependencyExpression(DxsString, Option.ModuleType, Option.Optimize)
425        if Option.OutputFile != None:
426            FileChangeFlag = Dpx.Generate(Option.OutputFile)
427            if not FileChangeFlag and DxsFile:
428                #
429                # Touch the output file if its time stamp is older than the original
430                # DXS file to avoid re-invoke this tool for the dependency check in build rule.
431                #
432                if os.stat(DxsFile)[8] > os.stat(Option.OutputFile)[8]:
433                    os.utime(Option.OutputFile, None)
434        else:
435            Dpx.Generate()
436    except BaseException, X:
437        EdkLogger.quiet("")
438        if Option != None and Option.debug != None:
439            EdkLogger.quiet(traceback.format_exc())
440        else:
441            EdkLogger.quiet(str(X))
442        return 1
443
444    return 0
445
446if __name__ == '__main__':
447    sys.exit(Main())
448
449