1## @file
2# This file is used to define each component of tools_def.txt file
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
14##
15# Import Modules
16#
17import Common.LongFilePathOs as os
18import re
19import EdkLogger
20
21from Dictionary import *
22from BuildToolError import *
23from TargetTxtClassObject import *
24from Common.LongFilePathSupport import OpenLongFilePath as open
25
26##
27# Static variables used for pattern
28#
29gMacroRefPattern = re.compile('(DEF\([^\(\)]+\))')
30gEnvRefPattern = re.compile('(ENV\([^\(\)]+\))')
31gMacroDefPattern = re.compile("DEFINE\s+([^\s]+)")
32gDefaultToolsDefFile = "tools_def.txt"
33
34## ToolDefClassObject
35#
36# This class defined content used in file tools_def.txt
37#
38# @param object:               Inherited from object class
39# @param Filename:             Input value for full path of tools_def.txt
40#
41# @var ToolsDefTxtDictionary:  To store keys and values defined in target.txt
42# @var MacroDictionary:        To store keys and values defined in DEFINE statement
43#
44class ToolDefClassObject(object):
45    def __init__(self, FileName=None):
46        self.ToolsDefTxtDictionary = {}
47        self.MacroDictionary = {}
48        for Env in os.environ:
49            self.MacroDictionary["ENV(%s)" % Env] = os.environ[Env]
50
51        if FileName != None:
52            self.LoadToolDefFile(FileName)
53
54    ## LoadToolDefFile
55    #
56    # Load target.txt file and parse it, return a set structure to store keys and values
57    #
58    # @param Filename:  Input value for full path of tools_def.txt
59    #
60    def LoadToolDefFile(self, FileName):
61        FileContent = []
62        if os.path.isfile(FileName):
63            try:
64                F = open(FileName, 'r')
65                FileContent = F.readlines()
66            except:
67                EdkLogger.error("tools_def.txt parser", FILE_OPEN_FAILURE, ExtraData=FileName)
68        else:
69            EdkLogger.error("tools_def.txt parser", FILE_NOT_FOUND, ExtraData=FileName)
70
71        self.ToolsDefTxtDatabase = {
72            TAB_TOD_DEFINES_TARGET          :   [],
73            TAB_TOD_DEFINES_TOOL_CHAIN_TAG  :   [],
74            TAB_TOD_DEFINES_TARGET_ARCH     :   [],
75            TAB_TOD_DEFINES_COMMAND_TYPE    :   []
76        }
77
78        for Index in range(len(FileContent)):
79            Line = FileContent[Index].strip()
80            if Line == "" or Line[0] == '#':
81                continue
82            NameValuePair = Line.split("=", 1)
83            if len(NameValuePair) != 2:
84                EdkLogger.warn("tools_def.txt parser", "Line %d: not correct assignment statement, skipped" % (Index + 1))
85                continue
86
87            Name = NameValuePair[0].strip()
88            Value = NameValuePair[1].strip()
89
90            if Name == "IDENTIFIER":
91                EdkLogger.debug(EdkLogger.DEBUG_8, "Line %d: Found identifier statement, skipped: %s" % ((Index + 1), Value))
92                continue
93
94            MacroDefinition = gMacroDefPattern.findall(Name)
95            if MacroDefinition != []:
96                Done, Value = self.ExpandMacros(Value)
97                if not Done:
98                    EdkLogger.error("tools_def.txt parser", ATTRIBUTE_NOT_AVAILABLE,
99                                    "Macro or Environment has not been defined",
100                                ExtraData=Value[4:-1], File=FileName, Line=Index+1)
101
102                MacroName = MacroDefinition[0].strip()
103                self.MacroDictionary["DEF(%s)" % MacroName] = Value
104                EdkLogger.debug(EdkLogger.DEBUG_8, "Line %d: Found macro: %s = %s" % ((Index + 1), MacroName, Value))
105                continue
106
107            Done, Value = self.ExpandMacros(Value)
108            if not Done:
109                EdkLogger.error("tools_def.txt parser", ATTRIBUTE_NOT_AVAILABLE,
110                                "Macro or Environment has not been defined",
111                                ExtraData=Value[4:-1], File=FileName, Line=Index+1)
112
113            List = Name.split('_')
114            if len(List) != 5:
115                EdkLogger.verbose("Line %d: Not a valid name of definition: %s" % ((Index + 1), Name))
116                continue
117            elif List[4] == '*':
118                EdkLogger.verbose("Line %d: '*' is not allowed in last field: %s" % ((Index + 1), Name))
119                continue
120            else:
121                self.ToolsDefTxtDictionary[Name] = Value
122                if List[0] != '*':
123                    self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TARGET] += [List[0]]
124                if List[1] != '*':
125                    self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG] += [List[1]]
126                if List[2] != '*':
127                    self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TARGET_ARCH] += [List[2]]
128                if List[3] != '*':
129                    self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_COMMAND_TYPE] += [List[3]]
130                if List[4] == TAB_TOD_DEFINES_FAMILY and List[2] == '*' and List[3] == '*':
131                    if TAB_TOD_DEFINES_FAMILY not in self.ToolsDefTxtDatabase:
132                        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_FAMILY] = {}
133                        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_FAMILY][List[1]] = Value
134                        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_BUILDRULEFAMILY] = {}
135                        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_BUILDRULEFAMILY][List[1]] = Value
136                    elif List[1] not in self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_FAMILY]:
137                        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_FAMILY][List[1]] = Value
138                        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_BUILDRULEFAMILY][List[1]] = Value
139                    elif self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_FAMILY][List[1]] != Value:
140                        EdkLogger.verbose("Line %d: No override allowed for the family of a tool chain: %s" % ((Index + 1), Name))
141                if List[4] == TAB_TOD_DEFINES_BUILDRULEFAMILY and List[2] == '*' and List[3] == '*':
142                    if TAB_TOD_DEFINES_BUILDRULEFAMILY not in self.ToolsDefTxtDatabase \
143                       or List[1] not in self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_FAMILY]:
144                        EdkLogger.verbose("Line %d: The family is not specified, but BuildRuleFamily is specified for the tool chain: %s" % ((Index + 1), Name))
145                    self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_BUILDRULEFAMILY][List[1]] = Value
146
147        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TARGET] = list(set(self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TARGET]))
148        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG] = list(set(self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]))
149        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TARGET_ARCH] = list(set(self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TARGET_ARCH]))
150        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_COMMAND_TYPE] = list(set(self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_COMMAND_TYPE]))
151
152        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TARGET].sort()
153        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG].sort()
154        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TARGET_ARCH].sort()
155        self.ToolsDefTxtDatabase[TAB_TOD_DEFINES_COMMAND_TYPE].sort()
156
157        KeyList = [TAB_TOD_DEFINES_TARGET, TAB_TOD_DEFINES_TOOL_CHAIN_TAG, TAB_TOD_DEFINES_TARGET_ARCH, TAB_TOD_DEFINES_COMMAND_TYPE]
158        for Index in range(3, -1, -1):
159            for Key in dict(self.ToolsDefTxtDictionary):
160                List = Key.split('_')
161                if List[Index] == '*':
162                    for String in self.ToolsDefTxtDatabase[KeyList[Index]]:
163                        List[Index] = String
164                        NewKey = '%s_%s_%s_%s_%s' % tuple(List)
165                        if NewKey not in self.ToolsDefTxtDictionary:
166                            self.ToolsDefTxtDictionary[NewKey] = self.ToolsDefTxtDictionary[Key]
167                        continue
168                    del self.ToolsDefTxtDictionary[Key]
169                elif List[Index] not in self.ToolsDefTxtDatabase[KeyList[Index]]:
170                    del self.ToolsDefTxtDictionary[Key]
171
172    ## ExpandMacros
173    #
174    # Replace defined macros with real value
175    #
176    # @param Value:   The string with unreplaced macros
177    #
178    # @retval Value:  The string which has been replaced with real value
179    #
180    def ExpandMacros(self, Value):
181        EnvReference = gEnvRefPattern.findall(Value)
182        for Ref in EnvReference:
183            if Ref not in self.MacroDictionary:
184                Value = Value.replace(Ref, "")
185            else:
186                Value = Value.replace(Ref, self.MacroDictionary[Ref])
187
188
189        MacroReference = gMacroRefPattern.findall(Value)
190        for Ref in MacroReference:
191            if Ref not in self.MacroDictionary:
192                return False, Ref
193            Value = Value.replace(Ref, self.MacroDictionary[Ref])
194
195        return True, Value
196
197## ToolDefDict
198#
199# Load tools_def.txt in input Conf dir
200#
201# @param ConfDir:  Conf dir
202#
203# @retval ToolDef An instance of ToolDefClassObject() with loaded tools_def.txt
204#
205def ToolDefDict(ConfDir):
206    Target = TargetTxtDict(ConfDir)
207    ToolDef = ToolDefClassObject()
208    if DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF in Target.TargetTxtDictionary:
209        ToolsDefFile = Target.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]
210        if ToolsDefFile:
211            ToolDef.LoadToolDefFile(os.path.normpath(ToolsDefFile))
212        else:
213            ToolDef.LoadToolDefFile(os.path.normpath(os.path.join(ConfDir, gDefaultToolsDefFile)))
214    else:
215        ToolDef.LoadToolDefFile(os.path.normpath(os.path.join(ConfDir, gDefaultToolsDefFile)))
216    return ToolDef
217
218##
219#
220# This acts like the main() function for the script, unless it is 'import'ed into another
221# script.
222#
223if __name__ == '__main__':
224    ToolDef = ToolDefDict(os.getenv("WORKSPACE"))
225    pass
226