2 # The engine for building files
4 # Copyright (c) 2007, 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
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.
22 from Common
.GlobalData
import *
23 from Common
.BuildToolError
import *
24 from Common
.Misc
import tdict
, PathClass
25 from Common
.String
import NormPath
26 from Common
.DataType
import *
28 import Common
.EdkLogger
as EdkLogger
30 ## Convert file type to file list macro name
32 # @param FileType The name of file type
34 # @retval string The name of macro
36 def FileListMacro(FileType
):
37 return "%sS" % FileType
.replace("-", "_").upper()
39 ## Convert file type to list file macro name
41 # @param FileType The name of file type
43 # @retval string The name of macro
45 def ListFileMacro(FileType
):
46 return "%s_LIST" % FileListMacro(FileType
)
48 class TargetDescBlock(object):
49 _Cache_
= {} # {TargetFile : TargetDescBlock object}
52 def __new__(Class
, Inputs
, Outputs
, Commands
, Dependencies
):
53 if Outputs
[0] in Class
._Cache
_:
54 Tdb
= Class
._Cache
_[Outputs
[0]]
58 Tdb
= super(TargetDescBlock
, Class
).__new
__(Class
)
59 Tdb
._Init
(Inputs
, Outputs
, Commands
, Dependencies
)
60 #Class._Cache_[Outputs[0]] = Tdb
63 def _Init(self
, Inputs
, Outputs
, Commands
, Dependencies
):
65 self
.Outputs
= Outputs
66 self
.Commands
= Commands
67 self
.Dependencies
= Dependencies
69 self
.Target
= self
.Outputs
[0]
74 return self
.Target
.Path
77 return hash(self
.Target
.Path
)
79 def __eq__(self
, Other
):
80 if type(Other
) == type(self
):
81 return Other
.Target
.Path
== self
.Target
.Path
83 return str(Other
) == self
.Target
.Path
85 def AddInput(self
, Input
):
86 if Input
not in self
.Inputs
:
87 self
.Inputs
.append(Input
)
89 def IsMultipleInput(self
):
90 return len(self
.Inputs
) > 1
94 TargetDescBlock
._Cache
_ = {}
96 ## Class for one build rule
98 # This represents a build rule which can give out corresponding command list for
99 # building the given source file(s). The result can be used for generating the
100 # target for makefile.
103 INC_LIST_MACRO
= "INC_LIST"
108 # @param Input The dictionary represeting input file(s) for a rule
109 # @param Output The list represeting output file(s) for a rule
110 # @param Command The list containing commands to generate the output from input
112 def __init__(self
, Type
, Input
, Output
, Command
, ExtraDependency
=None):
113 # The Input should not be empty
121 self
.FileListMacro
= FileListMacro(Type
)
122 self
.ListFileMacro
= ListFileMacro(Type
)
123 self
.IncListFileMacro
= self
.INC_LIST_MACRO
125 self
.SourceFileType
= Type
126 # source files listed not in "*" or "?" pattern format
127 if not ExtraDependency
:
128 self
.ExtraSourceFileList
= []
130 self
.ExtraSourceFileList
= ExtraDependency
133 # Search macros used in command lines for <FILE_TYPE>_LIST and INC_LIST.
134 # If found, generate a file to keep the input files used to get over the
135 # limitation of command line length
138 self
.CommandList
= []
139 for CmdLine
in Command
:
140 self
.MacroList
.extend(gMacroRefPattern
.findall(CmdLine
))
141 # replace path separator with native one
142 self
.CommandList
.append(CmdLine
)
144 # Indicate what should be generated
145 if self
.FileListMacro
in self
.MacroList
:
146 self
.GenFileListMacro
= True
148 self
.GenFileListMacro
= False
150 if self
.ListFileMacro
in self
.MacroList
:
151 self
.GenListFile
= True
152 self
.GenFileListMacro
= True
154 self
.GenListFile
= False
156 if self
.INC_LIST_MACRO
in self
.MacroList
:
157 self
.GenIncListFile
= True
159 self
.GenIncListFile
= False
162 self
.IsMultipleInput
= False
163 self
.SourceFileExtList
= []
165 Base
, Ext
= os
.path
.splitext(File
)
166 if Base
.find("*") >= 0:
167 # There's "*" in the file name
168 self
.IsMultipleInput
= True
169 self
.GenFileListMacro
= True
170 elif Base
.find("?") < 0:
171 # There's no "*" and "?" in file name
172 self
.ExtraSourceFileList
.append(File
)
174 if Ext
not in self
.SourceFileExtList
:
175 self
.SourceFileExtList
.append(Ext
)
178 self
.DestFileList
= []
180 self
.DestFileList
.append(File
)
182 # All build targets generated by this rule for a module
183 self
.BuildTargets
= {}
185 ## str() function support
191 SourceString
+= " %s %s %s" % (self
.SourceFileType
, " ".join(self
.SourceFileExtList
), self
.ExtraSourceFileList
)
192 DestString
= ", ".join(self
.DestFileList
)
193 CommandString
= "\n\t".join(self
.CommandList
)
194 return "%s : %s\n\t%s" % (DestString
, SourceString
, CommandString
)
196 ## Check if given file extension is supported by this rule
198 # @param FileExt The extension of a file
200 # @retval True If the extension is supported
201 # @retval False If the extension is not supported
203 def IsSupported(self
, FileExt
):
204 return FileExt
in self
.SourceFileExtList
206 def Instantiate(self
, Macros
={}):
207 NewRuleObject
= copy
.copy(self
)
208 NewRuleObject
.BuildTargets
= {}
209 NewRuleObject
.DestFileList
= []
210 for File
in self
.DestFileList
:
211 NewRuleObject
.DestFileList
.append(PathClass(NormPath(File
, Macros
)))
214 ## Apply the rule to given source file(s)
216 # @param SourceFile One file or a list of files to be built
217 # @param RelativeToDir The relative path of the source file
218 # @param PathSeparator Path separator
220 # @retval tuple (Source file in full path, List of individual sourcefiles, Destionation file, List of build commands)
222 def Apply(self
, SourceFile
):
223 if not self
.CommandList
or not self
.DestFileList
:
227 if self
.IsMultipleInput
:
233 # SourceFile must be a list
234 SrcFile
= "$(%s)" % self
.FileListMacro
236 SrcFileName
, SrcFileBase
, SrcFileExt
= SourceFile
.Name
, SourceFile
.BaseName
, SourceFile
.Ext
238 SrcFileDir
= SourceFile
.SubDir
243 SrcFile
= SourceFile
.Path
244 SrcPath
= SourceFile
.Dir
246 # destination file (the first one)
247 if self
.DestFileList
:
248 DestFile
= self
.DestFileList
[0].Path
249 DestPath
= self
.DestFileList
[0].Dir
250 DestFileName
= self
.DestFileList
[0].Name
251 DestFileBase
, DestFileExt
= self
.DestFileList
[0].BaseName
, self
.DestFileList
[0].Ext
259 BuildRulePlaceholderDict
= {
263 "s_dir" : SrcFileDir
,
264 "s_name" : SrcFileName
,
265 "s_base" : SrcFileBase
,
266 "s_ext" : SrcFileExt
,
270 "d_name" : DestFileName
,
271 "d_base" : DestFileBase
,
272 "d_ext" : DestFileExt
,
276 for File
in self
.DestFileList
:
277 File
= string
.Template(str(File
)).safe_substitute(BuildRulePlaceholderDict
)
278 File
= string
.Template(str(File
)).safe_substitute(BuildRulePlaceholderDict
)
279 DstFile
.append(PathClass(File
, IsBinary
=True))
281 if DstFile
[0] in self
.BuildTargets
:
282 TargetDesc
= self
.BuildTargets
[DstFile
[0]]
283 TargetDesc
.AddInput(SourceFile
)
286 for CommandString
in self
.CommandList
:
287 CommandString
= string
.Template(CommandString
).safe_substitute(BuildRulePlaceholderDict
)
288 CommandString
= string
.Template(CommandString
).safe_substitute(BuildRulePlaceholderDict
)
289 CommandList
.append(CommandString
)
290 TargetDesc
= TargetDescBlock([SourceFile
], DstFile
, CommandList
, self
.ExtraSourceFileList
)
291 TargetDesc
.ListFileMacro
= self
.ListFileMacro
292 TargetDesc
.FileListMacro
= self
.FileListMacro
293 TargetDesc
.IncListFileMacro
= self
.IncListFileMacro
294 TargetDesc
.GenFileListMacro
= self
.GenFileListMacro
295 TargetDesc
.GenListFile
= self
.GenListFile
296 TargetDesc
.GenIncListFile
= self
.GenIncListFile
297 self
.BuildTargets
[DstFile
[0]] = TargetDesc
300 ## Class for build rules
302 # BuildRule class parses rules defined in a file or passed by caller, and converts
303 # the rule into FileBuildRule object.
306 _SectionHeader
= "SECTIONHEADER"
308 _SubSectionHeader
= "SUBSECTIONHEADER"
309 _SubSection
= "SUBSECTION"
310 _InputFile
= "INPUTFILE"
311 _OutputFile
= "OUTPUTFILE"
312 _ExtraDependency
= "EXTRADEPENDENCY"
314 _UnknownSection
= "UNKNOWNSECTION"
316 _SubSectionList
= [_InputFile
, _OutputFile
, _Command
]
319 _FileTypePattern
= re
.compile("^[_a-zA-Z][_\-0-9a-zA-Z]*$")
320 _BinaryFileRule
= FileBuildRule(TAB_DEFAULT_BINARY_FILE
, [], [os
.path
.join("$(OUTPUT_DIR)", "${s_name}")],
321 ["$(CP) ${src} ${dst}"], [])
325 # @param File The file containing build rules in a well defined format
326 # @param Content The string list of build rules in a well defined format
327 # @param LineIndex The line number from which the parsing will begin
328 # @param SupportedFamily The list of supported tool chain families
330 def __init__(self
, File
=None, Content
=None, LineIndex
=0, SupportedFamily
=["MSFT", "INTEL", "GCC", "RVCT"]):
332 # Read build rules from file if it's not none
335 self
.RuleContent
= open(File
, 'r').readlines()
337 EdkLogger
.error("build", FILE_OPEN_FAILURE
, ExtraData
=File
)
338 elif Content
!= None:
339 self
.RuleContent
= Content
341 EdkLogger
.error("build", PARAMETER_MISSING
, ExtraData
="No rule file or string given")
343 self
.SupportedToolChainFamilyList
= SupportedFamily
344 self
.RuleDatabase
= tdict(True, 4) # {FileExt, ModuleType, Arch, Family : FileBuildRule object}
345 self
.Ext2FileType
= {} # {ext : file-type}
346 self
.FileTypeList
= set()
348 self
._LineIndex
= LineIndex
350 self
._RuleInfo
= tdict(True, 2) # {toolchain family : {"InputFile": {}, "OutputFile" : [], "Command" : []}}
352 self
._BuildTypeList
= []
354 self
._FamilyList
= []
355 self
._TotalToolChainFamilySet
= set()
356 self
._RuleObjectList
= [] # FileBuildRule object list
357 self
._FileVersion
= ""
361 # some intrinsic rules
362 self
.RuleDatabase
[TAB_DEFAULT_BINARY_FILE
, "COMMON", "COMMON", "COMMON"] = self
._BinaryFileRule
363 self
.FileTypeList
.add(TAB_DEFAULT_BINARY_FILE
)
365 ## Parse the build rule strings
367 self
._State
= self
._Section
368 for Index
in range(self
._LineIndex
, len(self
.RuleContent
)):
369 # Clean up the line and replace path separator with native one
370 Line
= self
.RuleContent
[Index
].strip().replace(self
._PATH
_SEP
, os
.path
.sep
)
371 self
.RuleContent
[Index
] = Line
373 # find the build_rule_version
374 if Line
and Line
[0] == "#" and Line
.find(TAB_BUILD_RULE_VERSION
) <> -1:
375 if Line
.find("=") <> -1 and Line
.find("=") < (len(Line
)-1) and (Line
[(Line
.find("=") + 1):]).split():
376 self
._FileVersion
= (Line
[(Line
.find("=") + 1):]).split()[0]
377 # skip empty or comment line
378 if Line
== "" or Line
[0] == "#":
381 # find out section header, enclosed by []
382 if Line
[0] == '[' and Line
[-1] == ']':
383 # merge last section information into rule database
385 self
._State
= self
._SectionHeader
386 # find out sub-section header, enclosed by <>
387 elif Line
[0] == '<' and Line
[-1] == '>':
388 if self
._State
!= self
._UnknownSection
:
389 self
._State
= self
._SubSectionHeader
391 # call section handler to parse each (sub)section
392 self
._StateHandler
[self
._State
](self
, Index
)
393 # merge last section information into rule database
396 ## Parse definitions under a section
398 # @param LineIndex The line index of build rule text
400 def ParseSection(self
, LineIndex
):
403 ## Parse definitions under a subsection
405 # @param LineIndex The line index of build rule text
407 def ParseSubSection(self
, LineIndex
):
408 # currenly nothing here
411 ## Placeholder for not supported sections
413 # @param LineIndex The line index of build rule text
415 def SkipSection(self
, LineIndex
):
418 ## Merge section information just got into rule database
419 def EndOfSection(self
):
420 Database
= self
.RuleDatabase
421 # if there's specific toochain family, 'COMMON' doesn't make sense any more
422 if len(self
._TotalToolChainFamilySet
) > 1 and 'COMMON' in self
._TotalToolChainFamilySet
:
423 self
._TotalToolChainFamilySet
.remove('COMMON')
424 for Family
in self
._TotalToolChainFamilySet
:
425 Input
= self
._RuleInfo
[Family
, self
._InputFile
]
426 Output
= self
._RuleInfo
[Family
, self
._OutputFile
]
427 Command
= self
._RuleInfo
[Family
, self
._Command
]
428 ExtraDependency
= self
._RuleInfo
[Family
, self
._ExtraDependency
]
430 BuildRule
= FileBuildRule(self
._FileType
, Input
, Output
, Command
, ExtraDependency
)
431 for BuildType
in self
._BuildTypeList
:
432 for Arch
in self
._ArchList
:
433 Database
[self
._FileType
, BuildType
, Arch
, Family
] = BuildRule
434 for FileExt
in BuildRule
.SourceFileExtList
:
435 self
.Ext2FileType
[FileExt
] = self
._FileType
437 ## Parse section header
439 # @param LineIndex The line index of build rule text
441 def ParseSectionHeader(self
, LineIndex
):
442 self
._RuleInfo
= tdict(True, 2)
443 self
._BuildTypeList
= []
445 self
._FamilyList
= []
446 self
._TotalToolChainFamilySet
= set()
448 RuleNameList
= self
.RuleContent
[LineIndex
][1:-1].split(',')
449 for RuleName
in RuleNameList
:
452 TokenList
= [Token
.strip().upper() for Token
in RuleName
.split('.')]
453 # old format: Build.File-Type
454 if TokenList
[0] == "BUILD":
455 if len(TokenList
) == 1:
456 EdkLogger
.error("build", FORMAT_INVALID
, "Invalid rule section",
457 File
=self
.RuleFile
, Line
=LineIndex
+1,
458 ExtraData
=self
.RuleContent
[LineIndex
])
460 FileType
= TokenList
[1]
462 EdkLogger
.error("build", FORMAT_INVALID
, "No file type given",
463 File
=self
.RuleFile
, Line
=LineIndex
+1,
464 ExtraData
=self
.RuleContent
[LineIndex
])
465 if self
._FileTypePattern
.match(FileType
) == None:
466 EdkLogger
.error("build", FORMAT_INVALID
, File
=self
.RuleFile
, Line
=LineIndex
+1,
467 ExtraData
="Only character, number (non-first character), '_' and '-' are allowed in file type")
468 # new format: File-Type.Build-Type.Arch
471 FileType
= TokenList
[0]
472 elif FileType
!= TokenList
[0]:
473 EdkLogger
.error("build", FORMAT_INVALID
,
474 "Different file types are not allowed in the same rule section",
475 File
=self
.RuleFile
, Line
=LineIndex
+1,
476 ExtraData
=self
.RuleContent
[LineIndex
])
477 if len(TokenList
) > 1:
478 BuildType
= TokenList
[1]
479 if len(TokenList
) > 2:
481 if BuildType
not in self
._BuildTypeList
:
482 self
._BuildTypeList
.append(BuildType
)
483 if Arch
not in self
._ArchList
:
484 self
._ArchList
.append(Arch
)
486 if 'COMMON' in self
._BuildTypeList
and len(self
._BuildTypeList
) > 1:
487 EdkLogger
.error("build", FORMAT_INVALID
,
488 "Specific build types must not be mixed with common one",
489 File
=self
.RuleFile
, Line
=LineIndex
+1,
490 ExtraData
=self
.RuleContent
[LineIndex
])
491 if 'COMMON' in self
._ArchList
and len(self
._ArchList
) > 1:
492 EdkLogger
.error("build", FORMAT_INVALID
,
493 "Specific ARCH must not be mixed with common one",
494 File
=self
.RuleFile
, Line
=LineIndex
+1,
495 ExtraData
=self
.RuleContent
[LineIndex
])
497 self
._FileType
= FileType
498 self
._State
= self
._Section
499 self
.FileTypeList
.add(FileType
)
501 ## Parse sub-section header
503 # @param LineIndex The line index of build rule text
505 def ParseSubSectionHeader(self
, LineIndex
):
507 List
= self
.RuleContent
[LineIndex
][1:-1].split(',')
510 TokenList
= Section
.split('.')
511 Type
= TokenList
[0].strip().upper()
513 if SectionType
== "":
515 elif SectionType
!= Type
:
516 EdkLogger
.error("build", FORMAT_INVALID
,
517 "Two different section types are not allowed in the same sub-section",
518 File
=self
.RuleFile
, Line
=LineIndex
+1,
519 ExtraData
=self
.RuleContent
[LineIndex
])
521 if len(TokenList
) > 1:
522 Family
= TokenList
[1].strip().upper()
526 if Family
not in FamilyList
:
527 FamilyList
.append(Family
)
529 self
._FamilyList
= FamilyList
530 self
._TotalToolChainFamilySet
.update(FamilyList
)
531 self
._State
= SectionType
.upper()
532 if 'COMMON' in FamilyList
and len(FamilyList
) > 1:
533 EdkLogger
.error("build", FORMAT_INVALID
,
534 "Specific tool chain family should not be mixed with general one",
535 File
=self
.RuleFile
, Line
=LineIndex
+1,
536 ExtraData
=self
.RuleContent
[LineIndex
])
537 if self
._State
not in self
._StateHandler
:
538 EdkLogger
.error("build", FORMAT_INVALID
, File
=self
.RuleFile
, Line
=LineIndex
+1,
539 ExtraData
="Unknown subsection: %s" % self
.RuleContent
[LineIndex
])
540 ## Parse <InputFile> sub-section
542 # @param LineIndex The line index of build rule text
544 def ParseInputFile(self
, LineIndex
):
545 FileList
= [File
.strip() for File
in self
.RuleContent
[LineIndex
].split(",")]
546 for ToolChainFamily
in self
._FamilyList
:
547 InputFiles
= self
._RuleInfo
[ToolChainFamily
, self
._State
]
548 if InputFiles
== None:
550 self
._RuleInfo
[ToolChainFamily
, self
._State
] = InputFiles
551 InputFiles
.extend(FileList
)
553 ## Parse <ExtraDependency> sub-section
555 # @param LineIndex The line index of build rule text
557 def ParseCommon(self
, LineIndex
):
558 for ToolChainFamily
in self
._FamilyList
:
559 Items
= self
._RuleInfo
[ToolChainFamily
, self
._State
]
562 self
._RuleInfo
[ToolChainFamily
, self
._State
] = Items
563 Items
.append(self
.RuleContent
[LineIndex
])
565 ## Get a build rule via [] operator
567 # @param FileExt The extension of a file
568 # @param ToolChainFamily The tool chain family name
569 # @param BuildVersion The build version number. "*" means any rule
572 # @retval FileType The file type string
573 # @retval FileBuildRule The object of FileBuildRule
575 # Key = (FileExt, ModuleType, Arch, ToolChainFamily)
576 def __getitem__(self
, Key
):
580 if Key
[0] in self
.Ext2FileType
:
581 Type
= self
.Ext2FileType
[Key
[0]]
582 elif Key
[0].upper() in self
.FileTypeList
:
583 Type
= Key
[0].upper()
588 Key
= (Type
,) + Key
[1:]
591 return self
.RuleDatabase
[Key
]
594 _SectionHeader
: ParseSectionHeader
,
595 _Section
: ParseSection
,
596 _SubSectionHeader
: ParseSubSectionHeader
,
597 _SubSection
: ParseSubSection
,
598 _InputFile
: ParseInputFile
,
599 _OutputFile
: ParseCommon
,
600 _ExtraDependency
: ParseCommon
,
601 _Command
: ParseCommon
,
602 _UnknownSection
: SkipSection
,
605 # This acts like the main() function for the script, unless it is 'import'ed into another
607 if __name__
== '__main__':
609 EdkLogger
.Initialize()
610 if len(sys
.argv
) > 1:
611 Br
= BuildRule(sys
.argv
[1])
612 print str(Br
[".c", "DXE_DRIVER", "IA32", "MSFT"][1])
614 print str(Br
[".c", "DXE_DRIVER", "IA32", "INTEL"][1])
616 print str(Br
[".c", "DXE_DRIVER", "IA32", "GCC"][1])
618 print str(Br
[".ac", "ACPI_TABLE", "IA32", "MSFT"][1])
620 print str(Br
[".h", "ACPI_TABLE", "IA32", "INTEL"][1])
622 print str(Br
[".ac", "ACPI_TABLE", "IA32", "MSFT"][1])
624 print str(Br
[".s", "SEC", "IPF", "COMMON"][1])
626 print str(Br
[".s", "SEC"][1])