c1e7d227de9b7741103eff44dc02c740018a20e1
[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             raise AutoGenError(msg="No input files")\r
35         if Output == []:\r
36             raise AutoGenError(msg="No output files")\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         if File != None:\r
171             try:\r
172                 self.RuleContent = open(File, 'r').readlines()\r
173             except:\r
174                 raise ParserError(FILE_OPEN_FAILURE, name=File)\r
175         elif Content != None:\r
176             self.RuleContent = Content\r
177         else:\r
178             raise ParserError(msg="No rule file or string given")\r
179 \r
180         self.SupportedToolChainFamilyList = SupportedFamily\r
181         self.RuleDatabase = {}  # {version : {family : {file type : FileBuildRule object}}}\r
182         self.FileTypeDict = {}  # {ext : file-type}\r
183 \r
184         self._LineIndex = LineIndex\r
185         self._BuildVersion = "*"\r
186         self._RuleInfo = {}     # {toolchain family : {"InputFile": {}, "OutputFile" : [], "Command" : []}}\r
187         self._FileTypeList = []\r
188         self._FamilyList = []\r
189         self._State = ""\r
190         self._RuleObjectList = [] # FileBuildRule object list\r
191         self.Parse()\r
192 \r
193     def Parse(self):\r
194         self._State = self._Section\r
195         for Index in range(self._LineIndex, len(self.RuleContent)):\r
196             Line = self.RuleContent[Index].strip()\r
197             self.RuleContent[Index] = Line\r
198 \r
199             # skip empty or comment line\r
200             if Line == "" or Line[0] == "#":\r
201                 continue\r
202 \r
203             if Line[0] == '[' and Line[-1] == ']':\r
204                 self.EndOfSection()\r
205                 self._State = self._SectionHeader\r
206             elif Line[0] == '<' and Line[-1] == '>':\r
207                 if self._State != self._UnknownSection:\r
208                     self._State = self._SubSectionHeader\r
209 \r
210             self._StateHandler[self._State](self, Index)\r
211         self.EndOfSection()\r
212         for RuleObject in self._RuleObjectList:\r
213             for FileType in RuleObject.SourceFileType:\r
214                 for FileExt in RuleObject.SourceFileType[FileType]:\r
215                     self.FileTypeDict[FileExt] = FileType\r
216 \r
217     def ParseSection(self, LineIndex):\r
218         TokenList = self.RuleContent[LineIndex].split("=", 1)\r
219         if len(TokenList) != 2 or TokenList[0] != "BUILD_VERSION":\r
220             raise ParserError(msg="Invalid define: %s, line %d" % (self.RuleContent[LineIndex], LineIndex+1))\r
221 \r
222         try:\r
223             self._BuildVersion = int(TokenList[1].strip(), 0)\r
224         except:\r
225             raise ParserError(msg="Version is not a valid number: %s, line %d" % (self.RuleContent[LineIndex], LineIndex+1))\r
226 \r
227     def ParseSubSection(self, LineIndex):\r
228         pass\r
229 \r
230     def SkipSection(self, LineIndex):\r
231         pass\r
232 \r
233     def EndOfSection(self):\r
234         if self._FileTypeList == [] or self._RuleInfo == {}:\r
235             return\r
236 \r
237         Database = self.RuleDatabase\r
238         if self._BuildVersion not in Database:\r
239             Database[self._BuildVersion] = {}\r
240         Database = self.RuleDatabase[self._BuildVersion]\r
241 \r
242         # expand *\r
243         FamilyList = self._RuleInfo.keys()\r
244         if "*" in FamilyList and len(FamilyList) > 1:\r
245             FamilyList.remove("*")\r
246 \r
247         NewRuleInfo = {}\r
248         for Family in self._RuleInfo:\r
249             Rule = self._RuleInfo[Family]\r
250             if Family == "*" and Family not in FamilyList:\r
251                 NewFamilyList = FamilyList\r
252             else:\r
253                 NewFamilyList = [Family]\r
254 \r
255             for NewFamily in NewFamilyList:\r
256                 if NewFamily not in NewRuleInfo:\r
257                     NewRuleInfo[NewFamily] = {}\r
258 \r
259                 if self._InputFile in Rule:\r
260                     NewRuleInfo[NewFamily][self._InputFile] = Rule[self._InputFile]\r
261                 if self._OutputFile in Rule:\r
262                     NewRuleInfo[NewFamily][self._OutputFile] = Rule[self._OutputFile]\r
263                 if self._Command in Rule:\r
264                     NewRuleInfo[NewFamily][self._Command] = Rule[self._Command]\r
265 \r
266         for NewFamily in FamilyList:\r
267             Rule = NewRuleInfo[NewFamily]\r
268             if NewFamily not in Database:\r
269                 Database[NewFamily] = {}\r
270 \r
271             if self._InputFile in Rule:\r
272                 Input = Rule[self._InputFile]\r
273             else:\r
274                 raise ParserError(msg="No input files found")\r
275 \r
276             if self._OutputFile in Rule:\r
277                 Output = Rule[self._OutputFile]\r
278             else:\r
279                 raise ParserError(msg="No output files found")\r
280 \r
281             if self._Command in Rule:\r
282                 Command = Rule[self._Command]\r
283             else:\r
284                 Command = []\r
285 \r
286             if NewFamily == "*":\r
287                 for Family in self.SupportedToolChainFamilyList:\r
288                     if Family not in Database:\r
289                         Database[Family] = {}\r
290                     RuleObject = FileBuildRule(Input, Output, Command)\r
291                     self._RuleObjectList.append(RuleObject)\r
292                     for FileType in RuleObject.SourceFileType:\r
293                         Database[Family][FileType] = RuleObject\r
294             else:\r
295                 RuleObject = FileBuildRule(Input, Output, Command)\r
296                 self._RuleObjectList.append(RuleObject)\r
297                 for FileType in RuleObject.SourceFileType:\r
298                     Database[NewFamily][FileType] = RuleObject\r
299 \r
300         self._RuleInfo = {}\r
301 \r
302     def ParseSectionHeader(self, LineIndex):\r
303         BuildVersion = ""\r
304         FileTypeList = []\r
305         RuleNameList = self.RuleContent[LineIndex][1:-1].split(',')\r
306         for RuleName in RuleNameList:\r
307             TokenList = RuleName.split('.')\r
308             if len(TokenList) == 1:\r
309                 raise ParserError(msg="Invalid rule section: %s, line %d"\r
310                                   % (self.RuleContent[LineIndex], LineIndex+1))\r
311 \r
312             Rule = TokenList[0].strip()\r
313             if Rule.upper() != "BUILD":\r
314                 #raise ParserError(msg="Not supported section %s" % Line)\r
315                 self._State = self._UnknownSection\r
316                 return\r
317 \r
318             FileType = TokenList[1].strip()\r
319             if FileType == '':\r
320                 raise ParserError(msg="No file type given: %s, line %d" % (Line, LineIndex+1))\r
321             FileTypeList.append(FileType)\r
322 \r
323         self._FileTypeList = FileTypeList\r
324         self._BuildVersion = "*"\r
325         self._State = self._Section\r
326 \r
327     def ParseSubSectionHeader(self, LineIndex):\r
328         SectionType = ""\r
329         List = self.RuleContent[LineIndex][1:-1].split(',')\r
330         FamilyList = []\r
331         for Section in List:\r
332             TokenList = Section.split('.')\r
333             Type = TokenList[0].strip().upper()\r
334 \r
335             if SectionType == "":\r
336                 SectionType = Type\r
337             elif SectionType != Type:\r
338                 raise ParserError(msg="Two different section types are not allowed: %s, line %d" % (Line, LineIndex+1))\r
339 \r
340             if len(TokenList) > 1:\r
341                 Family = TokenList[1].strip().upper()\r
342             else:\r
343                 Family = "*"\r
344 \r
345             if Family not in FamilyList:\r
346                 FamilyList.append(Family)\r
347 \r
348         self._FamilyList = FamilyList\r
349         self._State = SectionType.upper()\r
350 \r
351     def ParseInputFile(self, LineIndex):\r
352         Line = self.RuleContent[LineIndex]\r
353         TokenList = Line.split("=")\r
354         FileType = ""\r
355         if len(TokenList) > 1:\r
356             FileType = TokenList[0].strip()\r
357             if FileType not in self._FileTypeList:\r
358                 raise ParserError(msg="File type must be %s: %s, line %d"\r
359                                   % (Line, self._FileTypeList, LineIndex))\r
360             FileString = TokenList[1]\r
361         else:\r
362             if len(self._FileTypeList) > 1:\r
363                 raise ParserError(msg="File type must be given: %s, line %d" % (Line, LineIndex))\r
364             else:\r
365                 FileType = self._FileTypeList[0]\r
366             FileString = TokenList[0]\r
367 \r
368         FileList = FileString.split(",")\r
369         for File in FileList:\r
370             File = File.strip()\r
371             for Family in self._FamilyList:\r
372                 if Family not in self._RuleInfo:\r
373                     self._RuleInfo[Family] = {}\r
374                 if self._State not in self._RuleInfo[Family]:\r
375                     self._RuleInfo[Family][self._State] = {}\r
376                 if FileType not in self._RuleInfo[Family][self._State]:\r
377                     self._RuleInfo[Family][self._State][FileType] = []\r
378                 self._RuleInfo[Family][self._State][FileType].append(File)\r
379 \r
380     def ParseOutputFile(self, LineIndex):\r
381         FileList = self.RuleContent[LineIndex].split(",")\r
382         for File in FileList:\r
383             File = File.strip()\r
384             for Family in self._FamilyList:\r
385                 if Family not in self._RuleInfo:\r
386                     self._RuleInfo[Family] = {}\r
387                     self._RuleInfo[Family][self._State] = []\r
388                 if self._State not in self._RuleInfo[Family]:\r
389                     self._RuleInfo[Family][self._State] = []\r
390                 self._RuleInfo[Family][self._State].append(File)\r
391 \r
392     def ParseCommand(self, LineIndex):\r
393         Command = self.RuleContent[LineIndex]\r
394         for Family in self._FamilyList:\r
395             if Family not in self._RuleInfo:\r
396                 self._RuleInfo[Family] = {}\r
397                 self._RuleInfo[Family][self._State] = []\r
398             if self._State not in self._RuleInfo[Family]:\r
399                 self._RuleInfo[Family][self._State] = []\r
400             self._RuleInfo[Family][self._State].append(Command)\r
401 \r
402     # FileBuildRule object\r
403     def Get(self, FileExt, ToolChainFamily, BuildVersion="*"):\r
404         if FileExt not in self.FileTypeDict:\r
405             return None, None\r
406         FileType = self.FileTypeDict[FileExt]\r
407         Database = {}\r
408         BuildRuleObject = None\r
409         if BuildVersion in self.RuleDatabase:\r
410             Database = self.RuleDatabase[BuildVersion]\r
411         elif BuildVersion != "*":\r
412             if "*" not in self.RuleDatabase:\r
413                 return FileType, None\r
414             Database = self.RuleDatabase["*"]\r
415         else:\r
416             # BuildVersion == "*" and "*" not in self.RuleDatabase\r
417             # try to match ToolChainFamily\r
418             for Ver in self.RuleDatabase:\r
419                 Database = self.RuleDatabase[Ver]\r
420                 if ToolChainFamily not in Database:\r
421                     continue\r
422                 if FileType not in Database[ToolChainFamily]:\r
423                     continue\r
424                 break\r
425             else:\r
426                 return FileType, None\r
427 \r
428         if ToolChainFamily not in Database:\r
429             return FileType, None\r
430         if FileType not in Database[ToolChainFamily]:\r
431             return FileType, None\r
432         if not Database[ToolChainFamily][FileType].IsSupported(FileExt):\r
433             return FileType, None\r
434 \r
435         return FileType, Database[ToolChainFamily][FileType]\r
436 \r
437     _StateHandler = {\r
438         _SectionHeader     : ParseSectionHeader,\r
439         _Section           : ParseSection,\r
440         _SubSectionHeader  : ParseSubSectionHeader,\r
441         _SubSection        : ParseSubSection,\r
442         _InputFile         : ParseInputFile,\r
443         _OutputFile        : ParseOutputFile,\r
444         _Command           : ParseCommand,\r
445         _UnknownSection    : SkipSection,\r
446     }\r
447 \r
448 \r
449 \r
450 # This acts like the main() function for the script, unless it is 'import'ed into another\r
451 # script.\r
452 if __name__ == '__main__':\r
453     import sys\r
454     if len(sys.argv) <= 1:\r
455         print "No input file"\r
456         sys.exit(1)\r
457 \r
458     br = BuildRule(File=sys.argv[1])\r
459     for BuildVersion in br.RuleDatabase:\r
460         print "\nVVVVVVVVVVVVV %s VVVVVVVVVVVVVVV" % BuildVersion\r
461         for Family in br.RuleDatabase[BuildVersion]:\r
462             print "\n------------------ %s ----------------" % Family\r
463             for FileType in br.RuleDatabase[BuildVersion][Family]:\r
464                 print "\n%s" % br.RuleDatabase[BuildVersion][Family][FileType]\r
465 \r
466     print "\n------ .c"\r
467     ct, cr = br.Get(".c", "MSFT")\r
468     print cr\r
469     s, d, c = cr.Apply("abc\\y.c", "MdeModulePkg\\Universal", "\\")\r
470     print "\n------ .obj"\r
471     objt, objr = br.Get(".obj", "MSFT")\r
472     print objr\r
473     s, d, c = objr.Apply(["abc\\y.obj", "xyz\\z.obj"], "MdeModulePkg\\Universal", "\\")\r
474     print "\n------ .VFR"\r
475     print br.Get(".VFR", "GCC")\r
476     sys.exit(0)\r