## @file # Create makefile for MS nmake and GNU make # # Copyright (c) 2019, Intel Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent # from __future__ import absolute_import from Workspace.WorkspaceDatabase import WorkspaceDatabase,BuildDB from Common.caching import cached_property from AutoGen.BuildEngine import BuildRule,AutoGenReqBuildRuleVerNum from AutoGen.AutoGen import CalculatePriorityValue from Common.Misc import CheckPcdDatum,GuidValue from Common.Expression import ValueExpressionEx from Common.DataType import * from CommonDataClass.Exceptions import * from CommonDataClass.CommonClass import SkuInfoClass import Common.EdkLogger as EdkLogger from Common.BuildToolError import OPTION_CONFLICT,FORMAT_INVALID,RESOURCE_NOT_AVAILABLE from Common.MultipleWorkspace import MultipleWorkspace as mws from collections import defaultdict from Common.Misc import PathClass import os # # The priority list while override build option # PrioList = {"0x11111" : 16, # TARGET_TOOLCHAIN_ARCH_COMMANDTYPE_ATTRIBUTE (Highest) "0x01111" : 15, # ******_TOOLCHAIN_ARCH_COMMANDTYPE_ATTRIBUTE "0x10111" : 14, # TARGET_*********_ARCH_COMMANDTYPE_ATTRIBUTE "0x00111" : 13, # ******_*********_ARCH_COMMANDTYPE_ATTRIBUTE "0x11011" : 12, # TARGET_TOOLCHAIN_****_COMMANDTYPE_ATTRIBUTE "0x01011" : 11, # ******_TOOLCHAIN_****_COMMANDTYPE_ATTRIBUTE "0x10011" : 10, # TARGET_*********_****_COMMANDTYPE_ATTRIBUTE "0x00011" : 9, # ******_*********_****_COMMANDTYPE_ATTRIBUTE "0x11101" : 8, # TARGET_TOOLCHAIN_ARCH_***********_ATTRIBUTE "0x01101" : 7, # ******_TOOLCHAIN_ARCH_***********_ATTRIBUTE "0x10101" : 6, # TARGET_*********_ARCH_***********_ATTRIBUTE "0x00101" : 5, # ******_*********_ARCH_***********_ATTRIBUTE "0x11001" : 4, # TARGET_TOOLCHAIN_****_***********_ATTRIBUTE "0x01001" : 3, # ******_TOOLCHAIN_****_***********_ATTRIBUTE "0x10001" : 2, # TARGET_*********_****_***********_ATTRIBUTE "0x00001" : 1} # ******_*********_****_***********_ATTRIBUTE (Lowest) ## Base class for AutoGen # # This class just implements the cache mechanism of AutoGen objects. # class AutoGenInfo(object): # database to maintain the objects in each child class __ObjectCache = {} # (BuildTarget, ToolChain, ARCH, platform file): AutoGen object ## Factory method # # @param Class class object of real AutoGen class # (WorkspaceAutoGen, ModuleAutoGen or PlatformAutoGen) # @param Workspace Workspace directory or WorkspaceAutoGen object # @param MetaFile The path of meta file # @param Target Build target # @param Toolchain Tool chain name # @param Arch Target arch # @param *args The specific class related parameters # @param **kwargs The specific class related dict parameters # @classmethod def GetCache(cls): return cls.__ObjectCache def __new__(cls, Workspace, MetaFile, Target, Toolchain, Arch, *args, **kwargs): # check if the object has been created Key = (Target, Toolchain, Arch, MetaFile) if Key in cls.__ObjectCache: # if it exists, just return it directly return cls.__ObjectCache[Key] # it didnt exist. create it, cache it, then return it RetVal = cls.__ObjectCache[Key] = super(AutoGenInfo, cls).__new__(cls) return RetVal ## hash() operator # # The file path of platform file will be used to represent hash value of this object # # @retval int Hash value of the file path of platform file # def __hash__(self): return hash(self.MetaFile) ## str() operator # # The file path of platform file will be used to represent this object # # @retval string String of platform file path # def __str__(self): return str(self.MetaFile) ## "==" operator def __eq__(self, Other): return Other and self.MetaFile == Other ## Expand * in build option key # # @param Options Options to be expanded # @param ToolDef Use specified ToolDef instead of full version. # This is needed during initialization to prevent # infinite recursion betweeh BuildOptions, # ToolDefinition, and this function. # # @retval options Options expanded # def _ExpandBuildOption(self, Options, ModuleStyle=None, ToolDef=None): if not ToolDef: ToolDef = self.ToolDefinition BuildOptions = {} FamilyMatch = False FamilyIsNull = True OverrideList = {} # # Construct a list contain the build options which need override. # for Key in Options: # # Key[0] -- tool family # Key[1] -- TARGET_TOOLCHAIN_ARCH_COMMANDTYPE_ATTRIBUTE # if (Key[0] == self.BuildRuleFamily and (ModuleStyle is None or len(Key) < 3 or (len(Key) > 2 and Key[2] == ModuleStyle))): Target, ToolChain, Arch, CommandType, Attr = Key[1].split('_') if (Target == self.BuildTarget or Target == TAB_STAR) and\ (ToolChain == self.ToolChain or ToolChain == TAB_STAR) and\ (Arch == self.Arch or Arch == TAB_STAR) and\ Options[Key].startswith("="): if OverrideList.get(Key[1]) is not None: OverrideList.pop(Key[1]) OverrideList[Key[1]] = Options[Key] # # Use the highest priority value. # if (len(OverrideList) >= 2): KeyList = list(OverrideList.keys()) for Index in range(len(KeyList)): NowKey = KeyList[Index] Target1, ToolChain1, Arch1, CommandType1, Attr1 = NowKey.split("_") for Index1 in range(len(KeyList) - Index - 1): NextKey = KeyList[Index1 + Index + 1] # # Compare two Key, if one is included by another, choose the higher priority one # Target2, ToolChain2, Arch2, CommandType2, Attr2 = NextKey.split("_") if (Target1 == Target2 or Target1 == TAB_STAR or Target2 == TAB_STAR) and\ (ToolChain1 == ToolChain2 or ToolChain1 == TAB_STAR or ToolChain2 == TAB_STAR) and\ (Arch1 == Arch2 or Arch1 == TAB_STAR or Arch2 == TAB_STAR) and\ (CommandType1 == CommandType2 or CommandType1 == TAB_STAR or CommandType2 == TAB_STAR) and\ (Attr1 == Attr2 or Attr1 == TAB_STAR or Attr2 == TAB_STAR): if CalculatePriorityValue(NowKey) > CalculatePriorityValue(NextKey): if Options.get((self.BuildRuleFamily, NextKey)) is not None: Options.pop((self.BuildRuleFamily, NextKey)) else: if Options.get((self.BuildRuleFamily, NowKey)) is not None: Options.pop((self.BuildRuleFamily, NowKey)) for Key in Options: if ModuleStyle is not None and len (Key) > 2: # Check Module style is EDK or EDKII. # Only append build option for the matched style module. if ModuleStyle == EDK_NAME and Key[2] != EDK_NAME: continue elif ModuleStyle == EDKII_NAME and Key[2] != EDKII_NAME: continue Family = Key[0] Target, Tag, Arch, Tool, Attr = Key[1].split("_") # if tool chain family doesn't match, skip it if Tool in ToolDef and Family != "": FamilyIsNull = False if ToolDef[Tool].get(TAB_TOD_DEFINES_BUILDRULEFAMILY, "") != "": if Family != ToolDef[Tool][TAB_TOD_DEFINES_BUILDRULEFAMILY]: continue elif Family != ToolDef[Tool][TAB_TOD_DEFINES_FAMILY]: continue FamilyMatch = True # expand any wildcard if Target == TAB_STAR or Target == self.BuildTarget: if Tag == TAB_STAR or Tag == self.ToolChain: if Arch == TAB_STAR or Arch == self.Arch: if Tool not in BuildOptions: BuildOptions[Tool] = {} if Attr != "FLAGS" or Attr not in BuildOptions[Tool] or Options[Key].startswith('='): BuildOptions[Tool][Attr] = Options[Key] else: # append options for the same tool except PATH if Attr != 'PATH': BuildOptions[Tool][Attr] += " " + Options[Key] else: BuildOptions[Tool][Attr] = Options[Key] # Build Option Family has been checked, which need't to be checked again for family. if FamilyMatch or FamilyIsNull: return BuildOptions for Key in Options: if ModuleStyle is not None and len (Key) > 2: # Check Module style is EDK or EDKII. # Only append build option for the matched style module. if ModuleStyle == EDK_NAME and Key[2] != EDK_NAME: continue elif ModuleStyle == EDKII_NAME and Key[2] != EDKII_NAME: continue Family = Key[0] Target, Tag, Arch, Tool, Attr = Key[1].split("_") # if tool chain family doesn't match, skip it if Tool not in ToolDef or Family == "": continue # option has been added before if Family != ToolDef[Tool][TAB_TOD_DEFINES_FAMILY]: continue # expand any wildcard if Target == TAB_STAR or Target == self.BuildTarget: if Tag == TAB_STAR or Tag == self.ToolChain: if Arch == TAB_STAR or Arch == self.Arch: if Tool not in BuildOptions: BuildOptions[Tool] = {} if Attr != "FLAGS" or Attr not in BuildOptions[Tool] or Options[Key].startswith('='): BuildOptions[Tool][Attr] = Options[Key] else: # append options for the same tool except PATH if Attr != 'PATH': BuildOptions[Tool][Attr] += " " + Options[Key] else: BuildOptions[Tool][Attr] = Options[Key] return BuildOptions # #This class is the pruned WorkSpaceAutoGen for ModuleAutoGen in multiple thread # class WorkSpaceInfo(AutoGenInfo): def __init__(self,Workspace, MetaFile, Target, ToolChain, Arch): self._SrcTimeStamp = 0 self.Db = BuildDB self.BuildDatabase = self.Db.BuildObject self.Target = Target self.ToolChain = ToolChain self.WorkspaceDir = Workspace self.ActivePlatform = MetaFile self.ArchList = Arch class PlatformInfo(AutoGenInfo): def __init__(self, Workspace, MetaFile, Target, ToolChain, Arch,DataPipe): self.Wa = Workspace self.WorkspaceDir = self.Wa.WorkspaceDir self.MetaFile = MetaFile self.Arch = Arch self.Target = Target self.BuildTarget = Target self.ToolChain = ToolChain self.Platform = self.Wa.BuildDatabase[self.MetaFile, self.Arch, self.Target, self.ToolChain] self.SourceDir = MetaFile.SubDir self.DataPipe = DataPipe @cached_property def _AsBuildModuleList(self): retVal = self.DataPipe.Get("AsBuildModuleList") if retVal is None: retVal = {} return retVal ## Test if a module is supported by the platform # # An error will be raised directly if the module or its arch is not supported # by the platform or current configuration # def ValidModule(self, Module): return Module in self.Platform.Modules or Module in self.Platform.LibraryInstances \ or Module in self._AsBuildModuleList @cached_property def ToolChainFamily(self): retVal = self.DataPipe.Get("ToolChainFamily") if retVal is None: retVal = {} return retVal @cached_property def BuildRuleFamily(self): retVal = self.DataPipe.Get("BuildRuleFamily") if retVal is None: retVal = {} return retVal @cached_property def _MbList(self): return [self.Wa.BuildDatabase[m, self.Arch, self.BuildTarget, self.ToolChain] for m in self.Platform.Modules] @cached_property def PackageList(self): RetVal = set() for dec_file,Arch in self.DataPipe.Get("PackageList"): RetVal.add(self.Wa.BuildDatabase[dec_file,Arch,self.BuildTarget, self.ToolChain]) return list(RetVal) ## Return the directory to store all intermediate and final files built @cached_property def BuildDir(self): if os.path.isabs(self.OutputDir): RetVal = os.path.join( os.path.abspath(self.OutputDir), self.Target + "_" + self.ToolChain, ) else: RetVal = os.path.join( self.WorkspaceDir, self.OutputDir, self.Target + "_" + self.ToolChain, ) return RetVal ## Return the build output directory platform specifies @cached_property def OutputDir(self): return self.Platform.OutputDirectory ## Return platform name @cached_property def Name(self): return self.Platform.PlatformName ## Return meta-file GUID @cached_property def Guid(self): return self.Platform.Guid ## Return platform version @cached_property def Version(self): return self.Platform.Version ## Return paths of tools @cached_property def ToolDefinition(self): retVal = self.DataPipe.Get("TOOLDEF") if retVal is None: retVal = {} return retVal ## Return build command string # # @retval string Build command string # @cached_property def BuildCommand(self): retVal = self.DataPipe.Get("BuildCommand") if retVal is None: retVal = [] return retVal @cached_property def PcdTokenNumber(self): retVal = self.DataPipe.Get("PCD_TNUM") if retVal is None: retVal = {} return retVal ## Override PCD setting (type, value, ...) # # @param ToPcd The PCD to be overridden # @param FromPcd The PCD overriding from # def _OverridePcd(self, ToPcd, FromPcd, Module="", Msg="", Library=""): # # in case there's PCDs coming from FDF file, which have no type given. # at this point, ToPcd.Type has the type found from dependent # package # TokenCName = ToPcd.TokenCName for PcdItem in self.MixedPcd: if (ToPcd.TokenCName, ToPcd.TokenSpaceGuidCName) in self.MixedPcd[PcdItem]: TokenCName = PcdItem[0] break if FromPcd is not None: if ToPcd.Pending and FromPcd.Type: ToPcd.Type = FromPcd.Type elif ToPcd.Type and FromPcd.Type\ and ToPcd.Type != FromPcd.Type and ToPcd.Type in FromPcd.Type: if ToPcd.Type.strip() == TAB_PCDS_DYNAMIC_EX: ToPcd.Type = FromPcd.Type elif ToPcd.Type and FromPcd.Type \ and ToPcd.Type != FromPcd.Type: if Library: Module = str(Module) + " 's library file (" + str(Library) + ")" EdkLogger.error("build", OPTION_CONFLICT, "Mismatched PCD type", ExtraData="%s.%s is used as [%s] in module %s, but as [%s] in %s."\ % (ToPcd.TokenSpaceGuidCName, TokenCName, ToPcd.Type, Module, FromPcd.Type, Msg), File=self.MetaFile) if FromPcd.MaxDatumSize: ToPcd.MaxDatumSize = FromPcd.MaxDatumSize ToPcd.MaxSizeUserSet = FromPcd.MaxDatumSize if FromPcd.DefaultValue: ToPcd.DefaultValue = FromPcd.DefaultValue if FromPcd.TokenValue: ToPcd.TokenValue = FromPcd.TokenValue if FromPcd.DatumType: ToPcd.DatumType = FromPcd.DatumType if FromPcd.SkuInfoList: ToPcd.SkuInfoList = FromPcd.SkuInfoList if FromPcd.UserDefinedDefaultStoresFlag: ToPcd.UserDefinedDefaultStoresFlag = FromPcd.UserDefinedDefaultStoresFlag # Add Flexible PCD format parse if ToPcd.DefaultValue: try: ToPcd.DefaultValue = ValueExpressionEx(ToPcd.DefaultValue, ToPcd.DatumType, self._GuidDict)(True) except BadExpression as Value: EdkLogger.error('Parser', FORMAT_INVALID, 'PCD [%s.%s] Value "%s", %s' %(ToPcd.TokenSpaceGuidCName, ToPcd.TokenCName, ToPcd.DefaultValue, Value), File=self.MetaFile) # check the validation of datum IsValid, Cause = CheckPcdDatum(ToPcd.DatumType, ToPcd.DefaultValue) if not IsValid: EdkLogger.error('build', FORMAT_INVALID, Cause, File=self.MetaFile, ExtraData="%s.%s" % (ToPcd.TokenSpaceGuidCName, TokenCName)) ToPcd.validateranges = FromPcd.validateranges ToPcd.validlists = FromPcd.validlists ToPcd.expressions = FromPcd.expressions ToPcd.CustomAttribute = FromPcd.CustomAttribute if FromPcd is not None and ToPcd.DatumType == TAB_VOID and not ToPcd.MaxDatumSize: EdkLogger.debug(EdkLogger.DEBUG_9, "No MaxDatumSize specified for PCD %s.%s" \ % (ToPcd.TokenSpaceGuidCName, TokenCName)) Value = ToPcd.DefaultValue if not Value: ToPcd.MaxDatumSize = '1' elif Value[0] == 'L': ToPcd.MaxDatumSize = str((len(Value) - 2) * 2) elif Value[0] == '{': ToPcd.MaxDatumSize = str(len(Value.split(','))) else: ToPcd.MaxDatumSize = str(len(Value) - 1) # apply default SKU for dynamic PCDS if specified one is not available if (ToPcd.Type in PCD_DYNAMIC_TYPE_SET or ToPcd.Type in PCD_DYNAMIC_EX_TYPE_SET) \ and not ToPcd.SkuInfoList: if self.Platform.SkuName in self.Platform.SkuIds: SkuName = self.Platform.SkuName else: SkuName = TAB_DEFAULT ToPcd.SkuInfoList = { SkuName : SkuInfoClass(SkuName, self.Platform.SkuIds[SkuName][0], '', '', '', '', '', ToPcd.DefaultValue) } def ApplyPcdSetting(self, Module, Pcds, Library=""): # for each PCD in module for Name, Guid in Pcds: PcdInModule = Pcds[Name, Guid] # find out the PCD setting in platform if (Name, Guid) in self.Pcds: PcdInPlatform = self.Pcds[Name, Guid] else: PcdInPlatform = None # then override the settings if any self._OverridePcd(PcdInModule, PcdInPlatform, Module, Msg="DSC PCD sections", Library=Library) # resolve the VariableGuid value for SkuId in PcdInModule.SkuInfoList: Sku = PcdInModule.SkuInfoList[SkuId] if Sku.VariableGuid == '': continue Sku.VariableGuidValue = GuidValue(Sku.VariableGuid, self.PackageList, self.MetaFile.Path) if Sku.VariableGuidValue is None: PackageList = "\n\t".join(str(P) for P in self.PackageList) EdkLogger.error( 'build', RESOURCE_NOT_AVAILABLE, "Value of GUID [%s] is not found in" % Sku.VariableGuid, ExtraData=PackageList + "\n\t(used with %s.%s from module %s)" \ % (Guid, Name, str(Module)), File=self.MetaFile ) # override PCD settings with module specific setting if Module in self.Platform.Modules: PlatformModule = self.Platform.Modules[str(Module)] for Key in PlatformModule.Pcds: if self.BuildOptionPcd: for pcd in self.BuildOptionPcd: (TokenSpaceGuidCName, TokenCName, FieldName, pcdvalue, _) = pcd if (TokenCName, TokenSpaceGuidCName) == Key and FieldName =="": PlatformModule.Pcds[Key].DefaultValue = pcdvalue PlatformModule.Pcds[Key].PcdValueFromComm = pcdvalue break Flag = False if Key in Pcds: ToPcd = Pcds[Key] Flag = True elif Key in self.MixedPcd: for PcdItem in self.MixedPcd[Key]: if PcdItem in Pcds: ToPcd = Pcds[PcdItem] Flag = True break if Flag: self._OverridePcd(ToPcd, PlatformModule.Pcds[Key], Module, Msg="DSC Components Module scoped PCD section", Library=Library) # use PCD value to calculate the MaxDatumSize when it is not specified for Name, Guid in Pcds: Pcd = Pcds[Name, Guid] if Pcd.DatumType == TAB_VOID and not Pcd.MaxDatumSize: Pcd.MaxSizeUserSet = None Value = Pcd.DefaultValue if not Value: Pcd.MaxDatumSize = '1' elif Value[0] == 'L': Pcd.MaxDatumSize = str((len(Value) - 2) * 2) elif Value[0] == '{': Pcd.MaxDatumSize = str(len(Value.split(','))) else: Pcd.MaxDatumSize = str(len(Value) - 1) return list(Pcds.values()) @cached_property def Pcds(self): PlatformPcdData = self.DataPipe.Get("PLA_PCD") # for pcd in PlatformPcdData: # for skuid in pcd.SkuInfoList: # pcd.SkuInfoList[skuid] = self.CreateSkuInfoFromDict(pcd.SkuInfoList[skuid]) return {(pcddata.TokenCName,pcddata.TokenSpaceGuidCName):pcddata for pcddata in PlatformPcdData} def CreateSkuInfoFromDict(self,SkuInfoDict): return SkuInfoClass( SkuInfoDict.get("SkuIdName"), SkuInfoDict.get("SkuId"), SkuInfoDict.get("VariableName"), SkuInfoDict.get("VariableGuid"), SkuInfoDict.get("VariableOffset"), SkuInfoDict.get("HiiDefaultValue"), SkuInfoDict.get("VpdOffset"), SkuInfoDict.get("DefaultValue"), SkuInfoDict.get("VariableGuidValue"), SkuInfoDict.get("VariableAttribute",""), SkuInfoDict.get("DefaultStore",None) ) @cached_property def MixedPcd(self): return self.DataPipe.Get("MixedPcd") @cached_property def _GuidDict(self): RetVal = self.DataPipe.Get("GuidDict") if RetVal is None: RetVal = {} return RetVal @cached_property def BuildOptionPcd(self): return self.DataPipe.Get("BuildOptPcd") def ApplyBuildOption(self,module): PlatformOptions = self.DataPipe.Get("PLA_BO") ModuleBuildOptions = self.DataPipe.Get("MOL_BO") ModuleOptionFromDsc = ModuleBuildOptions.get((module.MetaFile.File,module.MetaFile.Root)) if ModuleOptionFromDsc: ModuleTypeOptions, PlatformModuleOptions = ModuleOptionFromDsc["ModuleTypeOptions"],ModuleOptionFromDsc["PlatformModuleOptions"] else: ModuleTypeOptions, PlatformModuleOptions = {}, {} ToolDefinition = self.DataPipe.Get("TOOLDEF") ModuleOptions = self._ExpandBuildOption(module.BuildOptions) BuildRuleOrder = None for Options in [ToolDefinition, ModuleOptions, PlatformOptions, ModuleTypeOptions, PlatformModuleOptions]: for Tool in Options: for Attr in Options[Tool]: if Attr == TAB_TOD_DEFINES_BUILDRULEORDER: BuildRuleOrder = Options[Tool][Attr] AllTools = set(list(ModuleOptions.keys()) + list(PlatformOptions.keys()) + list(PlatformModuleOptions.keys()) + list(ModuleTypeOptions.keys()) + list(ToolDefinition.keys())) BuildOptions = defaultdict(lambda: defaultdict(str)) for Tool in AllTools: for Options in [ToolDefinition, ModuleOptions, PlatformOptions, ModuleTypeOptions, PlatformModuleOptions]: if Tool not in Options: continue for Attr in Options[Tool]: # # Do not generate it in Makefile # if Attr == TAB_TOD_DEFINES_BUILDRULEORDER: continue Value = Options[Tool][Attr] # check if override is indicated if Value.startswith('='): BuildOptions[Tool][Attr] = mws.handleWsMacro(Value[1:]) else: if Attr != 'PATH': BuildOptions[Tool][Attr] += " " + mws.handleWsMacro(Value) else: BuildOptions[Tool][Attr] = mws.handleWsMacro(Value) return BuildOptions, BuildRuleOrder def ApplyLibraryInstance(self,module): alldeps = self.DataPipe.Get("DEPS") if alldeps is None: alldeps = {} mod_libs = alldeps.get((module.MetaFile.File,module.MetaFile.Root,module.Arch,module.MetaFile.Path),[]) retVal = [] for (file_path,root,arch,abs_path) in mod_libs: libMetaFile = PathClass(file_path,root) libMetaFile.OriginalPath = PathClass(file_path,root) libMetaFile.Path = abs_path retVal.append(self.Wa.BuildDatabase[libMetaFile, arch, self.Target,self.ToolChain]) return retVal ## Parse build_rule.txt in Conf Directory. # # @retval BuildRule object # @cached_property def BuildRule(self): WInfo = self.DataPipe.Get("P_Info") RetVal = WInfo.get("BuildRuleFile") if RetVal._FileVersion == "": RetVal._FileVersion = AutoGenReqBuildRuleVerNum return RetVal