]> git.proxmox.com Git - mirror_edk2.git/blobdiff - BaseTools/Source/Python/Workspace/MetaFileParser.py
Sync EDKII BaseTools to BaseTools project r2042.
[mirror_edk2.git] / BaseTools / Source / Python / Workspace / MetaFileParser.py
index 294237daee08f17d314f09036b182bbeb7ec0a3c..fb66e41fb5ce169e50c362abc6ecb9bbc78a4c52 100644 (file)
@@ -1,8 +1,8 @@
 ## @file
 # This file is used to parse meta files
 #
-# Copyright (c) 2008, Intel Corporation
-# All rights reserved. This program and the accompanying materials
+# Copyright (c) 2008 - 2010, 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
@@ -16,6 +16,7 @@
 #
 import os
 import time
+import copy
 
 import Common.EdkLogger as EdkLogger
 from CommonDataClass.DataClass import *
@@ -39,6 +40,28 @@ class MetaFileParser(object):
     # data type (file content) for specific file type
     DataType = {}
 
+    # Parser objects used to implement singleton
+    MetaFiles = {}
+
+    ## Factory method
+    #
+    # One file, one parser object. This factory method makes sure that there's
+    # only one object constructed for one meta file.
+    #
+    #   @param  Class           class object of real AutoGen class
+    #                           (InfParser, DecParser or DscParser)
+    #   @param  FilePath        The path of meta file
+    #   @param  *args           The specific class related parameters
+    #   @param  **kwargs        The specific class related dict parameters
+    #
+    def __new__(Class, FilePath, *args, **kwargs):
+        if FilePath in Class.MetaFiles:
+            return Class.MetaFiles[FilePath]
+        else:
+            ParserObject = super(MetaFileParser, Class).__new__(Class)
+            Class.MetaFiles[FilePath] = ParserObject
+            return ParserObject
+
     ## Constructor of MetaFileParser
     #
     #  Initialize object of MetaFileParser
@@ -51,11 +74,15 @@ class MetaFileParser(object):
     #   @param      From            ID from which the data comes (for !INCLUDE directive)
     #
     def __init__(self, FilePath, FileType, Table, Macros=None, Owner=-1, From=-1):
+        # prevent re-initialization
+        if hasattr(self, "_Table"):
+            return
         self._Table = Table
         self._FileType = FileType
         self.MetaFile = FilePath
         self._FileDir = os.path.dirname(self.MetaFile)
-        self._Macros = {}
+        self._Macros = copy.copy(Macros)
+        self._Macros["WORKSPACE"] = os.environ["WORKSPACE"]
 
         # for recursive parsing
         self._Owner = Owner
@@ -87,7 +114,9 @@ class MetaFileParser(object):
     ## Set parsing complete flag in both class and table
     def _Done(self):
         self._Finished = True
-        self._Table.SetEndFlag()
+        ## Do not set end flag when processing included files
+        if self._From == -1:
+            self._Table.SetEndFlag()
 
     ## Return the table containg parsed data
     #
@@ -208,11 +237,14 @@ class MetaFileParser(object):
         if TokenList[0] == '':
             EdkLogger.error('Parser', FORMAT_INVALID, "No macro name given",
                             ExtraData=self._CurrentLine, File=self.MetaFile, Line=self._LineIndex+1)
-        if len(TokenList) == 1:
-            self._Macros[TokenList[0]] = ''
-        else:
-            # keep the macro definition for later use
-            self._Macros[TokenList[0]] = ReplaceMacro(TokenList[1], self._Macros, False)
+
+        # Macros defined in the command line override ones defined in the meta-data file
+        if not TokenList[0] in self._Macros:
+            if len(TokenList) == 1:
+                self._Macros[TokenList[0]] = ''
+            else:
+                # keep the macro definition for later use
+                self._Macros[TokenList[0]] = ReplaceMacro(TokenList[1], self._Macros, False)
 
         return TokenList[0], self._Macros[TokenList[0]]
 
