Changed the uses of EdkLogger
[people/mcb30/basetools.git] / Source / Python / AutoGen / BuildEngine.py
1 #!/usr/bin/env python\r
2 ## @file\r
3 # The engine for building files\r
4 #\r
5 # Copyright (c) 2007, Intel Corporation\r
6 # All rights reserved. This program and the accompanying materials\r
7 # are licensed and made available under the terms and conditions of the BSD License\r
8 # which accompanies this distribution.  The full text of the license may be found at\r
9 # http://opensource.org/licenses/bsd-license.php\r
10 #\r
11 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13 #\r
14 \r
15 ##\r
16 # Import Modules\r
17 #\r
18 import os\r
19 #import re\r
20 import string\r
21 \r
22 from Common.BuildToolError import *\r
23 \r
24 import Common.EdkLogger as EdkLogger\r
25 \r
26 #gMacroPattern = re.compile("\$\([A-Z_]+\)(.*)")\r
27 \r
28 def FileType2Macro(FileType):\r
29     return "$(%s_LIST)" % FileType.replace("-", "_").upper()\r
30 \r
31 class FileBuildRule:\r
32     def __init__(self, Input, Output, Command):\r
33         if Input == {}:\r
34             EdkLogger.error("AutoGen", AUTOGEN_ERROR, "No input files for a build rule")\r
35         if Output == []:\r
36             EdkLogger.error("AutoGen", AUTOGEN_ERROR, "No output files for a build rule")\r
37 \r
38         self.SourceFileType = {}\r
39         self.SourceFileExtList = []\r
40         self.ExtraSourceFileList = []\r
41         self.IsMultipleInput = False\r
42         for FileType in Input:\r
43             if FileType not in self.SourceFileType:\r
44                 self.SourceFileType[FileType] = []\r
45             for File in Input[FileType]:\r
46                 Base, Ext = os.path.splitext(File)\r
47                 if Base.find("*") >= 0:\r
48                     # There's "*" in the file name\r
49                     self.IsMultipleInput = True\r
50                 elif Base.find("?") < 0:\r
51                     # There's no "*" and "?" in file name\r
52                     self.ExtraSourceFileList.append(File)\r
53                     continue\r
54                 self.SourceFileType[FileType].append(Ext)\r
55                 self.SourceFileExtList.append(Ext)\r
56 \r
57         if len(self.SourceFileType) > 1:\r
58             self.IsMultipleInput = True\r
59 \r
60         self.DestFileList = Output\r
61         self.DestFile = ""\r
62         self.DestFileExt = os.path.splitext(Output[0])[1]\r
63         self.DestPath = ""\r
64         self.DestFileName = ""\r
65         self.DestFileBase = ""\r
66         self.CommandList = Command\r
67 \r
68     def __str__(self):\r
69         SourceString = ""\r
70         for FileType in self.SourceFileType:\r
71             SourceString += " %s(%s)" % (FileType, " ".join(self.SourceFileType[FileType]))\r
72         DestString = ", ".join(self.DestFileList)\r
73         CommandString = "\n\t".join(self.CommandList)\r
74         return "%s : %s\n\t%s" % (DestString, SourceString, CommandString)\r
75 \r
76     def IsSupported(self, FileExt):\r
77         return FileExt in self.SourceFileExtList\r
78 \r
79     def Apply(self, SourceFile, RelativeToDir, PathSeparator):\r
80         # source file\r
81         if not self.IsMultipleInput:\r
82             SrcFileName = os.path.basename(SourceFile)\r
83             SrcFileBase, SrcFileExt = os.path.splitext(SrcFileName)\r
84             if RelativeToDir != None:\r
85                 SrcFileDir = os.path.dirname(SourceFile)\r
86                 if SrcFileDir == "":\r
87                     SrcFileDir = "."\r
88 \r
89                 SrcFile = PathSeparator.join(["$(WORKSPACE)", RelativeToDir, SourceFile])\r
90             else:\r
91                 SrcFileDir = "."\r
92                 SrcFile = SourceFile\r
93             SrcPath = os.path.dirname(SrcFile)\r
94         else:\r
95             SrcFileName = ""\r
96             SrcFileBase = ""\r
97             SrcFileExt = ""\r
98             SrcFileDir = ""\r
99             SrcPath = ""\r
100             # SourceFile must be a list\r
101             SrcFileList = []\r
102             #for File in SourceFile:\r
103             #    if RelativeToDir != None:\r
104             #        SrcFileList.append(PathSeparator.join(["$(WORKSPACE)", RelativeToDir, File]))\r
105             #    else:\r
106             #        SrcFileList.append(File)\r
107             for FileType in self.SourceFileType:\r
108                 Macro = FileType2Macro(FileType)\r
109                 SrcFileList.append(Macro)\r
110             SrcFile = " ".join(SrcFileList)\r
111 \r
112         # destination file\r
113         for Index in range(len(self.DestFileList)):\r
114             self.DestFileList[Index] = self.DestFileList[Index].replace("(+)", PathSeparator)\r
115         for Index in range(len(self.CommandList)):\r
116             self.CommandList[Index] = self.CommandList[Index].replace("(+)", PathSeparator)\r
117 \r
118         if self.DestFile == "":\r
119             self.DestFile = self.DestFileList[0]\r
120         if self.DestPath == "":\r
121             self.DestPath = os.path.dirname(self.DestFile)\r
122         if self.DestFileName == "":\r
123             self.DestFileName = os.path.basename(self.DestFile)\r
124         if self.DestFileBase == "":\r
125             self.DestFileBase = os.path.splitext(self.DestFileName)[0]\r
126 \r
127         BuildRulePlaceholderDict = {\r
128             # source file\r
129             "src"       :   SrcFile,\r
130             "s_path"    :   SrcPath,\r
131             "s_dir"     :   SrcFileDir,\r
132             "s_name"    :   SrcFileName,\r
133             "s_base"    :   SrcFileBase,\r
134             "s_ext"     :   SrcFileExt,\r
135             # destination file\r
136             "dst"       :   self.DestFile,\r
137             "d_path"    :   self.DestPath,\r
138             #"d_dir"     :   SrcFileDir,\r
139             "d_name"    :   self.DestFileName,\r
140             "d_base"    :   self.DestFileBase,\r
141             "d_ext"     :   self.DestFileExt,\r
142         }\r
143 \r
144         DstFileList = []\r
145         for FileString in self.DestFileList:\r
146             FileString = string.Template(FileString).safe_substitute(BuildRulePlaceholderDict)\r
147             FileString = string.Template(FileString).safe_substitute(BuildRulePlaceholderDict)\r
148             DstFileList.append(FileString)\r
149         CommandList = []\r
150         for CommandString in self.CommandList:\r
151             CommandString = string.Template(CommandString).safe_substitute(BuildRulePlaceholderDict)\r
152             CommandString = string.Template(CommandString).safe_substitute(BuildRulePlaceholderDict)\r
153             CommandList.append(CommandString)\r
154         #print "%s : %s\n\t%s" % (DstFileList[0], SrcFile, "\n\t".join(CommandList))\r
155         return SrcFile, self.ExtraSourceFileList, DstFileList[0], CommandList\r
156 \r
157 class BuildRule:\r
158     _SectionHeader = "SECTIONHEADER"\r
159     _Section = "SECTION"\r
160     _SubSectionHeader = "SUBSECTIONHEADER"\r
161     _SubSection = "SUBSECTION"\r
162     _InputFile = "INPUTFILE"\r
163     _OutputFile = "OUTPUTFILE"\r
164     _Command = "COMMAND"\r
165     _UnknownSection = "UNKNOWNSECTION"\r
166 \r
167     _SubSectionList = [_InputFile, _OutputFile, _Command]\r
168 \r
169     def __init__(self, File=None, Content=None, LineIndex=0, SupportedFamily=["MSFT", "INTEL", "GCC"]):\r
170         self.RuleFile = File\r
171         if File != None:\r
172             try:\r
173                 self.RuleContent = open(File, 'r').readlines()\r
174             except:\r
175                 EdkLogger.error("BuildRuleParser", FILE_OPEN_FAILURE, ExtraData=File)\r
176         elif Content != None:\r
177             self.RuleContent = Content\r
178         else:\r
179             EdkLogger.error("BuildRuleParser", PARSER_ERROR, "No rule file or string given")\r
180 \r
181         self.SupportedToolChainFamilyList = SupportedFamily\r
182         self.RuleDatabase = {}  # {version : {family : {file type : FileBuildRule object}}}\r
183         self.FileTypeDict = {}  # {ext : file-type}\r
184 \r
185         self._LineIndex = LineIndex\r
186         self._BuildVersion = "*"\r
187         self._RuleInfo = {}     # {toolchain family : {"InputFile": {}, "OutputFile" : [], "Command" : []}}\r
188         self._FileTypeList = []\r
189         self._FamilyList = []\r
190         self._State = ""\r
191         self._RuleObjectList = [] # FileBuildRule object list\r
192         self.Parse()\r
193 \r
194     def Parse(self):\r
195         self._State = self._Section\r
196         for Index in range(self._LineIndex, len(self.RuleContent)):\r
197             Line = self.RuleContent[Index].strip()\r
198             self.RuleContent[Index] = Line\r
199 \r
200             # skip empty or comment line\r
201             if Line == "" or Line[0] == "#":\r
202                 continue\r
203 \r
204             if Line[0] == '[' and Line[-1] == ']':\r
205                 self.EndOfSection()\r
206                 self._State = self._SectionHeader\r
207             elif Line[0] == '<' and Line[-1] == '>':\r
208                 if self._State != self._UnknownSection:\r
209                     self._State = self._SubSectionHeader\r
210 \r
211             self._StateHandler[self._State](self, Index)\r
212         self.EndOfSection()\r
213         for RuleObject in self._RuleObjectList:\r
214             for FileType in RuleObject.SourceFileType:\r
215                 for FileExt in RuleObject.SourceFileType[FileType]:\r
216                     self.FileTypeDict[FileExt] = FileType\r
217 \r
218     def ParseSection(self, LineIndex):\r
219         TokenList = self.RuleContent[LineIndex].split("=", 1)\r
220         if len(TokenList) != 2 or TokenList[0] != "BUILD_VERSION":\r
221             EdkLogger.error("BuildRuleParser", PARSER_ERROR, "Invalid definition",\r
222                             File=RuleFile, Line=LineIndex+1, ExtraData=self.RuleContent[LineIndex])\r
223 \r
224         try:\r
225             self._BuildVersion = int(TokenList[1].strip(), 0)\r
226         except:\r
227             EdkLogger.error("BuildRuleParser", PARSER_ERROR, "Version is not a valid number",\r
228                             File=self.RuleFile, Line=LineIndex+1, ExtraData=self.RuleContent[LineIndex])\r
229 \r
230     def ParseSubSection(self, LineIndex):\r
231         pass\r
232 \r
233     def SkipSection(self, LineIndex):\r
234         pass\r
235 \r
236     def EndOfSection(self):\r
237         if self._FileTypeList == [] or self._RuleInfo == {}:\r
238             return\r
239 \r
240         Database = self.RuleDatabase\r
241         if self._BuildVersion not in Database:\r
242             Database[self._BuildVersion] = {}\r
243         Database = self.RuleDatabase[self._BuildVersion]\r
244 \r
245         # expand *\r
246         FamilyList = self._RuleInfo.keys()\r
247         if "*" in FamilyList and len(FamilyList) > 1:\r
248             FamilyList.remove("*")\r
249 \r
250         NewRuleInfo = {}\r
251         for Family in self._RuleInfo:\r
252             Rule = self._RuleInfo[Family]\r
253             if Family == "*" and Family not in FamilyList:\r
254                 NewFamilyList = FamilyList\r
255             else:\r
256                 NewFamilyList = [Family]\r
257 \r
258             for NewFamily in NewFamilyList:\r
259                 if NewFamily not in NewRuleInfo:\r
260                     NewRuleInfo[NewFamily] = {}\r
261 \r
262                 if self._InputFile in Rule:\r
263                     NewRuleInfo[NewFamily][self._InputFile] = Rule[self._InputFile]\r
264                 if self._OutputFile in Rule:\r
265                     NewRuleInfo[NewFamily][self._OutputFile] = Rule[self._OutputFile]\r
266                 if self._Command in Rule:\r
267                     NewRuleInfo[NewFamily][self._Command] = Rule[self._Command]\r
268 \r
269         for NewFamily in FamilyList:\r
270             Rule = NewRuleInfo[NewFamily]\r
271             if NewFamily not in Database:\r
272                 Database[NewFamily] = {}\r
273 \r
274             if self._InputFile in Rule:\r
275                 Input = Rule[self._InputFile]\r
276             else:\r
277                 EdkLogger.error("BuildRuleParser", PARSER_ERROR, "No input files found for a rule")\r
278 \r
279             if self._OutputFile in Rule:\r
280                 Output = Rule[self._OutputFile]\r
281             else:\r
282                 EdkLogger.error("BuildRuleParser", PARSER_ERROR, "No output files found a rule")\r
283 \r
284             if self._Command in Rule:\r
285                 Command = Rule[self._Command]\r
286             else:\r
287                 Command = []\r
288 \r
289             if NewFamily == "*":\r
290                 for Family in self.SupportedToolChainFamilyList:\r
291                     if Family not in Database:\r
292                         Database[Family] = {}\r
293                     RuleObject = FileBuildRule(Input, Output, Command)\r
294                     self._RuleObjectList.append(RuleObject)\r
295                     for FileType in RuleObject.SourceFileType:\r
296                         Database[Family][FileType] = RuleObject\r
297             else:\r
298                 RuleObject = FileBuildRule(Input, Output, Command)\r
299                 self._RuleObjectList.append(RuleObject)\r
300                 for FileType in RuleObject.SourceFileType:\r
301                     Database[NewFamily][FileType] = RuleObject\r
302 \r
303         self._RuleInfo = {}\r
304 \r
305     def ParseSectionHeader(self, LineIndex):\r
306         BuildVersion = ""\r
307         FileTypeList = []\r
308         RuleNameList = self.RuleContent[LineIndex][1:-1].split(',')\r
309         for RuleName in RuleNameList:\r
310             TokenList = RuleName.split('.')\r
311             if len(TokenList) == 1:\r
312                 EdkLogger.error("BuildRuleParser", PARSER_ERROR, "Invalid rule section",\r
313                                 File=self.RuleFile, Line=LineIndex+1, ExtraData=self.RuleContent[LineIndex])\r
314 \r
315             Rule = TokenList[0].strip()\r
316             if Rule.upper() != "BUILD":\r
317                 self._State = self._UnknownSection\r
318                 return\r
319 \r
320             FileType = TokenList[1].strip()\r
321             if FileType == '':\r
322                 EdkLogger.error("BuildRuleParser", PARSER_ERROR, "No file type given",\r
323                                 File=self.RuleFile, Line=LineIndex+1, ExtraData=Line)\r
324             FileTypeList.append(FileType)\r
325 \r
326         self._FileTypeList = FileTypeList\r
327         self._BuildVersion = "*"\r
328         self._State = self._Section\r
329 \r
330     def ParseSubSectionHeader(self, LineIndex):\r
331         SectionType = ""\r
332         List = self.RuleContent[LineIndex][1:-1].split(',')\r
333         FamilyList = []\r
334         for Section in List:\r
335             TokenList = Section.split('.')\r
336             Type = TokenList[0].strip().upper()\r
337 \r
338             if SectionType == "":\r
339                 SectionType = Type\r
340             elif SectionType != Type:\r
341                 EdkLogger.error("BuildRuleParser", PARSER_ERROR, "Two different section types are not allowed",\r
342                                 File=self.RuleFile, Line=LineIndex+1, ExtraData=Line)\r
343 \r
344             if len(TokenList) > 1:\r
345                 Family = TokenList[1].strip().upper()\r
346             else:\r
347                 Family = "*"\r
348 \r
349             if Family not in FamilyList:\r
350                 FamilyList.append(Family)\r
351 \r
352         self._FamilyList = FamilyList\r
353         self._State = SectionType.upper()\r
354 \r
355     def ParseInputFile(self, LineIndex):\r
356         Line = self.RuleContent[LineIndex]\r
357         TokenList = Line.split("=")\r
358         FileType = ""\r
359         if len(TokenList) > 1:\r
360             FileType = TokenList[0].strip()\r
361             if FileType not in self._FileTypeList:\r
362                 EdkLogger.error("BuildRuleParser", PARSER_ERROR,\r
363                                 "File type must be one of %s: %s" % (self._FileTypeList, FileType),\r
364                                 File=self.RuleFile, ExtraData=Line, Line=LineIndex+1)\r
365             FileString = TokenList[1]\r
366         else:\r
367             if len(self._FileTypeList) > 1:\r
368                 EdkLogger.error("BuildRuleParser", PARSER_ERROR, "File type must be given",\r
369                                 File=self.RuleFile, Line=LineIndex, ExtraData=Line)\r
370             else:\r
371                 FileType = self._FileTypeList[0]\r
372             FileString = TokenList[0]\r
373 \r
374         FileList = FileString.split(",")\r
375         for File in FileList:\r
376             File = File.strip()\r
377             for Family in self._FamilyList:\r
378                 if Family not in self._RuleInfo:\r
379                     self._RuleInfo[Family] = {}\r
380                 if self._State not in self._RuleInfo[Family]:\r
381                     self._RuleInfo[Family][self._State] = {}\r
382                 if FileType not in self._RuleInfo[Family][self._State]:\r
383                     self._RuleInfo[Family][self._State][FileType] = []\r
384                 self._RuleInfo[Family][self._State][FileType].append(File)\r
385 \r
386     def ParseOutputFile(self, LineIndex):\r
387         FileList = self.RuleContent[LineIndex].split(",")\r
388         for File in FileList:\r
389             File = File.strip()\r
390             for Family in self._FamilyList:\r
391                 if Family not in self._RuleInfo:\r
392                     self._RuleInfo[Family] = {}\r
393                     self._RuleInfo[Family][self._State] = []\r
394                 if self._State not in self._RuleInfo[Family]:\r
395                     self._RuleInfo[Family][self._State] = []\r
396                 self._RuleInfo[Family][self._State].append(File)\r
397 \r
398     def ParseCommand(self, LineIndex):\r
399         Command = self.RuleContent[LineIndex]\r
400         for Family in self._FamilyList:\r
401             if Family not in self._RuleInfo:\r
402                 self._RuleInfo[Family] = {}\r
403                 self._RuleInfo[Family][self._State] = []\r
404             if self._State not in self._RuleInfo[Family]:\r
405                 self._RuleInfo[Family][self._State] = []\r
406             self._RuleInfo[Family][self._State].append(Command)\r
407 \r
408     # FileBuildRule object\r
409     def Get(self, FileExt, ToolChainFamily, BuildVersion="*"):\r
410         if FileExt not in self.FileTypeDict:\r
411             return None, None\r
412         FileType = self.FileTypeDict[FileExt]\r
413         Database = {}\r
414         BuildRuleObject = None\r
415         if BuildVersion in self.RuleDatabase:\r
416             Database = self.RuleDatabase[BuildVersion]\r
417         elif BuildVersion != "*":\r
418             if "*" not in self.RuleDatabase:\r
419                 return FileType, None\r
420             Database = self.RuleDatabase["*"]\r
421         else:\r
422             # BuildVersion == "*" and "*" not in self.RuleDatabase\r
423             # try to match ToolChainFamily\r
424             for Ver in self.RuleDatabase:\r
425                 Database = self.RuleDatabase[Ver]\r
426                 if ToolChainFamily not in Database:\r
427                     continue\r
428                 if FileType not in Database[ToolChainFamily]:\r
429                     continue\r
430                 break\r
431             else:\r
432                 return FileType, None\r
433 \r
434         if ToolChainFamily not in Database:\r
435             return FileType, None\r
436         if FileType not in Database[ToolChainFamily]:\r
437             return FileType, None\r
438         if not Database[ToolChainFamily][FileType].IsSupported(FileExt):\r
439             return FileType, None\r
440 \r
441         return FileType, Database[ToolChainFamily][FileType]\r
442 \r
443     _StateHandler = {\r
444         _SectionHeader     : ParseSectionHeader,\r
445         _Section           : ParseSection,\r
446         _SubSectionHeader  : ParseSubSectionHeader,\r
447         _SubSection        : ParseSubSection,\r
448         _InputFile         : ParseInputFile,\r
449         _OutputFile        : ParseOutputFile,\r
450         _Command           : ParseCommand,\r
451         _UnknownSection    : SkipSection,\r
452     }\r
453 \r
454 \r
455 \r
456 # This acts like the main() function for the script, unless it is 'import'ed into another\r
457 # script.\r
458 if __name__ == '__main__':\r
459     import sys\r
460     if len(sys.argv) <= 1:\r
461         print "No input file"\r
462         sys.exit(1)\r
463 \r
464     br = BuildRule(File=sys.argv[1])\r
465     for BuildVersion in br.RuleDatabase:\r
466         print "\nVVVVVVVVVVVVV %s VVVVVVVVVVVVVVV" % BuildVersion\r
467         for Family in br.RuleDatabase[BuildVersion]:\r
468             print "\n------------------ %s ----------------" % Family\r
469             for FileType in br.RuleDatabase[BuildVersion][Family]:\r
470                 print "\n%s" % br.RuleDatabase[BuildVersion][Family][FileType]\r
471 \r
472     print "\n------ .c"\r
473     ct, cr = br.Get(".c", "MSFT")\r
474     print cr\r
475     s, d, c = cr.Apply("abc\\y.c", "MdeModulePkg\\Universal", "\\")\r
476     print "\n------ .obj"\r
477     objt, objr = br.Get(".obj", "MSFT")\r
478     print objr\r
479     s, d, c = objr.Apply(["abc\\y.obj", "xyz\\z.obj"], "MdeModulePkg\\Universal", "\\")\r
480     print "\n------ .VFR"\r
481     print br.Get(".VFR", "GCC")\r
482     sys.exit(0)\r