## @file | |
# Create makefile for MS nmake and GNU make | |
# | |
# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> | |
# SPDX-License-Identifier: BSD-2-Clause-Patent | |
# | |
## Import Modules | |
# | |
from __future__ import print_function | |
from __future__ import absolute_import | |
import os.path as path | |
import hashlib | |
from collections import defaultdict | |
from GenFds.FdfParser import FdfParser | |
from Workspace.WorkspaceCommon import GetModuleLibInstances | |
from AutoGen import GenMake | |
from AutoGen.AutoGen import AutoGen | |
from AutoGen.PlatformAutoGen import PlatformAutoGen | |
from AutoGen.BuildEngine import gDefaultBuildRuleFile | |
from Common.ToolDefClassObject import gDefaultToolsDefFile | |
from Common.StringUtils import NormPath | |
from Common.BuildToolError import * | |
from Common.DataType import * | |
from Common.Misc import * | |
import json | |
## Regular expression for splitting Dependency Expression string into tokens | |
gDepexTokenPattern = re.compile("(\(|\)|\w+| \S+\.inf)") | |
## Regular expression for match: PCD(xxxx.yyy) | |
gPCDAsGuidPattern = re.compile(r"^PCD\(.+\..+\)$") | |
## Workspace AutoGen class | |
# | |
# This class is used mainly to control the whole platform build for different | |
# architecture. This class will generate top level makefile. | |
# | |
class WorkspaceAutoGen(AutoGen): | |
# call super().__init__ then call the worker function with different parameter count | |
def __init__(self, Workspace, MetaFile, Target, Toolchain, Arch, *args, **kwargs): | |
if not hasattr(self, "_Init"): | |
self._InitWorker(Workspace, MetaFile, Target, Toolchain, Arch, *args, **kwargs) | |
self._Init = True | |
## Initialize WorkspaceAutoGen | |
# | |
# @param WorkspaceDir Root directory of workspace | |
# @param ActivePlatform Meta-file of active platform | |
# @param Target Build target | |
# @param Toolchain Tool chain name | |
# @param ArchList List of architecture of current build | |
# @param MetaFileDb Database containing meta-files | |
# @param BuildConfig Configuration of build | |
# @param ToolDefinition Tool chain definitions | |
# @param FlashDefinitionFile File of flash definition | |
# @param Fds FD list to be generated | |
# @param Fvs FV list to be generated | |
# @param Caps Capsule list to be generated | |
# @param SkuId SKU id from command line | |
# | |
def _InitWorker(self, WorkspaceDir, ActivePlatform, Target, Toolchain, ArchList, MetaFileDb, | |
BuildConfig, ToolDefinition, FlashDefinitionFile='', Fds=None, Fvs=None, Caps=None, SkuId='', UniFlag=None, | |
Progress=None, BuildModule=None): | |
self.BuildDatabase = MetaFileDb | |
self.MetaFile = ActivePlatform | |
self.WorkspaceDir = WorkspaceDir | |
self.Platform = self.BuildDatabase[self.MetaFile, TAB_ARCH_COMMON, Target, Toolchain] | |
GlobalData.gActivePlatform = self.Platform | |
self.BuildTarget = Target | |
self.ToolChain = Toolchain | |
self.ArchList = ArchList | |
self.SkuId = SkuId | |
self.UniFlag = UniFlag | |
self.TargetTxt = BuildConfig | |
self.ToolDef = ToolDefinition | |
self.FdfFile = FlashDefinitionFile | |
self.FdTargetList = Fds if Fds else [] | |
self.FvTargetList = Fvs if Fvs else [] | |
self.CapTargetList = Caps if Caps else [] | |
self.AutoGenObjectList = [] | |
self._GuidDict = {} | |
# there's many relative directory operations, so ... | |
os.chdir(self.WorkspaceDir) | |
self.MergeArch() | |
self.ValidateBuildTarget() | |
EdkLogger.info("") | |
if self.ArchList: | |
EdkLogger.info('%-16s = %s' % ("Architecture(s)", ' '.join(self.ArchList))) | |
EdkLogger.info('%-16s = %s' % ("Build target", self.BuildTarget)) | |
EdkLogger.info('%-16s = %s' % ("Toolchain", self.ToolChain)) | |
EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.Platform)) | |
if BuildModule: | |
EdkLogger.info('%-24s = %s' % ("Active Module", BuildModule)) | |
if self.FdfFile: | |
EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.FdfFile)) | |
EdkLogger.verbose("\nFLASH_DEFINITION = %s" % self.FdfFile) | |
if Progress: | |
Progress.Start("\nProcessing meta-data") | |
# | |
# Mark now build in AutoGen Phase | |
# | |
GlobalData.gAutoGenPhase = True | |
self.ProcessModuleFromPdf() | |
self.ProcessPcdType() | |
self.ProcessMixedPcd() | |
self.VerifyPcdsFromFDF() | |
self.CollectAllPcds() | |
for Pa in self.AutoGenObjectList: | |
Pa.FillData_LibConstPcd() | |
self.GeneratePkgLevelHash() | |
# | |
# Check PCDs token value conflict in each DEC file. | |
# | |
self._CheckAllPcdsTokenValueConflict() | |
# | |
# Check PCD type and definition between DSC and DEC | |
# | |
self._CheckPcdDefineAndType() | |
self.CreateBuildOptionsFile() | |
self.CreatePcdTokenNumberFile() | |
self.GeneratePlatformLevelHash() | |
# | |
# Merge Arch | |
# | |
def MergeArch(self): | |
if not self.ArchList: | |
ArchList = set(self.Platform.SupArchList) | |
else: | |
ArchList = set(self.ArchList) & set(self.Platform.SupArchList) | |
if not ArchList: | |
EdkLogger.error("build", PARAMETER_INVALID, | |
ExtraData = "Invalid ARCH specified. [Valid ARCH: %s]" % (" ".join(self.Platform.SupArchList))) | |
elif self.ArchList and len(ArchList) != len(self.ArchList): | |
SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList)) | |
EdkLogger.verbose("\nArch [%s] is ignored because the platform supports [%s] only!" | |
% (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList))) | |
self.ArchList = tuple(ArchList) | |
# Validate build target | |
def ValidateBuildTarget(self): | |
if self.BuildTarget not in self.Platform.BuildTargets: | |
EdkLogger.error("build", PARAMETER_INVALID, | |
ExtraData="Build target [%s] is not supported by the platform. [Valid target: %s]" | |
% (self.BuildTarget, " ".join(self.Platform.BuildTargets))) | |
@cached_property | |
def FdfProfile(self): | |
if not self.FdfFile: | |
self.FdfFile = self.Platform.FlashDefinition | |
FdfProfile = None | |
if self.FdfFile: | |
Fdf = FdfParser(self.FdfFile.Path) | |
Fdf.ParseFile() | |
GlobalData.gFdfParser = Fdf | |
if Fdf.CurrentFdName and Fdf.CurrentFdName in Fdf.Profile.FdDict: | |
FdDict = Fdf.Profile.FdDict[Fdf.CurrentFdName] | |
for FdRegion in FdDict.RegionList: | |
if str(FdRegion.RegionType) == 'FILE' and self.Platform.VpdToolGuid in str(FdRegion.RegionDataList): | |
if int(FdRegion.Offset) % 8 != 0: | |
EdkLogger.error("build", FORMAT_INVALID, 'The VPD Base Address %s must be 8-byte aligned.' % (FdRegion.Offset)) | |
FdfProfile = Fdf.Profile | |
else: | |
if self.FdTargetList: | |
EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdTargetList)) | |
self.FdTargetList = [] | |
if self.FvTargetList: | |
EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvTargetList)) | |
self.FvTargetList = [] | |
if self.CapTargetList: | |
EdkLogger.info("No flash definition file found. Capsule [%s] will be ignored." % " ".join(self.CapTargetList)) | |
self.CapTargetList = [] | |
return FdfProfile | |
def ProcessModuleFromPdf(self): | |
if self.FdfProfile: | |
for fvname in self.FvTargetList: | |
if fvname.upper() not in self.FdfProfile.FvDict: | |
EdkLogger.error("build", OPTION_VALUE_INVALID, | |
"No such an FV in FDF file: %s" % fvname) | |
# In DSC file may use FILE_GUID to override the module, then in the Platform.Modules use FILE_GUIDmodule.inf as key, | |
# but the path (self.MetaFile.Path) is the real path | |
for key in self.FdfProfile.InfDict: | |
if key == 'ArchTBD': | |
MetaFile_cache = defaultdict(set) | |
for Arch in self.ArchList: | |
Current_Platform_cache = self.BuildDatabase[self.MetaFile, Arch, self.BuildTarget, self.ToolChain] | |
for Pkey in Current_Platform_cache.Modules: | |
MetaFile_cache[Arch].add(Current_Platform_cache.Modules[Pkey].MetaFile) | |
for Inf in self.FdfProfile.InfDict[key]: | |
ModuleFile = PathClass(NormPath(Inf), GlobalData.gWorkspace, Arch) | |
for Arch in self.ArchList: | |
if ModuleFile in MetaFile_cache[Arch]: | |
break | |
else: | |
ModuleData = self.BuildDatabase[ModuleFile, Arch, self.BuildTarget, self.ToolChain] | |
if not ModuleData.IsBinaryModule: | |
EdkLogger.error('build', PARSER_ERROR, "Module %s NOT found in DSC file; Is it really a binary module?" % ModuleFile) | |
else: | |
for Arch in self.ArchList: | |
if Arch == key: | |
Platform = self.BuildDatabase[self.MetaFile, Arch, self.BuildTarget, self.ToolChain] | |
MetaFileList = set() | |
for Pkey in Platform.Modules: | |
MetaFileList.add(Platform.Modules[Pkey].MetaFile) | |
for Inf in self.FdfProfile.InfDict[key]: | |
ModuleFile = PathClass(NormPath(Inf), GlobalData.gWorkspace, Arch) | |
if ModuleFile in MetaFileList: | |
continue | |
ModuleData = self.BuildDatabase[ModuleFile, Arch, self.BuildTarget, self.ToolChain] | |
if not ModuleData.IsBinaryModule: | |
EdkLogger.error('build', PARSER_ERROR, "Module %s NOT found in DSC file; Is it really a binary module?" % ModuleFile) | |
# parse FDF file to get PCDs in it, if any | |
def VerifyPcdsFromFDF(self): | |
if self.FdfProfile: | |
PcdSet = self.FdfProfile.PcdDict | |
self.VerifyPcdDeclearation(PcdSet) | |
def ProcessPcdType(self): | |
for Arch in self.ArchList: | |
Platform = self.BuildDatabase[self.MetaFile, Arch, self.BuildTarget, self.ToolChain] | |
Platform.Pcds | |
# generate the SourcePcdDict and BinaryPcdDict | |
Libs = [] | |
for BuildData in list(self.BuildDatabase._CACHE_.values()): | |
if BuildData.Arch != Arch: | |
continue | |
if BuildData.MetaFile.Ext == '.inf' and str(BuildData) in Platform.Modules : | |
Libs.extend(GetModuleLibInstances(BuildData, Platform, | |
self.BuildDatabase, | |
Arch, | |
self.BuildTarget, | |
self.ToolChain, | |
self.Platform.MetaFile, | |
EdkLogger | |
)) | |
for BuildData in list(self.BuildDatabase._CACHE_.values()): | |
if BuildData.Arch != Arch: | |
continue | |
if BuildData.MetaFile.Ext == '.inf': | |
for key in BuildData.Pcds: | |
if BuildData.Pcds[key].Pending: | |
if key in Platform.Pcds: | |
PcdInPlatform = Platform.Pcds[key] | |
if PcdInPlatform.Type: | |
BuildData.Pcds[key].Type = PcdInPlatform.Type | |
BuildData.Pcds[key].Pending = False | |
if BuildData.MetaFile in Platform.Modules: | |
PlatformModule = Platform.Modules[str(BuildData.MetaFile)] | |
if key in PlatformModule.Pcds: | |
PcdInPlatform = PlatformModule.Pcds[key] | |
if PcdInPlatform.Type: | |
BuildData.Pcds[key].Type = PcdInPlatform.Type | |
BuildData.Pcds[key].Pending = False | |
else: | |
#Pcd used in Library, Pcd Type from reference module if Pcd Type is Pending | |
if BuildData.Pcds[key].Pending: | |
if bool(BuildData.LibraryClass): | |
if BuildData in set(Libs): | |
ReferenceModules = BuildData.ReferenceModules | |
for ReferenceModule in ReferenceModules: | |
if ReferenceModule.MetaFile in Platform.Modules: | |
RefPlatformModule = Platform.Modules[str(ReferenceModule.MetaFile)] | |
if key in RefPlatformModule.Pcds: | |
PcdInReferenceModule = RefPlatformModule.Pcds[key] | |
if PcdInReferenceModule.Type: | |
BuildData.Pcds[key].Type = PcdInReferenceModule.Type | |
BuildData.Pcds[key].Pending = False | |
break | |
def ProcessMixedPcd(self): | |
for Arch in self.ArchList: | |
SourcePcdDict = {TAB_PCDS_DYNAMIC_EX:set(), TAB_PCDS_PATCHABLE_IN_MODULE:set(),TAB_PCDS_DYNAMIC:set(),TAB_PCDS_FIXED_AT_BUILD:set()} | |
BinaryPcdDict = {TAB_PCDS_DYNAMIC_EX:set(), TAB_PCDS_PATCHABLE_IN_MODULE:set()} | |
SourcePcdDict_Keys = SourcePcdDict.keys() | |
BinaryPcdDict_Keys = BinaryPcdDict.keys() | |
# generate the SourcePcdDict and BinaryPcdDict | |
for BuildData in list(self.BuildDatabase._CACHE_.values()): | |
if BuildData.Arch != Arch: | |
continue | |
if BuildData.MetaFile.Ext == '.inf': | |
for key in BuildData.Pcds: | |
if TAB_PCDS_DYNAMIC_EX in BuildData.Pcds[key].Type: | |
if BuildData.IsBinaryModule: | |
BinaryPcdDict[TAB_PCDS_DYNAMIC_EX].add((BuildData.Pcds[key].TokenCName, BuildData.Pcds[key].TokenSpaceGuidCName)) | |
else: | |
SourcePcdDict[TAB_PCDS_DYNAMIC_EX].add((BuildData.Pcds[key].TokenCName, BuildData.Pcds[key].TokenSpaceGuidCName)) | |
elif TAB_PCDS_PATCHABLE_IN_MODULE in BuildData.Pcds[key].Type: | |
if BuildData.MetaFile.Ext == '.inf': | |
if BuildData.IsBinaryModule: | |
BinaryPcdDict[TAB_PCDS_PATCHABLE_IN_MODULE].add((BuildData.Pcds[key].TokenCName, BuildData.Pcds[key].TokenSpaceGuidCName)) | |
else: | |
SourcePcdDict[TAB_PCDS_PATCHABLE_IN_MODULE].add((BuildData.Pcds[key].TokenCName, BuildData.Pcds[key].TokenSpaceGuidCName)) | |
elif TAB_PCDS_DYNAMIC in BuildData.Pcds[key].Type: | |
SourcePcdDict[TAB_PCDS_DYNAMIC].add((BuildData.Pcds[key].TokenCName, BuildData.Pcds[key].TokenSpaceGuidCName)) | |
elif TAB_PCDS_FIXED_AT_BUILD in BuildData.Pcds[key].Type: | |
SourcePcdDict[TAB_PCDS_FIXED_AT_BUILD].add((BuildData.Pcds[key].TokenCName, BuildData.Pcds[key].TokenSpaceGuidCName)) | |
# | |
# A PCD can only use one type for all source modules | |
# | |
for i in SourcePcdDict_Keys: | |
for j in SourcePcdDict_Keys: | |
if i != j: | |
Intersections = SourcePcdDict[i].intersection(SourcePcdDict[j]) | |
if len(Intersections) > 0: | |
EdkLogger.error( | |
'build', | |
FORMAT_INVALID, | |
"Building modules from source INFs, following PCD use %s and %s access method. It must be corrected to use only one access method." % (i, j), | |
ExtraData='\n\t'.join(str(P[1]+'.'+P[0]) for P in Intersections) | |
) | |
# | |
# intersection the BinaryPCD for Mixed PCD | |
# | |
for i in BinaryPcdDict_Keys: | |
for j in BinaryPcdDict_Keys: | |
if i != j: | |
Intersections = BinaryPcdDict[i].intersection(BinaryPcdDict[j]) | |
for item in Intersections: | |
NewPcd1 = (item[0] + '_' + i, item[1]) | |
NewPcd2 = (item[0] + '_' + j, item[1]) | |
if item not in GlobalData.MixedPcd: | |
GlobalData.MixedPcd[item] = [NewPcd1, NewPcd2] | |
else: | |
if NewPcd1 not in GlobalData.MixedPcd[item]: | |
GlobalData.MixedPcd[item].append(NewPcd1) | |
if NewPcd2 not in GlobalData.MixedPcd[item]: | |
GlobalData.MixedPcd[item].append(NewPcd2) | |
# | |
# intersection the SourcePCD and BinaryPCD for Mixed PCD | |
# | |
for i in SourcePcdDict_Keys: | |
for j in BinaryPcdDict_Keys: | |
if i != j: | |
Intersections = SourcePcdDict[i].intersection(BinaryPcdDict[j]) | |
for item in Intersections: | |
NewPcd1 = (item[0] + '_' + i, item[1]) | |
NewPcd2 = (item[0] + '_' + j, item[1]) | |
if item not in GlobalData.MixedPcd: | |
GlobalData.MixedPcd[item] = [NewPcd1, NewPcd2] | |
else: | |
if NewPcd1 not in GlobalData.MixedPcd[item]: | |
GlobalData.MixedPcd[item].append(NewPcd1) | |
if NewPcd2 not in GlobalData.MixedPcd[item]: | |
GlobalData.MixedPcd[item].append(NewPcd2) | |
BuildData = self.BuildDatabase[self.MetaFile, Arch, self.BuildTarget, self.ToolChain] | |
for key in BuildData.Pcds: | |
for SinglePcd in GlobalData.MixedPcd: | |
if (BuildData.Pcds[key].TokenCName, BuildData.Pcds[key].TokenSpaceGuidCName) == SinglePcd: | |
for item in GlobalData.MixedPcd[SinglePcd]: | |
Pcd_Type = item[0].split('_')[-1] | |
if (Pcd_Type == BuildData.Pcds[key].Type) or (Pcd_Type == TAB_PCDS_DYNAMIC_EX and BuildData.Pcds[key].Type in PCD_DYNAMIC_EX_TYPE_SET) or \ | |
(Pcd_Type == TAB_PCDS_DYNAMIC and BuildData.Pcds[key].Type in PCD_DYNAMIC_TYPE_SET): | |
Value = BuildData.Pcds[key] | |
Value.TokenCName = BuildData.Pcds[key].TokenCName + '_' + Pcd_Type | |
if len(key) == 2: | |
newkey = (Value.TokenCName, key[1]) | |
elif len(key) == 3: | |
newkey = (Value.TokenCName, key[1], key[2]) | |
del BuildData.Pcds[key] | |
BuildData.Pcds[newkey] = Value | |
break | |
break | |
if self.FdfProfile: | |
PcdSet = self.FdfProfile.PcdDict | |
# handle the mixed pcd in FDF file | |
for key in PcdSet: | |
if key in GlobalData.MixedPcd: | |
Value = PcdSet[key] | |
del PcdSet[key] | |
for item in GlobalData.MixedPcd[key]: | |
PcdSet[item] = Value | |
#Collect package set information from INF of FDF | |
@cached_property | |
def PkgSet(self): | |
if not self.FdfFile: | |
self.FdfFile = self.Platform.FlashDefinition | |
if self.FdfFile: | |
ModuleList = self.FdfProfile.InfList | |
else: | |
ModuleList = [] | |
Pkgs = {} | |
for Arch in self.ArchList: | |
Platform = self.BuildDatabase[self.MetaFile, Arch, self.BuildTarget, self.ToolChain] | |
PkgSet = set() | |
for mb in [self.BuildDatabase[m, Arch, self.BuildTarget, self.ToolChain] for m in Platform.Modules]: | |
PkgSet.update(mb.Packages) | |
for Inf in ModuleList: | |
ModuleFile = PathClass(NormPath(Inf), GlobalData.gWorkspace, Arch) | |
if ModuleFile in Platform.Modules: | |
continue | |
ModuleData = self.BuildDatabase[ModuleFile, Arch, self.BuildTarget, self.ToolChain] | |
PkgSet.update(ModuleData.Packages) | |
PkgSet.update(Platform.Packages) | |
Pkgs[Arch] = list(PkgSet) | |
return Pkgs | |
def VerifyPcdDeclearation(self,PcdSet): | |
for Arch in self.ArchList: | |
Platform = self.BuildDatabase[self.MetaFile, Arch, self.BuildTarget, self.ToolChain] | |
Pkgs = self.PkgSet[Arch] | |
DecPcds = set() | |
DecPcdsKey = set() | |
for Pkg in Pkgs: | |
for Pcd in Pkg.Pcds: | |
DecPcds.add((Pcd[0], Pcd[1])) | |
DecPcdsKey.add((Pcd[0], Pcd[1], Pcd[2])) | |
Platform.SkuName = self.SkuId | |
for Name, Guid,Fileds in PcdSet: | |
if (Name, Guid) not in DecPcds: | |
EdkLogger.error( | |
'build', | |
PARSER_ERROR, | |
"PCD (%s.%s) used in FDF is not declared in DEC files." % (Guid, Name), | |
File = self.FdfProfile.PcdFileLineDict[Name, Guid, Fileds][0], | |
Line = self.FdfProfile.PcdFileLineDict[Name, Guid, Fileds][1] | |
) | |
else: | |
# Check whether Dynamic or DynamicEx PCD used in FDF file. If used, build break and give a error message. | |
if (Name, Guid, TAB_PCDS_FIXED_AT_BUILD) in DecPcdsKey \ | |
or (Name, Guid, TAB_PCDS_PATCHABLE_IN_MODULE) in DecPcdsKey \ | |
or (Name, Guid, TAB_PCDS_FEATURE_FLAG) in DecPcdsKey: | |
continue | |
elif (Name, Guid, TAB_PCDS_DYNAMIC) in DecPcdsKey or (Name, Guid, TAB_PCDS_DYNAMIC_EX) in DecPcdsKey: | |
EdkLogger.error( | |
'build', | |
PARSER_ERROR, | |
"Using Dynamic or DynamicEx type of PCD [%s.%s] in FDF file is not allowed." % (Guid, Name), | |
File = self.FdfProfile.PcdFileLineDict[Name, Guid, Fileds][0], | |
Line = self.FdfProfile.PcdFileLineDict[Name, Guid, Fileds][1] | |
) | |
def CollectAllPcds(self): | |
for Arch in self.ArchList: | |
Pa = PlatformAutoGen(self, self.MetaFile, self.BuildTarget, self.ToolChain, Arch) | |
# | |
# Explicitly collect platform's dynamic PCDs | |
# | |
Pa.CollectPlatformDynamicPcds() | |
Pa.CollectFixedAtBuildPcds() | |
self.AutoGenObjectList.append(Pa) | |
# We need to calculate the PcdTokenNumber after all Arch Pcds are collected. | |
for Arch in self.ArchList: | |
#Pcd TokenNumber | |
Pa = PlatformAutoGen(self, self.MetaFile, self.BuildTarget, self.ToolChain, Arch) | |
self.UpdateModuleDataPipe(Arch, {"PCD_TNUM":Pa.PcdTokenNumber}) | |
def UpdateModuleDataPipe(self,arch, attr_dict): | |
for (Target, Toolchain, Arch, MetaFile) in AutoGen.Cache(): | |
if Arch != arch: | |
continue | |
try: | |
AutoGen.Cache()[(Target, Toolchain, Arch, MetaFile)].DataPipe.DataContainer = attr_dict | |
except Exception: | |
pass | |
# | |
# Generate Package level hash value | |
# | |
def GeneratePkgLevelHash(self): | |
for Arch in self.ArchList: | |
GlobalData.gPackageHash = {} | |
if GlobalData.gUseHashCache: | |
for Pkg in self.PkgSet[Arch]: | |
self._GenPkgLevelHash(Pkg) | |
def CreateBuildOptionsFile(self): | |
# | |
# Create BuildOptions Macro & PCD metafile, also add the Active Platform and FDF file. | |
# | |
content = 'gCommandLineDefines: ' | |
content += str(GlobalData.gCommandLineDefines) | |
content += TAB_LINE_BREAK | |
content += 'BuildOptionPcd: ' | |
content += str(GlobalData.BuildOptionPcd) | |
content += TAB_LINE_BREAK | |
content += 'Active Platform: ' | |
content += str(self.Platform) | |
content += TAB_LINE_BREAK | |
if self.FdfFile: | |
content += 'Flash Image Definition: ' | |
content += str(self.FdfFile) | |
content += TAB_LINE_BREAK | |
SaveFileOnChange(os.path.join(self.BuildDir, 'BuildOptions'), content, False) | |
def CreatePcdTokenNumberFile(self): | |
# | |
# Create PcdToken Number file for Dynamic/DynamicEx Pcd. | |
# | |
PcdTokenNumber = 'PcdTokenNumber: ' | |
Pa = self.AutoGenObjectList[0] | |
if Pa.PcdTokenNumber: | |
if Pa.DynamicPcdList: | |
for Pcd in Pa.DynamicPcdList: | |
PcdTokenNumber += TAB_LINE_BREAK | |
PcdTokenNumber += str((Pcd.TokenCName, Pcd.TokenSpaceGuidCName)) | |
PcdTokenNumber += ' : ' | |
PcdTokenNumber += str(Pa.PcdTokenNumber[Pcd.TokenCName, Pcd.TokenSpaceGuidCName]) | |
SaveFileOnChange(os.path.join(self.BuildDir, 'PcdTokenNumber'), PcdTokenNumber, False) | |
def GeneratePlatformLevelHash(self): | |
# | |
# Get set of workspace metafiles | |
# | |
AllWorkSpaceMetaFiles = self._GetMetaFiles(self.BuildTarget, self.ToolChain) | |
AllWorkSpaceMetaFileList = sorted(AllWorkSpaceMetaFiles, key=lambda x: str(x)) | |
# | |
# Retrieve latest modified time of all metafiles | |
# | |
SrcTimeStamp = 0 | |
for f in AllWorkSpaceMetaFiles: | |
if os.stat(f)[8] > SrcTimeStamp: | |
SrcTimeStamp = os.stat(f)[8] | |
self._SrcTimeStamp = SrcTimeStamp | |
if GlobalData.gUseHashCache: | |
FileList = [] | |
m = hashlib.md5() | |
for file in AllWorkSpaceMetaFileList: | |
if file.endswith('.dec'): | |
continue | |
f = open(file, 'rb') | |
Content = f.read() | |
f.close() | |
m.update(Content) | |
FileList.append((str(file), hashlib.md5(Content).hexdigest())) | |
HashDir = path.join(self.BuildDir, "Hash_Platform") | |
HashFile = path.join(HashDir, 'Platform.hash.' + m.hexdigest()) | |
SaveFileOnChange(HashFile, m.hexdigest(), False) | |
HashChainFile = path.join(HashDir, 'Platform.hashchain.' + m.hexdigest()) | |
GlobalData.gPlatformHashFile = HashChainFile | |
try: | |
with open(HashChainFile, 'w') as f: | |
json.dump(FileList, f, indent=2) | |
except: | |
EdkLogger.quiet("[cache warning]: fail to save hashchain file:%s" % HashChainFile) | |
if GlobalData.gBinCacheDest: | |
# Copy platform hash files to cache destination | |
FileDir = path.join(GlobalData.gBinCacheDest, self.OutputDir, self.BuildTarget + "_" + self.ToolChain, "Hash_Platform") | |
CacheFileDir = FileDir | |
CreateDirectory(CacheFileDir) | |
CopyFileOnChange(HashFile, CacheFileDir) | |
CopyFileOnChange(HashChainFile, CacheFileDir) | |
# | |
# Write metafile list to build directory | |
# | |
AutoGenFilePath = os.path.join(self.BuildDir, 'AutoGen') | |
if os.path.exists (AutoGenFilePath): | |
os.remove(AutoGenFilePath) | |
if not os.path.exists(self.BuildDir): | |
os.makedirs(self.BuildDir) | |
with open(os.path.join(self.BuildDir, 'AutoGen'), 'w+') as file: | |
for f in AllWorkSpaceMetaFileList: | |
print(f, file=file) | |
return True | |
def _GenPkgLevelHash(self, Pkg): | |
if Pkg.PackageName in GlobalData.gPackageHash: | |
return | |
PkgDir = os.path.join(self.BuildDir, Pkg.Arch, "Hash_Pkg", Pkg.PackageName) | |
CreateDirectory(PkgDir) | |
FileList = [] | |
m = hashlib.md5() | |
# Get .dec file's hash value | |
f = open(Pkg.MetaFile.Path, 'rb') | |
Content = f.read() | |
f.close() | |
m.update(Content) | |
FileList.append((str(Pkg.MetaFile.Path), hashlib.md5(Content).hexdigest())) | |
# Get include files hash value | |
if Pkg.Includes: | |
for inc in sorted(Pkg.Includes, key=lambda x: str(x)): | |
for Root, Dirs, Files in os.walk(str(inc)): | |
for File in sorted(Files): | |
File_Path = os.path.join(Root, File) | |
f = open(File_Path, 'rb') | |
Content = f.read() | |
f.close() | |
m.update(Content) | |
FileList.append((str(File_Path), hashlib.md5(Content).hexdigest())) | |
GlobalData.gPackageHash[Pkg.PackageName] = m.hexdigest() | |
HashDir = PkgDir | |
HashFile = path.join(HashDir, Pkg.PackageName + '.hash.' + m.hexdigest()) | |
SaveFileOnChange(HashFile, m.hexdigest(), False) | |
HashChainFile = path.join(HashDir, Pkg.PackageName + '.hashchain.' + m.hexdigest()) | |
GlobalData.gPackageHashFile[(Pkg.PackageName, Pkg.Arch)] = HashChainFile | |
try: | |
with open(HashChainFile, 'w') as f: | |
json.dump(FileList, f, indent=2) | |
except: | |
EdkLogger.quiet("[cache warning]: fail to save hashchain file:%s" % HashChainFile) | |
if GlobalData.gBinCacheDest: | |
# Copy Pkg hash files to cache destination dir | |
FileDir = path.join(GlobalData.gBinCacheDest, self.OutputDir, self.BuildTarget + "_" + self.ToolChain, Pkg.Arch, "Hash_Pkg", Pkg.PackageName) | |
CacheFileDir = FileDir | |
CreateDirectory(CacheFileDir) | |
CopyFileOnChange(HashFile, CacheFileDir) | |
CopyFileOnChange(HashChainFile, CacheFileDir) | |
def _GetMetaFiles(self, Target, Toolchain): | |
AllWorkSpaceMetaFiles = set() | |
# | |
# add fdf | |
# | |
if self.FdfFile: | |
AllWorkSpaceMetaFiles.add (self.FdfFile.Path) | |
for f in GlobalData.gFdfParser.GetAllIncludedFile(): | |
AllWorkSpaceMetaFiles.add (f.FileName) | |
# | |
# add dsc | |
# | |
AllWorkSpaceMetaFiles.add(self.MetaFile.Path) | |
# | |
# add build_rule.txt & tools_def.txt | |
# | |
AllWorkSpaceMetaFiles.add(os.path.join(GlobalData.gConfDirectory, gDefaultBuildRuleFile)) | |
AllWorkSpaceMetaFiles.add(os.path.join(GlobalData.gConfDirectory, gDefaultToolsDefFile)) | |
# add BuildOption metafile | |
# | |
AllWorkSpaceMetaFiles.add(os.path.join(self.BuildDir, 'BuildOptions')) | |
# add PcdToken Number file for Dynamic/DynamicEx Pcd | |
# | |
AllWorkSpaceMetaFiles.add(os.path.join(self.BuildDir, 'PcdTokenNumber')) | |
for Pa in self.AutoGenObjectList: | |
AllWorkSpaceMetaFiles.add(Pa.ToolDefinitionFile) | |
for Arch in self.ArchList: | |
# | |
# add dec | |
# | |
for Package in PlatformAutoGen(self, self.MetaFile, Target, Toolchain, Arch).PackageList: | |
AllWorkSpaceMetaFiles.add(Package.MetaFile.Path) | |
# | |
# add included dsc | |
# | |
for filePath in self.BuildDatabase[self.MetaFile, Arch, Target, Toolchain]._RawData.IncludedFiles: | |
AllWorkSpaceMetaFiles.add(filePath.Path) | |
return AllWorkSpaceMetaFiles | |
def _CheckPcdDefineAndType(self): | |
PcdTypeSet = {TAB_PCDS_FIXED_AT_BUILD, | |
TAB_PCDS_PATCHABLE_IN_MODULE, | |
TAB_PCDS_FEATURE_FLAG, | |
TAB_PCDS_DYNAMIC, | |
TAB_PCDS_DYNAMIC_EX} | |
# This dict store PCDs which are not used by any modules with specified arches | |
UnusedPcd = OrderedDict() | |
for Pa in self.AutoGenObjectList: | |
# Key of DSC's Pcds dictionary is PcdCName, TokenSpaceGuid | |
for Pcd in Pa.Platform.Pcds: | |
PcdType = Pa.Platform.Pcds[Pcd].Type | |
# If no PCD type, this PCD comes from FDF | |
if not PcdType: | |
continue | |
# Try to remove Hii and Vpd suffix | |
if PcdType.startswith(TAB_PCDS_DYNAMIC_EX): | |
PcdType = TAB_PCDS_DYNAMIC_EX | |
elif PcdType.startswith(TAB_PCDS_DYNAMIC): | |
PcdType = TAB_PCDS_DYNAMIC | |
for Package in Pa.PackageList: | |
# Key of DEC's Pcds dictionary is PcdCName, TokenSpaceGuid, PcdType | |
if (Pcd[0], Pcd[1], PcdType) in Package.Pcds: | |
break | |
for Type in PcdTypeSet: | |
if (Pcd[0], Pcd[1], Type) in Package.Pcds: | |
EdkLogger.error( | |
'build', | |
FORMAT_INVALID, | |
"Type [%s] of PCD [%s.%s] in DSC file doesn't match the type [%s] defined in DEC file." \ | |
% (Pa.Platform.Pcds[Pcd].Type, Pcd[1], Pcd[0], Type), | |
ExtraData=None | |
) | |
return | |
else: | |
UnusedPcd.setdefault(Pcd, []).append(Pa.Arch) | |
for Pcd in UnusedPcd: | |
EdkLogger.warn( | |
'build', | |
"The PCD was not specified by any INF module in the platform for the given architecture.\n" | |
"\tPCD: [%s.%s]\n\tPlatform: [%s]\n\tArch: %s" | |
% (Pcd[1], Pcd[0], os.path.basename(str(self.MetaFile)), str(UnusedPcd[Pcd])), | |
ExtraData=None | |
) | |
def __repr__(self): | |
return "%s [%s]" % (self.MetaFile, ", ".join(self.ArchList)) | |
## Return the directory to store FV files | |
@cached_property | |
def FvDir(self): | |
return path.join(self.BuildDir, TAB_FV_DIRECTORY) | |
## Return the directory to store all intermediate and final files built | |
@cached_property | |
def BuildDir(self): | |
return self.AutoGenObjectList[0].BuildDir | |
## 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): | |
return self.AutoGenObjectList[0].ToolDefinition | |
## Return directory of platform makefile | |
# | |
# @retval string Makefile directory | |
# | |
@cached_property | |
def MakeFileDir(self): | |
return self.BuildDir | |
## Return build command string | |
# | |
# @retval string Build command string | |
# | |
@cached_property | |
def BuildCommand(self): | |
# BuildCommand should be all the same. So just get one from platform AutoGen | |
return self.AutoGenObjectList[0].BuildCommand | |
## Check the PCDs token value conflict in each DEC file. | |
# | |
# Will cause build break and raise error message while two PCDs conflict. | |
# | |
# @return None | |
# | |
def _CheckAllPcdsTokenValueConflict(self): | |
for Pa in self.AutoGenObjectList: | |
for Package in Pa.PackageList: | |
PcdList = list(Package.Pcds.values()) | |
PcdList.sort(key=lambda x: int(x.TokenValue, 0)) | |
Count = 0 | |
while (Count < len(PcdList) - 1) : | |
Item = PcdList[Count] | |
ItemNext = PcdList[Count + 1] | |
# | |
# Make sure in the same token space the TokenValue should be unique | |
# | |
if (int(Item.TokenValue, 0) == int(ItemNext.TokenValue, 0)): | |
SameTokenValuePcdList = [] | |
SameTokenValuePcdList.append(Item) | |
SameTokenValuePcdList.append(ItemNext) | |
RemainPcdListLength = len(PcdList) - Count - 2 | |
for ValueSameCount in range(RemainPcdListLength): | |
if int(PcdList[len(PcdList) - RemainPcdListLength + ValueSameCount].TokenValue, 0) == int(Item.TokenValue, 0): | |
SameTokenValuePcdList.append(PcdList[len(PcdList) - RemainPcdListLength + ValueSameCount]) | |
else: | |
break; | |
# | |
# Sort same token value PCD list with TokenGuid and TokenCName | |
# | |
SameTokenValuePcdList.sort(key=lambda x: "%s.%s" % (x.TokenSpaceGuidCName, x.TokenCName)) | |
SameTokenValuePcdListCount = 0 | |
while (SameTokenValuePcdListCount < len(SameTokenValuePcdList) - 1): | |
Flag = False | |
TemListItem = SameTokenValuePcdList[SameTokenValuePcdListCount] | |
TemListItemNext = SameTokenValuePcdList[SameTokenValuePcdListCount + 1] | |
if (TemListItem.TokenSpaceGuidCName == TemListItemNext.TokenSpaceGuidCName) and (TemListItem.TokenCName != TemListItemNext.TokenCName): | |
for PcdItem in GlobalData.MixedPcd: | |
if (TemListItem.TokenCName, TemListItem.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdItem] or \ | |
(TemListItemNext.TokenCName, TemListItemNext.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdItem]: | |
Flag = True | |
if not Flag: | |
EdkLogger.error( | |
'build', | |
FORMAT_INVALID, | |
"The TokenValue [%s] of PCD [%s.%s] is conflict with: [%s.%s] in %s"\ | |
% (TemListItem.TokenValue, TemListItem.TokenSpaceGuidCName, TemListItem.TokenCName, TemListItemNext.TokenSpaceGuidCName, TemListItemNext.TokenCName, Package), | |
ExtraData=None | |
) | |
SameTokenValuePcdListCount += 1 | |
Count += SameTokenValuePcdListCount | |
Count += 1 | |
PcdList = list(Package.Pcds.values()) | |
PcdList.sort(key=lambda x: "%s.%s" % (x.TokenSpaceGuidCName, x.TokenCName)) | |
Count = 0 | |
while (Count < len(PcdList) - 1) : | |
Item = PcdList[Count] | |
ItemNext = PcdList[Count + 1] | |
# | |
# Check PCDs with same TokenSpaceGuidCName.TokenCName have same token value as well. | |
# | |
if (Item.TokenSpaceGuidCName == ItemNext.TokenSpaceGuidCName) and (Item.TokenCName == ItemNext.TokenCName) and (int(Item.TokenValue, 0) != int(ItemNext.TokenValue, 0)): | |
EdkLogger.error( | |
'build', | |
FORMAT_INVALID, | |
"The TokenValue [%s] of PCD [%s.%s] in %s defined in two places should be same as well."\ | |
% (Item.TokenValue, Item.TokenSpaceGuidCName, Item.TokenCName, Package), | |
ExtraData=None | |
) | |
Count += 1 | |
## Generate fds command | |
@property | |
def GenFdsCommand(self): | |
return (GenMake.TopLevelMakefile(self)._TEMPLATE_.Replace(GenMake.TopLevelMakefile(self)._TemplateDict)).strip() | |
@property | |
def GenFdsCommandDict(self): | |
FdsCommandDict = {} | |
LogLevel = EdkLogger.GetLevel() | |
if LogLevel == EdkLogger.VERBOSE: | |
FdsCommandDict["verbose"] = True | |
elif LogLevel <= EdkLogger.DEBUG_9: | |
FdsCommandDict["debug"] = LogLevel - 1 | |
elif LogLevel == EdkLogger.QUIET: | |
FdsCommandDict["quiet"] = True | |
FdsCommandDict["GenfdsMultiThread"] = GlobalData.gEnableGenfdsMultiThread | |
if GlobalData.gIgnoreSource: | |
FdsCommandDict["IgnoreSources"] = True | |
FdsCommandDict["OptionPcd"] = [] | |
for pcd in GlobalData.BuildOptionPcd: | |
if pcd[2]: | |
pcdname = '.'.join(pcd[0:3]) | |
else: | |
pcdname = '.'.join(pcd[0:2]) | |
if pcd[3].startswith('{'): | |
FdsCommandDict["OptionPcd"].append(pcdname + '=' + 'H' + '"' + pcd[3] + '"') | |
else: | |
FdsCommandDict["OptionPcd"].append(pcdname + '=' + pcd[3]) | |
MacroList = [] | |
# macros passed to GenFds | |
MacroDict = {} | |
MacroDict.update(GlobalData.gGlobalDefines) | |
MacroDict.update(GlobalData.gCommandLineDefines) | |
for MacroName in MacroDict: | |
if MacroDict[MacroName] != "": | |
MacroList.append('"%s=%s"' % (MacroName, MacroDict[MacroName].replace('\\', '\\\\'))) | |
else: | |
MacroList.append('"%s"' % MacroName) | |
FdsCommandDict["macro"] = MacroList | |
FdsCommandDict["fdf_file"] = [self.FdfFile] | |
FdsCommandDict["build_target"] = self.BuildTarget | |
FdsCommandDict["toolchain_tag"] = self.ToolChain | |
FdsCommandDict["active_platform"] = str(self) | |
FdsCommandDict["conf_directory"] = GlobalData.gConfDirectory | |
FdsCommandDict["build_architecture_list"] = ','.join(self.ArchList) | |
FdsCommandDict["platform_build_directory"] = self.BuildDir | |
FdsCommandDict["fd"] = self.FdTargetList | |
FdsCommandDict["fv"] = self.FvTargetList | |
FdsCommandDict["cap"] = self.CapTargetList | |
return FdsCommandDict | |
## Create makefile for the platform and modules in it | |
# | |
# @param CreateDepsMakeFile Flag indicating if the makefile for | |
# modules will be created as well | |
# | |
def CreateMakeFile(self, CreateDepsMakeFile=False): | |
if not CreateDepsMakeFile: | |
return | |
for Pa in self.AutoGenObjectList: | |
Pa.CreateMakeFile(CreateDepsMakeFile) | |
## Create autogen code for platform and modules | |
# | |
# Since there's no autogen code for platform, this method will do nothing | |
# if CreateModuleCodeFile is set to False. | |
# | |
# @param CreateDepsCodeFile Flag indicating if creating module's | |
# autogen code file or not | |
# | |
def CreateCodeFile(self, CreateDepsCodeFile=False): | |
if not CreateDepsCodeFile: | |
return | |
for Pa in self.AutoGenObjectList: | |
Pa.CreateCodeFile(CreateDepsCodeFile) | |
## Create AsBuilt INF file the platform | |
# | |
def CreateAsBuiltInf(self): | |
return | |