@@ -367,6 +399,9 @@ class InfParser(MetaFileParser):
                             -1,
                             0
                             )
+        if IsFindBlockComment:
+            EdkLogger.error("Parser", FORMAT_INVALID, "Open block comments (starting with /*) are expected to end with */", 
+                            File=self.MetaFile)
         self._Done()
 
     ## Data parser for the format in which there's path
@@ -456,7 +491,12 @@ class InfParser(MetaFileParser):
     ## [FixedPcd], [FeaturePcd], [PatchPcd], [Pcd] and [PcdEx] sections parser
     def _PcdParser(self):
         TokenList = GetSplitValueList(self._CurrentLine, TAB_VALUE_SPLIT, 1)
-        self._ValueList[0:1] = GetSplitValueList(TokenList[0], TAB_SPLIT)
+        ValueList = GetSplitValueList(TokenList[0], TAB_SPLIT)
+        if len(ValueList) != 2:
+            EdkLogger.error('Parser', FORMAT_INVALID, "Illegal token space GUID and PCD name format",
+                            ExtraData=self._CurrentLine + " (<TokenSpaceGuidCName>.<PcdCName>)",
+                            File=self.MetaFile, Line=self._LineIndex+1)
+        self._ValueList[0:1] = ValueList
         if len(TokenList) > 1:
             self._ValueList[2] = TokenList[1]
         if self._ValueList[0] == '' or self._ValueList[1] == '':
