2 # The engine for building files
4 # Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
5 # SPDX-License-Identifier: BSD-2-Clause-Patent
11 from __future__
import print_function
12 import Common
.LongFilePathOs
as os
16 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
18 from Common
.GlobalData
import *
19 from Common
.BuildToolError
import *
20 from Common
.Misc
import tdict
, PathClass
21 from Common
.StringUtils
import NormPath
22 from Common
.DataType
import *
23 from Common
.TargetTxtClassObject
import TargetTxt
24 gDefaultBuildRuleFile
= 'build_rule.txt'
25 AutoGenReqBuildRuleVerNum
= '0.1'
27 import Common
.EdkLogger
as EdkLogger
29 ## Convert file type to file list macro name
31 # @param FileType The name of file type
33 # @retval string The name of macro
35 def FileListMacro(FileType
):
36 return "%sS" % FileType
.replace("-", "_").upper()
38 ## Convert file type to list file macro name
40 # @param FileType The name of file type
42 # @retval string The name of macro
44 def ListFileMacro(FileType
):
45 return "%s_LIST" % FileListMacro(FileType
)
47 class TargetDescBlock(object):
48 def __init__(self
, Inputs
, Outputs
, Commands
, Dependencies
):
49 self
.InitWorker(Inputs
, Outputs
, Commands
, Dependencies
)
51 def InitWorker(self
, Inputs
, Outputs
, Commands
, Dependencies
):
53 self
.Outputs
= Outputs
54 self
.Commands
= Commands
55 self
.Dependencies
= Dependencies
57 self
.Target
= self
.Outputs
[0]
62 return self
.Target
.Path
65 return hash(self
.Target
.Path
)
67 def __eq__(self
, Other
):
68 if isinstance(Other
, type(self
)):
69 return Other
.Target
.Path
== self
.Target
.Path
71 return str(Other
) == self
.Target
.Path
73 def AddInput(self
, Input
):
74 if Input
not in self
.Inputs
:
75 self
.Inputs
.append(Input
)
77 def IsMultipleInput(self
):
78 return len(self
.Inputs
) > 1
80 ## Class for one build rule
82 # This represents a build rule which can give out corresponding command list for
83 # building the given source file(s). The result can be used for generating the
84 # target for makefile.
87 INC_LIST_MACRO
= "INC_LIST"
92 # @param Input The dictionary representing input file(s) for a rule
93 # @param Output The list representing output file(s) for a rule
94 # @param Command The list containing commands to generate the output from input
96 def __init__(self
, Type
, Input
, Output
, Command
, ExtraDependency
=None):
97 # The Input should not be empty
105 self
.FileListMacro
= FileListMacro(Type
)
106 self
.ListFileMacro
= ListFileMacro(Type
)
107 self
.IncListFileMacro
= self
.INC_LIST_MACRO
109 self
.SourceFileType
= Type
110 # source files listed not in TAB_STAR or "?" pattern format
111 if not ExtraDependency
:
112 self
.ExtraSourceFileList
= []
114 self
.ExtraSourceFileList
= ExtraDependency
117 # Search macros used in command lines for <FILE_TYPE>_LIST and INC_LIST.
118 # If found, generate a file to keep the input files used to get over the
119 # limitation of command line length
122 self
.CommandList
= []
123 for CmdLine
in Command
:
124 self
.MacroList
.extend(gMacroRefPattern
.findall(CmdLine
))
125 # replace path separator with native one
126 self
.CommandList
.append(CmdLine
)
128 # Indicate what should be generated
129 if self
.FileListMacro
in self
.MacroList
:
130 self
.GenFileListMacro
= True
132 self
.GenFileListMacro
= False
134 if self
.ListFileMacro
in self
.MacroList
:
135 self
.GenListFile
= True
136 self
.GenFileListMacro
= True
138 self
.GenListFile
= False
140 if self
.INC_LIST_MACRO
in self
.MacroList
:
141 self
.GenIncListFile
= True
143 self
.GenIncListFile
= False
146 self
.IsMultipleInput
= False
147 self
.SourceFileExtList
= set()
149 Base
, Ext
= os
.path
.splitext(File
)
150 if Base
.find(TAB_STAR
) >= 0:
151 # There's TAB_STAR in the file name
152 self
.IsMultipleInput
= True
153 self
.GenFileListMacro
= True
154 elif Base
.find("?") < 0:
155 # There's no TAB_STAR and "?" in file name
156 self
.ExtraSourceFileList
.append(File
)
158 self
.SourceFileExtList
.add(Ext
)
161 self
.DestFileList
= []
163 self
.DestFileList
.append(File
)
165 # All build targets generated by this rule for a module
166 self
.BuildTargets
= {}
168 ## str() function support
174 SourceString
+= " %s %s %s" % (self
.SourceFileType
, " ".join(self
.SourceFileExtList
), self
.ExtraSourceFileList
)
175 DestString
= ", ".join(self
.DestFileList
)
176 CommandString
= "\n\t".join(self
.CommandList
)
177 return "%s : %s\n\t%s" % (DestString
, SourceString
, CommandString
)
179 def Instantiate(self
, Macros
={}):
180 NewRuleObject
= copy
.copy(self
)
181 NewRuleObject
.BuildTargets
= {}
182 NewRuleObject
.DestFileList
= []
183 for File
in self
.DestFileList
:
184 NewRuleObject
.DestFileList
.append(PathClass(NormPath(File
, Macros
)))
187 ## Apply the rule to given source file(s)
189 # @param SourceFile One file or a list of files to be built
190 # @param RelativeToDir The relative path of the source file
191 # @param PathSeparator Path separator
193 # @retval tuple (Source file in full path, List of individual sourcefiles, Destination file, List of build commands)
195 def Apply(self
, SourceFile
, BuildRuleOrder
=None):
196 if not self
.CommandList
or not self
.DestFileList
:
200 if self
.IsMultipleInput
:
206 # SourceFile must be a list
207 SrcFile
= "$(%s)" % self
.FileListMacro
209 SrcFileName
, SrcFileBase
, SrcFileExt
= SourceFile
.Name
, SourceFile
.BaseName
, SourceFile
.Ext
211 SrcFileDir
= SourceFile
.SubDir
216 SrcFile
= SourceFile
.Path
217 SrcPath
= SourceFile
.Dir
219 # destination file (the first one)
220 if self
.DestFileList
:
221 DestFile
= self
.DestFileList
[0].Path
222 DestPath
= self
.DestFileList
[0].Dir
223 DestFileName
= self
.DestFileList
[0].Name
224 DestFileBase
, DestFileExt
= self
.DestFileList
[0].BaseName
, self
.DestFileList
[0].Ext
232 BuildRulePlaceholderDict
= {
236 "s_dir" : SrcFileDir
,
237 "s_name" : SrcFileName
,
238 "s_base" : SrcFileBase
,
239 "s_ext" : SrcFileExt
,
243 "d_name" : DestFileName
,
244 "d_base" : DestFileBase
,
245 "d_ext" : DestFileExt
,
249 for File
in self
.DestFileList
:
250 File
= string
.Template(str(File
)).safe_substitute(BuildRulePlaceholderDict
)
251 File
= string
.Template(str(File
)).safe_substitute(BuildRulePlaceholderDict
)
252 DstFile
.append(PathClass(File
, IsBinary
=True))
254 if DstFile
[0] in self
.BuildTargets
:
255 TargetDesc
= self
.BuildTargets
[DstFile
[0]]
256 if BuildRuleOrder
and SourceFile
.Ext
in BuildRuleOrder
:
257 Index
= BuildRuleOrder
.index(SourceFile
.Ext
)
258 for Input
in TargetDesc
.Inputs
:
259 if Input
.Ext
not in BuildRuleOrder
or BuildRuleOrder
.index(Input
.Ext
) > Index
:
261 # Command line should be regenerated since some macros are different
263 CommandList
= self
._BuildCommand
(BuildRulePlaceholderDict
)
264 TargetDesc
.InitWorker([SourceFile
], DstFile
, CommandList
, self
.ExtraSourceFileList
)
267 TargetDesc
.AddInput(SourceFile
)
269 CommandList
= self
._BuildCommand
(BuildRulePlaceholderDict
)
270 TargetDesc
= TargetDescBlock([SourceFile
], DstFile
, CommandList
, self
.ExtraSourceFileList
)
271 TargetDesc
.ListFileMacro
= self
.ListFileMacro
272 TargetDesc
.FileListMacro
= self
.FileListMacro
273 TargetDesc
.IncListFileMacro
= self
.IncListFileMacro
274 TargetDesc
.GenFileListMacro
= self
.GenFileListMacro
275 TargetDesc
.GenListFile
= self
.GenListFile
276 TargetDesc
.GenIncListFile
= self
.GenIncListFile
277 self
.BuildTargets
[DstFile
[0]] = TargetDesc
280 def _BuildCommand(self
, Macros
):
282 for CommandString
in self
.CommandList
:
283 CommandString
= string
.Template(CommandString
).safe_substitute(Macros
)
284 CommandString
= string
.Template(CommandString
).safe_substitute(Macros
)
285 CommandList
.append(CommandString
)
288 ## Class for build rules
290 # BuildRule class parses rules defined in a file or passed by caller, and converts
291 # the rule into FileBuildRule object.
294 _SectionHeader
= "SECTIONHEADER"
296 _SubSectionHeader
= "SUBSECTIONHEADER"
297 _SubSection
= "SUBSECTION"
298 _InputFile
= "INPUTFILE"
299 _OutputFile
= "OUTPUTFILE"
300 _ExtraDependency
= "EXTRADEPENDENCY"
302 _UnknownSection
= "UNKNOWNSECTION"
304 _SubSectionList
= [_InputFile
, _OutputFile
, _Command
]
307 _FileTypePattern
= re
.compile("^[_a-zA-Z][_\-0-9a-zA-Z]*$")
308 _BinaryFileRule
= FileBuildRule(TAB_DEFAULT_BINARY_FILE
, [], [os
.path
.join("$(OUTPUT_DIR)", "${s_name}")],
309 ["$(CP) ${src} ${dst}"], [])
313 # @param File The file containing build rules in a well defined format
314 # @param Content The string list of build rules in a well defined format
315 # @param LineIndex The line number from which the parsing will begin
316 # @param SupportedFamily The list of supported tool chain families
318 def __init__(self
, File
=None, Content
=None, LineIndex
=0, SupportedFamily
=[TAB_COMPILER_MSFT
, "INTEL", "GCC", "RVCT"]):
320 # Read build rules from file if it's not none
323 self
.RuleContent
= open(File
, 'r').readlines()
325 EdkLogger
.error("build", FILE_OPEN_FAILURE
, ExtraData
=File
)
326 elif Content
is not None:
327 self
.RuleContent
= Content
329 EdkLogger
.error("build", PARAMETER_MISSING
, ExtraData
="No rule file or string given")
331 self
.SupportedToolChainFamilyList
= SupportedFamily
332 self
.RuleDatabase
= tdict(True, 4) # {FileExt, ModuleType, Arch, Family : FileBuildRule object}
333 self
.Ext2FileType
= {} # {ext : file-type}
334 self
.FileTypeList
= set()
336 self
._LineIndex
= LineIndex
338 self
._RuleInfo
= tdict(True, 2) # {toolchain family : {"InputFile": {}, "OutputFile" : [], "Command" : []}}
340 self
._BuildTypeList
= set()
341 self
._ArchList
= set()
342 self
._FamilyList
= []
343 self
._TotalToolChainFamilySet
= set()
344 self
._RuleObjectList
= [] # FileBuildRule object list
345 self
._FileVersion
= ""
349 # some intrinsic rules
350 self
.RuleDatabase
[TAB_DEFAULT_BINARY_FILE
, TAB_COMMON
, TAB_COMMON
, TAB_COMMON
] = self
._BinaryFileRule
351 self
.FileTypeList
.add(TAB_DEFAULT_BINARY_FILE
)
353 ## Parse the build rule strings
355 self
._State
= self
._Section
356 for Index
in range(self
._LineIndex
, len(self
.RuleContent
)):
357 # Clean up the line and replace path separator with native one
358 Line
= self
.RuleContent
[Index
].strip().replace(self
._PATH
_SEP
, os
.path
.sep
)
359 self
.RuleContent
[Index
] = Line
361 # find the build_rule_version
362 if Line
and Line
[0] == "#" and Line
.find(TAB_BUILD_RULE_VERSION
) != -1:
363 if Line
.find("=") != -1 and Line
.find("=") < (len(Line
) - 1) and (Line
[(Line
.find("=") + 1):]).split():
364 self
._FileVersion
= (Line
[(Line
.find("=") + 1):]).split()[0]
365 # skip empty or comment line
366 if Line
== "" or Line
[0] == "#":
369 # find out section header, enclosed by []
370 if Line
[0] == '[' and Line
[-1] == ']':
371 # merge last section information into rule database
373 self
._State
= self
._SectionHeader
374 # find out sub-section header, enclosed by <>
375 elif Line
[0] == '<' and Line
[-1] == '>':
376 if self
._State
!= self
._UnknownSection
:
377 self
._State
= self
._SubSectionHeader
379 # call section handler to parse each (sub)section
380 self
._StateHandler
[self
._State
](self
, Index
)
381 # merge last section information into rule database
384 ## Parse definitions under a section
386 # @param LineIndex The line index of build rule text
388 def ParseSection(self
, LineIndex
):
391 ## Parse definitions under a subsection
393 # @param LineIndex The line index of build rule text
395 def ParseSubSection(self
, LineIndex
):
396 # currently nothing here
399 ## Placeholder for not supported sections
401 # @param LineIndex The line index of build rule text
403 def SkipSection(self
, LineIndex
):
406 ## Merge section information just got into rule database
407 def EndOfSection(self
):
408 Database
= self
.RuleDatabase
409 # if there's specific toolchain family, 'COMMON' doesn't make sense any more
410 if len(self
._TotalToolChainFamilySet
) > 1 and TAB_COMMON
in self
._TotalToolChainFamilySet
:
411 self
._TotalToolChainFamilySet
.remove(TAB_COMMON
)
412 for Family
in self
._TotalToolChainFamilySet
:
413 Input
= self
._RuleInfo
[Family
, self
._InputFile
]
414 Output
= self
._RuleInfo
[Family
, self
._OutputFile
]
415 Command
= self
._RuleInfo
[Family
, self
._Command
]
416 ExtraDependency
= self
._RuleInfo
[Family
, self
._ExtraDependency
]
418 BuildRule
= FileBuildRule(self
._FileType
, Input
, Output
, Command
, ExtraDependency
)
419 for BuildType
in self
._BuildTypeList
:
420 for Arch
in self
._ArchList
:
421 Database
[self
._FileType
, BuildType
, Arch
, Family
] = BuildRule
422 for FileExt
in BuildRule
.SourceFileExtList
:
423 self
.Ext2FileType
[FileExt
] = self
._FileType
425 ## Parse section header
427 # @param LineIndex The line index of build rule text
429 def ParseSectionHeader(self
, LineIndex
):
430 self
._RuleInfo
= tdict(True, 2)
431 self
._BuildTypeList
= set()
432 self
._ArchList
= set()
433 self
._FamilyList
= []
434 self
._TotalToolChainFamilySet
= set()
436 RuleNameList
= self
.RuleContent
[LineIndex
][1:-1].split(',')
437 for RuleName
in RuleNameList
:
439 BuildType
= TAB_COMMON
440 TokenList
= [Token
.strip().upper() for Token
in RuleName
.split('.')]
441 # old format: Build.File-Type
442 if TokenList
[0] == "BUILD":
443 if len(TokenList
) == 1:
444 EdkLogger
.error("build", FORMAT_INVALID
, "Invalid rule section",
445 File
=self
.RuleFile
, Line
=LineIndex
+ 1,
446 ExtraData
=self
.RuleContent
[LineIndex
])
448 FileType
= TokenList
[1]
450 EdkLogger
.error("build", FORMAT_INVALID
, "No file type given",
451 File
=self
.RuleFile
, Line
=LineIndex
+ 1,
452 ExtraData
=self
.RuleContent
[LineIndex
])
453 if self
._FileTypePattern
.match(FileType
) is None:
454 EdkLogger
.error("build", FORMAT_INVALID
, File
=self
.RuleFile
, Line
=LineIndex
+ 1,
455 ExtraData
="Only character, number (non-first character), '_' and '-' are allowed in file type")
456 # new format: File-Type.Build-Type.Arch
459 FileType
= TokenList
[0]
460 elif FileType
!= TokenList
[0]:
461 EdkLogger
.error("build", FORMAT_INVALID
,
462 "Different file types are not allowed in the same rule section",
463 File
=self
.RuleFile
, Line
=LineIndex
+ 1,
464 ExtraData
=self
.RuleContent
[LineIndex
])
465 if len(TokenList
) > 1:
466 BuildType
= TokenList
[1]
467 if len(TokenList
) > 2:
469 self
._BuildTypeList
.add(BuildType
)
470 self
._ArchList
.add(Arch
)
472 if TAB_COMMON
in self
._BuildTypeList
and len(self
._BuildTypeList
) > 1:
473 EdkLogger
.error("build", FORMAT_INVALID
,
474 "Specific build types must not be mixed with common one",
475 File
=self
.RuleFile
, Line
=LineIndex
+ 1,
476 ExtraData
=self
.RuleContent
[LineIndex
])
477 if TAB_COMMON
in self
._ArchList
and len(self
._ArchList
) > 1:
478 EdkLogger
.error("build", FORMAT_INVALID
,
479 "Specific ARCH must not be mixed with common one",
480 File
=self
.RuleFile
, Line
=LineIndex
+ 1,
481 ExtraData
=self
.RuleContent
[LineIndex
])
483 self
._FileType
= FileType
484 self
._State
= self
._Section
485 self
.FileTypeList
.add(FileType
)
487 ## Parse sub-section header
489 # @param LineIndex The line index of build rule text
491 def ParseSubSectionHeader(self
, LineIndex
):
493 List
= self
.RuleContent
[LineIndex
][1:-1].split(',')
496 TokenList
= Section
.split('.')
497 Type
= TokenList
[0].strip().upper()
499 if SectionType
== "":
501 elif SectionType
!= Type
:
502 EdkLogger
.error("build", FORMAT_INVALID
,
503 "Two different section types are not allowed in the same sub-section",
504 File
=self
.RuleFile
, Line
=LineIndex
+ 1,
505 ExtraData
=self
.RuleContent
[LineIndex
])
507 if len(TokenList
) > 1:
508 Family
= TokenList
[1].strip().upper()
512 if Family
not in FamilyList
:
513 FamilyList
.append(Family
)
515 self
._FamilyList
= FamilyList
516 self
._TotalToolChainFamilySet
.update(FamilyList
)
517 self
._State
= SectionType
.upper()
518 if TAB_COMMON
in FamilyList
and len(FamilyList
) > 1:
519 EdkLogger
.error("build", FORMAT_INVALID
,
520 "Specific tool chain family should not be mixed with general one",
521 File
=self
.RuleFile
, Line
=LineIndex
+ 1,
522 ExtraData
=self
.RuleContent
[LineIndex
])
523 if self
._State
not in self
._StateHandler
:
524 EdkLogger
.error("build", FORMAT_INVALID
, File
=self
.RuleFile
, Line
=LineIndex
+ 1,
525 ExtraData
="Unknown subsection: %s" % self
.RuleContent
[LineIndex
])
526 ## Parse <InputFile> sub-section
528 # @param LineIndex The line index of build rule text
530 def ParseInputFileSubSection(self
, LineIndex
):
531 FileList
= [File
.strip() for File
in self
.RuleContent
[LineIndex
].split(",")]
532 for ToolChainFamily
in self
._FamilyList
:
533 if self
._RuleInfo
[ToolChainFamily
, self
._State
] is None:
534 self
._RuleInfo
[ToolChainFamily
, self
._State
] = []
535 self
._RuleInfo
[ToolChainFamily
, self
._State
].extend(FileList
)
537 ## Parse <ExtraDependency> sub-section
538 ## Parse <OutputFile> sub-section
539 ## Parse <Command> sub-section
541 # @param LineIndex The line index of build rule text
543 def ParseCommonSubSection(self
, LineIndex
):
544 for ToolChainFamily
in self
._FamilyList
:
545 if self
._RuleInfo
[ToolChainFamily
, self
._State
] is None:
546 self
._RuleInfo
[ToolChainFamily
, self
._State
] = []
547 self
._RuleInfo
[ToolChainFamily
, self
._State
].append(self
.RuleContent
[LineIndex
])
549 ## Get a build rule via [] operator
551 # @param FileExt The extension of a file
552 # @param ToolChainFamily The tool chain family name
553 # @param BuildVersion The build version number. TAB_STAR means any rule
556 # @retval FileType The file type string
557 # @retval FileBuildRule The object of FileBuildRule
559 # Key = (FileExt, ModuleType, Arch, ToolChainFamily)
560 def __getitem__(self
, Key
):
564 if Key
[0] in self
.Ext2FileType
:
565 Type
= self
.Ext2FileType
[Key
[0]]
566 elif Key
[0].upper() in self
.FileTypeList
:
567 Type
= Key
[0].upper()
572 Key
= (Type
,) + Key
[1:]
575 return self
.RuleDatabase
[Key
]
578 _SectionHeader
: ParseSectionHeader
,
579 _Section
: ParseSection
,
580 _SubSectionHeader
: ParseSubSectionHeader
,
581 _SubSection
: ParseSubSection
,
582 _InputFile
: ParseInputFileSubSection
,
583 _OutputFile
: ParseCommonSubSection
,
584 _ExtraDependency
: ParseCommonSubSection
,
585 _Command
: ParseCommonSubSection
,
586 _UnknownSection
: SkipSection
,
591 if TAB_TAT_DEFINES_BUILD_RULE_CONF
in TargetTxt
.TargetTxtDictionary
:
592 BuildRuleFile
= TargetTxt
.TargetTxtDictionary
[TAB_TAT_DEFINES_BUILD_RULE_CONF
]
593 if not BuildRuleFile
:
594 BuildRuleFile
= gDefaultBuildRuleFile
595 RetVal
= BuildRule(BuildRuleFile
)
596 if RetVal
._FileVersion
== "":
597 RetVal
._FileVersion
= AutoGenReqBuildRuleVerNum
599 if RetVal
._FileVersion
< AutoGenReqBuildRuleVerNum
:
600 # If Build Rule's version is less than the version number required by the tools, halting the build.
601 EdkLogger
.error("build", AUTOGEN_ERROR
,
602 ExtraData
="The version number [%s] of build_rule.txt is less than the version number required by the AutoGen.(the minimum required version number is [%s])"\
603 % (RetVal
._FileVersion
, AutoGenReqBuildRuleVerNum
))
606 BuildRuleObj
= GetBuildRule()
608 # This acts like the main() function for the script, unless it is 'import'ed into another
610 if __name__
== '__main__':
612 EdkLogger
.Initialize()
613 if len(sys
.argv
) > 1:
614 Br
= BuildRule(sys
.argv
[1])
615 print(str(Br
[".c", SUP_MODULE_DXE_DRIVER
, "IA32", TAB_COMPILER_MSFT
][1]))
617 print(str(Br
[".c", SUP_MODULE_DXE_DRIVER
, "IA32", "INTEL"][1]))
619 print(str(Br
[".c", SUP_MODULE_DXE_DRIVER
, "IA32", "GCC"][1]))
621 print(str(Br
[".ac", "ACPI_TABLE", "IA32", TAB_COMPILER_MSFT
][1]))
623 print(str(Br
[".h", "ACPI_TABLE", "IA32", "INTEL"][1]))
625 print(str(Br
[".ac", "ACPI_TABLE", "IA32", TAB_COMPILER_MSFT
][1]))
627 print(str(Br
[".s", SUP_MODULE_SEC
, "IPF", "COMMON"][1]))
629 print(str(Br
[".s", SUP_MODULE_SEC
][1]))