From 52302d4dee589a5df43a464420c9fe68ba83937d Mon Sep 17 00:00:00 2001 From: lgao4 Date: Sun, 28 Feb 2010 23:39:39 +0000 Subject: Sync EDKII BaseTools to BaseTools project r1903. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10123 6f19259b-4bc3-4df7-8a09-765794883524 --- BaseTools/Source/Python/build/BuildReport.py | 1423 +++++++++++ BaseTools/Source/Python/build/__init__.py | 15 + BaseTools/Source/Python/build/build.py | 3320 +++++++++++++++----------- 3 files changed, 3311 insertions(+), 1447 deletions(-) create mode 100644 BaseTools/Source/Python/build/BuildReport.py (limited to 'BaseTools/Source/Python/build') diff --git a/BaseTools/Source/Python/build/BuildReport.py b/BaseTools/Source/Python/build/BuildReport.py new file mode 100644 index 0000000000..23e819e5ca --- /dev/null +++ b/BaseTools/Source/Python/build/BuildReport.py @@ -0,0 +1,1423 @@ +## @file +# Routines for generating build report. +# +# This module contains the functionality to generate build report after +# build all target completes successfully. +# +# Copyright (c) 2010, Intel Corporation +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# + +## Import Modules +# +import os +import re +import platform +import textwrap +import traceback +import sys +from datetime import datetime +from Common import EdkLogger +from Common.Misc import GuidStructureByteArrayToGuidString +from Common.Misc import GuidStructureStringToGuidString +from Common.InfClassObject import gComponentType2ModuleType +from Common.BuildToolError import FILE_OPEN_FAILURE +from Common.BuildToolError import FILE_WRITE_FAILURE +from Common.BuildToolError import CODE_ERROR + + +## Pattern to extract contents in EDK DXS files +gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOTALL) + +## Pattern to find total FV total size, occupied size in flash report intermediate file +gFvTotalSizePattern = re.compile(r"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)") +gFvTakenSizePattern = re.compile(r"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)") + +## Pattern to find module size and time stamp in module summary report intermediate file +gModuleSizePattern = re.compile(r"MODULE_SIZE = (\d+)") +gTimeStampPattern = re.compile(r"TIME_STAMP = (\d+)") + +## Pattern to find GUID value in flash description files +gPcdGuidPattern = re.compile(r"PCD\((\w+)[.](\w+)\)") + +## Pattern to collect offset, GUID value pair in the flash report intermediate file +gOffsetGuidPattern = re.compile(r"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)") + +## Pattern to find module base address and entry point in fixed flash map file +gModulePattern = r"\n[-\w]+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)" +gMapFileItemPattern = re.compile(gModulePattern % {"Address" : "(-?0[xX][0-9A-Fa-f]+)"}) + +## Pattern to find all module referenced header files in source files +gIncludePattern = re.compile(r'#include\s*["<]([^">]+)[">]') +gIncludePattern2 = re.compile(r"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]") + +## Pattern to find the entry point for EDK module using EDKII Glue library +gGlueLibEntryPoint = re.compile(r"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)") + +## Tags for section start, end and separator +gSectionStart = ">" + "=" * 118 + "<" +gSectionEnd = "<" + "=" * 118 + ">" + "\n" +gSectionSep = "=" * 120 + +## Tags for subsection start, end and separator +gSubSectionStart = ">" + "-" * 118 + "<" +gSubSectionEnd = "<" + "-" * 118 + ">" +gSubSectionSep = "-" * 120 + +## The look up table to map PCD type to pair of report display type and DEC type +gPcdTypeMap = { + 'FixedAtBuild' : ('FIXED', 'FixedAtBuild'), + 'PatchableInModule': ('PATCH', 'PatchableInModule'), + 'FeatureFlag' : ('FLAG', 'FeatureFlag'), + 'Dynamic' : ('DYN', 'Dynamic'), + 'DynamicHii' : ('DYNHII', 'Dynamic'), + 'DynamicVpd' : ('DYNVPD', 'Dynamic'), + 'DynamicEx' : ('DEX', 'Dynamic'), + 'DynamicExHii' : ('DEXHII', 'Dynamic'), + 'DynamicExVpd' : ('DEXVPD', 'Dynamic'), + } + +## The look up table to map module type to driver type +gDriverTypeMap = { + 'SEC' : '0x3 (SECURITY_CORE)', + 'PEI_CORE' : '0x4 (PEI_CORE)', + 'PEIM' : '0x6 (PEIM)', + 'DXE_CORE' : '0x5 (DXE_CORE)', + 'DXE_DRIVER' : '0x7 (DRIVER)', + 'DXE_SAL_DRIVER' : '0x7 (DRIVER)', + 'DXE_SMM_DRIVER' : '0x7 (DRIVER)', + 'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)', + 'UEFI_DRIVER' : '0x7 (DRIVER)', + 'UEFI_APPLICATION' : '0x9 (APPLICATION)', + 'SMM_CORE' : '0xD (SMM_CORE)', + 'SMM_DRIVER' : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers + } + +## +# Writes a string to the file object. +# +# This function writes a string to the file object and a new line is appended +# afterwards. It may optionally wraps the string for better readability. +# +# @File The file object to write +# @String The string to be written to the file +# @Wrapper Indicates whether to wrap the string +# +def FileWrite(File, String, Wrapper=False): + if Wrapper: + String = textwrap.fill(String, 120) + File.write(String + "\r\n") + +## +# Find all the header file that the module source directly includes. +# +# This function scans source code to find all header files the module may +# include. This is not accurate but very effective to find all the header +# file the module might include with #include statement. +# +# @Source The source file name +# @IncludePathList The list of include path to find the source file. +# @IncludeFiles The dictionary of current found include files. +# +def FindIncludeFiles(Source, IncludePathList, IncludeFiles): + FileContents = open(Source).read() + # + # Find header files with pattern #include "XXX.h" or #include + # + for Match in gIncludePattern.finditer(FileContents): + FileName = Match.group(1).strip() + for Dir in [os.path.dirname(Source)] + IncludePathList: + FullFileName = os.path.normpath(os.path.join(Dir, FileName)) + if os.path.exists(FullFileName): + IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName + break + + # + # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX) + # + for Match in gIncludePattern2.finditer(FileContents): + Key = Match.group(2) + Type = Match.group(1) + if "ARCH_PROTOCOL" in Type: + FileName = "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key} + elif "PROTOCOL" in Type: + FileName = "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key} + elif "PPI" in Type: + FileName = "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key} + elif "GUID" in Type: + FileName = "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key} + else: + continue + for Dir in IncludePathList: + FullFileName = os.path.normpath(os.path.join(Dir, FileName)) + if os.path.exists(FullFileName): + IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName + break + +## +# Reports library information +# +# This class reports the module library subsection in the build report file. +# +class LibraryReport(object): + ## + # Constructor function for class LibraryReport + # + # This constructor function generates LibraryReport object for + # a module. + # + # @param self The object pointer + # @param M Module context information + # + def __init__(self, M): + self.LibraryList = [] + if int(str(M.AutoGenVersion), 0) >= 0x00010005: + self._EdkIIModule = True + else: + self._EdkIIModule = False + + for Lib in M.DependentLibraryList: + LibInfPath = str(Lib) + LibClassList = Lib.LibraryClass[0].LibraryClass + LibConstructorList = Lib.ConstructorList + LibDesstructorList = Lib.DestructorList + LibDepexList = Lib.DepexExpression[M.Arch, M.ModuleType] + self.LibraryList.append((LibInfPath, LibClassList, LibConstructorList, LibDesstructorList, LibDepexList)) + + ## + # Generate report for module library information + # + # This function generates report for the module library. + # If the module is EDKII style one, the additional library class, library + # constructor/destructor and dependency expression may also be reported. + # + # @param self The object pointer + # @param File The file object for report + # + def GenerateReport(self, File): + FileWrite(File, gSubSectionStart) + FileWrite(File, "Library") + if len(self.LibraryList) > 0: + FileWrite(File, gSubSectionSep) + for LibraryItem in self.LibraryList: + LibInfPath = LibraryItem[0] + FileWrite(File, LibInfPath) + + # + # Report library class, library constructor and destructor for + # EDKII style module. + # + if self._EdkIIModule: + LibClass = LibraryItem[1] + EdkIILibInfo = "" + LibConstructor = " ".join(LibraryItem[2]) + if LibConstructor: + EdkIILibInfo += " C = " + LibConstructor + LibDestructor = " ".join(LibraryItem[3]) + if LibDestructor: + EdkIILibInfo += " D = " + LibConstructor + LibDepex = " ".join(LibraryItem[4]) + if LibDepex: + EdkIILibInfo += " Depex = " + LibDepex + if EdkIILibInfo: + FileWrite(File, "{%s: %s}" % (LibClass, EdkIILibInfo)) + else: + FileWrite(File, "{%s}" % LibClass) + + FileWrite(File, gSubSectionEnd) + +## +# Reports dependency expression information +# +# This class reports the module dependency expression subsection in the build report file. +# +class DepexReport(object): + ## + # Constructor function for class DepexReport + # + # This constructor function generates DepexReport object for + # a module. If the module source contains the DXS file (usually EDK + # style module), it uses the dependency in DXS file; otherwise, + # it uses the dependency expression from its own INF [Depex] section + # and then merges with the ones from its dependent library INF. + # + # @param self The object pointer + # @param M Module context information + # + def __init__(self, M): + self.Depex = "" + ModuleType = M.ModuleType + if not ModuleType: + ModuleType = gComponentType2ModuleType.get(M.ComponentType, "") + if ModuleType in ["SEC", "PEI_CORE", "DXE_CORE"]: + return + + for Source in M.SourceFileList: + if os.path.splitext(Source.Path)[1].lower() == ".dxs": + Match = gDxsDependencyPattern.search(open(Source.Path).read()) + if Match: + self.Depex = Match.group(1).strip() + self.Source = "DXS" + break + else: + self.Depex = M.DepexExpressionList.get(M.ModuleType, "") + self.ModuleDepex = " ".join(M.Module.DepexExpression[M.Arch, M.ModuleType]) + if not self.ModuleDepex: + self.ModuleDepex = "(None)" + + LibDepexList = [] + for Lib in M.DependentLibraryList: + LibDepex = " ".join(Lib.DepexExpression[M.Arch, M.ModuleType]).strip() + if LibDepex != "": + LibDepexList.append("(" + LibDepex + ")") + self.LibraryDepex = " AND ".join(LibDepexList) + if not self.LibraryDepex: + self.LibraryDepex = "(None)" + self.Source = "INF" + + ## + # Generate report for module dependency expression information + # + # This function generates report for the module dependency expression. + # + # @param self The object pointer + # @param File The file object for report + # + def GenerateReport(self, File): + if not self.Depex: + return + + FileWrite(File, gSubSectionStart) + FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source) + + if self.Source == "INF": + FileWrite(File, "%s" % self.Depex, True) + FileWrite(File, gSubSectionSep) + FileWrite(File, "From Module INF: %s" % self.ModuleDepex, True) + FileWrite(File, "From Library INF: %s" % self.LibraryDepex, True) + else: + FileWrite(File, "%s" % self.Depex) + FileWrite(File, gSubSectionEnd) + +## +# Reports dependency expression information +# +# This class reports the module build flags subsection in the build report file. +# +class BuildFlagsReport(object): + ## + # Constructor function for class BuildFlagsReport + # + # This constructor function generates BuildFlagsReport object for + # a module. It reports the build tool chain tag and all relevant + # build flags to build the module. + # + # @param self The object pointer + # @param M Module context information + # + def __init__(self, M): + BuildOptions = {} + # + # Add build flags according to source file extension so that + # irrelevant ones can be filtered out. + # + for Source in M.SourceFileList: + Ext = os.path.splitext(Source.File)[1].lower() + if Ext in [".c", ".cc", ".cpp"]: + BuildOptions["CC"] = 1 + elif Ext in [".s", ".asm"]: + BuildOptions["PP"] = 1 + BuildOptions["ASM"] = 1 + elif Ext in [".vfr"]: + BuildOptions["VFRPP"] = 1 + BuildOptions["VFR"] = 1 + elif Ext in [".dxs"]: + BuildOptions["APP"] = 1 + BuildOptions["CC"] = 1 + elif Ext in [".asl"]: + BuildOptions["ASLPP"] = 1 + BuildOptions["ASL"] = 1 + elif Ext in [".aslc"]: + BuildOptions["ASLCC"] = 1 + BuildOptions["ASLDLINK"] = 1 + BuildOptions["CC"] = 1 + elif Ext in [".asm16"]: + BuildOptions["ASMLINK"] = 1 + BuildOptions["SLINK"] = 1 + BuildOptions["DLINK"] = 1 + + # + # Save module build flags. + # + self.ToolChainTag = M.ToolChain + self.BuildFlags = {} + for Tool in BuildOptions: + self.BuildFlags[Tool + "_FLAGS"] = M.BuildOption.get(Tool, {}).get("FLAGS", "") + + ## + # Generate report for module build flags information + # + # This function generates report for the module build flags expression. + # + # @param self The object pointer + # @param File The file object for report + # + def GenerateReport(self, File): + FileWrite(File, gSubSectionStart) + FileWrite(File, "Build Flags") + FileWrite(File, "Tool Chain Tag: %s" % self.ToolChainTag) + for Tool in self.BuildFlags: + FileWrite(File, gSubSectionSep) + FileWrite(File, "%s = %s" % (Tool, self.BuildFlags[Tool]), True) + + FileWrite(File, gSubSectionEnd) + + +## +# Reports individual module information +# +# This class reports the module section in the build report file. +# It comprises of module summary, module PCD, library, dependency expression, +# build flags sections. +# +class ModuleReport(object): + ## + # Constructor function for class ModuleReport + # + # This constructor function generates ModuleReport object for + # a separate module in a platform build. + # + # @param self The object pointer + # @param M Module context information + # @param ReportType The kind of report items in the final report file + # + def __init__(self, M, ReportType): + self.ModuleName = M.Module.BaseName + self.ModuleInfPath = M.MetaFile.File + self.FileGuid = M.Guid + self.Size = 0 + self.BuildTimeStamp = None + self.DriverType = "" + ModuleType = M.ModuleType + if not ModuleType: + ModuleType = gComponentType2ModuleType.get(M.ComponentType, "") + # + # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER" + # + if ModuleType == "DXE_SMM_DRIVER": + PiSpec = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "0x00010000") + if int(PiSpec, 0) >= 0x0001000A: + ModuleType = "SMM_DRIVER" + self.DriverType = gDriverTypeMap.get(ModuleType, "") + self.UefiSpecVersion = M.Module.Specification.get("UEFI_SPECIFICATION_VERSION", "") + self.PiSpecVersion = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "") + self.PciDeviceId = M.Module.Defines.get("PCI_DEVICE_ID", "") + self.PciVendorId = M.Module.Defines.get("PCI_VENDOR_ID", "") + self.PciClassCode = M.Module.Defines.get("PCI_CLASS_CODE", "") + + self._BuildDir = M.BuildDir + self.ModulePcdSet = {} + if "PCD" in ReportType: + # + # Collect all module used PCD set: module INF referenced directly or indirectly. + # It also saves module INF default values of them in case they exist. + # + for Pcd in M.ModulePcdList + M.LibraryPcdList: + self.ModulePcdSet.setdefault((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Pcd.Type), (Pcd.InfDefaultValue, Pcd.DefaultValue)) + + self.LibraryReport = None + if "LIBRARY" in ReportType: + self.LibraryReport = LibraryReport(M) + + self.DepexReport = None + if "DEPEX" in ReportType: + self.DepexReport = DepexReport(M) + + if "BUILD_FLAGS" in ReportType: + self.BuildFlagsReport = BuildFlagsReport(M) + + + ## + # Generate report for module information + # + # This function generates report for separate module expression + # in a platform build. + # + # @param self The object pointer + # @param File The file object for report + # @param GlobalPcdReport The platform global PCD class object + # @param ReportType The kind of report items in the final report file + # + def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, ReportType): + FileWrite(File, gSectionStart) + + FwReportFileName = os.path.join(self._BuildDir, "DEBUG", self.ModuleName + ".txt") + if os.path.isfile(FwReportFileName): + try: + FileContents = open(FwReportFileName).read() + Match = gModuleSizePattern.search(FileContents) + if Match: + self.Size = int(Match.group(1)) + + Match = gTimeStampPattern.search(FileContents) + if Match: + self.BuildTimeStamp = datetime.fromtimestamp(int(Match.group(1))) + except IOError: + EdkLogger.warn(None, "Fail to read report file", FwReportFileName) + + FileWrite(File, "Module Summary") + FileWrite(File, "Module Name: %s" % self.ModuleName) + FileWrite(File, "Module INF Path: %s" % self.ModuleInfPath) + FileWrite(File, "File GUID: %s" % self.FileGuid) + if self.Size: + FileWrite(File, "Size: 0x%X (%.2fK)" % (self.Size, self.Size / 1024.0)) + if self.BuildTimeStamp: + FileWrite(File, "Build Time Stamp: %s" % self.BuildTimeStamp) + if self.DriverType: + FileWrite(File, "Driver Type: %s" % self.DriverType) + if self.UefiSpecVersion: + FileWrite(File, "UEFI Spec Version: %s" % self.UefiSpecVersion) + if self.PiSpecVersion: + FileWrite(File, "PI Spec Version: %s" % self.PiSpecVersion) + if self.PciDeviceId: + FileWrite(File, "PCI Device ID: %s" % self.PciDeviceId) + if self.PciVendorId: + FileWrite(File, "PCI Vendor ID: %s" % self.PciVendorId) + if self.PciClassCode: + FileWrite(File, "PCI Class Code: %s" % self.PciClassCode) + + FileWrite(File, gSectionSep) + + if "PCD" in ReportType: + GlobalPcdReport.GenerateReport(File, self.ModulePcdSet) + + if "LIBRARY" in ReportType: + self.LibraryReport.GenerateReport(File) + + if "DEPEX" in ReportType: + self.DepexReport.GenerateReport(File) + + if "BUILD_FLAGS" in ReportType: + self.BuildFlagsReport.GenerateReport(File) + + if "FIXED_ADDRESS" in ReportType and self.FileGuid: + GlobalPredictionReport.GenerateReport(File, self.FileGuid) + + FileWrite(File, gSectionEnd) + +## +# Reports platform and module PCD information +# +# This class reports the platform PCD section and module PCD subsection +# in the build report file. +# +class PcdReport(object): + ## + # Constructor function for class PcdReport + # + # This constructor function generates PcdReport object a platform build. + # It collects the whole PCD database from platform DSC files, platform + # flash description file and package DEC files. + # + # @param self The object pointer + # @param Wa Workspace context information + # + def __init__(self, Wa): + self.AllPcds = {} + self.MaxLen = 0 + if Wa.FdfProfile: + self.FdfPcdSet = Wa.FdfProfile.PcdDict + else: + self.FdfPcdSet = {} + + self.ModulePcdOverride = {} + for Pa in Wa.AutoGenObjectList: + # + # Collect all platform referenced PCDs and grouped them by PCD token space + # GUID C Names + # + for Pcd in Pa.AllPcdList: + PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, []) + if Pcd not in PcdList: + PcdList.append(Pcd) + if len(Pcd.TokenCName) > self.MaxLen: + self.MaxLen = len(Pcd.TokenCName) + + for Module in Pa.Platform.Modules.values(): + # + # Collect module override PCDs + # + for ModulePcd in Module.M.ModulePcdList + Module.M.LibraryPcdList: + TokenCName = ModulePcd.TokenCName + TokenSpaceGuid = ModulePcd.TokenSpaceGuidCName + ModuleDefault = ModulePcd.DefaultValue + ModulePath = os.path.basename(Module.M.MetaFile.File) + self.ModulePcdOverride.setdefault((TokenCName, TokenSpaceGuid), {})[ModulePath] = ModuleDefault + + + # + # Collect PCD DEC default value. + # + self.DecPcdDefault = {} + for Package in Wa.BuildDatabase.WorkspaceDb.PackageList: + for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds: + DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue + self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue) + # + # Collect PCDs defined in DSC common section + # + self.DscPcdDefault = {} + for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList: + for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds: + DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue + self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue + + ## + # Generate report for PCD information + # + # This function generates report for separate module expression + # in a platform build. + # + # @param self The object pointer + # @param File The file object for report + # @param ModulePcdSet Set of all PCDs referenced by module or None for + # platform PCD report + # @param DscOverridePcds Module DSC override PCDs set + # + def GenerateReport(self, File, ModulePcdSet): + if ModulePcdSet == None: + # + # For platform global PCD section + # + FileWrite(File, gSectionStart) + FileWrite(File, "Platform Configuration Database Report") + FileWrite(File, " *P - Platform scoped PCD override in DSC file") + FileWrite(File, " *F - Platform scoped PCD override in FDF file") + FileWrite(File, " *M - Module scoped PCD override in DSC file") + FileWrite(File, gSectionSep) + else: + # + # For module PCD sub-section + # + FileWrite(File, gSubSectionStart) + FileWrite(File, "PCD") + FileWrite(File, gSubSectionSep) + + for Key in self.AllPcds: + # + # Group PCD by their token space GUID C Name + # + First = True + for Type in self.AllPcds[Key]: + # + # Group PCD by their usage type + # + TypeName, DecType = gPcdTypeMap.get(Type, ("", Type)) + for Pcd in self.AllPcds[Key][Type]: + # + # Get PCD default value and their override relationship + # + DecDefaultValue = self.DecPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, DecType)) + DscDefaultValue = self.DscPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName)) + DscDefaultValue = self.FdfPcdSet.get((Pcd.TokenCName, Key), DscDefaultValue) + InfDefaultValue = None + + PcdValue = DecDefaultValue + if DscDefaultValue: + PcdValue = DscDefaultValue + if ModulePcdSet != None: + if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet: + continue + InfDefault, PcdValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type] + if InfDefault == "": + InfDefault = None + if First: + if ModulePcdSet == None: + FileWrite(File, "") + FileWrite(File, Key) + First = False + + + if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'): + PcdValueNumber = int(PcdValue.strip(), 0) + if DecDefaultValue == None: + DecMatch = True + else: + DecDefaultValueNumber = int(DecDefaultValue.strip(), 0) + DecMatch = (DecDefaultValueNumber == PcdValueNumber) + + if InfDefaultValue == None: + InfMatch = True + else: + InfDefaultValueNumber = int(InfDefaultValue.strip(), 0) + InfMatch = (InfDefaultValueNumber == PcdValueNumber) + + if DscDefaultValue == None: + DscMatch = True + else: + DscDefaultValueNumber = int(DscDefaultValue.strip(), 0) + DscMatch = (DscDefaultValueNumber == PcdValueNumber) + else: + if DecDefaultValue == None: + DecMatch = True + else: + DecMatch = (DecDefaultValue == PcdValue) + + if InfDefaultValue == None: + InfMatch = True + else: + InfMatch = (InfDefaultValue == PcdValue) + + if DscDefaultValue == None: + DscMatch = True + else: + DscMatch = (DscDefaultValue == PcdValue) + + # + # Report PCD item according to their override relationship + # + if DecMatch and InfMatch: + FileWrite(File, ' %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', PcdValue)) + else: + if DscMatch: + if (Pcd.TokenCName, Key) in self.FdfPcdSet: + FileWrite(File, ' *F %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', PcdValue)) + else: + FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', PcdValue)) + else: + FileWrite(File, ' *M %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', PcdValue)) + + if TypeName in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'): + for SkuInfo in Pcd.SkuInfoList.values(): + if TypeName in ('DYNHII', 'DEXHII'): + FileWrite(File, '%*s: %s: %s' % (self.MaxLen + 4, SkuInfo.VariableGuid, SkuInfo.VariableName, SkuInfo.VariableOffset)) + else: + FileWrite(File, '%*s' % (self.MaxLen + 4, SkuInfo.VpdOffset)) + + if not DscMatch and DscDefaultValue != None: + FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', DscDefaultValue)) + + if not InfMatch and InfDefaultValue != None: + FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue)) + + if not DecMatch and DecDefaultValue != None: + FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue)) + + if ModulePcdSet == None: + ModuleOverride = self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), {}) + for ModulePath in ModuleOverride: + ModuleDefault = ModuleOverride[ModulePath] + if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'): + ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0) + Match = (ModulePcdDefaultValueNumber == PcdValueNumber) + else: + Match = (ModuleDefault == PcdValue) + if Match: + continue + FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault)) + + if ModulePcdSet == None: + FileWrite(File, gSectionEnd) + else: + FileWrite(File, gSubSectionEnd) + + + +## +# Reports platform and module Prediction information +# +# This class reports the platform execution order prediction section and +# module load fixed address prediction subsection in the build report file. +# +class PredictionReport(object): + ## + # Constructor function for class PredictionReport + # + # This constructor function generates PredictionReport object for the platform. + # + # @param self: The object pointer + # @param Wa Workspace context information + # + def __init__(self, Wa): + self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map") + self._MapFileParsed = False + self._EotToolInvoked = False + self._FvDir = Wa.FvDir + self._EotDir = Wa.BuildDir + self._FfsEntryPoint = {} + self._GuidMap = {} + self._SourceList = [] + self.FixedMapDict = {} + self.ItemList = [] + self.MaxLen = 0 + + # + # Collect all platform reference source files and GUID C Name + # + for Pa in Wa.AutoGenObjectList: + for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList: + # + # Add module referenced source files + # + self._SourceList.append(str(Module)) + IncludeList = {} + for Source in Module.SourceFileList: + if os.path.splitext(str(Source))[1].lower() == ".c": + self._SourceList.append(" " + str(Source)) + FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList) + for IncludeFile in IncludeList.values(): + self._SourceList.append(" " + IncludeFile) + + for Guid in Module.PpiList: + self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid]) + for Guid in Module.ProtocolList: + self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid]) + for Guid in Module.GuidList: + self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid]) + + if Module.Guid and not Module.IsLibrary: + EntryPoint = " ".join(Module.Module.ModuleEntryPointList) + if int(str(Module.AutoGenVersion), 0) >= 0x00010005: + RealEntryPoint = "_ModuleEntryPoint" + else: + RealEntryPoint = EntryPoint + if EntryPoint == "_ModuleEntryPoint": + CCFlags = Module.BuildOption.get("CC", {}).get("FLAGS", "") + Match = gGlueLibEntryPoint.search(CCFlags) + if Match: + EntryPoint = Match.group(1) + + self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint) + + + # + # Collect platform firmware volume list as the input of EOT. + # + self._FvList = [] + if Wa.FdfProfile: + for Fd in Wa.FdfProfile.FdDict: + for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList: + if FdRegion.RegionType != "FV": + continue + for FvName in FdRegion.RegionDataList: + if FvName in self._FvList: + continue + self._FvList.append(FvName) + for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList: + for Section in Ffs.SectionList: + try: + for FvSection in Section.SectionList: + if FvSection.FvName in self._FvList: + continue + self._FvList.append(FvSection.FvName) + except AttributeError: + pass + + + ## + # Parse platform fixed address map files + # + # This function parses the platform final fixed address map file to get + # the database of predicted fixed address for module image base, entry point + # etc. + # + # @param self: The object pointer + # + def _ParseMapFile(self): + if self._MapFileParsed: + return + self._MapFileParsed = True + if os.path.isfile(self._MapFileName): + try: + FileContents = open(self._MapFileName).read() + for Match in gMapFileItemPattern.finditer(FileContents): + AddressType = Match.group(1) + BaseAddress = Match.group(2) + EntryPoint = Match.group(3) + Guid = Match.group(4).upper() + List = self.FixedMapDict.setdefault(Guid, []) + List.append((AddressType, BaseAddress, "*I")) + List.append((AddressType, EntryPoint, "*E")) + except: + EdkLogger.warn(None, "Cannot open file to read", self._MapFileName) + + ## + # Invokes EOT tool to get the predicted the execution order. + # + # This function invokes EOT tool to calculate the predicted dispatch order + # + # @param self: The object pointer + # + def _InvokeEotTool(self): + if self._EotToolInvoked: + return + + self._EotToolInvoked = True + FvFileList = [] + for FvName in self._FvList: + FvFile = os.path.join(self._FvDir, FvName + ".Fv") + if os.path.isfile(FvFile): + FvFileList.append(FvFile) + + if len(FvFileList) == 0: + return + # + # Write source file list and GUID file list to an intermediate file + # as the input for EOT tool and dispatch List as the output file + # from EOT tool. + # + SourceList = os.path.join(self._EotDir, "SourceFile.txt") + GuidList = os.path.join(self._EotDir, "GuidList.txt") + DispatchList = os.path.join(self._EotDir, "Dispatch.txt") + + TempFile = open(SourceList, "w+") + for Item in self._SourceList: + FileWrite(TempFile, Item) + TempFile.close() + TempFile = open(GuidList, "w+") + for Key in self._GuidMap: + FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key])) + TempFile.close() + + try: + from Eot.Eot import Eot + # + # Invoke EOT tool + # + Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList, + FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True) + + # + # Parse the output of EOT tool + # + for Line in open(DispatchList): + if len(Line.split()) < 4: + continue + (Guid, Phase, FfsName, FilePath) = Line.split() + Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0] + if len(Symbol) > self.MaxLen: + self.MaxLen = len(Symbol) + self.ItemList.append((Phase, Symbol, FilePath)) + except: + EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc())) + EdkLogger.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.") + + + ## + # Generate platform execution order report + # + # This function generates the predicted module execution order. + # + # @param self The object pointer + # @param File The file object for report + # + def _GenerateExecutionOrderReport(self, File): + self._InvokeEotTool() + if len(self.ItemList) == 0: + return + FileWrite(File, gSectionStart) + FileWrite(File, "Execution Order Prediction") + FileWrite(File, "*P PEI phase") + FileWrite(File, "*D DXE phase") + FileWrite(File, "*E Module INF entry point name") + FileWrite(File, "*N Module notification function name") + + FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path")) + FileWrite(File, gSectionSep) + for Item in self.ItemList: + FileWrite(File, "*%sE %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2])) + + FileWrite(File, gSectionStart) + + ## + # Generate Fixed Address report. + # + # This function generate the predicted fixed address report for a module + # specified by Guid. + # + # @param self The object pointer + # @param File The file object for report + # @param Guid The module Guid value. + # @param NotifyList The list of all notify function in a module + # + def _GenerateFixedAddressReport(self, File, Guid, NotifyList): + self._ParseMapFile() + FixedAddressList = self.FixedMapDict.get(Guid) + if not FixedAddressList: + return + + FileWrite(File, gSubSectionStart) + FileWrite(File, "Fixed Address Prediction") + FileWrite(File, "*I Image Loading Address") + FileWrite(File, "*E Entry Point Address") + FileWrite(File, "*N Notification Function Address") + FileWrite(File, "*F Flash Address") + FileWrite(File, "*M Memory Address") + FileWrite(File, "*S SMM RAM Offset") + FileWrite(File, "TOM Top of Memory") + + FileWrite(File, "Type Address Name") + FileWrite(File, gSubSectionSep) + for Item in FixedAddressList: + Type = Item[0] + Value = Item[1] + Symbol = Item[2] + if Symbol == "*I": + Name = "(Image Base)" + elif Symbol == "*E": + Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1] + elif Symbol in NotifyList: + Name = Symbol + Symbol = "*N" + else: + continue + + if "Flash" in Type: + Symbol += "F" + elif "Memory" in Type: + Symbol += "M" + else: + Symbol += "S" + + if Value[0] == "-": + Value = "TOM" + Value + + FileWrite(File, "%s %-16s %s" % (Symbol, Value, Name)) + + ## + # Generate report for the prediction part + # + # This function generate the predicted fixed address report for a module or + # predicted module execution order for a platform. + # If the input Guid is None, then, it generates the predicted module execution order; + # otherwise it generated the module fixed loading address for the module specified by + # Guid. + # + # @param self The object pointer + # @param File The file object for report + # @param Guid The module Guid value. + # + def GenerateReport(self, File, Guid): + if Guid: + self._GenerateFixedAddressReport(File, Guid.upper(), []) + else: + self._GenerateExecutionOrderReport(File) + +## +# Reports FD region information +# +# This class reports the FD subsection in the build report file. +# It collects region information of platform flash device. +# If the region is a firmware volume, it lists the set of modules +# and its space information; otherwise, it only lists its region name, +# base address and size in its sub-section header. +# If there are nesting FVs, the nested FVs will list immediate after +# this FD region subsection +# +class FdRegionReport(object): + ## + # Discover all the nested FV name list. + # + # This is an internal worker function to discover the all the nested FV information + # in the parent firmware volume. It uses deep first search algorithm recursively to + # find all the FV list name and append them to the list. + # + # @param self The object pointer + # @param FvName The name of current firmware file system + # @param Wa Workspace context information + # + def _DiscoverNestedFvList(self, FvName, Wa): + for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList: + for Section in Ffs.SectionList: + try: + for FvSection in Section.SectionList: + if FvSection.FvName in self.FvList: + continue + self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName + self.FvList.append(FvSection.FvName) + self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0) + self._DiscoverNestedFvList(FvSection.FvName, Wa) + except AttributeError: + pass + + ## + # Constructor function for class FdRegionReport + # + # This constructor function generates FdRegionReport object for a specified FdRegion. + # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware + # volume list. This function also collects GUID map in order to dump module identification + # in the final report. + # + # @param self: The object pointer + # @param FdRegion The current FdRegion object + # @param Wa Workspace context information + # + def __init__(self, FdRegion, Wa): + self.Type = FdRegion.RegionType + self.BaseAddress = FdRegion.Offset + self.Size = FdRegion.Size + self.FvList = [] + self.FvInfo = {} + self._GuidsDb = {} + self._FvDir = Wa.FvDir + + # + # If the input FdRegion is not a firmware volume, + # we are done. + # + if self.Type != "FV": + return + + # + # Find all nested FVs in the FdRegion + # + for FvName in FdRegion.RegionDataList: + if FvName in self.FvList: + continue + self.FvList.append(FvName) + self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size) + self._DiscoverNestedFvList(FvName, Wa) + + PlatformPcds = {} + + # + # Collect PCDs declared in DEC files. + # + for Package in Wa.BuildDatabase.WorkspaceDb.PackageList: + for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds: + DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue + PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue + # + # Collect PCDs defined in DSC common section + # + for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList: + for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds: + DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue + PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue + + # + # Add PEI and DXE a priori files GUIDs defined in PI specification. + # + self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori" + self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori" + # + # Add ACPI table storage file + # + self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage" + + for Pa in Wa.AutoGenObjectList: + for ModuleKey in Pa.Platform.Modules: + M = Pa.Platform.Modules[ModuleKey].M + InfPath = os.path.join(Wa.WorkspaceDir, M.MetaFile.File) + self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, InfPath) + + # + # Collect the GUID map in the FV firmware volume + # + for FvName in self.FvList: + for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList: + try: + # + # collect GUID map for binary EFI file in FDF file. + # + Guid = Ffs.NameGuid.upper() + Match = gPcdGuidPattern.match(Ffs.NameGuid) + if Match: + PcdTokenspace = Match.group(1) + PcdToken = Match.group(2) + if (PcdToken, PcdTokenspace) in PlatformPcds: + GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)] + Guid = GuidStructureByteArrayToGuidString(GuidValue).upper() + for Section in Ffs.SectionList: + try: + ModuleSectFile = os.path.join(Wa.WorkspaceDir, Section.SectFileName) + self._GuidsDb[Guid] = ModuleSectFile + except AttributeError: + pass + except AttributeError: + pass + + + ## + # Internal worker function to generate report for the FD region + # + # This internal worker function to generate report for the FD region. + # It the type is firmware volume, it lists offset and module identification. + # + # @param self The object pointer + # @param File The file object for report + # @param Title The title for the FD subsection + # @param BaseAddress The base address for the FD region + # @param Size The size of the FD region + # @param FvName The FV name if the FD region is a firmware volume + # + def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None): + FileWrite(File, gSubSectionStart) + FileWrite(File, Title) + FileWrite(File, "Type: %s" % Type) + FileWrite(File, "Base Address: 0x%X" % BaseAddress) + + if self.Type == "FV": + FvTotalSize = 0 + FvTakenSize = 0 + FvFreeSize = 0 + FvReportFileName = os.path.join(self._FvDir, FvName + ".fv.txt") + try: + # + # Collect size info in the firmware volume. + # + FvReport = open(FvReportFileName).read() + Match = gFvTotalSizePattern.search(FvReport) + if Match: + FvTotalSize = int(Match.group(1), 16) + Match = gFvTakenSizePattern.search(FvReport) + if Match: + FvTakenSize = int(Match.group(1), 16) + FvFreeSize = FvTotalSize - FvTakenSize + # + # Write size information to the report file. + # + FileWrite(File, "Size: 0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0)) + FileWrite(File, "Fv Name: %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize)) + FileWrite(File, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0)) + FileWrite(File, "Free Size: 0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0)) + FileWrite(File, "Offset Module") + FileWrite(File, gSubSectionSep) + # + # Write module offset and module identification to the report file. + # + OffsetInfo = {} + for Match in gOffsetGuidPattern.finditer(FvReport): + Guid = Match.group(2).upper() + OffsetInfo[Match.group(1)] = self._GuidsDb.get(Guid, Guid) + OffsetList = OffsetInfo.keys() + OffsetList.sort() + for Offset in OffsetList: + FileWrite (File, "%s %s" % (Offset, OffsetInfo[Offset])) + except IOError: + EdkLogger.warn(None, "Fail to read report file", FvReportFileName) + else: + FileWrite(File, "Size: 0x%X (%.0fK)" % (Size, Size / 1024.0)) + FileWrite(File, gSubSectionEnd) + + ## + # Generate report for the FD region + # + # This function generates report for the FD region. + # + # @param self The object pointer + # @param File The file object for report + # + def GenerateReport(self, File): + if (len(self.FvList) > 0): + for FvItem in self.FvList: + Info = self.FvInfo[FvItem] + self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem) + else: + self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size) + +## +# Reports FD information +# +# This class reports the FD section in the build report file. +# It collects flash device information for a platform. +# +class FdReport(object): + ## + # Constructor function for class FdReport + # + # This constructor function generates FdReport object for a specified + # firmware device. + # + # @param self The object pointer + # @param Fd The current Firmware device object + # @param Wa Workspace context information + # + def __init__(self, Fd, Wa): + self.FdName = Fd.FdUiName + self.BaseAddress = Fd.BaseAddress + self.Size = Fd.Size + self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList] + + ## + # Generate report for the firmware device. + # + # This function generates report for the firmware device. + # + # @param self The object pointer + # @param File The file object for report + # + def GenerateReport(self, File): + FileWrite(File, gSectionStart) + FileWrite(File, "Firmware Device (FD)") + FileWrite(File, "FD Name: %s" % self.FdName) + FileWrite(File, "Base Address: %s" % self.BaseAddress) + FileWrite(File, "Size: 0x%X (%.0fK)" % (self.Size, self.Size / 1024.0)) + if len(self.FdRegionList) > 0: + FileWrite(File, gSectionSep) + for FdRegionItem in self.FdRegionList: + FdRegionItem.GenerateReport(File) + + FileWrite(File, gSectionEnd) + + + +## +# Reports platform information +# +# This class reports the whole platform information +# +class PlatformReport(object): + ## + # Constructor function for class PlatformReport + # + # This constructor function generates PlatformReport object a platform build. + # It generates report for platform summary, flash, global PCDs and detailed + # module information for modules involved in platform build. + # + # @param self The object pointer + # @param Wa Workspace context information + # + def __init__(self, Wa, ReportType): + self._WorkspaceDir = Wa.WorkspaceDir + self.PlatformName = Wa.Name + self.PlatformDscPath = Wa.Platform + self.Architectures = " ".join(Wa.ArchList) + self.ToolChain = Wa.ToolChain + self.Target = Wa.BuildTarget + self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir) + self.BuildEnvironment = platform.platform() + + self.PcdReport = None + if "PCD" in ReportType: + self.PcdReport = PcdReport(Wa) + + self.FdReportList = [] + if "FLASH" in ReportType and Wa.FdfProfile: + for Fd in Wa.FdfProfile.FdDict: + self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa)) + + self.PredictionReport = None + if "FIXED_ADDRESS" in ReportType or "EXECUTION_ORDER" in ReportType: + self.PredictionReport = PredictionReport(Wa) + + self.ModuleReportList = [] + for Pa in Wa.AutoGenObjectList: + for ModuleKey in Pa.Platform.Modules: + self.ModuleReportList.append(ModuleReport(Pa.Platform.Modules[ModuleKey].M, ReportType)) + + + + ## + # Generate report for the whole platform. + # + # This function generates report for platform information. + # It comprises of platform summary, global PCD, flash and + # module list sections. + # + # @param self The object pointer + # @param File The file object for report + # @param BuildDuration The total time to build the modules + # @param ReportType The kind of report items in the final report file + # + def GenerateReport(self, File, BuildDuration, ReportType): + FileWrite(File, "Platform Summary") + FileWrite(File, "Platform Name: %s" % self.PlatformName) + FileWrite(File, "Platform DSC Path: %s" % self.PlatformDscPath) + FileWrite(File, "Architectures: %s" % self.Architectures) + FileWrite(File, "Tool Chain: %s" % self.ToolChain) + FileWrite(File, "Target: %s" % self.Target) + FileWrite(File, "Output Path: %s" % self.OutputPath) + FileWrite(File, "Build Environment: %s" % self.BuildEnvironment) + FileWrite(File, "Build Duration: %s" % BuildDuration) + FileWrite(File, "Report Content: %s" % ", ".join(ReportType)) + + if "PCD" in ReportType: + self.PcdReport.GenerateReport(File, None) + + if "FLASH" in ReportType: + for FdReportListItem in self.FdReportList: + FdReportListItem.GenerateReport(File) + + for ModuleReportItem in self.ModuleReportList: + ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, ReportType) + + if "EXECUTION_ORDER" in ReportType: + self.PredictionReport.GenerateReport(File, None) + +## BuildReport class +# +# This base class contain the routines to collect data and then +# applies certain format to the output report +# +class BuildReport(object): + ## + # Constructor function for class BuildReport + # + # This constructor function generates BuildReport object a platform build. + # It generates report for platform summary, flash, global PCDs and detailed + # module information for modules involved in platform build. + # + # @param self The object pointer + # @param ReportFile The file name to save report file + # @param ReportType The kind of report items in the final report file + # + def __init__(self, ReportFile, ReportType): + self.ReportFile = ReportFile + if ReportFile: + self.ReportList = [] + self.ReportType = [] + if ReportType: + for ReportTypeItem in ReportType: + if ReportTypeItem not in self.ReportType: + self.ReportType.append(ReportTypeItem) + else: + self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "FIXED_ADDRESS"] + ## + # Adds platform report to the list + # + # This function adds a platform report to the final report list. + # + # @param self The object pointer + # @param Wa Workspace context information + # + def AddPlatformReport(self, Wa): + if self.ReportFile: + self.ReportList.append(Wa) + + ## + # Generates the final report. + # + # This function generates platform build report. It invokes GenerateReport() + # method for every platform report in the list. + # + # @param self The object pointer + # @param BuildDuration The total time to build the modules + # + def GenerateReport(self, BuildDuration): + if self.ReportFile: + try: + File = open(self.ReportFile, "w+") + except IOError: + EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=self.ReportFile) + try: + for Wa in self.ReportList: + PlatformReport(Wa, self.ReportType).GenerateReport(File, BuildDuration, self.ReportType) + EdkLogger.quiet("Report successfully saved to %s" % os.path.abspath(self.ReportFile)) + except IOError: + EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile) + except: + EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report", ExtraData=self.ReportFile, RaiseError=False) + EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc())) + File.close() + +# This acts like the main() function for the script, unless it is 'import'ed into another script. +if __name__ == '__main__': + pass + diff --git a/BaseTools/Source/Python/build/__init__.py b/BaseTools/Source/Python/build/__init__.py index e69de29bb2..1c7f31a8bc 100644 --- a/BaseTools/Source/Python/build/__init__.py +++ b/BaseTools/Source/Python/build/__init__.py @@ -0,0 +1,15 @@ +## @file +# Python 'build' package initialization file. +# +# This file is required to make Python interpreter treat the directory +# as containing package. +# +# Copyright (c) 2007 - 2010, Intel Corporation
+# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# diff --git a/BaseTools/Source/Python/build/build.py b/BaseTools/Source/Python/build/build.py index 624d941b0f..9705097606 100644 --- a/BaseTools/Source/Python/build/build.py +++ b/BaseTools/Source/Python/build/build.py @@ -1,1447 +1,1873 @@ -## @file -# build a platform or a module -# -# Copyright (c) 2007, Intel Corporation -# -# All rights reserved. This program and the accompanying materials -# are licensed and made available under the terms and conditions of the BSD License -# which accompanies this distribution. The full text of the license may be found at -# http://opensource.org/licenses/bsd-license.php -# -# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. -# - -## -# Import Modules -# -import os -import re -import sys -import glob -import time -import platform -import traceback - -from threading import * -from optparse import OptionParser -from subprocess import * -from Common import Misc as Utils - -from Common.TargetTxtClassObject import * -from Common.ToolDefClassObject import * -from Common.DataType import * -from AutoGen.AutoGen import * -from Common.BuildToolError import * -from Workspace.WorkspaceDatabase import * - -import Common.EdkLogger -import Common.GlobalData as GlobalData - -# Version and Copyright -VersionNumber = "0.5" -__version__ = "%prog Version " + VersionNumber -__copyright__ = "Copyright (c) 2007, Intel Corporation All rights reserved." - -## standard targets of build command -gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run'] - -## build configuration file -gBuildConfiguration = "Conf/target.txt" -gBuildCacheDir = "Conf/.cache" -gToolsDefinition = "Conf/tools_def.txt" - -## Check environment PATH variable to make sure the specified tool is found -# -# If the tool is found in the PATH, then True is returned -# Otherwise, False is returned -# -def IsToolInPath(tool): - if os.environ.has_key('PATHEXT'): - extns = os.environ['PATHEXT'].split(os.path.pathsep) - else: - extns = ('',) - for pathDir in os.environ['PATH'].split(os.path.pathsep): - for ext in extns: - if os.path.exists(os.path.join(pathDir, tool + ext)): - return True - return False - -## Check environment variables -# -# Check environment variables that must be set for build. Currently they are -# -# WORKSPACE The directory all packages/platforms start from -# EDK_TOOLS_PATH The directory contains all tools needed by the build -# PATH $(EDK_TOOLS_PATH)/Bin/ must be set in PATH -# -# If any of above environment variable is not set or has error, the build -# will be broken. -# -def CheckEnvVariable(): - # check WORKSPACE - if "WORKSPACE" not in os.environ: - EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found", - ExtraData="WORKSPACE") - - WorkspaceDir = os.path.normcase(os.path.normpath(os.environ["WORKSPACE"])) - if not os.path.exists(WorkspaceDir): - EdkLogger.error("build", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData="%s" % WorkspaceDir) - elif ' ' in WorkspaceDir: - EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path", - ExtraData=WorkspaceDir) - os.environ["WORKSPACE"] = WorkspaceDir - - # - # Check EFI_SOURCE (R8 build convention). EDK_SOURCE will always point to ECP - # - os.environ["ECP_SOURCE"] = os.path.join(WorkspaceDir, GlobalData.gEdkCompatibilityPkg) - if "EFI_SOURCE" not in os.environ: - os.environ["EFI_SOURCE"] = os.environ["ECP_SOURCE"] - if "EDK_SOURCE" not in os.environ: - os.environ["EDK_SOURCE"] = os.environ["ECP_SOURCE"] - - # - # Unify case of characters on case-insensitive systems - # - EfiSourceDir = os.path.normcase(os.path.normpath(os.environ["EFI_SOURCE"])) - EdkSourceDir = os.path.normcase(os.path.normpath(os.environ["EDK_SOURCE"])) - EcpSourceDir = os.path.normcase(os.path.normpath(os.environ["ECP_SOURCE"])) - - os.environ["EFI_SOURCE"] = EfiSourceDir - os.environ["EDK_SOURCE"] = EdkSourceDir - os.environ["ECP_SOURCE"] = EcpSourceDir - os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"]) - - if not os.path.exists(EcpSourceDir): - EdkLogger.verbose("ECP_SOURCE = %s doesn't exist. R8 modules could not be built." % EcpSourceDir) - elif ' ' in EcpSourceDir: - EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in ECP_SOURCE path", - ExtraData=EcpSourceDir) - if not os.path.exists(EdkSourceDir): - if EdkSourceDir == EcpSourceDir: - EdkLogger.verbose("EDK_SOURCE = %s doesn't exist. R8 modules could not be built." % EdkSourceDir) - else: - EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE does not exist", - ExtraData=EdkSourceDir) - elif ' ' in EdkSourceDir: - EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EDK_SOURCE path", - ExtraData=EdkSourceDir) - if not os.path.exists(EfiSourceDir): - if EfiSourceDir == EcpSourceDir: - EdkLogger.verbose("EFI_SOURCE = %s doesn't exist. R8 modules could not be built." % EfiSourceDir) - else: - EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE does not exist", - ExtraData=EfiSourceDir) - elif ' ' in EfiSourceDir: - EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EFI_SOURCE path", - ExtraData=EfiSourceDir) - - # change absolute path to relative path to WORKSPACE - if EfiSourceDir.upper().find(WorkspaceDir.upper()) != 0: - EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE is not under WORKSPACE", - ExtraData="WORKSPACE = %s\n EFI_SOURCE = %s" % (WorkspaceDir, EfiSourceDir)) - if EdkSourceDir.upper().find(WorkspaceDir.upper()) != 0: - EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE is not under WORKSPACE", - ExtraData="WORKSPACE = %s\n EDK_SOURCE = %s" % (WorkspaceDir, EdkSourceDir)) - if EcpSourceDir.upper().find(WorkspaceDir.upper()) != 0: - EdkLogger.error("build", PARAMETER_INVALID, "ECP_SOURCE is not under WORKSPACE", - ExtraData="WORKSPACE = %s\n ECP_SOURCE = %s" % (WorkspaceDir, EcpSourceDir)) - - # check EDK_TOOLS_PATH - if "EDK_TOOLS_PATH" not in os.environ: - EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found", - ExtraData="EDK_TOOLS_PATH") - - # check PATH - if "PATH" not in os.environ: - EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found", - ExtraData="PATH") - - GlobalData.gWorkspace = WorkspaceDir - GlobalData.gEfiSource = EfiSourceDir - GlobalData.gEdkSource = EdkSourceDir - GlobalData.gEcpSource = EcpSourceDir - -## Get normalized file path -# -# Convert the path to be local format, and remove the WORKSPACE path at the -# beginning if the file path is given in full path. -# -# @param FilePath File path to be normalized -# @param Workspace Workspace path which the FilePath will be checked against -# -# @retval string The normalized file path -# -def NormFile(FilePath, Workspace): - # check if the path is absolute or relative - if os.path.isabs(FilePath): - FileFullPath = os.path.normpath(FilePath) - else: - FileFullPath = os.path.normpath(os.path.join(Workspace, FilePath)) - - # check if the file path exists or not - if not os.path.isfile(FileFullPath): - EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)" % FileFullPath) - - # remove workspace directory from the beginning part of the file path - if Workspace[-1] in ["\\", "/"]: - return FileFullPath[len(Workspace):] - else: - return FileFullPath[(len(Workspace) + 1):] - -## Get the output of an external program -# -# This is the entrance method of thread reading output of an external program and -# putting them in STDOUT/STDERR of current program. -# -# @param From The stream message read from -# @param To The stream message put on -# @param ExitFlag The flag used to indicate stopping reading -# -def ReadMessage(From, To, ExitFlag): - while True: - # read one line a time - Line = From.readline() - # empty string means "end" - if Line != None and Line != "": - To(Line.rstrip()) - else: - break - if ExitFlag.isSet(): - break - -## Launch an external program -# -# This method will call subprocess.Popen to execute an external program with -# given options in specified directory. Because of the dead-lock issue during -# redirecting output of the external program, threads are used to to do the -# redirection work. -# -# @param Command A list or string containing the call of the program -# @param WorkingDir The directory in which the program will be running -# -def LaunchCommand(Command, WorkingDir): - # if working directory doesn't exist, Popen() will raise an exception - if not os.path.isdir(WorkingDir): - EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir) - - Proc = None - EndOfProcedure = None - try: - # launch the command - Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1) - - # launch two threads to read the STDOUT and STDERR - EndOfProcedure = Event() - EndOfProcedure.clear() - if Proc.stdout: - StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure)) - StdOutThread.setName("STDOUT-Redirector") - StdOutThread.setDaemon(False) - StdOutThread.start() - - if Proc.stderr: - StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure)) - StdErrThread.setName("STDERR-Redirector") - StdErrThread.setDaemon(False) - StdErrThread.start() - - # waiting for program exit - Proc.wait() - except: # in case of aborting - # terminate the threads redirecting the program output - if EndOfProcedure != None: - EndOfProcedure.set() - if Proc == None: - if type(Command) != type(""): - Command = " ".join(Command) - EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir)) - - if Proc.stdout: - StdOutThread.join() - if Proc.stderr: - StdErrThread.join() - - # check the return code of the program - if Proc.returncode != 0: - if type(Command) != type(""): - Command = " ".join(Command) - EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir)) - -## The smallest unit that can be built in multi-thread build mode -# -# This is the base class of build unit. The "Obj" parameter must provide -# __str__(), __eq__() and __hash__() methods. Otherwise there could be build units -# missing build. -# -# Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects. -# -class BuildUnit: - ## The constructor - # - # @param self The object pointer - # @param Obj The object the build is working on - # @param Target The build target name, one of gSupportedTarget - # @param Dependency The BuildUnit(s) which must be completed in advance - # @param WorkingDir The directory build command starts in - # - def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."): - self.BuildObject = Obj - self.Dependency = Dependency - self.WorkingDir = WorkingDir - self.Target = Target - self.BuildCommand = BuildCommand - if BuildCommand == None or len(BuildCommand) == 0: - EdkLogger.error("build", OPTION_MISSING, "No build command found for", - ExtraData=str(Obj)) - - ## str() method - # - # It just returns the string representaion of self.BuildObject - # - # @param self The object pointer - # - def __str__(self): - return str(self.BuildObject) - - ## "==" operator method - # - # It just compares self.BuildObject with "Other". So self.BuildObject must - # provide its own __eq__() method. - # - # @param self The object pointer - # @param Other The other BuildUnit object compared to - # - def __eq__(self, Other): - return Other != None and self.BuildObject == Other.BuildObject \ - and self.BuildObject.Arch == Other.BuildObject.Arch - - ## hash() method - # - # It just returns the hash value of self.BuildObject which must be hashable. - # - # @param self The object pointer - # - def __hash__(self): - return hash(self.BuildObject) + hash(self.BuildObject.Arch) - - def __repr__(self): - return repr(self.BuildObject) - -## The smallest module unit that can be built by nmake/make command in multi-thread build mode -# -# This class is for module build by nmake/make build system. The "Obj" parameter -# must provide __str__(), __eq__() and __hash__() methods. Otherwise there could -# be make units missing build. -# -# Currently the "Obj" should be only ModuleAutoGen object. -# -class ModuleMakeUnit(BuildUnit): - ## The constructor - # - # @param self The object pointer - # @param Obj The ModuleAutoGen object the build is working on - # @param Target The build target name, one of gSupportedTarget - # - def __init__(self, Obj, Target): - Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList] - BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir) - if Target in [None, "", "all"]: - self.Target = "tbuild" - -## The smallest platform unit that can be built by nmake/make command in multi-thread build mode -# -# This class is for platform build by nmake/make build system. The "Obj" parameter -# must provide __str__(), __eq__() and __hash__() methods. Otherwise there could -# be make units missing build. -# -# Currently the "Obj" should be only PlatformAutoGen object. -# -class PlatformMakeUnit(BuildUnit): - ## The constructor - # - # @param self The object pointer - # @param Obj The PlatformAutoGen object the build is working on - # @param Target The build target name, one of gSupportedTarget - # - def __init__(self, Obj, Target): - Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList] - Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList]) - BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir) - -## The class representing the task of a module build or platform build -# -# This class manages the build tasks in multi-thread build mode. Its jobs include -# scheduling thread running, catching thread error, monitor the thread status, etc. -# -class BuildTask: - # queue for tasks waiting for schedule - _PendingQueue = sdict() - _PendingQueueLock = threading.Lock() - - # queue for tasks ready for running - _ReadyQueue = sdict() - _ReadyQueueLock = threading.Lock() - - # queue for run tasks - _RunningQueue = sdict() - _RunningQueueLock = threading.Lock() - - # queue containing all build tasks, in case duplicate build - _TaskQueue = sdict() - - # flag indicating error occurs in a running thread - _ErrorFlag = threading.Event() - _ErrorFlag.clear() - _ErrorMessage = "" - - # BoundedSemaphore object used to control the number of running threads - _Thread = None - - # flag indicating if the scheduler is started or not - _SchedulerStopped = threading.Event() - _SchedulerStopped.set() - - ## Start the task scheduler thread - # - # @param MaxThreadNumber The maximum thread number - # @param ExitFlag Flag used to end the scheduler - # - @staticmethod - def StartScheduler(MaxThreadNumber, ExitFlag): - SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag)) - SchedulerThread.setName("Build-Task-Scheduler") - SchedulerThread.setDaemon(False) - SchedulerThread.start() - # wait for the scheduler to be started, especially useful in Linux - while not BuildTask.IsOnGoing(): - time.sleep(0.01) - - ## Scheduler method - # - # @param MaxThreadNumber The maximum thread number - # @param ExitFlag Flag used to end the scheduler - # - @staticmethod - def Scheduler(MaxThreadNumber, ExitFlag): - BuildTask._SchedulerStopped.clear() - try: - # use BoundedSemaphore to control the maximum running threads - BuildTask._Thread = BoundedSemaphore(MaxThreadNumber) - # - # scheduling loop, which will exits when no pending/ready task and - # indicated to do so, or there's error in running thread - # - while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \ - or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet(): - EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)" - % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue))) - - # get all pending tasks - BuildTask._PendingQueueLock.acquire() - BuildObjectList = BuildTask._PendingQueue.keys() - # - # check if their dependency is resolved, and if true, move them - # into ready queue - # - for BuildObject in BuildObjectList: - Bt = BuildTask._PendingQueue[BuildObject] - if Bt.IsReady(): - BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject) - BuildTask._PendingQueueLock.release() - - # launch build thread until the maximum number of threads is reached - while not BuildTask._ErrorFlag.isSet(): - # empty ready queue, do nothing further - if len(BuildTask._ReadyQueue) == 0: - break - - # wait for active thread(s) exit - BuildTask._Thread.acquire(True) - - # start a new build thread - Bo = BuildTask._ReadyQueue.keys()[0] - Bt = BuildTask._ReadyQueue.pop(Bo) - - # move into running queue - BuildTask._RunningQueueLock.acquire() - BuildTask._RunningQueue[Bo] = Bt - BuildTask._RunningQueueLock.release() - - Bt.Start() - # avoid tense loop - time.sleep(0.01) - - # avoid tense loop - time.sleep(0.01) - - # wait for all running threads exit - if BuildTask._ErrorFlag.isSet(): - EdkLogger.quiet("\nWaiting for all build threads exit...") - # while not BuildTask._ErrorFlag.isSet() and \ - while len(BuildTask._RunningQueue) > 0: - EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue)) - EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()])) - # avoid tense loop - time.sleep(0.1) - except BaseException, X: - # - # TRICK: hide the output of threads left runing, so that the user can - # catch the error message easily - # - EdkLogger.SetLevel(EdkLogger.ERROR) - BuildTask._ErrorFlag.set() - BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X) - - BuildTask._PendingQueue.clear() - BuildTask._ReadyQueue.clear() - BuildTask._RunningQueue.clear() - BuildTask._TaskQueue.clear() - BuildTask._SchedulerStopped.set() - - ## Wait for all running method exit - # - @staticmethod - def WaitForComplete(): - BuildTask._SchedulerStopped.wait() - - ## Check if the scheduler is running or not - # - @staticmethod - def IsOnGoing(): - return not BuildTask._SchedulerStopped.isSet() - - ## Abort the build - @staticmethod - def Abort(): - if BuildTask.IsOnGoing(): - BuildTask._ErrorFlag.set() - BuildTask.WaitForComplete() - - ## Check if there's error in running thread - # - # Since the main thread cannot catch exceptions in other thread, we have to - # use threading.Event to communicate this formation to main thread. - # - @staticmethod - def HasError(): - return BuildTask._ErrorFlag.isSet() - - ## Get error message in running thread - # - # Since the main thread cannot catch exceptions in other thread, we have to - # use a static variable to communicate this message to main thread. - # - @staticmethod - def GetErrorMessage(): - return BuildTask._ErrorMessage - - ## Factory method to create a BuildTask object - # - # This method will check if a module is building or has been built. And if - # true, just return the associated BuildTask object in the _TaskQueue. If - # not, create and return a new BuildTask object. The new BuildTask object - # will be appended to the _PendingQueue for scheduling later. - # - # @param BuildItem A BuildUnit object representing a build object - # @param Dependency The dependent build object of BuildItem - # - @staticmethod - def New(BuildItem, Dependency=None): - if BuildItem in BuildTask._TaskQueue: - Bt = BuildTask._TaskQueue[BuildItem] - return Bt - - Bt = BuildTask() - Bt._Init(BuildItem, Dependency) - BuildTask._TaskQueue[BuildItem] = Bt - - BuildTask._PendingQueueLock.acquire() - BuildTask._PendingQueue[BuildItem] = Bt - BuildTask._PendingQueueLock.release() - - return Bt - - ## The real constructor of BuildTask - # - # @param BuildItem A BuildUnit object representing a build object - # @param Dependency The dependent build object of BuildItem - # - def _Init(self, BuildItem, Dependency=None): - self.BuildItem = BuildItem - - self.DependencyList = [] - if Dependency == None: - Dependency = BuildItem.Dependency - else: - Dependency.extend(BuildItem.Dependency) - self.AddDependency(Dependency) - # flag indicating build completes, used to avoid unnecessary re-build - self.CompleteFlag = False - - ## Check if all dependent build tasks are completed or not - # - def IsReady(self): - ReadyFlag = True - for Dep in self.DependencyList: - if Dep.CompleteFlag == True: - continue - ReadyFlag = False - break - - return ReadyFlag - - ## Add dependent build task - # - # @param Dependency The list of dependent build objects - # - def AddDependency(self, Dependency): - for Dep in Dependency: - self.DependencyList.append(BuildTask.New(Dep)) # BuildTask list - - ## The thread wrapper of LaunchCommand function - # - # @param Command A list or string contains the call of the command - # @param WorkingDir The directory in which the program will be running - # - def _CommandThread(self, Command, WorkingDir): - try: - LaunchCommand(Command, WorkingDir) - self.CompleteFlag = True - except: - # - # TRICK: hide the output of threads left runing, so that the user can - # catch the error message easily - # - if not BuildTask._ErrorFlag.isSet(): - GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject), - self.BuildItem.BuildObject.Arch, - self.BuildItem.BuildObject.ToolChain, - self.BuildItem.BuildObject.BuildTarget - ) - EdkLogger.SetLevel(EdkLogger.ERROR) - BuildTask._ErrorFlag.set() - BuildTask._ErrorMessage = "%s broken\n %s [%s]" % \ - (threading.currentThread().getName(), Command, WorkingDir) - # indicate there's a thread is available for another build task - BuildTask._RunningQueueLock.acquire() - BuildTask._RunningQueue.pop(self.BuildItem) - BuildTask._RunningQueueLock.release() - BuildTask._Thread.release() - - ## Start build task thread - # - def Start(self): - EdkLogger.quiet("Building ... %s" % repr(self.BuildItem)) - Command = self.BuildItem.BuildCommand + [self.BuildItem.Target] - self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir)) - self.BuildTread.setName("build thread") - self.BuildTread.setDaemon(False) - self.BuildTread.start() - -## The class implementing the EDK2 build process -# -# The build process includes: -# 1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf -# 2. Parse DSC file of active platform -# 3. Parse FDF file if any -# 4. Establish build database, including parse all other files (module, package) -# 5. Create AutoGen files (C code file, depex file, makefile) if necessary -# 6. Call build command -# -class Build(): - ## Constructor - # - # Constructor will load all necessary configurations, parse platform, modules - # and packages and the establish a database for AutoGen. - # - # @param Target The build command target, one of gSupportedTarget - # @param WorkspaceDir The directory of workspace - # @param Platform The DSC file of active platform - # @param Module The INF file of active module, if any - # @param Arch The Arch list of platform or module - # @param ToolChain The name list of toolchain - # @param BuildTarget The "DEBUG" or "RELEASE" build - # @param FlashDefinition The FDF file of active platform - # @param FdList=[] The FD names to be individually built - # @param FvList=[] The FV names to be individually built - # @param MakefileType The type of makefile (for MSFT make or GNU make) - # @param SilentMode Indicate multi-thread build mode - # @param ThreadNumber The maximum number of thread if in multi-thread build mode - # @param SkipAutoGen Skip AutoGen step - # @param Reparse Re-parse all meta files - # @param SkuId SKU id from command line - # - def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain, - BuildTarget, FlashDefinition, FdList=[], FvList=[], - MakefileType="nmake", SilentMode=False, ThreadNumber=2, - SkipAutoGen=False, Reparse=False, SkuId=None, - ReportFile=None, ReportType=None): - - self.WorkspaceDir = WorkspaceDir - self.Target = Target - self.PlatformFile = Platform - self.ModuleFile = Module - self.ArchList = Arch - self.ToolChainList = ToolChain - self.BuildTargetList= BuildTarget - self.Fdf = FlashDefinition - self.FdList = FdList - self.FvList = FvList - self.MakefileType = MakefileType - self.SilentMode = SilentMode - self.ThreadNumber = ThreadNumber - self.SkipAutoGen = SkipAutoGen - self.Reparse = Reparse - self.SkuId = SkuId - self.SpawnMode = True - self.ReportFile = ReportFile - if ReportType == None: - self.ReportType = ['ALL'] - else: - self.ReportType = ReportType - - self.TargetTxt = TargetTxtClassObject() - self.ToolDef = ToolDefClassObject() - self.Db = WorkspaceDatabase(None, GlobalData.gGlobalDefines, self.Reparse) - #self.Db = WorkspaceDatabase(None, {}, self.Reparse) - self.BuildDatabase = self.Db.BuildObject - self.Platform = None - - # print dot charater during doing some time-consuming work - self.Progress = Utils.Progressor() - - # parse target.txt, tools_def.txt, and platform file - #self.RestoreBuildData() - self.LoadConfiguration() - self.InitBuild() - - # print current build environment and configuration - EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"])) - EdkLogger.quiet("%-24s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"])) - EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"])) - EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"])) - EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"])) - - EdkLogger.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList))) - EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList))) - EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList))) - - EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile)) - - if self.Fdf != None and self.Fdf != "": - EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf)) - - if self.ModuleFile != None and self.ModuleFile != "": - EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile)) - - os.chdir(self.WorkspaceDir) - self.Progress.Start("\nProcessing meta-data") - - ## Load configuration - # - # This method will parse target.txt and get the build configurations. - # - def LoadConfiguration(self): - # - # Check target.txt and tools_def.txt and Init them - # - BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration)) - if os.path.isfile(BuildConfigurationFile) == True: - StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile) - - ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF] - if ToolDefinitionFile == '': - ToolDefinitionFile = gToolsDefinition - ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile)) - if os.path.isfile(ToolDefinitionFile) == True: - StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile) - else: - EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile) - else: - EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile) - - # if no ARCH given in command line, get it from target.txt - if self.ArchList == None or len(self.ArchList) == 0: - self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH] - - # if no build target given in command line, get it from target.txt - if self.BuildTargetList == None or len(self.BuildTargetList) == 0: - self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET] - - # if no tool chain given in command line, get it from target.txt - if self.ToolChainList == None or len(self.ToolChainList) == 0: - self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG] - if self.ToolChainList == None or len(self.ToolChainList) == 0: - EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n") - - # check if the tool chains are defined or not - NewToolChainList = [] - for ToolChain in self.ToolChainList: - if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]: - EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain) - else: - NewToolChainList.append(ToolChain) - # if no tool chain available, break the build - if len(NewToolChainList) == 0: - EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, - ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList)) - else: - self.ToolChainList = NewToolChainList - - if self.ThreadNumber == None: - self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER] - if self.ThreadNumber == '': - self.ThreadNumber = 0 - else: - self.ThreadNumber = int(self.ThreadNumber, 0) - - if self.ThreadNumber == 0: - self.ThreadNumber = 1 - - if not self.PlatformFile: - PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM] - if not PlatformFile: - # Try to find one in current directory - WorkingDirectory = os.getcwd() - FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc'))) - FileNum = len(FileList) - if FileNum >= 2: - EdkLogger.error("build", OPTION_MISSING, - ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory)) - elif FileNum == 1: - PlatformFile = FileList[0] - else: - EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, - ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n") - - self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir) - ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False) - if ErrorCode != 0: - EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) - - ## Initialize build configuration - # - # This method will parse DSC file and merge the configurations from - # command line and target.txt, then get the final build configurations. - # - def InitBuild(self): - ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc") - if ErrorCode != 0: - EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) - - # create metafile database - self.Db.InitDatabase() - - # we need information in platform description file to determine how to build - self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON'] - if not self.Fdf: - self.Fdf = self.Platform.FlashDefinition - - if self.SkuId == None or self.SkuId == '': - self.SkuId = self.Platform.SkuName - - # check FD/FV build target - if self.Fdf == None or self.Fdf == "": - if self.FdList != []: - EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList)) - self.FdList = [] - if self.FvList != []: - EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList)) - self.FvList = [] - else: - FdfParserObj = FdfParser(str(self.Fdf)) - FdfParserObj.ParseFile() - for fvname in self.FvList: - if fvname.upper() not in FdfParserObj.Profile.FvDict.keys(): - EdkLogger.error("build", OPTION_VALUE_INVALID, - "No such an FV in FDF file: %s" % fvname) - - # - # Merge Arch - # - if self.ArchList == None or len(self.ArchList) == 0: - ArchList = set(self.Platform.SupArchList) - else: - ArchList = set(self.ArchList) & set(self.Platform.SupArchList) - if len(ArchList) == 0: - EdkLogger.error("build", PARAMETER_INVALID, - ExtraData = "Active platform supports [%s] only, but [%s] is given." - % (" ".join(self.Platform.SupArchList), " ".join(self.ArchList))) - elif len(ArchList) != len(self.ArchList): - SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList)) - EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !" - % (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList), " ".join(self.ArchList))) - self.ArchList = tuple(ArchList) - - # Merge build target - if self.BuildTargetList == None or len(self.BuildTargetList) == 0: - BuildTargetList = self.Platform.BuildTargets - else: - BuildTargetList = list(set(self.BuildTargetList) & set(self.Platform.BuildTargets)) - if BuildTargetList == []: - EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given" - % (" ".join(self.Platform.BuildTargets), " ".join(self.BuildTargetList))) - self.BuildTargetList = BuildTargetList - - ## Build a module or platform - # - # Create autogen code and makfile for a module or platform, and the launch - # "make" command to build it - # - # @param Target The target of build command - # @param Platform The platform file - # @param Module The module file - # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE" - # @param ToolChain The name of toolchain to build - # @param Arch The arch of the module/platform - # @param CreateDepModuleCodeFile Flag used to indicate creating code - # for dependent modules/Libraries - # @param CreateDepModuleMakeFile Flag used to indicate creating makefile - # for dependent modules/Libraries - # - def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True): - if AutoGenObject == None: - return False - - # skip file generation for cleanxxx targets, run and fds target - if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']: - # for target which must generate AutoGen code and makefile - if not self.SkipAutoGen or Target == 'genc': - self.Progress.Start("Generating code") - AutoGenObject.CreateCodeFile(CreateDepsCodeFile) - self.Progress.Stop("done!") - if Target == "genc": - return True - - if not self.SkipAutoGen or Target == 'genmake': - self.Progress.Start("Generating makefile") - AutoGenObject.CreateMakeFile(CreateDepsMakeFile) - self.Progress.Stop("done!") - if Target == "genmake": - return True - else: - # always recreate top/platform makefile when clean, just in case of inconsistency - AutoGenObject.CreateCodeFile(False) - AutoGenObject.CreateMakeFile(False) - - if EdkLogger.GetLevel() == EdkLogger.QUIET: - EdkLogger.quiet("Building ... %s" % repr(AutoGenObject)) - - BuildCommand = AutoGenObject.BuildCommand - if BuildCommand == None or len(BuildCommand) == 0: - EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key) - - BuildCommand = BuildCommand + [Target] - LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir) - if Target == 'cleanall': - try: - #os.rmdir(AutoGenObject.BuildDir) - RemoveDirectory(AutoGenObject.BuildDir, True) - except WindowsError, X: - EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X)) - return True - - ## Build active platform for different build targets and different tool chains - # - def _BuildPlatform(self): - for BuildTarget in self.BuildTargetList: - for ToolChain in self.ToolChainList: - Wa = WorkspaceAutoGen( - self.WorkspaceDir, - self.Platform, - BuildTarget, - ToolChain, - self.ArchList, - self.BuildDatabase, - self.TargetTxt, - self.ToolDef, - self.Fdf, - self.FdList, - self.FvList, - self.SkuId, - self.ReportFile, - self.ReportType - ) - self.Progress.Stop("done!") - self._Build(self.Target, Wa) - - ## Build active module for different build targets, different tool chains and different archs - # - def _BuildModule(self): - for BuildTarget in self.BuildTargetList: - for ToolChain in self.ToolChainList: - # - # module build needs platform build information, so get platform - # AutoGen first - # - Wa = WorkspaceAutoGen( - self.WorkspaceDir, - self.Platform, - BuildTarget, - ToolChain, - self.ArchList, - self.BuildDatabase, - self.TargetTxt, - self.ToolDef, - self.Fdf, - self.FdList, - self.FvList, - self.SkuId, - self.ReportFile, - self.ReportType - ) - Wa.CreateMakeFile(False) - self.Progress.Stop("done!") - MaList = [] - for Arch in self.ArchList: - Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile) - if Ma == None: continue - MaList.append(Ma) - self._Build(self.Target, Ma) - if MaList == []: - EdkLogger.error( - 'build', - BUILD_ERROR, - "Module for [%s] is not a component of active platform."\ - " Please make sure that the ARCH and inf file path are"\ - " given in the same as in [%s]" %\ - (', '.join(self.ArchList), self.Platform), - ExtraData=self.ModuleFile - ) - - ## Build a platform in multi-thread mode - # - def _MultiThreadBuildPlatform(self): - for BuildTarget in self.BuildTargetList: - for ToolChain in self.ToolChainList: - Wa = WorkspaceAutoGen( - self.WorkspaceDir, - self.Platform, - BuildTarget, - ToolChain, - self.ArchList, - self.BuildDatabase, - self.TargetTxt, - self.ToolDef, - self.Fdf, - self.FdList, - self.FvList, - self.SkuId, - self.ReportFile, - self.ReportType - ) - Wa.CreateMakeFile(False) - - # multi-thread exit flag - ExitFlag = threading.Event() - ExitFlag.clear() - for Arch in self.ArchList: - Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch) - if Pa == None: - continue - for Module in Pa.Platform.Modules: - # Get ModuleAutoGen object to generate C code file and makefile - Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile) - if Ma == None: - continue - # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds' - if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']: - # for target which must generate AutoGen code and makefile - if not self.SkipAutoGen or self.Target == 'genc': - Ma.CreateCodeFile(True) - if self.Target == "genc": - continue - - if not self.SkipAutoGen or self.Target == 'genmake': - Ma.CreateMakeFile(True) - if self.Target == "genmake": - continue - self.Progress.Stop("done!") - # Generate build task for the module - Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target)) - # Break build if any build thread has error - if BuildTask.HasError(): - # we need a full version of makefile for platform - ExitFlag.set() - BuildTask.WaitForComplete() - Pa.CreateMakeFile(False) - EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule) - # Start task scheduler - if not BuildTask.IsOnGoing(): - BuildTask.StartScheduler(self.ThreadNumber, ExitFlag) - - # in case there's an interruption. we need a full version of makefile for platform - Pa.CreateMakeFile(False) - if BuildTask.HasError(): - EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule) - - # - # All modules have been put in build tasks queue. Tell task scheduler - # to exit if all tasks are completed - # - ExitFlag.set() - BuildTask.WaitForComplete() - - # - # Check for build error, and raise exception if one - # has been signaled. - # - if BuildTask.HasError(): - EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule) - - # Generate FD image if there's a FDF file found - if self.Fdf != '' and self.Target in ["", "all", "fds"]: - LaunchCommand(Wa.BuildCommand + ["fds"], Wa.MakeFileDir) - - ## Generate GuidedSectionTools.txt in the FV directories. - # - def CreateGuidedSectionToolsFile(self): - for Arch in self.ArchList: - for BuildTarget in self.BuildTargetList: - for ToolChain in self.ToolChainList: - FvDir = os.path.join( - self.WorkspaceDir, - self.Platform.OutputDirectory, - '_'.join((BuildTarget, ToolChain)), - 'FV' - ) - if not os.path.exists(FvDir): - continue - # Build up the list of supported architectures for this build - prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch) - - # Look through the tool definitions for GUIDed tools - guidAttribs = [] - for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems(): - if attrib.upper().endswith('_GUID'): - split = attrib.split('_') - thisPrefix = '_'.join(split[0:3]) + '_' - if thisPrefix == prefix: - guid = self.ToolDef.ToolsDefTxtDictionary[attrib] - guid = guid.lower() - toolName = split[3] - path = '_'.join(split[0:4]) + '_PATH' - path = self.ToolDef.ToolsDefTxtDictionary[path] - path = self.GetFullPathOfTool(path) - guidAttribs.append((guid, toolName, path)) - - # Write out GuidedSecTools.txt - toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt') - toolsFile = open(toolsFile, 'wt') - for guidedSectionTool in guidAttribs: - print >> toolsFile, ' '.join(guidedSectionTool) - toolsFile.close() - - ## Returns the full path of the tool. - # - def GetFullPathOfTool (self, tool): - if os.path.exists(tool): - return os.path.realpath(tool) - else: - # We need to search for the tool using the - # PATH environment variable. - for dirInPath in os.environ['PATH'].split(os.pathsep): - foundPath = os.path.join(dirInPath, tool) - if os.path.exists(foundPath): - return os.path.realpath(foundPath) - - # If the tool was not found in the path then we just return - # the input tool. - return tool - - ## Launch the module or platform build - # - def Launch(self): - if self.ModuleFile == None or self.ModuleFile == "": - if not self.SpawnMode or self.Target not in ["", "all"]: - self.SpawnMode = False - self._BuildPlatform() - else: - self._MultiThreadBuildPlatform() - self.CreateGuidedSectionToolsFile() - else: - self.SpawnMode = False - self._BuildModule() - - ## Do some clean-up works when error occurred - def Relinquish(self): - OldLogLevel = EdkLogger.GetLevel() - EdkLogger.SetLevel(EdkLogger.ERROR) - #self.DumpBuildData() - Utils.Progressor.Abort() - if self.SpawnMode == True: - BuildTask.Abort() - EdkLogger.SetLevel(OldLogLevel) - - def DumpBuildData(self): - CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir) - Utils.CreateDirectory(CacheDirectory) - Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache")) - Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase")) - - def RestoreBuildData(self): - FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache") - if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath): - Utils.gFileTimeStampCache = Utils.DataRestore(FilePath) - if Utils.gFileTimeStampCache == None: - Utils.gFileTimeStampCache = {} - - FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase") - if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath): - Utils.gDependencyDatabase = Utils.DataRestore(FilePath) - if Utils.gDependencyDatabase == None: - Utils.gDependencyDatabase = {} - -def ParseDefines(DefineList=[]): - DefineDict = {} - if DefineList != None: - for Define in DefineList: - DefineTokenList = Define.split("=", 1) - if len(DefineTokenList) == 1: - DefineDict[DefineTokenList[0]] = "" - else: - DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip() - return DefineDict - -gParamCheck = [] -def SingleCheckCallback(option, opt_str, value, parser): - if option not in gParamCheck: - setattr(parser.values, option.dest, value) - gParamCheck.append(option) - else: - parser.error("Option %s only allows one instance in command line!" % option) - -## Parse command line options -# -# Using standard Python module optparse to parse command line option of this tool. -# -# @retval Opt A optparse.Values object containing the parsed options -# @retval Args Target of build command -# -def MyOptionParser(): - Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]") - Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC','ARM'], dest="TargetArch", - help="ARCHS is one of list: IA32, X64, IPF, ARM or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.") - Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback, - help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.") - Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback, - help="Build the module specified by the INF file name argument.") - Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget", - help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.") - Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain", - help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.") - Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback, - help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.") - - Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback, - help="Build the platform using multi-threaded compiler. The value overrides target.txt's MAX_CONCURRENT_THREAD_NUMBER. Less than 2 will disable multi-thread builds.") - - Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback, - help="The name of the FDF file to use, which overrides the setting in the DSC file.") - Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[], - help="The name of FD to be generated. The name must be from [FD] section in FDF file.") - Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[], - help="The name of FV to be generated. The name must be from [FV] section in FDF file.") - - Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.") - Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.") - - Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.") - - # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]", - # help="Define global macro which can be used in DSC/DEC/INF files.") - - Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.") - Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.") - - Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode", - help="Make use of silent mode of (n)make.") - Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.") - Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\ - "including library instances selected, final dependency expression, "\ - "and warning messages, etc.") - Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.") - Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".") - - Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.") - Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['ALL','PCD',], dest="ReportType", - help="Flags that control the type of build report to generate. Must be one of: [ALL, PCD]. To specify more than one flag, repeat this option on the command line.") - - (Opt, Args)=Parser.parse_args() - return (Opt, Args) - -## Tool entrance method -# -# This method mainly dispatch specific methods per the command line options. -# If no error found, return zero value so the caller of this tool can know -# if it's executed successfully or not. -# -# @retval 0 Tool was successful -# @retval 1 Tool failed -# -def Main(): - StartTime = time.time() - - # Initialize log system - EdkLogger.Initialize() - - # - # Parse the options and args - # - (Option, Target) = MyOptionParser() - GlobalData.gOptions = Option - GlobalData.gCaseInsensitive = Option.CaseInsensitive - - # Set log level - if Option.verbose != None: - EdkLogger.SetLevel(EdkLogger.VERBOSE) - elif Option.quiet != None: - EdkLogger.SetLevel(EdkLogger.QUIET) - elif Option.debug != None: - EdkLogger.SetLevel(Option.debug + 1) - else: - EdkLogger.SetLevel(EdkLogger.INFO) - - if Option.LogFile != None: - EdkLogger.SetLogFile(Option.LogFile) - - if Option.WarningAsError == True: - EdkLogger.SetWarningAsError() - - if platform.platform().find("Windows") >= 0: - GlobalData.gIsWindows = True - else: - GlobalData.gIsWindows = False - - EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[%s]\n" % platform.platform()) - ReturnCode = 0 - MyBuild = None - try: - if len(Target) == 0: - Target = "all" - elif len(Target) >= 2: - EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.", - ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget))) - else: - Target = Target[0].lower() - - if Target not in gSupportedTarget: - EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target, - ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget))) - - GlobalData.gGlobalDefines = ParseDefines(Option.Macros) - # - # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH - # - CheckEnvVariable() - Workspace = os.getenv("WORKSPACE") - # - # Get files real name in workspace dir - # - GlobalData.gAllFiles = Utils.DirCache(Workspace) - - WorkingDirectory = os.getcwd() - if not Option.ModuleFile: - FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf'))) - FileNum = len(FileList) - if FileNum >= 2: - EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory), - ExtraData="Please use '-m ' switch to choose one.") - elif FileNum == 1: - Option.ModuleFile = NormFile(FileList[0], Workspace) - - if Option.ModuleFile: - Option.ModuleFile = PathClass(Option.ModuleFile, Workspace) - ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False) - if ErrorCode != 0: - EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) - - if Option.PlatformFile != None: - Option.PlatformFile = PathClass(Option.PlatformFile, Workspace) - ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False) - if ErrorCode != 0: - EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) - - if Option.FdfFile != None: - Option.FdfFile = PathClass(Option.FdfFile, Workspace) - ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False) - if ErrorCode != 0: - EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) - - MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile, - Option.TargetArch, Option.ToolChain, Option.BuildTarget, - Option.FdfFile, Option.RomImage, Option.FvImage, - None, Option.SilentMode, Option.ThreadNumber, - Option.SkipAutoGen, Option.Reparse, Option.SkuId, - Option.ReportFile, Option.ReportType) - MyBuild.Launch() - #MyBuild.DumpBuildData() - except FatalError, X: - if MyBuild != None: - # for multi-thread build exits safely - MyBuild.Relinquish() - if Option != None and Option.debug != None: - EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) - ReturnCode = X.args[0] - except Warning, X: - # error from Fdf parser - if MyBuild != None: - # for multi-thread build exits safely - MyBuild.Relinquish() - if Option != None and Option.debug != None: - EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) - else: - EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError = False) - ReturnCode = FORMAT_INVALID - except KeyboardInterrupt: - ReturnCode = ABORT_ERROR - if Option != None and Option.debug != None: - EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) - except: - if MyBuild != None: - # for multi-thread build exits safely - MyBuild.Relinquish() - - # try to get the meta-file from the object causing exception - Tb = sys.exc_info()[-1] - MetaFile = GlobalData.gProcessingFile - while Tb != None: - if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'): - MetaFile = Tb.tb_frame.f_locals['self'].MetaFile - Tb = Tb.tb_next - EdkLogger.error( - "\nbuild", - CODE_ERROR, - "Unknown fatal error when processing [%s]" % MetaFile, - ExtraData="\n(Please send email to dev@buildtools.tianocore.org for help, attaching following call stack trace!)\n", - RaiseError=False - ) - EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) - ReturnCode = CODE_ERROR - finally: - Utils.Progressor.Abort() - - if MyBuild != None: - MyBuild.Db.Close() - - if ReturnCode == 0: - Conclusion = "Done" - elif ReturnCode == ABORT_ERROR: - Conclusion = "Aborted" - else: - Conclusion = "Failed" - FinishTime = time.time() - BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime)))) - EdkLogger.SetLevel(EdkLogger.QUIET) - EdkLogger.quiet("\n- %s -\n%s [%s]" % (Conclusion, time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration)) - - return ReturnCode - -if __name__ == '__main__': - r = Main() - ## 0-127 is a safe return range, and 1 is a standard default error - if r < 0 or r > 127: r = 1 - sys.exit(r) - +## @file +# build a platform or a module +# +# Copyright (c) 2007 - 2010, Intel Corporation +# +# All rights reserved. This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# + +## +# Import Modules +# +import os +import re +import StringIO +import sys +import glob +import time +import platform +import traceback + +from struct import * +from threading import * +from optparse import OptionParser +from subprocess import * +from Common import Misc as Utils + +from Common.TargetTxtClassObject import * +from Common.ToolDefClassObject import * +from Common.DataType import * +from AutoGen.AutoGen import * +from Common.BuildToolError import * +from Workspace.WorkspaceDatabase import * + +from BuildReport import BuildReport +from GenPatchPcdTable.GenPatchPcdTable import * +from PatchPcdValue.PatchPcdValue import * + +import Common.EdkLogger +import Common.GlobalData as GlobalData + +# Version and Copyright +VersionNumber = "0.5" +__version__ = "%prog Version " + VersionNumber +__copyright__ = "Copyright (c) 2007 - 2010, Intel Corporation All rights reserved." + +## standard targets of build command +gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run'] + +## build configuration file +gBuildConfiguration = "Conf/target.txt" +gBuildCacheDir = "Conf/.cache" +gToolsDefinition = "Conf/tools_def.txt" + +## Check environment PATH variable to make sure the specified tool is found +# +# If the tool is found in the PATH, then True is returned +# Otherwise, False is returned +# +def IsToolInPath(tool): + if os.environ.has_key('PATHEXT'): + extns = os.environ['PATHEXT'].split(os.path.pathsep) + else: + extns = ('',) + for pathDir in os.environ['PATH'].split(os.path.pathsep): + for ext in extns: + if os.path.exists(os.path.join(pathDir, tool + ext)): + return True + return False + +## Check environment variables +# +# Check environment variables that must be set for build. Currently they are +# +# WORKSPACE The directory all packages/platforms start from +# EDK_TOOLS_PATH The directory contains all tools needed by the build +# PATH $(EDK_TOOLS_PATH)/Bin/ must be set in PATH +# +# If any of above environment variable is not set or has error, the build +# will be broken. +# +def CheckEnvVariable(): + # check WORKSPACE + if "WORKSPACE" not in os.environ: + EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found", + ExtraData="WORKSPACE") + + WorkspaceDir = os.path.normcase(os.path.normpath(os.environ["WORKSPACE"])) + if not os.path.exists(WorkspaceDir): + EdkLogger.error("build", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData="%s" % WorkspaceDir) + elif ' ' in WorkspaceDir: + EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path", + ExtraData=WorkspaceDir) + os.environ["WORKSPACE"] = WorkspaceDir + + # + # Check EFI_SOURCE (R8 build convention). EDK_SOURCE will always point to ECP + # + os.environ["ECP_SOURCE"] = os.path.join(WorkspaceDir, GlobalData.gEdkCompatibilityPkg) + if "EFI_SOURCE" not in os.environ: + os.environ["EFI_SOURCE"] = os.environ["ECP_SOURCE"] + if "EDK_SOURCE" not in os.environ: + os.environ["EDK_SOURCE"] = os.environ["ECP_SOURCE"] + + # + # Unify case of characters on case-insensitive systems + # + EfiSourceDir = os.path.normcase(os.path.normpath(os.environ["EFI_SOURCE"])) + EdkSourceDir = os.path.normcase(os.path.normpath(os.environ["EDK_SOURCE"])) + EcpSourceDir = os.path.normcase(os.path.normpath(os.environ["ECP_SOURCE"])) + + os.environ["EFI_SOURCE"] = EfiSourceDir + os.environ["EDK_SOURCE"] = EdkSourceDir + os.environ["ECP_SOURCE"] = EcpSourceDir + os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"]) + + if not os.path.exists(EcpSourceDir): + EdkLogger.verbose("ECP_SOURCE = %s doesn't exist. R8 modules could not be built." % EcpSourceDir) + elif ' ' in EcpSourceDir: + EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in ECP_SOURCE path", + ExtraData=EcpSourceDir) + if not os.path.exists(EdkSourceDir): + if EdkSourceDir == EcpSourceDir: + EdkLogger.verbose("EDK_SOURCE = %s doesn't exist. R8 modules could not be built." % EdkSourceDir) + else: + EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE does not exist", + ExtraData=EdkSourceDir) + elif ' ' in EdkSourceDir: + EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EDK_SOURCE path", + ExtraData=EdkSourceDir) + if not os.path.exists(EfiSourceDir): + if EfiSourceDir == EcpSourceDir: + EdkLogger.verbose("EFI_SOURCE = %s doesn't exist. R8 modules could not be built." % EfiSourceDir) + else: + EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE does not exist", + ExtraData=EfiSourceDir) + elif ' ' in EfiSourceDir: + EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EFI_SOURCE path", + ExtraData=EfiSourceDir) + + # change absolute path to relative path to WORKSPACE + if EfiSourceDir.upper().find(WorkspaceDir.upper()) != 0: + EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE is not under WORKSPACE", + ExtraData="WORKSPACE = %s\n EFI_SOURCE = %s" % (WorkspaceDir, EfiSourceDir)) + if EdkSourceDir.upper().find(WorkspaceDir.upper()) != 0: + EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE is not under WORKSPACE", + ExtraData="WORKSPACE = %s\n EDK_SOURCE = %s" % (WorkspaceDir, EdkSourceDir)) + if EcpSourceDir.upper().find(WorkspaceDir.upper()) != 0: + EdkLogger.error("build", PARAMETER_INVALID, "ECP_SOURCE is not under WORKSPACE", + ExtraData="WORKSPACE = %s\n ECP_SOURCE = %s" % (WorkspaceDir, EcpSourceDir)) + + # check EDK_TOOLS_PATH + if "EDK_TOOLS_PATH" not in os.environ: + EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found", + ExtraData="EDK_TOOLS_PATH") + + # check PATH + if "PATH" not in os.environ: + EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found", + ExtraData="PATH") + + GlobalData.gWorkspace = WorkspaceDir + GlobalData.gEfiSource = EfiSourceDir + GlobalData.gEdkSource = EdkSourceDir + GlobalData.gEcpSource = EcpSourceDir + +## Get normalized file path +# +# Convert the path to be local format, and remove the WORKSPACE path at the +# beginning if the file path is given in full path. +# +# @param FilePath File path to be normalized +# @param Workspace Workspace path which the FilePath will be checked against +# +# @retval string The normalized file path +# +def NormFile(FilePath, Workspace): + # check if the path is absolute or relative + if os.path.isabs(FilePath): + FileFullPath = os.path.normpath(FilePath) + else: + FileFullPath = os.path.normpath(os.path.join(Workspace, FilePath)) + + # check if the file path exists or not + if not os.path.isfile(FileFullPath): + EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)" % FileFullPath) + + # remove workspace directory from the beginning part of the file path + if Workspace[-1] in ["\\", "/"]: + return FileFullPath[len(Workspace):] + else: + return FileFullPath[(len(Workspace) + 1):] + +## Get the output of an external program +# +# This is the entrance method of thread reading output of an external program and +# putting them in STDOUT/STDERR of current program. +# +# @param From The stream message read from +# @param To The stream message put on +# @param ExitFlag The flag used to indicate stopping reading +# +def ReadMessage(From, To, ExitFlag): + while True: + # read one line a time + Line = From.readline() + # empty string means "end" + if Line != None and Line != "": + To(Line.rstrip()) + else: + break + if ExitFlag.isSet(): + break + +## Launch an external program +# +# This method will call subprocess.Popen to execute an external program with +# given options in specified directory. Because of the dead-lock issue during +# redirecting output of the external program, threads are used to to do the +# redirection work. +# +# @param Command A list or string containing the call of the program +# @param WorkingDir The directory in which the program will be running +# +def LaunchCommand(Command, WorkingDir): + # if working directory doesn't exist, Popen() will raise an exception + if not os.path.isdir(WorkingDir): + EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir) + + Proc = None + EndOfProcedure = None + try: + # launch the command + Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1) + + # launch two threads to read the STDOUT and STDERR + EndOfProcedure = Event() + EndOfProcedure.clear() + if Proc.stdout: + StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure)) + StdOutThread.setName("STDOUT-Redirector") + StdOutThread.setDaemon(False) + StdOutThread.start() + + if Proc.stderr: + StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure)) + StdErrThread.setName("STDERR-Redirector") + StdErrThread.setDaemon(False) + StdErrThread.start() + + # waiting for program exit + Proc.wait() + except: # in case of aborting + # terminate the threads redirecting the program output + if EndOfProcedure != None: + EndOfProcedure.set() + if Proc == None: + if type(Command) != type(""): + Command = " ".join(Command) + EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir)) + + if Proc.stdout: + StdOutThread.join() + if Proc.stderr: + StdErrThread.join() + + # check the return code of the program + if Proc.returncode != 0: + if type(Command) != type(""): + Command = " ".join(Command) + EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir)) + +## The smallest unit that can be built in multi-thread build mode +# +# This is the base class of build unit. The "Obj" parameter must provide +# __str__(), __eq__() and __hash__() methods. Otherwise there could be build units +# missing build. +# +# Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects. +# +class BuildUnit: + ## The constructor + # + # @param self The object pointer + # @param Obj The object the build is working on + # @param Target The build target name, one of gSupportedTarget + # @param Dependency The BuildUnit(s) which must be completed in advance + # @param WorkingDir The directory build command starts in + # + def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."): + self.BuildObject = Obj + self.Dependency = Dependency + self.WorkingDir = WorkingDir + self.Target = Target + self.BuildCommand = BuildCommand + if BuildCommand == None or len(BuildCommand) == 0: + EdkLogger.error("build", OPTION_MISSING, "No build command found for", + ExtraData=str(Obj)) + + ## str() method + # + # It just returns the string representaion of self.BuildObject + # + # @param self The object pointer + # + def __str__(self): + return str(self.BuildObject) + + ## "==" operator method + # + # It just compares self.BuildObject with "Other". So self.BuildObject must + # provide its own __eq__() method. + # + # @param self The object pointer + # @param Other The other BuildUnit object compared to + # + def __eq__(self, Other): + return Other != None and self.BuildObject == Other.BuildObject \ + and self.BuildObject.Arch == Other.BuildObject.Arch + + ## hash() method + # + # It just returns the hash value of self.BuildObject which must be hashable. + # + # @param self The object pointer + # + def __hash__(self): + return hash(self.BuildObject) + hash(self.BuildObject.Arch) + + def __repr__(self): + return repr(self.BuildObject) + +## The smallest module unit that can be built by nmake/make command in multi-thread build mode +# +# This class is for module build by nmake/make build system. The "Obj" parameter +# must provide __str__(), __eq__() and __hash__() methods. Otherwise there could +# be make units missing build. +# +# Currently the "Obj" should be only ModuleAutoGen object. +# +class ModuleMakeUnit(BuildUnit): + ## The constructor + # + # @param self The object pointer + # @param Obj The ModuleAutoGen object the build is working on + # @param Target The build target name, one of gSupportedTarget + # + def __init__(self, Obj, Target): + Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList] + BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir) + if Target in [None, "", "all"]: + self.Target = "tbuild" + +## The smallest platform unit that can be built by nmake/make command in multi-thread build mode +# +# This class is for platform build by nmake/make build system. The "Obj" parameter +# must provide __str__(), __eq__() and __hash__() methods. Otherwise there could +# be make units missing build. +# +# Currently the "Obj" should be only PlatformAutoGen object. +# +class PlatformMakeUnit(BuildUnit): + ## The constructor + # + # @param self The object pointer + # @param Obj The PlatformAutoGen object the build is working on + # @param Target The build target name, one of gSupportedTarget + # + def __init__(self, Obj, Target): + Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList] + Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList]) + BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir) + +## The class representing the task of a module build or platform build +# +# This class manages the build tasks in multi-thread build mode. Its jobs include +# scheduling thread running, catching thread error, monitor the thread status, etc. +# +class BuildTask: + # queue for tasks waiting for schedule + _PendingQueue = sdict() + _PendingQueueLock = threading.Lock() + + # queue for tasks ready for running + _ReadyQueue = sdict() + _ReadyQueueLock = threading.Lock() + + # queue for run tasks + _RunningQueue = sdict() + _RunningQueueLock = threading.Lock() + + # queue containing all build tasks, in case duplicate build + _TaskQueue = sdict() + + # flag indicating error occurs in a running thread + _ErrorFlag = threading.Event() + _ErrorFlag.clear() + _ErrorMessage = "" + + # BoundedSemaphore object used to control the number of running threads + _Thread = None + + # flag indicating if the scheduler is started or not + _SchedulerStopped = threading.Event() + _SchedulerStopped.set() + + ## Start the task scheduler thread + # + # @param MaxThreadNumber The maximum thread number + # @param ExitFlag Flag used to end the scheduler + # + @staticmethod + def StartScheduler(MaxThreadNumber, ExitFlag): + SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag)) + SchedulerThread.setName("Build-Task-Scheduler") + SchedulerThread.setDaemon(False) + SchedulerThread.start() + # wait for the scheduler to be started, especially useful in Linux + while not BuildTask.IsOnGoing(): + time.sleep(0.01) + + ## Scheduler method + # + # @param MaxThreadNumber The maximum thread number + # @param ExitFlag Flag used to end the scheduler + # + @staticmethod + def Scheduler(MaxThreadNumber, ExitFlag): + BuildTask._SchedulerStopped.clear() + try: + # use BoundedSemaphore to control the maximum running threads + BuildTask._Thread = BoundedSemaphore(MaxThreadNumber) + # + # scheduling loop, which will exits when no pending/ready task and + # indicated to do so, or there's error in running thread + # + while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \ + or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet(): + EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)" + % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue))) + + # get all pending tasks + BuildTask._PendingQueueLock.acquire() + BuildObjectList = BuildTask._PendingQueue.keys() + # + # check if their dependency is resolved, and if true, move them + # into ready queue + # + for BuildObject in BuildObjectList: + Bt = BuildTask._PendingQueue[BuildObject] + if Bt.IsReady(): + BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject) + BuildTask._PendingQueueLock.release() + + # launch build thread until the maximum number of threads is reached + while not BuildTask._ErrorFlag.isSet(): + # empty ready queue, do nothing further + if len(BuildTask._ReadyQueue) == 0: + break + + # wait for active thread(s) exit + BuildTask._Thread.acquire(True) + + # start a new build thread + Bo = BuildTask._ReadyQueue.keys()[0] + Bt = BuildTask._ReadyQueue.pop(Bo) + + # move into running queue + BuildTask._RunningQueueLock.acquire() + BuildTask._RunningQueue[Bo] = Bt + BuildTask._RunningQueueLock.release() + + Bt.Start() + # avoid tense loop + time.sleep(0.01) + + # avoid tense loop + time.sleep(0.01) + + # wait for all running threads exit + if BuildTask._ErrorFlag.isSet(): + EdkLogger.quiet("\nWaiting for all build threads exit...") + # while not BuildTask._ErrorFlag.isSet() and \ + while len(BuildTask._RunningQueue) > 0: + EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue)) + EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()])) + # avoid tense loop + time.sleep(0.1) + except BaseException, X: + # + # TRICK: hide the output of threads left runing, so that the user can + # catch the error message easily + # + EdkLogger.SetLevel(EdkLogger.ERROR) + BuildTask._ErrorFlag.set() + BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X) + + BuildTask._PendingQueue.clear() + BuildTask._ReadyQueue.clear() + BuildTask._RunningQueue.clear() + BuildTask._TaskQueue.clear() + BuildTask._SchedulerStopped.set() + + ## Wait for all running method exit + # + @staticmethod + def WaitForComplete(): + BuildTask._SchedulerStopped.wait() + + ## Check if the scheduler is running or not + # + @staticmethod + def IsOnGoing(): + return not BuildTask._SchedulerStopped.isSet() + + ## Abort the build + @staticmethod + def Abort(): + if BuildTask.IsOnGoing(): + BuildTask._ErrorFlag.set() + BuildTask.WaitForComplete() + + ## Check if there's error in running thread + # + # Since the main thread cannot catch exceptions in other thread, we have to + # use threading.Event to communicate this formation to main thread. + # + @staticmethod + def HasError(): + return BuildTask._ErrorFlag.isSet() + + ## Get error message in running thread + # + # Since the main thread cannot catch exceptions in other thread, we have to + # use a static variable to communicate this message to main thread. + # + @staticmethod + def GetErrorMessage(): + return BuildTask._ErrorMessage + + ## Factory method to create a BuildTask object + # + # This method will check if a module is building or has been built. And if + # true, just return the associated BuildTask object in the _TaskQueue. If + # not, create and return a new BuildTask object. The new BuildTask object + # will be appended to the _PendingQueue for scheduling later. + # + # @param BuildItem A BuildUnit object representing a build object + # @param Dependency The dependent build object of BuildItem + # + @staticmethod + def New(BuildItem, Dependency=None): + if BuildItem in BuildTask._TaskQueue: + Bt = BuildTask._TaskQueue[BuildItem] + return Bt + + Bt = BuildTask() + Bt._Init(BuildItem, Dependency) + BuildTask._TaskQueue[BuildItem] = Bt + + BuildTask._PendingQueueLock.acquire() + BuildTask._PendingQueue[BuildItem] = Bt + BuildTask._PendingQueueLock.release() + + return Bt + + ## The real constructor of BuildTask + # + # @param BuildItem A BuildUnit object representing a build object + # @param Dependency The dependent build object of BuildItem + # + def _Init(self, BuildItem, Dependency=None): + self.BuildItem = BuildItem + + self.DependencyList = [] + if Dependency == None: + Dependency = BuildItem.Dependency + else: + Dependency.extend(BuildItem.Dependency) + self.AddDependency(Dependency) + # flag indicating build completes, used to avoid unnecessary re-build + self.CompleteFlag = False + + ## Check if all dependent build tasks are completed or not + # + def IsReady(self): + ReadyFlag = True + for Dep in self.DependencyList: + if Dep.CompleteFlag == True: + continue + ReadyFlag = False + break + + return ReadyFlag + + ## Add dependent build task + # + # @param Dependency The list of dependent build objects + # + def AddDependency(self, Dependency): + for Dep in Dependency: + self.DependencyList.append(BuildTask.New(Dep)) # BuildTask list + + ## The thread wrapper of LaunchCommand function + # + # @param Command A list or string contains the call of the command + # @param WorkingDir The directory in which the program will be running + # + def _CommandThread(self, Command, WorkingDir): + try: + LaunchCommand(Command, WorkingDir) + self.CompleteFlag = True + except: + # + # TRICK: hide the output of threads left runing, so that the user can + # catch the error message easily + # + if not BuildTask._ErrorFlag.isSet(): + GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject), + self.BuildItem.BuildObject.Arch, + self.BuildItem.BuildObject.ToolChain, + self.BuildItem.BuildObject.BuildTarget + ) + EdkLogger.SetLevel(EdkLogger.ERROR) + BuildTask._ErrorFlag.set() + BuildTask._ErrorMessage = "%s broken\n %s [%s]" % \ + (threading.currentThread().getName(), Command, WorkingDir) + # indicate there's a thread is available for another build task + BuildTask._RunningQueueLock.acquire() + BuildTask._RunningQueue.pop(self.BuildItem) + BuildTask._RunningQueueLock.release() + BuildTask._Thread.release() + + ## Start build task thread + # + def Start(self): + EdkLogger.quiet("Building ... %s" % repr(self.BuildItem)) + Command = self.BuildItem.BuildCommand + [self.BuildItem.Target] + self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir)) + self.BuildTread.setName("build thread") + self.BuildTread.setDaemon(False) + self.BuildTread.start() + +## The class contains the information related to EFI image +# +class PeImageInfo(): + ## Constructor + # + # Constructor will load all required image information. + # + # @param BaseName The full file path of image. + # @param Guid The GUID for image. + # @param Arch Arch of this image. + # @param OutpuDir The output directory for image. + # @param ImageClass PeImage Information + # + def __init__(self, BaseName, Guid, Arch, OutpuDir, ImageClass): + self.BaseName = BaseName + self.Guid = Guid + self.Arch = Arch + self.OutpuDir = OutpuDir + self.Image = ImageClass + self.Image.Size = (self.Image.Size / 0x1000 + 1) * 0x1000 + +## The class implementing the EDK2 build process +# +# The build process includes: +# 1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf +# 2. Parse DSC file of active platform +# 3. Parse FDF file if any +# 4. Establish build database, including parse all other files (module, package) +# 5. Create AutoGen files (C code file, depex file, makefile) if necessary +# 6. Call build command +# +class Build(): + ## Constructor + # + # Constructor will load all necessary configurations, parse platform, modules + # and packages and the establish a database for AutoGen. + # + # @param Target The build command target, one of gSupportedTarget + # @param WorkspaceDir The directory of workspace + # @param Platform The DSC file of active platform + # @param Module The INF file of active module, if any + # @param Arch The Arch list of platform or module + # @param ToolChain The name list of toolchain + # @param BuildTarget The "DEBUG" or "RELEASE" build + # @param FlashDefinition The FDF file of active platform + # @param FdList=[] The FD names to be individually built + # @param FvList=[] The FV names to be individually built + # @param MakefileType The type of makefile (for MSFT make or GNU make) + # @param SilentMode Indicate multi-thread build mode + # @param ThreadNumber The maximum number of thread if in multi-thread build mode + # @param SkipAutoGen Skip AutoGen step + # @param Reparse Re-parse all meta files + # @param SkuId SKU id from command line + # + def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain, + BuildTarget, FlashDefinition, FdList=[], FvList=[], + MakefileType="nmake", SilentMode=False, ThreadNumber=2, + SkipAutoGen=False, Reparse=False, SkuId=None, + ReportFile=None, ReportType=None): + + self.WorkspaceDir = WorkspaceDir + self.Target = Target + self.PlatformFile = Platform + self.ModuleFile = Module + self.ArchList = Arch + self.ToolChainList = ToolChain + self.BuildTargetList= BuildTarget + self.Fdf = FlashDefinition + self.FdList = FdList + self.FvList = FvList + self.MakefileType = MakefileType + self.SilentMode = SilentMode + self.ThreadNumber = ThreadNumber + self.SkipAutoGen = SkipAutoGen + self.Reparse = Reparse + self.SkuId = SkuId + self.SpawnMode = True + self.BuildReport = BuildReport(ReportFile, ReportType) + self.TargetTxt = TargetTxtClassObject() + self.ToolDef = ToolDefClassObject() + self.Db = WorkspaceDatabase(None, GlobalData.gGlobalDefines, self.Reparse) + #self.Db = WorkspaceDatabase(None, {}, self.Reparse) + self.BuildDatabase = self.Db.BuildObject + self.Platform = None + self.LoadFixAddress = 0 + + # print dot charater during doing some time-consuming work + self.Progress = Utils.Progressor() + + # parse target.txt, tools_def.txt, and platform file + #self.RestoreBuildData() + self.LoadConfiguration() + self.InitBuild() + + # print current build environment and configuration + EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"])) + EdkLogger.quiet("%-24s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"])) + EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"])) + EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"])) + EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"])) + + EdkLogger.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList))) + EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList))) + EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList))) + + EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile)) + + if self.Fdf != None and self.Fdf != "": + EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf)) + + if self.ModuleFile != None and self.ModuleFile != "": + EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile)) + + os.chdir(self.WorkspaceDir) + self.Progress.Start("\nProcessing meta-data") + + ## Load configuration + # + # This method will parse target.txt and get the build configurations. + # + def LoadConfiguration(self): + # + # Check target.txt and tools_def.txt and Init them + # + BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration)) + if os.path.isfile(BuildConfigurationFile) == True: + StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile) + + ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF] + if ToolDefinitionFile == '': + ToolDefinitionFile = gToolsDefinition + ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile)) + if os.path.isfile(ToolDefinitionFile) == True: + StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile) + else: + EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile) + else: + EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile) + + # if no ARCH given in command line, get it from target.txt + if self.ArchList == None or len(self.ArchList) == 0: + self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH] + + # if no build target given in command line, get it from target.txt + if self.BuildTargetList == None or len(self.BuildTargetList) == 0: + self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET] + + # if no tool chain given in command line, get it from target.txt + if self.ToolChainList == None or len(self.ToolChainList) == 0: + self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG] + if self.ToolChainList == None or len(self.ToolChainList) == 0: + EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n") + + # check if the tool chains are defined or not + NewToolChainList = [] + for ToolChain in self.ToolChainList: + if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]: + EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain) + else: + NewToolChainList.append(ToolChain) + # if no tool chain available, break the build + if len(NewToolChainList) == 0: + EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, + ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList)) + else: + self.ToolChainList = NewToolChainList + + if self.ThreadNumber == None: + self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER] + if self.ThreadNumber == '': + self.ThreadNumber = 0 + else: + self.ThreadNumber = int(self.ThreadNumber, 0) + + if self.ThreadNumber == 0: + self.ThreadNumber = 1 + + if not self.PlatformFile: + PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM] + if not PlatformFile: + # Try to find one in current directory + WorkingDirectory = os.getcwd() + FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc'))) + FileNum = len(FileList) + if FileNum >= 2: + EdkLogger.error("build", OPTION_MISSING, + ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory)) + elif FileNum == 1: + PlatformFile = FileList[0] + else: + EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, + ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n") + + self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir) + ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False) + if ErrorCode != 0: + EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) + + ## Initialize build configuration + # + # This method will parse DSC file and merge the configurations from + # command line and target.txt, then get the final build configurations. + # + def InitBuild(self): + ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc") + if ErrorCode != 0: + EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) + + # create metafile database + self.Db.InitDatabase() + + # we need information in platform description file to determine how to build + self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON'] + if not self.Fdf: + self.Fdf = self.Platform.FlashDefinition + + LoadFixAddressString = None + if TAB_FIX_LOAD_TOP_MEMORY_ADDRESS in GlobalData.gGlobalDefines: + LoadFixAddressString = GlobalData.gGlobalDefines[TAB_FIX_LOAD_TOP_MEMORY_ADDRESS] + else: + LoadFixAddressString = self.Platform.LoadFixAddress + + if LoadFixAddressString != None and LoadFixAddressString != '': + try: + if LoadFixAddressString.upper().startswith('0X'): + self.LoadFixAddress = int (LoadFixAddressString, 16) + else: + self.LoadFixAddress = int (LoadFixAddressString) + except: + EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS %s is not valid dec or hex string" % (LoadFixAddressString)) + if self.LoadFixAddress < 0: + EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is set to the invalid negative value %s" % (LoadFixAddressString)) + if self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress % 0x1000 != 0: + EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is set to the invalid unaligned 4K value %s" % (LoadFixAddressString)) + + if self.SkuId == None or self.SkuId == '': + self.SkuId = self.Platform.SkuName + + # check FD/FV build target + if self.Fdf == None or self.Fdf == "": + if self.FdList != []: + EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList)) + self.FdList = [] + if self.FvList != []: + EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList)) + self.FvList = [] + else: + FdfParserObj = FdfParser(str(self.Fdf)) + FdfParserObj.ParseFile() + for fvname in self.FvList: + if fvname.upper() not in FdfParserObj.Profile.FvDict.keys(): + EdkLogger.error("build", OPTION_VALUE_INVALID, + "No such an FV in FDF file: %s" % fvname) + + # + # Merge Arch + # + if self.ArchList == None or len(self.ArchList) == 0: + ArchList = set(self.Platform.SupArchList) + else: + ArchList = set(self.ArchList) & set(self.Platform.SupArchList) + if len(ArchList) == 0: + EdkLogger.error("build", PARAMETER_INVALID, + ExtraData = "Active platform supports [%s] only, but [%s] is given." + % (" ".join(self.Platform.SupArchList), " ".join(self.ArchList))) + elif len(ArchList) != len(self.ArchList): + SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList)) + EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !" + % (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList), " ".join(self.ArchList))) + self.ArchList = tuple(ArchList) + + # Merge build target + if self.BuildTargetList == None or len(self.BuildTargetList) == 0: + BuildTargetList = self.Platform.BuildTargets + else: + BuildTargetList = list(set(self.BuildTargetList) & set(self.Platform.BuildTargets)) + if BuildTargetList == []: + EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given" + % (" ".join(self.Platform.BuildTargets), " ".join(self.BuildTargetList))) + self.BuildTargetList = BuildTargetList + + ## Build a module or platform + # + # Create autogen code and makfile for a module or platform, and the launch + # "make" command to build it + # + # @param Target The target of build command + # @param Platform The platform file + # @param Module The module file + # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE" + # @param ToolChain The name of toolchain to build + # @param Arch The arch of the module/platform + # @param CreateDepModuleCodeFile Flag used to indicate creating code + # for dependent modules/Libraries + # @param CreateDepModuleMakeFile Flag used to indicate creating makefile + # for dependent modules/Libraries + # + def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True): + if AutoGenObject == None: + return False + + # skip file generation for cleanxxx targets, run and fds target + if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']: + # for target which must generate AutoGen code and makefile + if not self.SkipAutoGen or Target == 'genc': + self.Progress.Start("Generating code") + AutoGenObject.CreateCodeFile(CreateDepsCodeFile) + self.Progress.Stop("done!") + if Target == "genc": + return True + + if not self.SkipAutoGen or Target == 'genmake': + self.Progress.Start("Generating makefile") + AutoGenObject.CreateMakeFile(CreateDepsMakeFile) + self.Progress.Stop("done!") + if Target == "genmake": + return True + else: + # always recreate top/platform makefile when clean, just in case of inconsistency + AutoGenObject.CreateCodeFile(False) + AutoGenObject.CreateMakeFile(False) + + if EdkLogger.GetLevel() == EdkLogger.QUIET: + EdkLogger.quiet("Building ... %s" % repr(AutoGenObject)) + + BuildCommand = AutoGenObject.BuildCommand + if BuildCommand == None or len(BuildCommand) == 0: + EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key) + + BuildCommand = BuildCommand + [Target] + LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir) + if Target == 'cleanall': + try: + #os.rmdir(AutoGenObject.BuildDir) + RemoveDirectory(AutoGenObject.BuildDir, True) + except WindowsError, X: + EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X)) + return True + + ## Rebase module image and Get function address for the inpug module list. + # + def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False): + if ModeIsSmm: + AddrIsOffset = False + InfFileNameList = ModuleList.keys() + #InfFileNameList.sort() + for InfFile in InfFileNameList: + sys.stdout.write (".") + sys.stdout.flush() + ModuleInfo = ModuleList[InfFile] + ModuleName = ModuleInfo.BaseName + ## for SMM module in SMRAM, the SMRAM will be allocated from base to top. + if not ModeIsSmm: + BaseAddress = BaseAddress - ModuleInfo.Image.Size + # + # Update Image to new BaseAddress by GenFw tool + # + LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleInfo.Image.FileName], ModuleInfo.OutpuDir) + else: + # + # Set new address to the section header only for SMM driver. + # + LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleInfo.Image.FileName], ModuleInfo.OutpuDir) + # + # Collect funtion address from Map file + # + ImageMapTable = ModuleInfo.Image.FileName.replace('.efi', '.map') + FunctionList = [] + if os.path.exists(ImageMapTable): + OrigImageBaseAddress = 0 + ImageMap = open (ImageMapTable, 'r') + for LinStr in ImageMap: + if len (LinStr.strip()) == 0: + continue + # + # Get the preferred address set on link time. + # + if LinStr.find ('Preferred load address is') != -1: + StrList = LinStr.split() + OrigImageBaseAddress = int (StrList[len(StrList) - 1], 16) + + StrList = LinStr.split() + if len (StrList) > 4: + if StrList[3] == 'f' or StrList[3] =='F': + Name = StrList[1] + RelativeAddress = int (StrList[2], 16) - OrigImageBaseAddress + FunctionList.append ((Name, RelativeAddress)) + if ModuleInfo.Arch == 'IPF' and Name.endswith('_ModuleEntryPoint'): + # + # Get the real entry point address for IPF image. + # + ModuleInfo.Image.EntryPoint = RelativeAddress + ImageMap.close() + # + # Add general information. + # + if ModeIsSmm: + MapBuffer.write('\n\n%s (Fixed SMRAM Offset, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint)) + elif AddrIsOffset: + MapBuffer.write('\n\n%s (Fixed Memory Offset, BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint))) + else: + MapBuffer.write('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint)) + # + # Add guid and general seciton section. + # + TextSectionAddress = 0 + DataSectionAddress = 0 + for SectionHeader in ModuleInfo.Image.SectionHeaderList: + if SectionHeader[0] == '.text': + TextSectionAddress = SectionHeader[1] + elif SectionHeader[0] in ['.data', '.sdata']: + DataSectionAddress = SectionHeader[1] + if AddrIsOffset: + MapBuffer.write('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress))) + else: + MapBuffer.write('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress)) + # + # Add funtion address + # + for Function in FunctionList: + if AddrIsOffset: + MapBuffer.write(' -0x%010X %s\n' % (0 - (BaseAddress + Function[1]), Function[0])) + else: + MapBuffer.write(' 0x%010X %s\n' % (BaseAddress + Function[1], Function[0])) + ImageMap.close() + + # + # for SMM module in SMRAM, the SMRAM will be allocated from base to top. + # + if ModeIsSmm: + BaseAddress = BaseAddress + ModuleInfo.Image.Size + + ## Collect MAP information of all FVs + # + def _CollectFvMapBuffer (self, MapBuffer, Wa): + if self.Fdf != '': + # First get the XIP base address for FV map file. + for FvName in Wa.FdfProfile.FvDict.keys(): + FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map') + if not os.path.exists(FvMapBuffer): + continue + FvMap = open (FvMapBuffer, 'r') + #skip FV size information + FvMap.readline() + FvMap.readline() + FvMap.readline() + FvMap.readline() + MapBuffer.write(FvMap.read()) + FvMap.close() + + ## Collect MAP information of all modules + # + def _CollectModuleMapBuffer (self, MapBuffer, ModuleList): + sys.stdout.write ("Generate Load Module At Fix Address Map") + sys.stdout.flush() + PatchEfiImageList = [] + PeiModuleList = {} + BtModuleList = {} + RtModuleList = {} + SmmModuleList = {} + PeiSize = 0 + BtSize = 0 + RtSize = 0 + # reserve 4K size in SMRAM to make SMM module address not from 0. + SmmSize = 0x1000 + IsIpfPlatform = False + if 'IPF' in self.ArchList: + IsIpfPlatform = True + for Module in ModuleList: + GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget) + + OutputImageFile = '' + for ResultFile in Module.CodaTargetList: + if str(ResultFile.Target).endswith('.efi'): + # + # module list for PEI, DXE, RUNTIME and SMM + # + OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi') + ImageClass = PeImageClass (OutputImageFile) + if not ImageClass.IsValid: + EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo) + ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, ImageClass) + if Module.ModuleType in ['PEI_CORE', 'PEIM', 'COMBINED_PEIM_DRIVER','PIC_PEIM', 'RELOCATABLE_PEIM', 'DXE_CORE']: + PeiModuleList[Module.MetaFile] = ImageInfo + PeiSize += ImageInfo.Image.Size + elif Module.ModuleType in ['BS_DRIVER', 'DXE_DRIVER', 'UEFI_DRIVER']: + BtModuleList[Module.MetaFile] = ImageInfo + BtSize += ImageInfo.Image.Size + elif Module.ModuleType in ['DXE_RUNTIME_DRIVER', 'RT_DRIVER', 'DXE_SAL_DRIVER', 'SAL_RT_DRIVER']: + RtModuleList[Module.MetaFile] = ImageInfo + #IPF runtime driver needs to be at 2 page alignment. + if IsIpfPlatform and ImageInfo.Image.Size % 0x2000 != 0: + ImageInfo.Image.Size = (ImageInfo.Image.Size / 0x2000 + 1) * 0x2000 + RtSize += ImageInfo.Image.Size + elif Module.ModuleType in ['SMM_CORE', 'DXE_SMM_DRIVER']: + SmmModuleList[Module.MetaFile] = ImageInfo + SmmSize += ImageInfo.Image.Size + if Module.ModuleType == 'DXE_SMM_DRIVER': + PiSpecVersion = 0 + if 'PI_SPECIFICATION_VERSION' in Module.Module.Specification: + PiSpecVersion = Module.Module.Specification['PI_SPECIFICATION_VERSION'] + # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver. + if PiSpecVersion < 0x0001000A: + BtModuleList[Module.MetaFile] = ImageInfo + BtSize += ImageInfo.Image.Size + break + # + # EFI image is final target. + # Check EFI image contains patchable FixAddress related PCDs. + # + if OutputImageFile != '': + ModuleIsPatch = False + for Pcd in Module.ModulePcdList: + if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST: + ModuleIsPatch = True + break + if not ModuleIsPatch: + for Pcd in Module.LibraryPcdList: + if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST: + ModuleIsPatch = True + break + + if not ModuleIsPatch: + continue + # + # Module includes the patchable load fix address PCDs. + # It will be fixed up later. + # + PatchEfiImageList.append (OutputImageFile) + + # + # Get Top Memory address + # + ReservedRuntimeMemorySize = 0 + TopMemoryAddress = 0 + if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF: + TopMemoryAddress = 0 + else: + TopMemoryAddress = self.LoadFixAddress + if TopMemoryAddress < RtSize + BtSize + PeiSize: + EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver") + # Make IPF runtime driver at 2 page alignment. + if IsIpfPlatform: + ReservedRuntimeMemorySize = TopMemoryAddress % 0x2000 + RtSize = RtSize + ReservedRuntimeMemorySize + + # + # Patch FixAddress related PCDs into EFI image + # + for EfiImage in PatchEfiImageList: + EfiImageMap = EfiImage.replace('.efi', '.map') + if not os.path.exists(EfiImageMap): + continue + # + # Get PCD offset in EFI image by GenPatchPcdTable function + # + PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage) + # + # Patch real PCD value by PatchPcdValue tool + # + for PcdInfo in PcdTable: + ReturnValue = 0 + if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE: + ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize/0x1000)) + elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE: + ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize/0x1000)) + elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE: + ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize/0x1000)) + elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0: + ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize/0x1000)) + if ReturnValue != 0: + EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo) + + MapBuffer.write('PEI_CODE_PAGE_NUMBER = 0x%x\n' % (PeiSize/0x1000)) + MapBuffer.write('BOOT_CODE_PAGE_NUMBER = 0x%x\n' % (BtSize/0x1000)) + MapBuffer.write('RUNTIME_CODE_PAGE_NUMBER = 0x%x\n' % (RtSize/0x1000)) + if len (SmmModuleList) > 0: + MapBuffer.write('SMM_CODE_PAGE_NUMBER = 0x%x\n' % (SmmSize/0x1000)) + + PeiBaseAddr = TopMemoryAddress - RtSize - BtSize + BtBaseAddr = TopMemoryAddress - RtSize + RtBaseAddr = TopMemoryAddress - ReservedRuntimeMemorySize + + self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0) + self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0) + self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0) + self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset = False, ModeIsSmm = True) + MapBuffer.write('\n\n') + sys.stdout.write ("\n") + sys.stdout.flush() + + ## Save platform Map file + # + def _SaveMapFile (self, MapBuffer, Wa): + # + # Map file path is got. + # + MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map') + # + # Save address map into MAP file. + # + SaveFileOnChange(MapFilePath, MapBuffer.getvalue(), False) + MapBuffer.close() + sys.stdout.write ("\nLoad Module At Fix Address Map file saved to %s\n" %(MapFilePath)) + sys.stdout.flush() + + ## Build active platform for different build targets and different tool chains + # + def _BuildPlatform(self): + for BuildTarget in self.BuildTargetList: + for ToolChain in self.ToolChainList: + Wa = WorkspaceAutoGen( + self.WorkspaceDir, + self.Platform, + BuildTarget, + ToolChain, + self.ArchList, + self.BuildDatabase, + self.TargetTxt, + self.ToolDef, + self.Fdf, + self.FdList, + self.FvList, + self.SkuId + ) + self.BuildReport.AddPlatformReport(Wa) + self.Progress.Stop("done!") + self._Build(self.Target, Wa) + + # Create MAP file when Load Fix Address is enabled. + if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0: + for Arch in self.ArchList: + # + # Check whether the set fix address is above 4G for 32bit image. + # + if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000: + EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules") + # + # Get Module List + # + ModuleList = [] + for Pa in Wa.AutoGenObjectList: + for Ma in Pa.ModuleAutoGenList: + if Ma == None: + continue + if not Ma.IsLibrary: + ModuleList.append (Ma) + + MapBuffer = StringIO('') + # + # Rebase module to the preferred memory address before GenFds + # + self._CollectModuleMapBuffer(MapBuffer, ModuleList) + if self.Fdf != '': + # + # create FDS again for the updated EFI image + # + self._Build("fds", Wa) + # + # Create MAP file for all platform FVs after GenFds. + # + self._CollectFvMapBuffer(MapBuffer, Wa) + # + # Save MAP buffer into MAP file. + # + self._SaveMapFile (MapBuffer, Wa) + + ## Build active module for different build targets, different tool chains and different archs + # + def _BuildModule(self): + for BuildTarget in self.BuildTargetList: + for ToolChain in self.ToolChainList: + # + # module build needs platform build information, so get platform + # AutoGen first + # + Wa = WorkspaceAutoGen( + self.WorkspaceDir, + self.Platform, + BuildTarget, + ToolChain, + self.ArchList, + self.BuildDatabase, + self.TargetTxt, + self.ToolDef, + self.Fdf, + self.FdList, + self.FvList, + self.SkuId + ) + self.BuildReport.AddPlatformReport(Wa) + Wa.CreateMakeFile(False) + self.Progress.Stop("done!") + MaList = [] + for Arch in self.ArchList: + Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile) + if Ma == None: continue + MaList.append(Ma) + self._Build(self.Target, Ma) + if MaList == []: + EdkLogger.error( + 'build', + BUILD_ERROR, + "Module for [%s] is not a component of active platform."\ + " Please make sure that the ARCH and inf file path are"\ + " given in the same as in [%s]" %\ + (', '.join(self.ArchList), self.Platform), + ExtraData=self.ModuleFile + ) + # Create MAP file when Load Fix Address is enabled. + if self.LoadFixAddress != 0 and self.Target == "fds" and self.Fdf != '': + for Arch in self.ArchList: + # + # Check whether the set fix address is above 4G for 32bit image. + # + if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000: + EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules") + # + # Get Module List + # + ModuleList = [] + for Pa in Wa.AutoGenObjectList: + for Ma in Pa.ModuleAutoGenList: + if Ma == None: + continue + if not Ma.IsLibrary: + ModuleList.append (Ma) + + MapBuffer = StringIO('') + # + # Rebase module to the preferred memory address before GenFds + # + self._CollectModuleMapBuffer(MapBuffer, ModuleList) + # + # create FDS again for the updated EFI image + # + self._Build("fds", Wa) + # + # Create MAP file for all platform FVs after GenFds. + # + self._CollectFvMapBuffer(MapBuffer, Wa) + # + # Save MAP buffer into MAP file. + # + self._SaveMapFile (MapBuffer, Wa) + + ## Build a platform in multi-thread mode + # + def _MultiThreadBuildPlatform(self): + for BuildTarget in self.BuildTargetList: + for ToolChain in self.ToolChainList: + Wa = WorkspaceAutoGen( + self.WorkspaceDir, + self.Platform, + BuildTarget, + ToolChain, + self.ArchList, + self.BuildDatabase, + self.TargetTxt, + self.ToolDef, + self.Fdf, + self.FdList, + self.FvList, + self.SkuId + ) + self.BuildReport.AddPlatformReport(Wa) + Wa.CreateMakeFile(False) + + # multi-thread exit flag + ExitFlag = threading.Event() + ExitFlag.clear() + for Arch in self.ArchList: + Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch) + if Pa == None: + continue + for Module in Pa.Platform.Modules: + # Get ModuleAutoGen object to generate C code file and makefile + Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile) + if Ma == None: + continue + # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds' + if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']: + # for target which must generate AutoGen code and makefile + if not self.SkipAutoGen or self.Target == 'genc': + Ma.CreateCodeFile(True) + if self.Target == "genc": + continue + + if not self.SkipAutoGen or self.Target == 'genmake': + Ma.CreateMakeFile(True) + if self.Target == "genmake": + continue + self.Progress.Stop("done!") + # Generate build task for the module + Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target)) + # Break build if any build thread has error + if BuildTask.HasError(): + # we need a full version of makefile for platform + ExitFlag.set() + BuildTask.WaitForComplete() + Pa.CreateMakeFile(False) + EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule) + # Start task scheduler + if not BuildTask.IsOnGoing(): + BuildTask.StartScheduler(self.ThreadNumber, ExitFlag) + + # in case there's an interruption. we need a full version of makefile for platform + Pa.CreateMakeFile(False) + if BuildTask.HasError(): + EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule) + + # + # All modules have been put in build tasks queue. Tell task scheduler + # to exit if all tasks are completed + # + ExitFlag.set() + BuildTask.WaitForComplete() + + # + # Check for build error, and raise exception if one + # has been signaled. + # + if BuildTask.HasError(): + EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule) + + # Create MAP file when Load Fix Address is enabled. + if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0: + for Arch in self.ArchList: + # + # Check whether the set fix address is above 4G for 32bit image. + # + if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000: + EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules") + # + # Get Module List + # + ModuleList = [] + for Pa in Wa.AutoGenObjectList: + for Ma in Pa.ModuleAutoGenList: + if Ma == None: + continue + if not Ma.IsLibrary: + ModuleList.append (Ma) + # + # Rebase module to the preferred memory address before GenFds + # + MapBuffer = StringIO('') + self._CollectModuleMapBuffer(MapBuffer, ModuleList) + + # Generate FD image if there's a FDF file found + if self.Fdf != '' and self.Target in ["", "all", "fds"]: + LaunchCommand(Wa.BuildCommand + ["fds"], Wa.MakeFileDir) + + # Create MAP file for all platform FV after GenFds + if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0: + if self.Fdf != '': + # + # Create MAP file for all platform FVs after GenFds. + # + self._CollectFvMapBuffer(MapBuffer, Wa) + # + # Save MAP buffer into MAP file. + # + self._SaveMapFile(MapBuffer, Wa) + + ## Generate GuidedSectionTools.txt in the FV directories. + # + def CreateGuidedSectionToolsFile(self): + for Arch in self.ArchList: + for BuildTarget in self.BuildTargetList: + for ToolChain in self.ToolChainList: + FvDir = os.path.join( + self.WorkspaceDir, + self.Platform.OutputDirectory, + '_'.join((BuildTarget, ToolChain)), + 'FV' + ) + if not os.path.exists(FvDir): + continue + # Build up the list of supported architectures for this build + prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch) + + # Look through the tool definitions for GUIDed tools + guidAttribs = [] + for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems(): + if attrib.upper().endswith('_GUID'): + split = attrib.split('_') + thisPrefix = '_'.join(split[0:3]) + '_' + if thisPrefix == prefix: + guid = self.ToolDef.ToolsDefTxtDictionary[attrib] + guid = guid.lower() + toolName = split[3] + path = '_'.join(split[0:4]) + '_PATH' + path = self.ToolDef.ToolsDefTxtDictionary[path] + path = self.GetFullPathOfTool(path) + guidAttribs.append((guid, toolName, path)) + + # Write out GuidedSecTools.txt + toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt') + toolsFile = open(toolsFile, 'wt') + for guidedSectionTool in guidAttribs: + print >> toolsFile, ' '.join(guidedSectionTool) + toolsFile.close() + + ## Returns the full path of the tool. + # + def GetFullPathOfTool (self, tool): + if os.path.exists(tool): + return os.path.realpath(tool) + else: + # We need to search for the tool using the + # PATH environment variable. + for dirInPath in os.environ['PATH'].split(os.pathsep): + foundPath = os.path.join(dirInPath, tool) + if os.path.exists(foundPath): + return os.path.realpath(foundPath) + + # If the tool was not found in the path then we just return + # the input tool. + return tool + + ## Launch the module or platform build + # + def Launch(self): + if self.ModuleFile == None or self.ModuleFile == "": + if not self.SpawnMode or self.Target not in ["", "all"]: + self.SpawnMode = False + self._BuildPlatform() + else: + self._MultiThreadBuildPlatform() + self.CreateGuidedSectionToolsFile() + else: + self.SpawnMode = False + self._BuildModule() + + ## Do some clean-up works when error occurred + def Relinquish(self): + OldLogLevel = EdkLogger.GetLevel() + EdkLogger.SetLevel(EdkLogger.ERROR) + #self.DumpBuildData() + Utils.Progressor.Abort() + if self.SpawnMode == True: + BuildTask.Abort() + EdkLogger.SetLevel(OldLogLevel) + + def DumpBuildData(self): + CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir) + Utils.CreateDirectory(CacheDirectory) + Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache")) + Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase")) + + def RestoreBuildData(self): + FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache") + if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath): + Utils.gFileTimeStampCache = Utils.DataRestore(FilePath) + if Utils.gFileTimeStampCache == None: + Utils.gFileTimeStampCache = {} + + FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase") + if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath): + Utils.gDependencyDatabase = Utils.DataRestore(FilePath) + if Utils.gDependencyDatabase == None: + Utils.gDependencyDatabase = {} + +def ParseDefines(DefineList=[]): + DefineDict = {} + if DefineList != None: + for Define in DefineList: + DefineTokenList = Define.split("=", 1) + if len(DefineTokenList) == 1: + DefineDict[DefineTokenList[0]] = "" + else: + DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip() + return DefineDict + +gParamCheck = [] +def SingleCheckCallback(option, opt_str, value, parser): + if option not in gParamCheck: + setattr(parser.values, option.dest, value) + gParamCheck.append(option) + else: + parser.error("Option %s only allows one instance in command line!" % option) + +## Parse command line options +# +# Using standard Python module optparse to parse command line option of this tool. +# +# @retval Opt A optparse.Values object containing the parsed options +# @retval Args Target of build command +# +def MyOptionParser(): + Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]") + Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC','ARM'], dest="TargetArch", + help="ARCHS is one of list: IA32, X64, IPF, ARM or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.") + Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback, + help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.") + Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback, + help="Build the module specified by the INF file name argument.") + Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget", + help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.") + Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain", + help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.") + Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback, + help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.") + + Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback, + help="Build the platform using multi-threaded compiler. The value overrides target.txt's MAX_CONCURRENT_THREAD_NUMBER. Less than 2 will disable multi-thread builds.") + + Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback, + help="The name of the FDF file to use, which overrides the setting in the DSC file.") + Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[], + help="The name of FD to be generated. The name must be from [FD] section in FDF file.") + Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[], + help="The name of FV to be generated. The name must be from [FV] section in FDF file.") + + Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.") + Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.") + + Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.") + + # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]", + # help="Define global macro which can be used in DSC/DEC/INF files.") + + Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.") + Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.") + + Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode", + help="Make use of silent mode of (n)make.") + Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.") + Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\ + "including library instances selected, final dependency expression, "\ + "and warning messages, etc.") + Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.") + Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".") + + Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.") + Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['PCD','LIBRARY','FLASH','DEPEX','BUILD_FLAGS','FIXED_ADDRESS', 'EXECUTION_ORDER'], dest="ReportType", default=[], + help="Flags that control the type of build report to generate. Must be one of: [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS, EXECUTION_ORDER]. "\ + "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS]") + + (Opt, Args)=Parser.parse_args() + return (Opt, Args) + +## Tool entrance method +# +# This method mainly dispatch specific methods per the command line options. +# If no error found, return zero value so the caller of this tool can know +# if it's executed successfully or not. +# +# @retval 0 Tool was successful +# @retval 1 Tool failed +# +def Main(): + StartTime = time.time() + + # Initialize log system + EdkLogger.Initialize() + + # + # Parse the options and args + # + (Option, Target) = MyOptionParser() + GlobalData.gOptions = Option + GlobalData.gCaseInsensitive = Option.CaseInsensitive + + # Set log level + if Option.verbose != None: + EdkLogger.SetLevel(EdkLogger.VERBOSE) + elif Option.quiet != None: + EdkLogger.SetLevel(EdkLogger.QUIET) + elif Option.debug != None: + EdkLogger.SetLevel(Option.debug + 1) + else: + EdkLogger.SetLevel(EdkLogger.INFO) + + if Option.LogFile != None: + EdkLogger.SetLogFile(Option.LogFile) + + if Option.WarningAsError == True: + EdkLogger.SetWarningAsError() + + if platform.platform().find("Windows") >= 0: + GlobalData.gIsWindows = True + else: + GlobalData.gIsWindows = False + + EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[%s]\n" % platform.platform()) + ReturnCode = 0 + MyBuild = None + try: + if len(Target) == 0: + Target = "all" + elif len(Target) >= 2: + EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.", + ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget))) + else: + Target = Target[0].lower() + + if Target not in gSupportedTarget: + EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target, + ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget))) + + GlobalData.gGlobalDefines = ParseDefines(Option.Macros) + # + # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH + # + CheckEnvVariable() + Workspace = os.getenv("WORKSPACE") + # + # Get files real name in workspace dir + # + GlobalData.gAllFiles = Utils.DirCache(Workspace) + + WorkingDirectory = os.getcwd() + if not Option.ModuleFile: + FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf'))) + FileNum = len(FileList) + if FileNum >= 2: + EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory), + ExtraData="Please use '-m ' switch to choose one.") + elif FileNum == 1: + Option.ModuleFile = NormFile(FileList[0], Workspace) + + if Option.ModuleFile: + if os.path.isabs (Option.ModuleFile): + if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0: + Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace) + Option.ModuleFile = PathClass(Option.ModuleFile, Workspace) + ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False) + if ErrorCode != 0: + EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) + + if Option.PlatformFile != None: + if os.path.isabs (Option.PlatformFile): + if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0: + Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace) + Option.PlatformFile = PathClass(Option.PlatformFile, Workspace) + ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False) + if ErrorCode != 0: + EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) + + if Option.FdfFile != None: + if os.path.isabs (Option.FdfFile): + if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0: + Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace) + Option.FdfFile = PathClass(Option.FdfFile, Workspace) + ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False) + if ErrorCode != 0: + EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) + + MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile, + Option.TargetArch, Option.ToolChain, Option.BuildTarget, + Option.FdfFile, Option.RomImage, Option.FvImage, + None, Option.SilentMode, Option.ThreadNumber, + Option.SkipAutoGen, Option.Reparse, Option.SkuId, + Option.ReportFile, Option.ReportType) + MyBuild.Launch() + #MyBuild.DumpBuildData() + except FatalError, X: + if MyBuild != None: + # for multi-thread build exits safely + MyBuild.Relinquish() + if Option != None and Option.debug != None: + EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) + ReturnCode = X.args[0] + except Warning, X: + # error from Fdf parser + if MyBuild != None: + # for multi-thread build exits safely + MyBuild.Relinquish() + if Option != None and Option.debug != None: + EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) + else: + EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError = False) + ReturnCode = FORMAT_INVALID + except KeyboardInterrupt: + ReturnCode = ABORT_ERROR + if Option != None and Option.debug != None: + EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) + except: + if MyBuild != None: + # for multi-thread build exits safely + MyBuild.Relinquish() + + # try to get the meta-file from the object causing exception + Tb = sys.exc_info()[-1] + MetaFile = GlobalData.gProcessingFile + while Tb != None: + if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'): + MetaFile = Tb.tb_frame.f_locals['self'].MetaFile + Tb = Tb.tb_next + EdkLogger.error( + "\nbuild", + CODE_ERROR, + "Unknown fatal error when processing [%s]" % MetaFile, + ExtraData="\n(Please send email to edk2-buildtools-devel@lists.sourceforge.net for help, attaching following call stack trace!)\n", + RaiseError=False + ) + EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) + ReturnCode = CODE_ERROR + finally: + Utils.Progressor.Abort() + + if ReturnCode == 0: + Conclusion = "Done" + elif ReturnCode == ABORT_ERROR: + Conclusion = "Aborted" + else: + Conclusion = "Failed" + FinishTime = time.time() + BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime)))) + if MyBuild != None: + MyBuild.BuildReport.GenerateReport(BuildDuration) + MyBuild.Db.Close() + EdkLogger.SetLevel(EdkLogger.QUIET) + EdkLogger.quiet("\n- %s -\n%s [%s]" % (Conclusion, time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration)) + + return ReturnCode + +if __name__ == '__main__': + r = Main() + ## 0-127 is a safe return range, and 1 is a standard default error + if r < 0 or r > 127: r = 1 + sys.exit(r) + -- cgit v1.2.3