@@ -530,6 +570,7 @@ class DscParser(MetaFileParser):
 
     # sections which allow "!include" directive
     _IncludeAllowedSection = [
+        TAB_COMMON_DEFINES.upper(),
         TAB_LIBRARIES.upper(),
         TAB_LIBRARY_CLASSES.upper(),
         TAB_SKUIDS.upper(),
@@ -590,7 +631,9 @@ class DscParser(MetaFileParser):
                 continue
             self._CurrentLine = Line
             self._LineIndex = Index
-
+            if self._InSubsection and self._Owner == -1:
+                self._Owner = self._LastItem
+            
             # section header
             if Line[0] == TAB_SECTION_START and Line[-1] == TAB_SECTION_END:
                 self._SectionHeaderParser()
@@ -612,7 +655,25 @@ class DscParser(MetaFileParser):
                 continue
             # file private macros
             elif Line.upper().startswith('DEFINE '):
-                self._MacroParser()
+                (Name, Value) = self._MacroParser()
+                # Make the defined macro in DSC [Defines] section also
+                # available for FDF file.
+                if self._SectionName == TAB_COMMON_DEFINES.upper():
+                    self._LastItem = self._Store(
+                    MODEL_META_DATA_GLOBAL_DEFINE,
+                    Name,
+                    Value,
+                    '',
+                    'COMMON',
+                    'COMMON',
+                    self._Owner,
+                    self._From,
+                    self._LineIndex+1,
+                    -1,
+                    self._LineIndex+1,
+                    -1,
+                    self._Enabled
+                    )
                 continue
             elif Line.upper().startswith('EDK_GLOBAL '):
                 (Name, Value) = self._MacroParser()
@@ -638,8 +699,6 @@ class DscParser(MetaFileParser):
             if self._InSubsection:
                 SectionType = self._SubsectionType
                 SectionName = self._SubsectionName
-                if self._Owner == -1:
-                    self._Owner = self._LastItem
             else:
                 SectionType = self._SectionType
                 SectionName = self._SectionName
@@ -681,6 +740,22 @@ class DscParser(MetaFileParser):
         if TokenList[0] in ['FLASH_DEFINITION', 'OUTPUT_DIRECTORY']:
             TokenList[1] = NormPath(TokenList[1], self._Macros)
         self._ValueList[0:len(TokenList)] = TokenList
+        # Treat elements in the [defines] section as global macros for FDF file.
+        self._LastItem = self._Store(
+                            MODEL_META_DATA_GLOBAL_DEFINE,
+                            TokenList[0],
+                            TokenList[1],
+                            '',
+                            'COMMON',
+                            'COMMON',
+                            self._Owner,
+                            self._From,
+                            self._LineIndex+1,
+                            -1,
+                            self._LineIndex+1,
+                            -1,
+                            self._Enabled
+                            )
 
     ## <subsection_header> parser
     def _SubsectionHeaderParser(self):
@@ -728,7 +803,7 @@ class DscParser(MetaFileParser):
                 EdkLogger.error("Parser", FORMAT_INVALID, File=self.MetaFile, Line=self._LineIndex+1,
                                 ExtraData="'!include' is not allowed under section [%s]" % self._SectionName)
             # the included file must be relative to the parsing file
-            IncludedFile = os.path.join(self._FileDir, self._ValueList[1])
+            IncludedFile = os.path.join(self._FileDir, NormPath(self._ValueList[1], self._Macros))
             Parser = DscParser(IncludedFile, self._FileType, self._Table, self._Macros, From=self._LastItem)
             # set the parser status with current status
             Parser._SectionName = self._SectionName
@@ -740,11 +815,14 @@ class DscParser(MetaFileParser):
             except:
                 EdkLogger.error("Parser", PARSER_ERROR, File=self.MetaFile, Line=self._LineIndex+1,
                                 ExtraData="Failed to parse content in file %s" % IncludedFile)
+            # insert an imaginary token in the DSC table to indicate its external dependency on another file
+            self._Store(MODEL_EXTERNAL_DEPENDENCY, IncludedFile, str(os.stat(IncludedFile)[8]), "")
             # update current status with sub-parser's status
             self._SectionName = Parser._SectionName
             self._SectionType = Parser._SectionType
             self._Scope       = Parser._Scope
             self._Enabled     = Parser._Enabled
+            self._Macros.update(Parser._Macros)
         else:
             if DirectiveName in ["!IF", "!IFDEF", "!IFNDEF"]:
                 # evaluate the expression
@@ -768,13 +846,24 @@ class DscParser(MetaFileParser):
             else:
                 self._Enabled = len(self._Eval)
 
-    ## Evaludate the value of expression in "if/ifdef/ifndef" directives
+    ## Evaluate the Token for its value; for now only macros are supported.
+    def _EvaluateToken(self, TokenName, Expression):
+        if TokenName.startswith("$(") and TokenName.endswith(")"):
+            Name = TokenName[2:-1]
+            return self._Macros.get(Name)
+        else:
+            EdkLogger.error('Parser', FORMAT_INVALID, "Unknown operand '%(Token)s', "
+                            "please use '$(%(Token)s)' if '%(Token)s' is a macro" % {"Token" : TokenName},
+                            File=self.MetaFile, Line=self._LineIndex+1, ExtraData=Expression)
+    
+    ## Evaluate the value of expression in "if/ifdef/ifndef" directives
     def _Evaluate(self, Expression):
         TokenList = Expression.split()
         TokenNumber = len(TokenList)
         # one operand, guess it's just a macro name
         if TokenNumber == 1:
-            return TokenList[0] in self._Macros
+            TokenValue =  self._EvaluateToken(TokenList[0], Expression)
+            return TokenValue != None
         # two operands, suppose it's "!xxx" format
         elif TokenNumber == 2:
             Op = TokenList[0]
@@ -788,8 +877,8 @@ class DscParser(MetaFileParser):
             return self._OP_[Op](Value)
         # three operands
         elif TokenNumber == 3:
-            Name = TokenList[0]
-            if Name not in self._Macros:
+            TokenValue = self._EvaluateToken(TokenList[0], Expression)
+            if TokenValue == None:
                 return False
             Value = TokenList[2]
             if Value[0] in ["'", '"'] and Value[-1] in ["'", '"']:
@@ -798,7 +887,7 @@ class DscParser(MetaFileParser):
             if Op not in self._OP_:
                 EdkLogger.error('Parser', FORMAT_INVALID, "Unsupported operator [%s]" % Op, File=self.MetaFile,
                                 Line=self._LineIndex+1, ExtraData=Expression)
-            return self._OP_[Op](self._Macros[Name], Value)
+            return self._OP_[Op](TokenValue, Value)
         else:
             EdkLogger.error('Parser', FORMAT_INVALID, File=self.MetaFile, Line=self._LineIndex+1,
                             ExtraData=Expression)
@@ -818,7 +907,7 @@ class DscParser(MetaFileParser):
     #   [PcdsDynamicHii]
     #
     def _PcdParser(self):
-        TokenList = GetSplitValueList(self._CurrentLine, TAB_VALUE_SPLIT, 1)
+        TokenList = GetSplitValueList(ReplaceMacro(self._CurrentLine, self._Macros), TAB_VALUE_SPLIT, 1)
         self._ValueList[0:1] = GetSplitValueList(TokenList[0], TAB_SPLIT)
         if len(TokenList) == 2:
             self._ValueList[2] = TokenList[1]
@@ -918,6 +1007,7 @@ class DecParser(MetaFileParser):
     #
     def __init__(self, FilePath, FileType, Table, Macro=None):
         MetaFileParser.__init__(self, FilePath, FileType, Table, Macro, -1)
+        self._Comments = []
 
     ## Parser starter
     def Start(self):
@@ -928,27 +1018,34 @@ class DecParser(MetaFileParser):
             EdkLogger.error("Parser", FILE_READ_FAILURE, ExtraData=self.MetaFile)
 
         for Index in range(0, len(self._Content)):
-            Line = CleanString(self._Content[Index])
+            Line, Comment = CleanString2(self._Content[Index])
+            self._CurrentLine = Line
+            self._LineIndex = Index
+
+            # save comment for later use
+            if Comment:
+                self._Comments.append((Comment, self._LineIndex+1))
             # skip empty line
             if Line == '':
                 continue
-            self._CurrentLine = Line
-            self._LineIndex = Index
 
             # section header
             if Line[0] == TAB_SECTION_START and Line[-1] == TAB_SECTION_END:
                 self._SectionHeaderParser()
+                self._Comments = []
                 continue
             elif Line.startswith('DEFINE '):
                 self._MacroParser()
                 continue
             elif len(self._SectionType) == 0:
+                self._Comments = []
                 continue
 
             # section content
             self._ValueList = ['','','']
             self._SectionParser[self._SectionType[0]](self)
             if self._ValueList == None:
+                self._Comments = []
                 continue
 
             #
@@ -970,6 +1067,22 @@ class DecParser(MetaFileParser):
                     -1,
                     0
                     )
+                for Comment, LineNo in self._Comments:
+                    self._Store(
+                        MODEL_META_DATA_COMMENT,
+                        Comment,
+                        self._ValueList[0],
+                        self._ValueList[1],
+                        Arch,
+                        ModuleType,
+                        self._LastItem,
+                        LineNo,
+                        -1,
+                        LineNo,
+                        -1,
+                        0
+                        )
+            self._Comments = []
         self._Done()
 
     ## Section header parser
@@ -1103,7 +1216,8 @@ class DecParser(MetaFileParser):
         if not IsValid:
             EdkLogger.error('Parser', FORMAT_INVALID, Cause, ExtraData=self._CurrentLine,
                             File=self.MetaFile, Line=self._LineIndex+1)
-        self._ValueList[2] = TokenList[1]
+
+        self._ValueList[2] = ValueList[0].strip() + '|' + ValueList[1].strip() + '|' + ValueList[2].strip()
 
     _SectionParser = {
         MODEL_META_DATA_HEADER          :   MetaFileParser._DefineParser,