summaryrefslogtreecommitdiff
path: root/BaseTools/Source/Python/build
diff options
context:
space:
mode:
authorlgao4 <lgao4@6f19259b-4bc3-4df7-8a09-765794883524>2010-02-28 23:39:39 +0000
committerlgao4 <lgao4@6f19259b-4bc3-4df7-8a09-765794883524>2010-02-28 23:39:39 +0000
commit52302d4dee589a5df43a464420c9fe68ba83937d (patch)
tree2393f61b9e8975134e3cdfa0352d4c51a3b2ac8d /BaseTools/Source/Python/build
parentfe35c036354c4b6bf18c4699a45156f3965fae2a (diff)
downloadedk2-platforms-52302d4dee589a5df43a464420c9fe68ba83937d.tar.xz
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
Diffstat (limited to 'BaseTools/Source/Python/build')
-rw-r--r--BaseTools/Source/Python/build/BuildReport.py1423
-rw-r--r--BaseTools/Source/Python/build/__init__.py15
-rw-r--r--BaseTools/Source/Python/build/build.py3320
3 files changed, 3311 insertions, 1447 deletions
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 <XXX.h>
+ #
+ 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<BR>
+# 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/<sys> 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 <INF_FILE_PATH>' 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/<sys> 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 <INF_FILE_PATH>' 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)
+