diff options
Diffstat (limited to '')
-rwxr-xr-x | src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/AutoGen/BuildEngine.py | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/AutoGen/BuildEngine.py b/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/AutoGen/BuildEngine.py new file mode 100755 index 00000000..8a321bf8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/AutoGen/BuildEngine.py @@ -0,0 +1,650 @@ +## @file +# The engine for building files +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +## +# Import Modules +# +from __future__ import print_function +import Common.LongFilePathOs as os +import re +import copy +import string +from Common.LongFilePathSupport import OpenLongFilePath as open + +from Common.GlobalData import * +from Common.BuildToolError import * +from Common.Misc import tdict, PathClass +from Common.StringUtils import NormPath +from Common.DataType import * +from Common.TargetTxtClassObject import TargetTxtDict +gDefaultBuildRuleFile = 'build_rule.txt' +AutoGenReqBuildRuleVerNum = '0.1' + +import Common.EdkLogger as EdkLogger + +## Convert file type to file list macro name +# +# @param FileType The name of file type +# +# @retval string The name of macro +# +def FileListMacro(FileType): + return "%sS" % FileType.replace("-", "_").upper() + +## Convert file type to list file macro name +# +# @param FileType The name of file type +# +# @retval string The name of macro +# +def ListFileMacro(FileType): + return "%s_LIST" % FileListMacro(FileType) + +class TargetDescBlock(object): + def __init__(self, Inputs, Outputs, Commands, Dependencies): + self.InitWorker(Inputs, Outputs, Commands, Dependencies) + + def InitWorker(self, Inputs, Outputs, Commands, Dependencies): + self.Inputs = Inputs + self.Outputs = Outputs + self.Commands = Commands + self.Dependencies = Dependencies + if self.Outputs: + self.Target = self.Outputs[0] + else: + self.Target = None + + def __str__(self): + return self.Target.Path + + def __hash__(self): + return hash(self.Target.Path) + + def __eq__(self, Other): + if isinstance(Other, type(self)): + return Other.Target.Path == self.Target.Path + else: + return str(Other) == self.Target.Path + + def AddInput(self, Input): + if Input not in self.Inputs: + self.Inputs.append(Input) + + def IsMultipleInput(self): + return len(self.Inputs) > 1 + +## Class for one build rule +# +# This represents a build rule which can give out corresponding command list for +# building the given source file(s). The result can be used for generating the +# target for makefile. +# +class FileBuildRule: + INC_LIST_MACRO = "INC_LIST" + INC_MACRO = "INC" + + ## constructor + # + # @param Input The dictionary representing input file(s) for a rule + # @param Output The list representing output file(s) for a rule + # @param Command The list containing commands to generate the output from input + # + def __init__(self, Type, Input, Output, Command, ExtraDependency=None): + # The Input should not be empty + if not Input: + Input = [] + if not Output: + Output = [] + if not Command: + Command = [] + + self.FileListMacro = FileListMacro(Type) + self.ListFileMacro = ListFileMacro(Type) + self.IncListFileMacro = self.INC_LIST_MACRO + + self.SourceFileType = Type + # source files listed not in TAB_STAR or "?" pattern format + if not ExtraDependency: + self.ExtraSourceFileList = [] + else: + self.ExtraSourceFileList = ExtraDependency + + # + # Search macros used in command lines for <FILE_TYPE>_LIST and INC_LIST. + # If found, generate a file to keep the input files used to get over the + # limitation of command line length + # + self.MacroList = [] + self.CommandList = [] + for CmdLine in Command: + self.MacroList.extend(gMacroRefPattern.findall(CmdLine)) + # replace path separator with native one + self.CommandList.append(CmdLine) + + # Indicate what should be generated + if self.FileListMacro in self.MacroList: + self.GenFileListMacro = True + else: + self.GenFileListMacro = False + + if self.ListFileMacro in self.MacroList: + self.GenListFile = True + self.GenFileListMacro = True + else: + self.GenListFile = False + + if self.INC_LIST_MACRO in self.MacroList: + self.GenIncListFile = True + else: + self.GenIncListFile = False + + # Check input files + self.IsMultipleInput = False + self.SourceFileExtList = set() + for File in Input: + Base, Ext = os.path.splitext(File) + if Base.find(TAB_STAR) >= 0: + # There's TAB_STAR in the file name + self.IsMultipleInput = True + self.GenFileListMacro = True + elif Base.find("?") < 0: + # There's no TAB_STAR and "?" in file name + self.ExtraSourceFileList.append(File) + continue + self.SourceFileExtList.add(Ext) + + # Check output files + self.DestFileList = [] + for File in Output: + self.DestFileList.append(File) + + # All build targets generated by this rule for a module + self.BuildTargets = {} + + ## str() function support + # + # @retval string + # + def __str__(self): + SourceString = "" + SourceString += " %s %s %s" % (self.SourceFileType, " ".join(self.SourceFileExtList), self.ExtraSourceFileList) + DestString = ", ".join([str(i) for i in self.DestFileList]) + CommandString = "\n\t".join(self.CommandList) + return "%s : %s\n\t%s" % (DestString, SourceString, CommandString) + + def Instantiate(self, Macros = None): + if Macros is None: + Macros = {} + NewRuleObject = copy.copy(self) + NewRuleObject.BuildTargets = {} + NewRuleObject.DestFileList = [] + for File in self.DestFileList: + NewRuleObject.DestFileList.append(PathClass(NormPath(File, Macros))) + return NewRuleObject + + ## Apply the rule to given source file(s) + # + # @param SourceFile One file or a list of files to be built + # @param RelativeToDir The relative path of the source file + # @param PathSeparator Path separator + # + # @retval tuple (Source file in full path, List of individual sourcefiles, Destination file, List of build commands) + # + def Apply(self, SourceFile, BuildRuleOrder=None): + if not self.CommandList or not self.DestFileList: + return None + + # source file + if self.IsMultipleInput: + SrcFileName = "" + SrcFileBase = "" + SrcFileExt = "" + SrcFileDir = "" + SrcPath = "" + # SourceFile must be a list + SrcFile = "$(%s)" % self.FileListMacro + else: + SrcFileName, SrcFileBase, SrcFileExt = SourceFile.Name, SourceFile.BaseName, SourceFile.Ext + if SourceFile.Root: + SrcFileDir = SourceFile.SubDir + if SrcFileDir == "": + SrcFileDir = "." + else: + SrcFileDir = "." + SrcFile = SourceFile.Path + SrcPath = SourceFile.Dir + + # destination file (the first one) + if self.DestFileList: + DestFile = self.DestFileList[0].Path + DestPath = self.DestFileList[0].Dir + DestFileName = self.DestFileList[0].Name + DestFileBase, DestFileExt = self.DestFileList[0].BaseName, self.DestFileList[0].Ext + else: + DestFile = "" + DestPath = "" + DestFileName = "" + DestFileBase = "" + DestFileExt = "" + + BuildRulePlaceholderDict = { + # source file + "src" : SrcFile, + "s_path" : SrcPath, + "s_dir" : SrcFileDir, + "s_name" : SrcFileName, + "s_base" : SrcFileBase, + "s_ext" : SrcFileExt, + # destination file + "dst" : DestFile, + "d_path" : DestPath, + "d_name" : DestFileName, + "d_base" : DestFileBase, + "d_ext" : DestFileExt, + } + + DstFile = [] + for File in self.DestFileList: + File = string.Template(str(File)).safe_substitute(BuildRulePlaceholderDict) + File = string.Template(str(File)).safe_substitute(BuildRulePlaceholderDict) + DstFile.append(PathClass(File, IsBinary=True)) + + if DstFile[0] in self.BuildTargets: + TargetDesc = self.BuildTargets[DstFile[0]] + if BuildRuleOrder and SourceFile.Ext in BuildRuleOrder: + Index = BuildRuleOrder.index(SourceFile.Ext) + for Input in TargetDesc.Inputs: + if Input.Ext not in BuildRuleOrder or BuildRuleOrder.index(Input.Ext) > Index: + # + # Command line should be regenerated since some macros are different + # + CommandList = self._BuildCommand(BuildRulePlaceholderDict) + TargetDesc.InitWorker([SourceFile], DstFile, CommandList, self.ExtraSourceFileList) + break + else: + TargetDesc.AddInput(SourceFile) + else: + CommandList = self._BuildCommand(BuildRulePlaceholderDict) + TargetDesc = TargetDescBlock([SourceFile], DstFile, CommandList, self.ExtraSourceFileList) + TargetDesc.ListFileMacro = self.ListFileMacro + TargetDesc.FileListMacro = self.FileListMacro + TargetDesc.IncListFileMacro = self.IncListFileMacro + TargetDesc.GenFileListMacro = self.GenFileListMacro + TargetDesc.GenListFile = self.GenListFile + TargetDesc.GenIncListFile = self.GenIncListFile + self.BuildTargets[DstFile[0]] = TargetDesc + return TargetDesc + + def _BuildCommand(self, Macros): + CommandList = [] + for CommandString in self.CommandList: + CommandString = string.Template(CommandString).safe_substitute(Macros) + CommandString = string.Template(CommandString).safe_substitute(Macros) + CommandList.append(CommandString) + return CommandList + +## Class for build rules +# +# BuildRule class parses rules defined in a file or passed by caller, and converts +# the rule into FileBuildRule object. +# +class BuildRule: + _SectionHeader = "SECTIONHEADER" + _Section = "SECTION" + _SubSectionHeader = "SUBSECTIONHEADER" + _SubSection = "SUBSECTION" + _InputFile = "INPUTFILE" + _OutputFile = "OUTPUTFILE" + _ExtraDependency = "EXTRADEPENDENCY" + _Command = "COMMAND" + _UnknownSection = "UNKNOWNSECTION" + + _SubSectionList = [_InputFile, _OutputFile, _Command] + + _PATH_SEP = "(+)" + _FileTypePattern = re.compile("^[_a-zA-Z][_\-0-9a-zA-Z]*$") + _BinaryFileRule = FileBuildRule(TAB_DEFAULT_BINARY_FILE, [], [os.path.join("$(OUTPUT_DIR)", "${s_name}")], + ["$(CP) ${src} ${dst}"], []) + + ## Constructor + # + # @param File The file containing build rules in a well defined format + # @param Content The string list of build rules in a well defined format + # @param LineIndex The line number from which the parsing will begin + # @param SupportedFamily The list of supported tool chain families + # + def __init__(self, File=None, Content=None, LineIndex=0, SupportedFamily=[TAB_COMPILER_MSFT, "INTEL", "GCC", "RVCT"]): + self.RuleFile = File + # Read build rules from file if it's not none + if File is not None: + try: + self.RuleContent = open(File, 'r').readlines() + except: + EdkLogger.error("build", FILE_OPEN_FAILURE, ExtraData=File) + elif Content is not None: + self.RuleContent = Content + else: + EdkLogger.error("build", PARAMETER_MISSING, ExtraData="No rule file or string given") + + self.SupportedToolChainFamilyList = SupportedFamily + self.RuleDatabase = tdict(True, 4) # {FileExt, ModuleType, Arch, Family : FileBuildRule object} + self.Ext2FileType = {} # {ext : file-type} + self.FileTypeList = set() + + self._LineIndex = LineIndex + self._State = "" + self._RuleInfo = tdict(True, 2) # {toolchain family : {"InputFile": {}, "OutputFile" : [], "Command" : []}} + self._FileType = '' + self._BuildTypeList = set() + self._ArchList = set() + self._FamilyList = [] + self._TotalToolChainFamilySet = set() + self._RuleObjectList = [] # FileBuildRule object list + self._FileVersion = "" + + self.Parse() + + # some intrinsic rules + self.RuleDatabase[TAB_DEFAULT_BINARY_FILE, TAB_COMMON, TAB_COMMON, TAB_COMMON] = self._BinaryFileRule + self.FileTypeList.add(TAB_DEFAULT_BINARY_FILE) + + ## Parse the build rule strings + def Parse(self): + self._State = self._Section + for Index in range(self._LineIndex, len(self.RuleContent)): + # Clean up the line and replace path separator with native one + Line = self.RuleContent[Index].strip().replace(self._PATH_SEP, os.path.sep) + self.RuleContent[Index] = Line + + # find the build_rule_version + if Line and Line[0] == "#" and Line.find(TAB_BUILD_RULE_VERSION) != -1: + if Line.find("=") != -1 and Line.find("=") < (len(Line) - 1) and (Line[(Line.find("=") + 1):]).split(): + self._FileVersion = (Line[(Line.find("=") + 1):]).split()[0] + # skip empty or comment line + if Line == "" or Line[0] == "#": + continue + + # find out section header, enclosed by [] + if Line[0] == '[' and Line[-1] == ']': + # merge last section information into rule database + self.EndOfSection() + self._State = self._SectionHeader + # find out sub-section header, enclosed by <> + elif Line[0] == '<' and Line[-1] == '>': + if self._State != self._UnknownSection: + self._State = self._SubSectionHeader + + # call section handler to parse each (sub)section + self._StateHandler[self._State](self, Index) + # merge last section information into rule database + self.EndOfSection() + + ## Parse definitions under a section + # + # @param LineIndex The line index of build rule text + # + def ParseSection(self, LineIndex): + pass + + ## Parse definitions under a subsection + # + # @param LineIndex The line index of build rule text + # + def ParseSubSection(self, LineIndex): + # currently nothing here + pass + + ## Placeholder for not supported sections + # + # @param LineIndex The line index of build rule text + # + def SkipSection(self, LineIndex): + pass + + ## Merge section information just got into rule database + def EndOfSection(self): + Database = self.RuleDatabase + # if there's specific toolchain family, 'COMMON' doesn't make sense any more + if len(self._TotalToolChainFamilySet) > 1 and TAB_COMMON in self._TotalToolChainFamilySet: + self._TotalToolChainFamilySet.remove(TAB_COMMON) + for Family in self._TotalToolChainFamilySet: + Input = self._RuleInfo[Family, self._InputFile] + Output = self._RuleInfo[Family, self._OutputFile] + Command = self._RuleInfo[Family, self._Command] + ExtraDependency = self._RuleInfo[Family, self._ExtraDependency] + + BuildRule = FileBuildRule(self._FileType, Input, Output, Command, ExtraDependency) + for BuildType in self._BuildTypeList: + for Arch in self._ArchList: + Database[self._FileType, BuildType, Arch, Family] = BuildRule + for FileExt in BuildRule.SourceFileExtList: + self.Ext2FileType[FileExt] = self._FileType + + ## Parse section header + # + # @param LineIndex The line index of build rule text + # + def ParseSectionHeader(self, LineIndex): + self._RuleInfo = tdict(True, 2) + self._BuildTypeList = set() + self._ArchList = set() + self._FamilyList = [] + self._TotalToolChainFamilySet = set() + FileType = '' + RuleNameList = self.RuleContent[LineIndex][1:-1].split(',') + for RuleName in RuleNameList: + Arch = TAB_COMMON + BuildType = TAB_COMMON + TokenList = [Token.strip().upper() for Token in RuleName.split('.')] + # old format: Build.File-Type + if TokenList[0] == "BUILD": + if len(TokenList) == 1: + EdkLogger.error("build", FORMAT_INVALID, "Invalid rule section", + File=self.RuleFile, Line=LineIndex + 1, + ExtraData=self.RuleContent[LineIndex]) + + FileType = TokenList[1] + if FileType == '': + EdkLogger.error("build", FORMAT_INVALID, "No file type given", + File=self.RuleFile, Line=LineIndex + 1, + ExtraData=self.RuleContent[LineIndex]) + if self._FileTypePattern.match(FileType) is None: + EdkLogger.error("build", FORMAT_INVALID, File=self.RuleFile, Line=LineIndex + 1, + ExtraData="Only character, number (non-first character), '_' and '-' are allowed in file type") + # new format: File-Type.Build-Type.Arch + else: + if FileType == '': + FileType = TokenList[0] + elif FileType != TokenList[0]: + EdkLogger.error("build", FORMAT_INVALID, + "Different file types are not allowed in the same rule section", + File=self.RuleFile, Line=LineIndex + 1, + ExtraData=self.RuleContent[LineIndex]) + if len(TokenList) > 1: + BuildType = TokenList[1] + if len(TokenList) > 2: + Arch = TokenList[2] + self._BuildTypeList.add(BuildType) + self._ArchList.add(Arch) + + if TAB_COMMON in self._BuildTypeList and len(self._BuildTypeList) > 1: + EdkLogger.error("build", FORMAT_INVALID, + "Specific build types must not be mixed with common one", + File=self.RuleFile, Line=LineIndex + 1, + ExtraData=self.RuleContent[LineIndex]) + if TAB_COMMON in self._ArchList and len(self._ArchList) > 1: + EdkLogger.error("build", FORMAT_INVALID, + "Specific ARCH must not be mixed with common one", + File=self.RuleFile, Line=LineIndex + 1, + ExtraData=self.RuleContent[LineIndex]) + + self._FileType = FileType + self._State = self._Section + self.FileTypeList.add(FileType) + + ## Parse sub-section header + # + # @param LineIndex The line index of build rule text + # + def ParseSubSectionHeader(self, LineIndex): + SectionType = "" + List = self.RuleContent[LineIndex][1:-1].split(',') + FamilyList = [] + for Section in List: + TokenList = Section.split('.') + Type = TokenList[0].strip().upper() + + if SectionType == "": + SectionType = Type + elif SectionType != Type: + EdkLogger.error("build", FORMAT_INVALID, + "Two different section types are not allowed in the same sub-section", + File=self.RuleFile, Line=LineIndex + 1, + ExtraData=self.RuleContent[LineIndex]) + + if len(TokenList) > 1: + Family = TokenList[1].strip().upper() + else: + Family = TAB_COMMON + + if Family not in FamilyList: + FamilyList.append(Family) + + self._FamilyList = FamilyList + self._TotalToolChainFamilySet.update(FamilyList) + self._State = SectionType.upper() + if TAB_COMMON in FamilyList and len(FamilyList) > 1: + EdkLogger.error("build", FORMAT_INVALID, + "Specific tool chain family should not be mixed with general one", + File=self.RuleFile, Line=LineIndex + 1, + ExtraData=self.RuleContent[LineIndex]) + if self._State not in self._StateHandler: + EdkLogger.error("build", FORMAT_INVALID, File=self.RuleFile, Line=LineIndex + 1, + ExtraData="Unknown subsection: %s" % self.RuleContent[LineIndex]) + ## Parse <InputFile> sub-section + # + # @param LineIndex The line index of build rule text + # + def ParseInputFileSubSection(self, LineIndex): + FileList = [File.strip() for File in self.RuleContent[LineIndex].split(",")] + for ToolChainFamily in self._FamilyList: + if self._RuleInfo[ToolChainFamily, self._State] is None: + self._RuleInfo[ToolChainFamily, self._State] = [] + self._RuleInfo[ToolChainFamily, self._State].extend(FileList) + + ## Parse <ExtraDependency> sub-section + ## Parse <OutputFile> sub-section + ## Parse <Command> sub-section + # + # @param LineIndex The line index of build rule text + # + def ParseCommonSubSection(self, LineIndex): + for ToolChainFamily in self._FamilyList: + if self._RuleInfo[ToolChainFamily, self._State] is None: + self._RuleInfo[ToolChainFamily, self._State] = [] + self._RuleInfo[ToolChainFamily, self._State].append(self.RuleContent[LineIndex]) + + ## Get a build rule via [] operator + # + # @param FileExt The extension of a file + # @param ToolChainFamily The tool chain family name + # @param BuildVersion The build version number. TAB_STAR means any rule + # is applicable. + # + # @retval FileType The file type string + # @retval FileBuildRule The object of FileBuildRule + # + # Key = (FileExt, ModuleType, Arch, ToolChainFamily) + def __getitem__(self, Key): + if not Key: + return None + + if Key[0] in self.Ext2FileType: + Type = self.Ext2FileType[Key[0]] + elif Key[0].upper() in self.FileTypeList: + Type = Key[0].upper() + else: + return None + + if len(Key) > 1: + Key = (Type,) + Key[1:] + else: + Key = (Type,) + return self.RuleDatabase[Key] + + _StateHandler = { + _SectionHeader : ParseSectionHeader, + _Section : ParseSection, + _SubSectionHeader : ParseSubSectionHeader, + _SubSection : ParseSubSection, + _InputFile : ParseInputFileSubSection, + _OutputFile : ParseCommonSubSection, + _ExtraDependency : ParseCommonSubSection, + _Command : ParseCommonSubSection, + _UnknownSection : SkipSection, + } + +class ToolBuildRule(): + + def __new__(cls, *args, **kw): + if not hasattr(cls, '_instance'): + orig = super(ToolBuildRule, cls) + cls._instance = orig.__new__(cls, *args, **kw) + return cls._instance + + def __init__(self): + if not hasattr(self, 'ToolBuildRule'): + self._ToolBuildRule = None + + @property + def ToolBuildRule(self): + if not self._ToolBuildRule: + self._GetBuildRule() + return self._ToolBuildRule + + def _GetBuildRule(self): + BuildRuleFile = None + TargetObj = TargetTxtDict() + TargetTxt = TargetObj.Target + if TAB_TAT_DEFINES_BUILD_RULE_CONF in TargetTxt.TargetTxtDictionary: + BuildRuleFile = TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_BUILD_RULE_CONF] + if not BuildRuleFile: + BuildRuleFile = gDefaultBuildRuleFile + RetVal = BuildRule(BuildRuleFile) + if RetVal._FileVersion == "": + RetVal._FileVersion = AutoGenReqBuildRuleVerNum + else: + if RetVal._FileVersion < AutoGenReqBuildRuleVerNum : + # If Build Rule's version is less than the version number required by the tools, halting the build. + EdkLogger.error("build", AUTOGEN_ERROR, + 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])"\ + % (RetVal._FileVersion, AutoGenReqBuildRuleVerNum)) + self._ToolBuildRule = RetVal + +# This acts like the main() function for the script, unless it is 'import'ed into another +# script. +if __name__ == '__main__': + import sys + EdkLogger.Initialize() + if len(sys.argv) > 1: + Br = BuildRule(sys.argv[1]) + print(str(Br[".c", SUP_MODULE_DXE_DRIVER, "IA32", TAB_COMPILER_MSFT][1])) + print() + print(str(Br[".c", SUP_MODULE_DXE_DRIVER, "IA32", "INTEL"][1])) + print() + print(str(Br[".c", SUP_MODULE_DXE_DRIVER, "IA32", "GCC"][1])) + print() + print(str(Br[".ac", "ACPI_TABLE", "IA32", TAB_COMPILER_MSFT][1])) + print() + print(str(Br[".h", "ACPI_TABLE", "IA32", "INTEL"][1])) + print() + print(str(Br[".ac", "ACPI_TABLE", "IA32", TAB_COMPILER_MSFT][1])) + print() + print(str(Br[".s", SUP_MODULE_SEC, "IPF", "COMMON"][1])) + print() + print(str(Br[".s", SUP_MODULE_SEC][1])) + |